diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml
index 051cfc483..d94c7d537 100644
--- a/.github/workflows/test-mingw.yml
+++ b/.github/workflows/test-mingw.yml
@@ -60,7 +60,7 @@ jobs:
pushd depends && ./install_extra_test_images.sh && popd
- name: Build Pillow
- run: CFLAGS="-coverage" python3 setup.py build_ext install
+ run: CFLAGS="-coverage" python3 -m pip install --global-option="build_ext" .
- name: Test Pillow
run: |
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9bbc426e2..414c7e94e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
os: [
- "macOS-latest",
+ "macos-latest",
"ubuntu-latest",
]
python-version: [
@@ -29,7 +29,7 @@ jobs:
# Include new variables for Codecov
- os: ubuntu-latest
codecov-flag: GHA_Ubuntu
- - os: macOS-latest
+ - os: macos-latest
codecov-flag: GHA_macOS
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/tidelift.yml b/.github/workflows/tidelift.yml
new file mode 100644
index 000000000..c2b8b3bda
--- /dev/null
+++ b/.github/workflows/tidelift.yml
@@ -0,0 +1,26 @@
+name: Tidelift Align
+on:
+ schedule:
+ - cron: "30 2 * * *" # daily at 02:30 UTC
+ push:
+ paths:
+ - ".github/workflows/tidelift.yml"
+ pull_request:
+ paths:
+ - ".github/workflows/tidelift.yml"
+ workflow_dispatch:
+
+jobs:
+ build:
+ if: github.repository_owner == 'python-pillow'
+ name: Run Tidelift to ensure approved open source packages are in use
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Scan
+ uses: tidelift/alignment-action@main
+ env:
+ TIDELIFT_API_KEY: ${{ secrets.TIDELIFT_API_KEY }}
+ TIDELIFT_ORGANIZATION: team/aclark4life
+ TIDELIFT_PROJECT: pillow
diff --git a/CHANGES.rst b/CHANGES.rst
index 3cf83a7a9..e0f347d58 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,12 @@ Changelog (Pillow)
9.0.0 (unreleased)
------------------
+- Added ImageShow support for xdg-open #5897
+ [m-shinder, radarhere]
+
+- Support 16-bit grayscale ImageQt conversion #5856
+ [cmbruns, radarhere]
+
- Convert subsequent GIF frames to RGB or RGBA #5857
[radarhere]
diff --git a/MANIFEST.in b/MANIFEST.in
index e9aaa8318..26f9401f2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,7 @@
include *.c
include *.h
include *.in
+include *.lock
include *.md
include *.py
include *.rst
@@ -9,6 +10,7 @@ include *.txt
include *.yaml
include LICENSE
include Makefile
+include Pipfile
include tox.ini
graft Tests
graft src
diff --git a/Makefile b/Makefile
index 546b91838..0dac63d39 100644
--- a/Makefile
+++ b/Makefile
@@ -50,16 +50,16 @@ help:
.PHONY: inplace
inplace: clean
- python3 setup.py develop build_ext --inplace
+ python3 -m pip install -e --global-option="build_ext" --global-option="--inplace" .
.PHONY: install
install:
- python3 setup.py install
+ python3 -m pip install .
python3 selftest.py
.PHONY: install-coverage
install-coverage:
- CFLAGS="-coverage -Werror=implicit-function-declaration" python3 setup.py build_ext install
+ CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install --global-option="build_ext" .
python3 selftest.py
.PHONY: debug
@@ -68,7 +68,7 @@ debug:
# for our stuff, kills optimization, and redirects to dev null so we
# see any build failures.
make clean > /dev/null
- CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
+ CFLAGS='-g -O0' python3 -m pip install --global-option="build_ext" . > /dev/null
.PHONY: install-req
install-req:
@@ -83,10 +83,10 @@ install-venv:
.PHONY: release-test
release-test:
$(MAKE) install-req
- python3 setup.py develop
+ python3 -m pip install -e .
python3 selftest.py
python3 -m pytest Tests
- python3 setup.py install
+ python3 -m pip install .
-rm dist/*.egg
-rmdir dist
python3 -m pytest -qq
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 000000000..1e611a63c
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,22 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+black = "*"
+check-manifest = "*"
+coverage = "*"
+defusedxml = "*"
+packaging = "*"
+markdown2 = "*"
+olefile = "*"
+pyroma = "*"
+pytest = "*"
+pytest-cov = "*"
+pytest-timeout = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.9"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 000000000..600b19050
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,324 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "e5cad23bf4187647d53b613a64dc4792b7064bf86b08dfb5737580e32943f54d"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.9"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "attrs": {
+ "hashes": [
+ "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
+ "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==21.2.0"
+ },
+ "black": {
+ "hashes": [
+ "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3",
+ "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"
+ ],
+ "index": "pypi",
+ "version": "==21.12b0"
+ },
+ "build": {
+ "hashes": [
+ "sha256:1aaadcd69338252ade4f7ec1265e1a19184bf916d84c9b7df095f423948cb89f",
+ "sha256:21b7ebbd1b22499c4dac536abc7606696ea4d909fd755e00f09f3c0f2c05e3c8"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==0.7.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
+ "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
+ ],
+ "version": "==2021.10.8"
+ },
+ "charset-normalizer": {
+ "hashes": [
+ "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721",
+ "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==2.0.9"
+ },
+ "check-manifest": {
+ "hashes": [
+ "sha256:365c94d65de4c927d9d8b505371d08ee19f9f369c86b9ac3db97c2754c827c95",
+ "sha256:56dadd260a9c7d550b159796d2894b6d0bcc176a94cbc426d9bb93e5e48d12ce"
+ ],
+ "index": "pypi",
+ "version": "==0.47"
+ },
+ "click": {
+ "hashes": [
+ "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
+ "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==8.0.3"
+ },
+ "coverage": {
+ "hashes": [
+ "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0",
+ "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd",
+ "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884",
+ "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48",
+ "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76",
+ "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0",
+ "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64",
+ "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685",
+ "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47",
+ "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d",
+ "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840",
+ "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f",
+ "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971",
+ "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c",
+ "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a",
+ "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de",
+ "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17",
+ "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4",
+ "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521",
+ "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57",
+ "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b",
+ "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282",
+ "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644",
+ "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475",
+ "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d",
+ "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da",
+ "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953",
+ "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2",
+ "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e",
+ "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c",
+ "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc",
+ "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64",
+ "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74",
+ "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617",
+ "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3",
+ "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d",
+ "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa",
+ "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739",
+ "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8",
+ "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8",
+ "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781",
+ "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58",
+ "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9",
+ "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c",
+ "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd",
+ "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e",
+ "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"
+ ],
+ "index": "pypi",
+ "version": "==6.2"
+ },
+ "defusedxml": {
+ "hashes": [
+ "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
+ "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
+ ],
+ "index": "pypi",
+ "version": "==0.7.1"
+ },
+ "docutils": {
+ "hashes": [
+ "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c",
+ "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.18.1"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
+ "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==3.3"
+ },
+ "iniconfig": {
+ "hashes": [
+ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
+ "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
+ ],
+ "version": "==1.1.1"
+ },
+ "markdown2": {
+ "hashes": [
+ "sha256:8f4ac8d9a124ab408c67361090ed512deda746c04362c36c2ec16190c720c2b0",
+ "sha256:91113caf23aa662570fe21984f08fe74f814695c0a0ea8e863a8b4c4f63f9f6e"
+ ],
+ "index": "pypi",
+ "version": "==2.4.2"
+ },
+ "mypy-extensions": {
+ "hashes": [
+ "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
+ "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
+ ],
+ "version": "==0.4.3"
+ },
+ "olefile": {
+ "hashes": [
+ "sha256:133b031eaf8fd2c9399b78b8bc5b8fcbe4c31e85295749bb17a87cba8f3c3964"
+ ],
+ "index": "pypi",
+ "version": "==0.46"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
+ "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
+ ],
+ "index": "pypi",
+ "version": "==21.3"
+ },
+ "pathspec": {
+ "hashes": [
+ "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
+ "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
+ ],
+ "version": "==0.9.0"
+ },
+ "pep517": {
+ "hashes": [
+ "sha256:931378d93d11b298cf511dd634cf5ea4cb249a28ef84160b3247ee9afb4e8ab0",
+ "sha256:dd884c326898e2c6e11f9e0b64940606a93eb10ea022a2e067959f3a110cf161"
+ ],
+ "version": "==0.12.0"
+ },
+ "platformdirs": {
+ "hashes": [
+ "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
+ "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==2.4.0"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
+ "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.0.0"
+ },
+ "py": {
+ "hashes": [
+ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
+ "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==1.11.0"
+ },
+ "pygments": {
+ "hashes": [
+ "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380",
+ "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==2.10.0"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4",
+ "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.0.6"
+ },
+ "pyroma": {
+ "hashes": [
+ "sha256:0fba67322913026091590e68e0d9e0d4fbd6420fcf34d315b2ad6985ab104d65",
+ "sha256:f8c181e0d5d292f11791afc18f7d0218a83c85cf64d6f8fb1571ce9d29a24e4a"
+ ],
+ "index": "pypi",
+ "version": "==3.2"
+ },
+ "pytest": {
+ "hashes": [
+ "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
+ "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
+ ],
+ "index": "pypi",
+ "version": "==6.2.5"
+ },
+ "pytest-cov": {
+ "hashes": [
+ "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6",
+ "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"
+ ],
+ "index": "pypi",
+ "version": "==3.0.0"
+ },
+ "pytest-timeout": {
+ "hashes": [
+ "sha256:e6f98b54dafde8d70e4088467ff621260b641eb64895c4195b6e5c8f45638112",
+ "sha256:fe9c3d5006c053bb9e062d60f641e6a76d6707aedb645350af9593e376fcc717"
+ ],
+ "index": "pypi",
+ "version": "==2.0.2"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
+ "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
+ "version": "==2.26.0"
+ },
+ "setuptools": {
+ "hashes": [
+ "sha256:5ec2bbb534ed160b261acbbdd1b463eb3cf52a8d223d96a8ab9981f63798e85c",
+ "sha256:75fd345a47ce3d79595b27bf57e6f49c2ca7904f3c7ce75f8a87012046c86b0b"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==60.0.0"
+ },
+ "toml": {
+ "hashes": [
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
+ ],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
+ "version": "==0.10.2"
+ },
+ "tomli": {
+ "hashes": [
+ "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f",
+ "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.2.3"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
+ "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==4.0.1"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
+ "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.7"
+ }
+ },
+ "develop": {}
+}
diff --git a/README.md b/README.md
index 205eada76..782b81f33 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,9 @@ As of 2019, Pillow development is
+