diff --git a/.appveyor.yml b/.appveyor.yml index da8c1708b..0cf10597a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,8 +25,6 @@ environment: - PYTHON: C:/Python36-x64 - PYTHON: C:/Python35 - PYTHON: C:/Python35-x64 - - PYTHON: C:/Python34 - - PYTHON: C:/Python34-x64 - PYTHON: C:/msys64/mingw32 EXECUTABLE: bin/python3 PIP_DIR: bin diff --git a/.gitignore b/.gitignore index 87ae69d97..861b801b5 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,9 @@ test_images # Sphinx documentation docs/_build/ +# viewdoc output +.long-description.html + # Vim cruft .*.swp diff --git a/.travis.yml b/.travis.yml index 61fdfec5b..9550eb8f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ matrix: dist: trusty - python: "2.7_with_system_site_packages" # For PyQt4 name: "2.7_with_system_site_packages Xenial" + services: xvfb - python: "2.7_with_system_site_packages" # For PyQt4 name: "2.7_with_system_site_packages Trusty" dist: trusty @@ -46,44 +47,46 @@ matrix: name: "3.5 Trusty PYTHONOPTIMIZE=2" dist: trusty env: PYTHONOPTIMIZE=2 - - python: '3.4' - name: "3.4 Trusty" - dist: trusty - - env: DOCKER="alpine" DOCKER_TAG="pytest" - - env: DOCKER="arch" DOCKER_TAG="pytest" # contains PyQt5 - - env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="pytest" - - env: DOCKER="ubuntu-xenial-amd64" DOCKER_TAG="pytest" - - env: DOCKER="debian-stretch-x86" DOCKER_TAG="pytest" - - env: DOCKER="centos-6-amd64" DOCKER_TAG="pytest" - - env: DOCKER="centos-7-amd64" DOCKER_TAG="pytest" - - env: DOCKER="amazon-1-amd64" DOCKER_TAG="pytest" - - env: DOCKER="amazon-2-amd64" DOCKER_TAG="pytest" + - python: "3.8-dev" + name: "3.8-dev Xenial" + - env: DOCKER="alpine" DOCKER_TAG="master" + - env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5 + - env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="master" + - env: DOCKER="ubuntu-xenial-amd64" DOCKER_TAG="master" + - env: DOCKER="debian-stretch-x86" DOCKER_TAG="master" + - env: DOCKER="centos-6-amd64" DOCKER_TAG="master" + - env: DOCKER="centos-7-amd64" DOCKER_TAG="master" + - env: DOCKER="amazon-1-amd64" DOCKER_TAG="master" + - env: DOCKER="amazon-2-amd64" DOCKER_TAG="master" - env: DOCKER="fedora-28-amd64" DOCKER_TAG="master" - env: DOCKER="fedora-29-amd64" DOCKER_TAG="master" services: - docker +before_install: + - if [ "$DOCKER" ]; then travis_retry docker pull pythonpillow/$DOCKER:$DOCKER_TAG; fi + install: - | if [ "$LINT" == "true" ]; then - pip install -U flake8 + pip install tox elif [ "$DOCKER" == "" ]; then .travis/install.sh; fi -before_install: - - if [ "$DOCKER" ]; then travis_retry docker pull pythonpillow/$DOCKER:$DOCKER_TAG; fi - before_script: # Qt needs a display for some of the tests, and it's only run on the system site packages install - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" +- | + if [ "$TRAVIS_JOB_NAME" == "2.7_with_system_site_packages Trusty" ]; then + export DISPLAY=:99.0 + sh -e /etc/init.d/xvfb start + fi script: - | if [ "$LINT" == "true" ]; then - flake8 --statistics --count + tox -e lint elif [ "$DOCKER" == "" ]; then .travis/script.sh elif [ "$DOCKER" ]; then diff --git a/CHANGES.rst b/CHANGES.rst index be0ac6de5..01fc87bf7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,9 +2,108 @@ Changelog (Pillow) ================== -5.4.0 (unreleased) +6.0.0 (unreleased) ------------------ +- Python 2.7 support will be removed in Pillow 7.0.0 #3682 + [hugovk] + +- Removed deprecated VERSION #3624 + [hugovk] + +- Fix 'BytesWarning: Comparison between bytes and string' in PdfDict #3580 + [jdufresne] + +- Do not resize in Image.thumbnail if already the destination size #3632 + [radarhere] + +- Replace .seek() magic numbers with io.SEEK_* constants #3572 + [jdufresne] + +- Make ContainerIO.isatty() return a bool, not int #3568 + [jdufresne] + +- Add support for I;16 modes for more transpose operations #3563 + [radarhere] + +- Deprecate support for PyQt4 and PySide #3655 + [hugovk, radarhere] + +- Add TIFF compression codecs: LZMA, Zstd, WebP #3555 + [cgohlke] + +- Fixed pickling of iTXt class with protocol > 1 #3537 + [radarhere] + +- _util.isPath returns True for pathlib.Path objects #3616 + [wbadart] + +- Remove unnecessary unittest.main() boilerplate from test files #3631 + [jdufresne] + +- Exif: Seek to IFD offset #3584 + [radarhere] + +- Deprecate PIL.*ImagePlugin.__version__ attributes #3628 + [jdufresne] + +- Docs: Add note about ImageDraw operations that exceed image bounds #3620 + [radarhere] + +- Allow for unknown PNG chunks after image data #3558 + [radarhere] + +- Changed EPS subprocess stdin from devnull to None #3611 + [radarhere] + +- Fix possible integer overflow #3609 + [cgohlke] + +- Catch BaseException for resource cleanup handlers #3574 + [jdufresne] + +- Improve pytest configuration to allow specific tests as CLI args #3579 + [jdufresne] + +- Drop support for Python 3.4 #3596 + [hugovk] + +- Remove deprecated PIL.OleFileIO #3598 + [hugovk] + +- Remove deprecated ImageOps undocumented functions #3599 + [hugovk] + +- Depends: Update libwebp to 1.0.2 #3602 + [radarhere] + +- Detect MIME types #3525 + [radarhere] + +5.4.1 (2019-01-06) +------------------ + +- File closing: Only close __fp if not fp #3540 + [radarhere] + +- Fix build for Termux #3529 + [pslacerda] + +- PNG: Detect MIME types #3525 + [radarhere] + +- PNG: Handle IDAT chunks after image end #3532 + [radarhere] + +5.4.0 (2019-01-01) +------------------ + +- Docs: Improved ImageChops documentation #3522 + [radarhere] + +- Allow RGB and RGBA values for P image putpixel #3519 + [radarhere] + - Add APNG extension to PNG plugin #3501 [pirate486743186, radarhere] diff --git a/LICENSE b/LICENSE index 80456a753..c106eeb1a 100644 --- a/LICENSE +++ b/LICENSE @@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is Pillow is the friendly PIL fork. It is - Copyright © 2010-2018 by Alex Clark and contributors + Copyright © 2010-2019 by Alex Clark and contributors Like PIL, Pillow is licensed under the open source PIL Software License: diff --git a/MANIFEST.in b/MANIFEST.in index 40b2ef5d7..809d0d667 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -22,6 +22,7 @@ exclude .coveragerc exclude .codecov.yml exclude .editorconfig exclude .landscape.yaml +exclude .readthedocs.yml exclude .travis exclude .travis/* exclude tox.ini diff --git a/RELEASING.md b/RELEASING.md index b5e548e06..f28e5f134 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -5,52 +5,52 @@ Released quarterly on the first day of January, April, July, October. * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154 -* [ ] Develop and prepare release in ``master`` branch. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch. +* [ ] Develop and prepare release in `master` branch. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `master` branch. * [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI. * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. * [ ] Create branch and tag for release e.g.: -``` - $ git branch 5.2.x - $ git tag 5.2.0 - $ git push --all - $ git push --tags -``` + ```bash + git branch 5.2.x + git tag 5.2.0 + git push --all + git push --tags + ``` * [ ] Create source distributions e.g.: -``` - $ make sdist -``` -* [ ] Create [binary distributions](#binary-distributions) -* [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*`` + ```bash + make sdist + ``` +* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions) +* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.0*` * [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) -* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), append `.dev0` to version identifier in `src/PIL/_version.py` +* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py` ## Point Release Released as needed for security, installation or critical bug fixes. -* [ ] Make necessary changes in ``master`` branch. +* [ ] Make necessary changes in `master` branch. * [ ] Update `CHANGES.rst`. -* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``5.2.x``. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``5.2.x``. * [ ] Check out release branch e.g.: -``` - git checkout -t remotes/origin/5.2.x -``` + ```bash + git checkout -t remotes/origin/5.2.x + ``` +* [ ] Cherry pick individual commits from `master` branch to release branch e.g. `5.2.x`. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`. * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Run pre-release check via `make release-test`. * [ ] Create tag for release e.g.: -``` - $ git tag 5.2.1 - $ git push --tags -``` + ```bash + git tag 5.2.1 + git push --tags + ``` * [ ] Create source distributions e.g.: -``` - $ make sdist -``` -* [ ] Create [binary distributions](#binary-distributions) + ```bash + make sdist + ``` +* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions) * [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) ## Embargoed Release @@ -64,41 +64,37 @@ Released as needed privately to individual vendors for critical security-related * [ ] Run pre-release check via `make release-test` * [ ] Amend any commits with the CVE # * [ ] On release date, tag and push to GitHub. -``` - git checkout 2.5.x - git tag 2.5.3 - git push origin 2.5.x - git push origin --tags -``` + ```bash + git checkout 2.5.x + git tag 2.5.3 + git push origin 2.5.x + git push origin --tags + ``` * [ ] Create source distributions e.g.: -``` - $ make sdist -``` -* [ ] Create [binary distributions](#binary-distributions) + ```bash + make sdist + ``` +* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions) * [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) ## Binary Distributions ### Windows -* [ ] Contact @cgohlke for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174. -* [ ] Download and extract tarball from @cgohlke and ``twine upload *``. +* [ ] Contact `@cgohlke` for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174. +* [ ] Download and extract tarball from `@cgohlke` and `twine upload *`. ### Mac and Linux * [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels): -``` - $ git clone https://github.com/python-pillow/pillow-wheels - $ cd pillow-wheels - $ git submodule init - $ git submodule update Pillow - $ cd Pillow - $ git fetch --all - $ git checkout [[release tag]] - $ cd .. - $ git commit -m "Pillow -> 5.2.0" Pillow - $ git push -``` + ```bash + git clone https://github.com/python-pillow/pillow-wheels + cd pillow-wheels + ./update-pillow-tag.sh [[release tag]] + ``` * [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/). - + ```bash + wget -m -A 'Pillow-*' \ + http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com + ``` ## Publicize Release @@ -106,4 +102,4 @@ Released as needed privately to individual vendors for critical security-related ## Documentation -* [ ] Make sure the default version for Read the Docs is the latest release version, i.e. ``5.2.0`` rather than ``latest`` e.g. https://pillow.readthedocs.io/en/5.2.x/ +* [ ] Make sure the default version for Read the Docs is the latest tagged release e.g. `d2d43879` (5.4.0) diff --git a/Tests/README.rst b/Tests/README.rst index 44f6f4792..d64a34bd7 100644 --- a/Tests/README.rst +++ b/Tests/README.rst @@ -13,28 +13,20 @@ Install:: Execution --------- -**If Pillow has been built in-place** - To run an individual test:: - python Tests/test_image.py + pytest Tests/test_image.py -Run all the tests from the root of the Pillow source distribution:: +Or:: - pytest -vx Tests - -Or with coverage:: - - pytest -vx --cov PIL --cov-report term Tests - coverage html - open htmlcov/index.html - -**If Pillow has been installed** - -To run an individual test:: - - pytest -k Tests/test_image.py + pytest -k test_image.py Run all the tests from the root of the Pillow source distribution:: pytest + +Or with coverage:: + + pytest --cov PIL --cov-report term + coverage html + open htmlcov/index.html diff --git a/Tests/__init__.py b/Tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 300017168..79427dca3 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper # Not running this test by default. No DOS against Travis CI. diff --git a/Tests/bench_get.py b/Tests/bench_get.py index e9afe1af5..68ac2c9a2 100644 --- a/Tests/bench_get.py +++ b/Tests/bench_get.py @@ -1,4 +1,4 @@ -import helper +from . import helper import timeit import sys diff --git a/Tests/check_fli_overflow.py b/Tests/check_fli_overflow.py index 9b370da3c..3f7c58015 100644 --- a/Tests/check_fli_overflow.py +++ b/Tests/check_fli_overflow.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image TEST_FILE = "Tests/images/fli_overflow.fli" diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index 0c41b6da1..7fa0663e8 100755 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from __future__ import division -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase import sys from PIL import Image diff --git a/Tests/check_j2k_leaks.py b/Tests/check_j2k_leaks.py index 5fafccbc0..d87b7f041 100755 --- a/Tests/check_j2k_leaks.py +++ b/Tests/check_j2k_leaks.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase import sys from PIL import Image from io import BytesIO diff --git a/Tests/check_j2k_overflow.py b/Tests/check_j2k_overflow.py index 1dd8db69f..f456ebb32 100644 --- a/Tests/check_j2k_overflow.py +++ b/Tests/check_j2k_overflow.py @@ -1,5 +1,5 @@ from PIL import Image -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase class TestJ2kEncodeOverflow(PillowTestCase): diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index c85f6f030..97a5650e0 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from io import BytesIO import sys diff --git a/Tests/check_large_memory.py b/Tests/check_large_memory.py index 5c8dc10f7..24d687ea6 100644 --- a/Tests/check_large_memory.py +++ b/Tests/check_large_memory.py @@ -1,6 +1,6 @@ import sys -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase # This test is not run automatically. # diff --git a/Tests/check_large_memory_numpy.py b/Tests/check_large_memory_numpy.py index e48d98367..b66988fd5 100644 --- a/Tests/check_large_memory_numpy.py +++ b/Tests/check_large_memory_numpy.py @@ -1,6 +1,6 @@ import sys -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase # This test is not run automatically. # diff --git a/Tests/check_libtiff_segfault.py b/Tests/check_libtiff_segfault.py index 6611648a5..f8c4a3090 100644 --- a/Tests/check_libtiff_segfault.py +++ b/Tests/check_libtiff_segfault.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image TEST_FILE = "Tests/images/libtiff_segfault.tif" diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 0cb775aed..9a446bf84 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image, PngImagePlugin, ImageFile from io import BytesIO import zlib diff --git a/Tests/helper.py b/Tests/helper.py index c16798dca..b47604a60 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -53,10 +53,6 @@ class PillowTestCase(unittest.TestCase): # holds last result object passed to run method: self.currentResult = None - # Nicer output for --verbose - def __str__(self): - return self.__class__.__name__ + "." + self._testMethodName - def run(self, result=None): self.currentResult = result # remember result for use later unittest.TestCase.run(self, result) # call superclass run method diff --git a/Tests/images/balloon.jpf b/Tests/images/balloon.jpf new file mode 100644 index 000000000..767eab5dd Binary files /dev/null and b/Tests/images/balloon.jpf differ diff --git a/Tests/images/exif-ifd-offset.jpg b/Tests/images/exif-ifd-offset.jpg new file mode 100644 index 000000000..e5dfc6807 Binary files /dev/null and b/Tests/images/exif-ifd-offset.jpg differ diff --git a/Tests/images/hopper.pnm b/Tests/images/hopper.pnm new file mode 100644 index 000000000..52368b2e2 Binary files /dev/null and b/Tests/images/hopper.pnm differ diff --git a/Tests/images/hopper_idat_after_image_end.png b/Tests/images/hopper_idat_after_image_end.png new file mode 100644 index 000000000..70b4a6400 Binary files /dev/null and b/Tests/images/hopper_idat_after_image_end.png differ diff --git a/Tests/images/hopper_unknown_pixel_mode.tif b/Tests/images/hopper_unknown_pixel_mode.tif new file mode 100644 index 000000000..89a8c5e17 Binary files /dev/null and b/Tests/images/hopper_unknown_pixel_mode.tif differ diff --git a/Tests/images/itxt_chunks.png b/Tests/images/itxt_chunks.png new file mode 100644 index 000000000..ca098440c Binary files /dev/null and b/Tests/images/itxt_chunks.png differ diff --git a/Tests/images/sugarshack_ifd_offset.mpo b/Tests/images/sugarshack_ifd_offset.mpo new file mode 100644 index 000000000..2dcac876f Binary files /dev/null and b/Tests/images/sugarshack_ifd_offset.mpo differ diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py index 62a9b5870..b9dd413f9 100644 --- a/Tests/test_000_sanity.py +++ b/Tests/test_000_sanity.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase import PIL import PIL.Image @@ -11,8 +11,6 @@ class TestSanity(PillowTestCase): # Make sure we have the binary extension PIL.Image.core.new("L", (100, 100)) - self.assertEqual(PIL.Image.VERSION[:3], '1.1') - # Create an image and do stuff with it. im = PIL.Image.new("1", (100, 100)) self.assertEqual((im.mode, im.size), ('1', (100, 100))) @@ -24,7 +22,3 @@ class TestSanity(PillowTestCase): PIL.Image.new("RGB", (100, 100)) PIL.Image.new("I", (100, 100)) PIL.Image.new("F", (100, 100)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_binary.py b/Tests/test_binary.py index 7e22e0f59..bf9ba1e5f 100644 --- a/Tests/test_binary.py +++ b/Tests/test_binary.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import _binary @@ -22,7 +22,3 @@ class TestBinary(PillowTestCase): self.assertEqual(_binary.o16be(65535), b'\xff\xff') self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff') - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index b3121e305..0e32c93dd 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -1,5 +1,5 @@ from __future__ import print_function -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image import os @@ -103,7 +103,3 @@ class TestBmpReference(PillowTestCase): os.path.join(base, 'g', 'pal4rle.bmp')) if f not in unsupported: self.fail("Unsupported Image %s: %s" % (f, msg)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index 2787dfc0d..b9939c5f2 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, ImageFilter @@ -215,7 +215,3 @@ class TestBoxBlur(PillowTestCase): passes=3, delta=1, ) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 6c8a185df..97035c793 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -3,7 +3,7 @@ from __future__ import division from array import array from PIL import Image, ImageFilter -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase try: import numpy @@ -519,7 +519,3 @@ class TestTransformColorLut3D(PillowTestCase): self.assertEqual(lut.table[0:16], [ 0.0, 0.0, 0.0, 0.5, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5]) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_core_resources.py b/Tests/test_core_resources.py index fd8fb4244..fe9f5ccc4 100644 --- a/Tests/test_core_resources.py +++ b/Tests/test_core_resources.py @@ -2,7 +2,7 @@ from __future__ import division, print_function import sys -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image @@ -179,7 +179,3 @@ class TestEnvVars(PillowTestCase): self.assert_warning( UserWarning, Image._apply_env_variables, {'PILLOW_BLOCKS_MAX': 'wat'}) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index 0a30e25f1..bc0bab525 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -83,7 +83,3 @@ class TestDecompressionCrop(PillowTestCase): for value in error_values: with self.assertRaises(Image.DecompressionBombError): im.crop(value) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_features.py b/Tests/test_features.py index 03ad0d4c9..15b5982ce 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import features @@ -63,7 +63,3 @@ class TestFeatures(PillowTestCase): module = "unsupported_module" # Act / Assert self.assertRaises(ValueError, features.check_module, module) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index e0530cb1f..59951a890 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -1,6 +1,6 @@ from PIL import Image -from helper import PillowTestCase, unittest +from .helper import PillowTestCase class TestFileBlp(PillowTestCase): @@ -18,7 +18,3 @@ class TestFileBlp(PillowTestCase): im = Image.open("Tests/images/blp/blp2_dxt1a.blp") target = Image.open("Tests/images/blp/blp2_dxt1a.png") self.assert_image_equal(im, target) - - -if __name__ == "__main__": - unittest.main() diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index bfd97016f..c51e2bb4e 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, BmpImagePlugin import io @@ -75,7 +75,3 @@ class TestFileBmp(PillowTestCase): im = BmpImagePlugin.DibImageFile('Tests/images/clipboard.dib') target = Image.open('Tests/images/clipboard_target.png') self.assert_image_equal(im, target) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index 08980a996..307029136 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import BufrStubImagePlugin, Image @@ -40,7 +40,3 @@ class TestFileBufrStub(PillowTestCase): # Act / Assert: stub cannot save without an implemented handler self.assertRaises(IOError, im.save, tmpfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_container.py b/Tests/test_file_container.py index 55228be0c..04f055464 100644 --- a/Tests/test_file_container.py +++ b/Tests/test_file_container.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ContainerIO @@ -16,7 +16,7 @@ class TestFileContainer(PillowTestCase): im = hopper() container = ContainerIO.ContainerIO(im, 0, 0) - self.assertEqual(container.isatty(), 0) + self.assertFalse(container.isatty()) def test_seek_mode_0(self): # Arrange @@ -123,7 +123,3 @@ class TestFileContainer(PillowTestCase): # Assert self.assertEqual(data, expected) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index 1628007ee..734c330e6 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, CurImagePlugin @@ -29,7 +29,3 @@ class TestFileCur(PillowTestCase): cur.fp.close() with open(no_cursors_file, "rb") as cur.fp: self.assertRaises(TypeError, cur._open) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 52c0e13f4..0da364648 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, DcxImagePlugin @@ -64,7 +64,3 @@ class TestFileDcx(PillowTestCase): # Act / Assert self.assertRaises(EOFError, im.seek, frame) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index ec975f7ec..af2524c4a 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -1,6 +1,6 @@ from io import BytesIO -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, DdsImagePlugin TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" @@ -110,7 +110,3 @@ class TestFileDds(PillowTestCase): im.load() self.assertRaises(IOError, short_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index dea795ec3..1f6025d69 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, EpsImagePlugin import io @@ -251,7 +251,3 @@ class TestFileEps(PillowTestCase): self.assertEqual(image.mode, "RGB") self.assertEqual(image.size, (460, 352)) self.assertEqual(image.format, "EPS") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fitsstub.py index d74e983ce..6cb2de110 100644 --- a/Tests/test_file_fitsstub.py +++ b/Tests/test_file_fitsstub.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import FitsStubImagePlugin, Image @@ -44,7 +44,3 @@ class TestFileFitsStub(PillowTestCase): self.assertRaises( IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 77eaebaa5..f67f0ada1 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, FliImagePlugin @@ -97,7 +97,3 @@ class TestFileFli(PillowTestCase): expected = Image.open("Tests/images/a_fli.png") self.assert_image_equal(im, expected) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 441a3e635..7283ba088 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase try: from PIL import FpxImagePlugin @@ -21,7 +21,3 @@ class TestFileFpx(PillowTestCase): ole_file = "Tests/images/test-ole-file.doc" self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, ole_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index 6b43244c9..07e29d7e0 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -14,7 +14,3 @@ class TestFileFtex(PillowTestCase): im = Image.open('Tests/images/ftex_dxt1.ftc') target = Image.open('Tests/images/ftex_dxt1.png') self.assert_image_similar(im, target.convert('RGBA'), 15) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 346605bd4..1eb66264d 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, GbrImagePlugin @@ -17,7 +17,3 @@ class TestFileGbr(PillowTestCase): target = Image.open('Tests/images/gbr.png') self.assert_image_equal(target, im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gd.py b/Tests/test_file_gd.py index b303369b4..67c9fba7b 100644 --- a/Tests/test_file_gd.py +++ b/Tests/test_file_gd.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import GdImageFile @@ -20,7 +20,3 @@ class TestFileGd(PillowTestCase): invalid_file = "Tests/images/flower.jpg" self.assertRaises(IOError, GdImageFile.open, invalid_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index a85655535..6a4b14d40 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, netpbm_available +from .helper import unittest, PillowTestCase, hopper, netpbm_available from PIL import Image, ImagePalette, GifImagePlugin @@ -654,7 +654,3 @@ class TestFileGif(PillowTestCase): self.assertEqual(im.tile[0][3][0], 11) # LZW bits # codec error prepatch im.load() - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gimpgradient.py b/Tests/test_file_gimpgradient.py index b29f6f13b..c89440239 100644 --- a/Tests/test_file_gimpgradient.py +++ b/Tests/test_file_gimpgradient.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import GimpGradientFile @@ -119,7 +119,3 @@ class TestImage(PillowTestCase): # load returns raw palette information self.assertEqual(len(palette[0]), 1024) self.assertEqual(palette[1], "RGBA") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py index 4ee5323bc..1ebac44e7 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL.GimpPaletteFile import GimpPaletteFile @@ -28,7 +28,3 @@ class TestImage(PillowTestCase): # Assert self.assertEqual(mode, "RGB") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index b3a6f1a5a..79e826945 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import GribStubImagePlugin, Image @@ -40,7 +40,3 @@ class TestFileGribStub(PillowTestCase): # Act / Assert: stub cannot save without an implemented handler self.assertRaises(IOError, im.save, tmpfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index 6cddd8d7b..598ef0c5c 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Hdf5StubImagePlugin, Image @@ -44,7 +44,3 @@ class TestFileHdf5Stub(PillowTestCase): self.assertRaises( IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 5e0f377c4..ac60731f9 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image, IcnsImagePlugin @@ -121,7 +121,3 @@ class TestFileIcns(PillowTestCase): with io.BytesIO(b'invalid\n') as fp: self.assertRaises(SyntaxError, IcnsImagePlugin.IcnsFile, fp) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index e1ec27932..f6244e086 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper import io from PIL import Image, IcoImagePlugin @@ -14,6 +14,7 @@ class TestFileIco(PillowTestCase): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (16, 16)) self.assertEqual(im.format, "ICO") + self.assertEqual(im.get_format_mimetype(), "image/x-icon") def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: @@ -82,7 +83,3 @@ class TestFileIco(PillowTestCase): self.assertEqual( im_saved.info['sizes'], {(16, 16), (24, 24), (32, 32), (48, 48)}) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index f3349b736..8e774ce0a 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImImagePlugin @@ -69,7 +69,3 @@ class TestFileIm(PillowTestCase): def test_number(self): self.assertEqual(1.2, ImImagePlugin.number("1.2")) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_iptc.py b/Tests/test_file_iptc.py index e08d994a2..83b735464 100644 --- a/Tests/test_file_iptc.py +++ b/Tests/test_file_iptc.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, IptcImagePlugin @@ -69,7 +69,3 @@ class TestFileIptc(PillowTestCase): # Assert self.assertEqual(mystdout.getvalue(), "61 62 63 \n") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 1485651c7..cbe894f34 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase, hopper -from helper import djpeg_available, cjpeg_available +from .helper import unittest, PillowTestCase, hopper +from .helper import djpeg_available, cjpeg_available from io import BytesIO import os @@ -581,6 +581,15 @@ class TestFileJpeg(PillowTestCase): # OSError for unidentified image. self.assertEqual(im.info.get("dpi"), (72, 72)) + def test_ifd_offset_exif(self): + # Arrange + # This image has been manually hexedited to have an IFD offset of 10, + # in contrast to normal 8 + im = Image.open("Tests/images/exif-ifd-offset.jpg") + + # Act / Assert + self.assertEqual(im._getexif()[306], '2017:03:13 23:03:09') + @unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") class TestFileCloseW32(PillowTestCase): @@ -602,7 +611,3 @@ class TestFileCloseW32(PillowTestCase): self.assertTrue(fp.closed) # this should not fail, as load should have closed the file. os.remove(tmpfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 8f027b651..4b34354e2 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, Jpeg2KImagePlugin from io import BytesIO @@ -39,6 +39,12 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(im.mode, 'RGB') self.assertEqual(im.size, (640, 480)) self.assertEqual(im.format, 'JPEG2000') + self.assertEqual(im.get_format_mimetype(), 'image/jp2') + + def test_jpf(self): + im = Image.open('Tests/images/balloon.jpf') + self.assertEqual(im.format, 'JPEG2000') + self.assertEqual(im.get_format_mimetype(), 'image/jpx') def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -204,7 +210,3 @@ class TestFileJpeg2k(PillowTestCase): # Assert self.assertEqual(p.image.size, (640, 480)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 6834b7256..56564ebde 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,5 +1,5 @@ from __future__ import print_function -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import features from PIL._util import py3 @@ -195,7 +195,7 @@ class TestFileLibTiff(LibTiffTestCase): im = Image.open('Tests/images/hopper_g4.tif') for tag in im.tag_v2: try: - del(core_items[tag]) + del core_items[tag] except KeyError: pass @@ -205,7 +205,7 @@ class TestFileLibTiff(LibTiffTestCase): # 4: "long", # 5: "rational", # 12: "double", - # type: dummy value + # Type: dummy value values = {2: 'test', 3: 1, 4: 2**20, @@ -223,7 +223,7 @@ class TestFileLibTiff(LibTiffTestCase): for _ in range(info.length)) # Extra samples really doesn't make sense in this application. - del(new_ifd[338]) + del new_ifd[338] out = self.tempfile("temp.tif") TiffImagePlugin.WRITE_LIBTIFF = True @@ -700,7 +700,3 @@ class TestFileLibTiff(LibTiffTestCase): im = Image.open(infile) self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index c402673d8..6b379718e 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -1,8 +1,6 @@ -from helper import unittest - from PIL import Image -from test_file_libtiff import LibTiffTestCase +from .test_file_libtiff import LibTiffTestCase class TestFileLibTiffSmall(LibTiffTestCase): @@ -46,7 +44,3 @@ class TestFileLibTiffSmall(LibTiffTestCase): self.assertEqual(im.size, (128, 128)) self._assert_noerr(im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 491d8ea03..e273faad9 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, McIdasImagePlugin @@ -28,7 +28,3 @@ class TestFileMcIdas(PillowTestCase): self.assertEqual(im.size, (1800, 400)) im2 = Image.open(saved_file) self.assert_image_equal(im, im2) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index f4059f9c9..3a7daa459 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, ImagePalette, features @@ -64,7 +64,3 @@ class TestFileMic(PillowTestCase): ole_file = "Tests/images/test-ole-file.doc" self.assertRaises(SyntaxError, MicImagePlugin.MicImageFile, ole_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 9f79d8cfa..45172472a 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from io import BytesIO from PIL import Image @@ -62,6 +62,14 @@ class TestFileMpo(PillowTestCase): self.assertEqual(mpinfo[45056], b'0100') self.assertEqual(mpinfo[45057], 2) + def test_mp_offset(self): + # This image has been manually hexedited to have an IFD offset of 10 + # in APP2 data, in contrast to normal 8 + im = Image.open("Tests/images/sugarshack_ifd_offset.mpo") + mpinfo = im._getmp() + self.assertEqual(mpinfo[45056], b'0100') + self.assertEqual(mpinfo[45057], 2) + def test_mp_attribute(self): for test_file in test_files: im = Image.open(test_file) @@ -142,7 +150,3 @@ class TestFileMpo(PillowTestCase): self.assertEqual(im.tell(), 1) jpg1 = self.frame_roundtrip(im) self.assert_image_similar(im, jpg1, 30) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 4aac88092..724fc78b1 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, MspImagePlugin @@ -12,11 +12,11 @@ YA_EXTRA_DIR = "Tests/images/msp" class TestFileMsp(PillowTestCase): def test_sanity(self): - file = self.tempfile("temp.msp") + test_file = self.tempfile("temp.msp") - hopper("1").save(file) + hopper("1").save(test_file) - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "1") self.assertEqual(im.size, (128, 128)) @@ -78,7 +78,3 @@ class TestFileMsp(PillowTestCase): # Act/Assert self.assertRaises(IOError, im.save, filename) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index b97a9b19e..a6491634a 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, imagemagick_available +from .helper import PillowTestCase, hopper, imagemagick_available import os.path @@ -52,7 +52,3 @@ class TestFilePalm(PillowTestCase): # Act / Assert self.assertRaises(IOError, self.helper_save_as_palm, mode) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_pcd.py b/Tests/test_file_pcd.py index 06fd33043..7296a303f 100644 --- a/Tests/test_file_pcd.py +++ b/Tests/test_file_pcd.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -16,7 +16,3 @@ class TestFilePcd(PillowTestCase): # target = hopper().resize((768,512)) # self.assert_image_similar(im, target, 10) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 415827e49..7608db47c 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImageFile, PcxImagePlugin @@ -13,6 +13,7 @@ class TestFilePcx(PillowTestCase): self.assertEqual(im2.mode, im.mode) self.assertEqual(im2.size, im.size) self.assertEqual(im2.format, "PCX") + self.assertEqual(im2.get_format_mimetype(), "image/x-pcx") self.assert_image_equal(im2, im) def test_sanity(self): @@ -128,7 +129,3 @@ class TestFilePcx(PillowTestCase): for x in range(5): px[x, 3] = 0 self._test_buffer_overflow(im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 57f1c2118..7b024426b 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, PdfParser import io import os @@ -266,7 +266,3 @@ class TestFilePdf(PillowTestCase): f = io.BytesIO(f.getvalue()) im.save(f, format="PDF", append=True) self.assertGreater(len(f.getvalue()), initial_size) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_pixar.py b/Tests/test_file_pixar.py index 3b00c710e..3b998c0b5 100644 --- a/Tests/test_file_pixar.py +++ b/Tests/test_file_pixar.py @@ -1,4 +1,4 @@ -from helper import hopper, unittest, PillowTestCase +from .helper import hopper, PillowTestCase from PIL import Image, PixarImagePlugin @@ -24,7 +24,3 @@ class TestFilePixar(PillowTestCase): self.assertRaises( SyntaxError, PixarImagePlugin.PixarImageFile, invalid_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index be52b3bd1..2b80bf357 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, PillowLeakTestCase, hopper +from .helper import unittest, PillowTestCase, PillowLeakTestCase, hopper from PIL import Image, ImageFile, PngImagePlugin from PIL._util import py3 @@ -86,6 +86,7 @@ class TestFilePng(PillowTestCase): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PNG") + self.assertEqual(im.get_format_mimetype(), 'image/png') hopper("1").save(test_file) Image.open(test_file) @@ -585,10 +586,17 @@ class TestFilePng(PillowTestCase): self.assertIsInstance(im.text, dict) ImageFile.LOAD_TRUNCATED_IMAGES = False + # Raises an EOFError in load_end + im = Image.open("Tests/images/hopper_idat_after_image_end.png") + self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + @unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation") def test_apng(self): im = Image.open("Tests/images/iss634.apng") + self.assertEqual(im.get_format_mimetype(), 'image/apng') + + # This also tests reading unknown PNG chunks (fcTL and fdAT) in load_end expected = Image.open("Tests/images/iss634.webp") self.assert_image_similar(im, expected, 0.23) @@ -618,7 +626,3 @@ class TestTruncatedPngPLeaks(PillowLeakTestCase): self._test_leak(core) finally: ImageFile.LOAD_TRUNCATED_IMAGES = False - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 937a9dc32..b9241e81d 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase, hopper from PIL import Image @@ -34,6 +34,16 @@ class TestFilePpm(PillowTestCase): reloaded = Image.open(f) self.assert_image_equal(im, reloaded) + def test_pnm(self): + im = Image.open('Tests/images/hopper.pnm') + self.assert_image_similar(im, hopper(), 0.0001) + + f = self.tempfile('temp.pnm') + im.save(f) + + reloaded = Image.open(f) + self.assert_image_equal(im, reloaded) + def test_truncated_file(self): path = self.tempfile('temp.pgm') with open(path, 'w') as f: @@ -49,7 +59,3 @@ class TestFilePpm(PillowTestCase): with self.assertRaises(IOError): Image.open('Tests/images/negative_size.ppm') - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index afc69694d..3b8a7add6 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -1,4 +1,4 @@ -from helper import hopper, unittest, PillowTestCase +from .helper import hopper, PillowTestCase from PIL import Image, PsdImagePlugin @@ -76,7 +76,3 @@ class TestImagePsd(PillowTestCase): im = Image.open("Tests/images/hopper_merged.psd") self.assertNotIn("icc_profile", im.info) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index aa33c9d93..1ad70e24f 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, SgiImagePlugin @@ -12,6 +12,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) self.assert_image_equal(im, hopper()) + self.assertEqual(im.get_format_mimetype(), 'image/rgb') def test_rgb16(self): test_file = "Tests/images/hopper16.rgb" @@ -26,6 +27,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) self.assert_image_similar(im, hopper('L'), 2) + self.assertEqual(im.get_format_mimetype(), 'image/sgi') def test_rgba(self): # Created with ImageMagick: @@ -35,6 +37,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) target = Image.open('Tests/images/transparent.png') self.assert_image_equal(im, target) + self.assertEqual(im.get_format_mimetype(), 'image/sgi') def test_rle(self): # Created with ImageMagick: @@ -86,7 +89,3 @@ class TestFileSgi(PillowTestCase): out = self.tempfile('temp.sgi') self.assertRaises(ValueError, im.save, out, format='sgi') - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 4612b36a1..f160272fd 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ImageSequence @@ -119,7 +119,3 @@ class TestImageSpider(PillowTestCase): for i, frame in enumerate(ImageSequence.Iterator(im)): if i > 1: self.fail("Non-stack DOS file test failed") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index 61cfdf367..65c00eea2 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, SunImagePlugin @@ -45,7 +45,3 @@ class TestFileSun(PillowTestCase): # im.save(target_file) with Image.open(target_path) as target: self.assert_image_equal(im, target) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index 7f29c5fc5..cd2b95778 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, TarIO @@ -34,7 +34,3 @@ class TestFileTar(PillowTestCase): def test_contextmanager(self): with TarIO.TarIO(TEST_TAR_FILE, 'hopper.jpg'): pass - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 77695f2d1..a8ab00d7f 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -2,7 +2,7 @@ import os from glob import glob from itertools import product -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -37,6 +37,8 @@ class TestFileTga(PillowTestCase): path_no_ext, origin, "rle" if rle else "raw") original_im = Image.open(tga_path) + self.assertEqual(original_im.format, "TGA") + self.assertEqual(original_im.get_format_mimetype(), "image/x-tga") if rle: self.assertEqual( original_im.info["compression"], "tga_rle") @@ -201,7 +203,3 @@ class TestFileTga(PillowTestCase): test_im.getchannel("A").getcolors()[0][0], num_transparent) self.assert_image_equal(im, test_im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 95a181363..9a4104b78 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -2,7 +2,7 @@ import logging from io import BytesIO import sys -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin from PIL._util import py3 @@ -220,6 +220,10 @@ class TestFileTiff(PillowTestCase): self.assertEqual( im.getextrema(), (-3.140936851501465, 3.140684127807617)) + def test_unknown_pixel_mode(self): + self.assertRaises( + IOError, Image.open, 'Tests/images/hopper_unknown_pixel_mode.tif') + def test_n_frames(self): for path, n_frames in [ ['Tests/images/multipage-lastframe.tif', 1], @@ -561,7 +565,3 @@ class TestFileTiffW32(PillowTestCase): # this should not fail, as load should have closed the file pointer, # and close should have closed the mmap os.remove(tmpfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 7407cc456..14ec3ab6c 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -1,7 +1,7 @@ import io import struct -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags from PIL.TiffImagePlugin import _limit_rational, IFDRational @@ -247,7 +247,3 @@ class TestFileTiffMetadata(PillowTestCase): # Should not raise ValueError. self.assert_warning(UserWarning, lambda: ifd[277]) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_wal.py b/Tests/test_file_wal.py index 13b1e3a2f..1e0a835d2 100644 --- a/Tests/test_file_wal.py +++ b/Tests/test_file_wal.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import WalImageFile @@ -17,7 +17,3 @@ class TestFileWal(PillowTestCase): self.assertEqual(im.format_description, "Quake2 Texture") self.assertEqual(im.mode, "P") self.assertEqual(im.size, (128, 128)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index a79ae8d9f..7e6fad93c 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, WebPImagePlugin @@ -172,7 +172,3 @@ class TestFileWebp(PillowTestCase): difference = sum([abs(original_value[i] - reread_value[i]) for i in range(0, 3)]) self.assertLess(difference, 5) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 85682e382..c868fa1df 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image @@ -115,7 +115,3 @@ class TestFileWebpAlpha(PillowTestCase): target = Image.open(file_path).convert("RGBA") self.assert_image_similar(image, target, 25.0) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 6b3dc1622..c751545c7 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -151,7 +151,3 @@ class TestFileWebpAnimation(PillowTestCase): self.assertEqual(im.info["duration"], dur) self.assertEqual(im.info["timestamp"], ts) ts -= dur - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index 4d9eff7f1..528c9177d 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -36,7 +36,3 @@ class TestFileWebpLossless(PillowTestCase): image.getdata() self.assert_image_equal(image, hopper(self.rgb_mode)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index c04443f46..402b6ce42 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -133,7 +133,3 @@ class TestFileWebpMetadata(PillowTestCase): self.assertEqual(iccp_data, image.info.get('icc_profile', None)) self.assertEqual(exif_data, image.info.get('exif', None)) self.assertEqual(xmp_data, image.info.get('xmp', None)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index 1a15a514f..146888491 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import WmfImagePlugin @@ -51,7 +51,3 @@ class TestFileWmf(PillowTestCase): for ext in [".wmf", ".emf"]: tmpfile = self.tempfile("temp"+ext) self.assertRaises(IOError, im.save, tmpfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index 398dae98c..fbcb30e90 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -60,7 +60,3 @@ class TestFileXbm(PillowTestCase): # Assert self.assertEqual(im.mode, '1') self.assertEqual(im.size, (128, 128)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 4fa3f743f..b57cda2e3 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, XpmImagePlugin @@ -33,7 +33,3 @@ class TestFileXpm(PillowTestCase): # Assert self.assertEqual(len(data), 16384) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_file_xvthumb.py b/Tests/test_file_xvthumb.py index d0256cabf..11672ebae 100644 --- a/Tests/test_file_xvthumb.py +++ b/Tests/test_file_xvthumb.py @@ -1,4 +1,4 @@ -from helper import hopper, unittest, PillowTestCase +from .helper import hopper, PillowTestCase from PIL import Image, XVThumbImagePlugin @@ -34,7 +34,3 @@ class TestFileXVThumb(PillowTestCase): # Act / Assert self.assertRaises(SyntaxError, XVThumbImagePlugin.XVThumbImageFile, invalid_file) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index 7c8fe579e..be49e818e 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import FontFile, BdfFontFile @@ -18,7 +18,3 @@ class TestFontBdf(PillowTestCase): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: self.assertRaises(SyntaxError, BdfFontFile.BdfFontFile, fp) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index f1ce44e6d..119012bc5 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -1,5 +1,5 @@ from __future__ import division -from helper import unittest, PillowLeakTestCase +from .helper import unittest, PillowLeakTestCase import sys from PIL import Image, features, ImageDraw, ImageFont @@ -31,7 +31,3 @@ class TestDefaultFontLeak(TestTTypeFontLeak): def test_leak(self): default_font = ImageFont.load_default() self._test_font(default_font) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 20869505f..d092634f3 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, FontFile, PcfFontFile from PIL import ImageFont, ImageDraw @@ -79,7 +79,3 @@ class TestFontPcf(PillowTestCase): # accept bytes instances in Py3. if py3: self._test_high_characters(message.encode('latin1')) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index 0385bd66f..3e1c00c9e 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL._util import py3 @@ -129,7 +129,3 @@ class TestFormatHSV(PillowTestCase): self.assert_image_similar(converted.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py index a243afe62..8a096e672 100644 --- a/Tests/test_format_lab.py +++ b/Tests/test_format_lab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -40,7 +40,3 @@ class TestFormatLab(PillowTestCase): k = i.getpixel((0, 0)) self.assertEqual(k, (128, 228, 128)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image.py b/Tests/test_image.py index 9618e1427..330048057 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image from PIL._util import py3 @@ -560,7 +560,3 @@ class TestRegistry(PillowTestCase): 'DoesNotExist', ('args',), extra=('extra',)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index ae9692b39..937584cff 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, on_appveyor +from .helper import unittest, PillowTestCase, hopper, on_appveyor from PIL import Image import sys @@ -175,6 +175,12 @@ class TestImageGetPixel(AccessTest): self.check(mode, 2**15+1) self.check(mode, 2**16-1) + def test_p_putpixel_rgb_rgba(self): + for color in [(255, 0, 0), (255, 0, 0, 255)]: + im = Image.new("P", (1, 1), 0) + im.putpixel((0, 0), color) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) + @unittest.skipIf(cffi is None, "No cffi") class TestCffiPutPixel(TestImagePutPixel): @@ -294,13 +300,18 @@ class TestCffi(AccessTest): # pixels can contain garbage if image is released self.assertEqual(px[i, 0], 0) + def test_p_putpixel_rgb_rgba(self): + for color in [(255, 0, 0), (255, 0, 0, 255)]: + im = Image.new("P", (1, 1), 0) + access = PyAccess.new(im, False) + access.putpixel((0, 0), color) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) + class TestEmbeddable(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith('win32') or - sys.version_info[:2] == (3, 4) or - on_appveyor(), # failing on appveyor when run from - # subprocess, not from shell - "requires Python 2.7 or >=3.5 for Windows") + on_appveyor(), + "Failing on AppVeyor when run from subprocess, not from shell") def test_embeddable(self): import subprocess import ctypes @@ -355,7 +366,3 @@ int main(int argc, char* argv[]) process = subprocess.Popen(['embed_pil.exe'], env=env) process.communicate() self.assertEqual(process.returncode, 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 7a86a3e54..cf104217a 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -55,7 +55,3 @@ class TestImageArray(PillowTestCase): self.assertEqual(test("RGB"), ("RGB", (128, 100), True)) self.assertEqual(test("RGBA"), ("RGBA", (128, 100), True)) self.assertEqual(test("RGBX"), ("RGBA", (128, 100), True)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index e57ae4305..1ba1794eb 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -230,7 +230,3 @@ class TestImageConvert(PillowTestCase): # Assert # No change self.assert_image_equal(converted_im, im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py index bb1246a73..19679fe22 100644 --- a/Tests/test_image_copy.py +++ b/Tests/test_image_copy.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -40,7 +40,3 @@ class TestImageCopy(PillowTestCase): out = im.copy() self.assertEqual(out.mode, im.mode) self.assertEqual(out.size, im.size) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index fe92dd865..45123a631 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -102,7 +102,3 @@ class TestImageCrop(PillowTestCase): cropped = im.crop((10, 10, 20, 20)) self.assertEqual(cropped.size, (10, 10)) self.assertEqual(cropped.getdata()[2], (0, 0, 0)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_draft.py b/Tests/test_image_draft.py index 18982fc99..9ebd68945 100644 --- a/Tests/test_image_draft.py +++ b/Tests/test_image_draft.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, fromstring, tostring +from .helper import PillowTestCase, fromstring, tostring from PIL import Image @@ -69,7 +69,3 @@ class TestImageDraft(PillowTestCase): im = self.draft_roundtrip('L', (128, 128), None, (64, 64)) im.draft(None, (64, 64)) im.load() - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 5e732b345..a91387f81 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImageFilter @@ -136,7 +136,3 @@ class TestImageFilter(PillowTestCase): Image.merge(mode, source[:len(mode)]).filter(kernel), Image.merge(mode, reference[:len(mode)]), ) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_frombytes.py b/Tests/test_image_frombytes.py index 2d48bb6b8..3b224e71a 100644 --- a/Tests/test_image_frombytes.py +++ b/Tests/test_image_frombytes.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -13,7 +13,3 @@ class TestImageFromBytes(PillowTestCase): def test_not_implemented(self): self.assertRaises(NotImplementedError, Image.fromstring) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index 2e5d95aa7..8a29a2715 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQtTestCase +from .helper import PillowTestCase, hopper +from .test_imageqt import PillowQtTestCase from PIL import ImageQt, Image @@ -42,7 +42,3 @@ class TestFromQImage(PillowQtTestCase, PillowTestCase): def test_sanity_p(self): for im in self.files_to_test: self.roundtrip(im.convert('P')) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getbands.py b/Tests/test_image_getbands.py index 5eecbf044..6d79bf280 100644 --- a/Tests/test_image_getbands.py +++ b/Tests/test_image_getbands.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -18,7 +18,3 @@ class TestImageGetBands(PillowTestCase): Image.new("CMYK", (1, 1)).getbands(), ("C", "M", "Y", "K")) self.assertEqual( Image.new("YCbCr", (1, 1)).getbands(), ("Y", "Cb", "Cr")) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getbbox.py b/Tests/test_image_getbbox.py index f29032143..9bf39752b 100644 --- a/Tests/test_image_getbbox.py +++ b/Tests/test_image_getbbox.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -37,7 +37,3 @@ class TestImageGetBbox(PillowTestCase): im.paste(255, (-10, -10, 110, 110)) self.assertEqual(im.getbbox(), (0, 0, 100, 100)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py index ca7a9d93d..aa2a40976 100644 --- a/Tests/test_image_getcolors.py +++ b/Tests/test_image_getcolors.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageGetColors(PillowTestCase): @@ -65,7 +65,3 @@ class TestImageGetColors(PillowTestCase): A = im.getcolors(maxcolors=16) A.sort() self.assertEqual(A, expected) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index de502065b..fe8f9adcd 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageGetData(PillowTestCase): @@ -27,7 +27,3 @@ class TestImageGetData(PillowTestCase): self.assertEqual(getdata("RGBA"), ((11, 13, 52, 255), 960, 960)) self.assertEqual(getdata("CMYK"), ((244, 242, 203, 0), 960, 960)) self.assertEqual(getdata("YCbCr"), ((16, 147, 123), 960, 960)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py index f002419da..1689744af 100644 --- a/Tests/test_image_getextrema.py +++ b/Tests/test_image_getextrema.py @@ -1,5 +1,5 @@ from PIL import Image -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageGetExtrema(PillowTestCase): @@ -27,7 +27,3 @@ class TestImageGetExtrema(PillowTestCase): self.assertEqual(im.mode, 'I;16') extrema = im.getextrema() self.assertEqual(extrema, (106, 285)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index 1452e584e..6d3682caf 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL._util import py3 @@ -12,7 +12,3 @@ class TestImageGetIm(PillowTestCase): self.assertIn("PyCapsule", type_repr) self.assertIsInstance(im.im.id, int) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py index 01a6ac7ad..98f8142dc 100644 --- a/Tests/test_image_getpalette.py +++ b/Tests/test_image_getpalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageGetPalette(PillowTestCase): @@ -18,7 +18,3 @@ class TestImageGetPalette(PillowTestCase): self.assertIsNone(palette("RGBA")) self.assertIsNone(palette("CMYK")) self.assertIsNone(palette("YCbCr")) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_getprojection.py b/Tests/test_image_getprojection.py index 9d3f2d9ed..85d40e859 100644 --- a/Tests/test_image_getprojection.py +++ b/Tests/test_image_getprojection.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -30,7 +30,3 @@ class TestImageGetProjection(PillowTestCase): im.paste(255, (2, 4, 8, 6)) self.assertEqual(im.getprojection()[0], [0, 0, 1, 1, 1, 1, 1, 1, 0, 0]) self.assertEqual(im.getprojection()[1], [0, 0, 0, 0, 1, 1, 0, 0, 0, 0]) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_histogram.py b/Tests/test_image_histogram.py index 892e89328..0a023cd69 100644 --- a/Tests/test_image_histogram.py +++ b/Tests/test_image_histogram.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageHistogram(PillowTestCase): @@ -18,7 +18,3 @@ class TestImageHistogram(PillowTestCase): self.assertEqual(histogram("RGBA"), (1024, 0, 16384)) self.assertEqual(histogram("CMYK"), (1024, 0, 16384)) self.assertEqual(histogram("YCbCr"), (768, 0, 1908)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_load.py b/Tests/test_image_load.py index 88b6e9b5c..d8f323eb8 100644 --- a/Tests/test_image_load.py +++ b/Tests/test_image_load.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -28,7 +28,3 @@ class TestImageLoad(PillowTestCase): os.fstat(fn) self.assertRaises(OSError, os.fstat, fn) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py index 0596af397..26266611d 100644 --- a/Tests/test_image_mode.py +++ b/Tests/test_image_mode.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -51,7 +51,3 @@ class TestImageMode(PillowTestCase): check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index e782008a7..5586e8618 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, cached_property +from .helper import PillowTestCase, cached_property from PIL import Image @@ -250,7 +250,3 @@ class TestImagingPaste(PillowTestCase): im.copy().paste(im2) im.copy().paste(im2, (0, 0)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 977e98e83..90498fcff 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImagePoint(PillowTestCase): @@ -38,7 +38,3 @@ class TestImagePoint(PillowTestCase): def test_f_mode(self): im = hopper('F') self.assertRaises(ValueError, im.point, None) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py index 823e0612f..7b66b8833 100644 --- a/Tests/test_image_putalpha.py +++ b/Tests/test_image_putalpha.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -44,7 +44,3 @@ class TestImagePutAlpha(PillowTestCase): self.assertFalse(im.readonly) self.assertEqual(im.mode, 'RGBA') self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 2008c1307..1b57e38b7 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from array import array import sys @@ -83,7 +83,3 @@ class TestImagePutData(PillowTestCase): im.putdata(arr) self.assertEqual(len(im.getdata()), len(arr)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index e173f0000..34b585f55 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import ImagePalette @@ -28,7 +28,3 @@ class TestImagePutPalette(PillowTestCase): im.putpalette(ImagePalette.random()) im.putpalette(ImagePalette.sepia()) im.putpalette(ImagePalette.wedge()) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 5b10d2de3..2f0b65758 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -46,7 +46,3 @@ class TestImageQuantize(PillowTestCase): converted = image.quantize() self.assert_image(converted, 'P', converted.size) self.assert_image_similar(converted.convert('RGB'), image, 1) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 46a45833e..9ab8280ed 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -2,7 +2,7 @@ from __future__ import division, print_function from contextlib import contextmanager -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image, ImageDraw @@ -544,7 +544,3 @@ class CoreResampleBoxTest(PillowTestCase): except AssertionError: print('>>>', size, box, flt) raise - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 535f1d77e..c47c7317b 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -3,7 +3,7 @@ Tests for resize functionality. """ from itertools import permutations -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -115,7 +115,3 @@ class TestImageResize(PillowTestCase): # Test unknown resampling filter im = hopper() self.assertRaises(ValueError, im.resize, (10, 10), "unknown") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index e788e722f..05043cd06 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -123,7 +123,3 @@ class TestImageRotate(PillowTestCase): im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255)) corner = im.getpixel((0, 0)) self.assertEqual(corner, (255, 0, 0, 255)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 6f312ff80..2d97dabc2 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -59,7 +59,3 @@ class TestImageSplit(PillowTestCase): self.assertEqual(split_open("RGB"), 3) if 'zip_encoder' in codecs: self.assertEqual(split_open("RGBA"), 4) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 6b92dbb24..fbadf50cf 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -1,4 +1,5 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper +from PIL import Image class TestImageThumbnail(PillowTestCase): @@ -36,6 +37,13 @@ class TestImageThumbnail(PillowTestCase): im.thumbnail((100, 100)) self.assert_image(im, im.mode, (100, 100)) + def test_no_resize(self): + # Check that draft() can resize the image to the destination size + im = Image.open("Tests/images/hopper.jpg") + im.draft(None, (64, 64)) + self.assertEqual(im.size, (64, 64)) -if __name__ == '__main__': - unittest.main() + # Test thumbnail(), where only draft() is necessary to resize the image + im = Image.open("Tests/images/hopper.jpg") + im.thumbnail((64, 64)) + self.assert_image(im, im.mode, (64, 64)) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py index f93fce6a8..c5c06ba01 100644 --- a/Tests/test_image_tobitmap.py +++ b/Tests/test_image_tobitmap.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, fromstring +from .helper import PillowTestCase, hopper, fromstring class TestImageToBitmap(PillowTestCase): @@ -13,7 +13,3 @@ class TestImageToBitmap(PillowTestCase): self.assertIsInstance(bitmap, bytes) self.assert_image_equal(im1, fromstring(bitmap)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py index f5a738695..2bfee2da3 100644 --- a/Tests/test_image_tobytes.py +++ b/Tests/test_image_tobytes.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestImageToBytes(PillowTestCase): @@ -6,7 +6,3 @@ class TestImageToBytes(PillowTestCase): def test_sanity(self): data = hopper().tobytes() self.assertIsInstance(data, bytes) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 313e7ad09..ae1cf6e3d 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -1,6 +1,6 @@ import math -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -271,7 +271,3 @@ class TestImageTransformAffine(PillowTestCase): class TestImageTransformPerspective(TestImageTransformAffine): # Repeat all tests for AFFINE transformations with PERSPECTIVE transform = Image.PERSPECTIVE - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index a6b1191db..ad8c77126 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,5 +1,5 @@ -import helper -from helper import unittest, PillowTestCase +from . import helper +from .helper import PillowTestCase from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270, TRANSPOSE, TRANSVERSE) @@ -7,10 +7,9 @@ from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, class TestImageTranspose(PillowTestCase): - hopper = { - 'L': helper.hopper('L').crop((0, 0, 121, 127)).copy(), - 'RGB': helper.hopper('RGB').crop((0, 0, 121, 127)).copy(), - } + hopper = {mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() for mode in [ + 'L', 'RGB', 'I;16', 'I;16L', 'I;16B' + ]} def test_flip_left_right(self): def transpose(mode): @@ -25,7 +24,7 @@ class TestImageTranspose(PillowTestCase): self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) - for mode in ("L", "RGB"): + for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"): transpose(mode) def test_flip_top_bottom(self): @@ -41,7 +40,7 @@ class TestImageTranspose(PillowTestCase): self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) - for mode in ("L", "RGB"): + for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"): transpose(mode) def test_rotate_90(self): @@ -73,7 +72,7 @@ class TestImageTranspose(PillowTestCase): self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) - for mode in ("L", "RGB"): + for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"): transpose(mode) def test_rotate_270(self): @@ -146,7 +145,3 @@ class TestImageTranspose(PillowTestCase): im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM)) self.assert_image_equal( im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 06febc6d2..8aa784cdd 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ImageChops @@ -373,7 +373,3 @@ class TestImageChops(PillowTestCase): table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) self.assertEqual( table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index b4d53a2df..cdd6f00c3 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper import datetime from PIL import Image, ImageMode @@ -514,7 +514,3 @@ class TestImageCms(PillowTestCase): self.assert_image_equal(test_image.convert(dst_format[2]), reference_image) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index 1ea37544b..cb9c9843c 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image from PIL import ImageColor @@ -192,7 +192,3 @@ class TestImageColor(PillowTestCase): self.assertEqual( (162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) Image.new("LA", (1, 1), "white") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 7f2accdcb..bceb0e3d4 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,6 +1,6 @@ import os.path -from helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper from PIL import Image, ImageColor, ImageDraw BLACK = (0, 0, 0) @@ -762,7 +762,3 @@ class TestImageDraw(PillowTestCase): expected = ("Tests/images/imagedraw_outline" "_{}_{}.png".format(operation, mode)) self.assert_image_similar(im, Image.open(expected), 1) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index c5faeb616..97033c8a7 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -1,6 +1,6 @@ import os.path -from helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper, unittest from PIL import Image, ImageDraw2, features BLACK = (0, 0, 0) @@ -223,7 +223,3 @@ class TestImageDraw(PillowTestCase): # Assert self.assert_image_equal(im, im2) - - -if __name__ == "__main__": - unittest.main() diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 54288f4db..0e4e8c4f3 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ImageEnhance @@ -48,7 +48,3 @@ class TestImageEnhance(PillowTestCase): self._check_alpha( getattr(ImageEnhance, op)(original).enhance(amount), original, op, amount) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 4f81df12e..5853fb28f 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, fromstring, tostring +from .helper import PillowTestCase, hopper, fromstring, tostring from io import BytesIO @@ -233,7 +233,3 @@ class TestPyDecoder(PillowTestCase): im = MockImageFile(buf) self.assertIsNone(im.format) self.assertIsNone(im.get_format_mimetype()) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 82108638c..be8667211 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image, ImageDraw, ImageFont, features from io import BytesIO @@ -529,7 +529,3 @@ class TestImageFont(PillowTestCase): @unittest.skipUnless(HAS_RAQM, "Raqm not Available") class TestImageFont_RaqmLayout(TestImageFont): LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index 8376728a2..eb44957e4 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image, ImageFont, ImageDraw @@ -34,7 +34,3 @@ class TestImageFontBitmap(PillowTestCase): draw_outline.text((0, size_final[1] - size_outline[1]), text, fill=(0, 0, 0), font=font_outline) self.assert_image_similar(im_bitmap, im_outline, 20) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 04432b14f..d23f6d86f 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image, ImageDraw, ImageFont, features @@ -130,9 +130,3 @@ class TestImagecomplextext(PillowTestCase): target_img = Image.open(target) self.assert_image_similar(im, target_img, .5) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 10e150fc4..a2e7a028d 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase import sys import subprocess @@ -53,7 +53,3 @@ class TestImageGrabImport(PillowTestCase): self.assertIsInstance(exception, ImportError) self.assertEqual(str(exception), "ImageGrab is macOS and Windows only") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index efbc91787..8273b63c1 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -1,5 +1,5 @@ from __future__ import print_function -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image from PIL import ImageMath @@ -182,7 +182,3 @@ class TestImageMath(PillowTestCase): pixel(ImageMath.eval("notequal(B, A)", A=A, B=B)), "I 1") self.assertEqual( pixel(ImageMath.eval("notequal(A, Z)", A=A, Z=Z)), "I 1") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index dceadebf4..0cf15bd6c 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -1,5 +1,5 @@ # Test the ImageMorphology functionality -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImageMorph, _imagingmorph @@ -321,7 +321,3 @@ class MorphTests(PillowTestCase): # Should not raise _imagingmorph.match(bytes(lut), iml.im.id) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 70b1659d9..c5e48c431 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import ImageOps from PIL import Image @@ -218,7 +218,3 @@ class TestImageOps(PillowTestCase): (0, 127, 0), threshold=1, msg='white test pixel incorrect') - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 20758e9f8..a867e5430 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -1,7 +1,6 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image -from PIL import ImageOps from PIL import ImageFilter im = Image.open("Tests/images/hopper.ppm") @@ -10,31 +9,6 @@ snakes = Image.open("Tests/images/color_snakes.png") class TestImageOpsUsm(PillowTestCase): - def test_ops_api(self): - - i = self.assert_warning(DeprecationWarning, - ImageOps.gaussian_blur, im, 2.0) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, ImageOps.box_blur, im, 1) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, ImageOps.gblur, im, 2.0) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, - ImageOps.unsharp_mask, im, 2.0, 125, 8) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, - ImageOps.usm, im, 2.0, 125, 8) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - def test_filter_api(self): test_filter = ImageFilter.GaussianBlur(2.0) @@ -99,7 +73,3 @@ class TestImageOpsUsm(PillowTestCase): self.assertTrue(236 <= gp(8, 5)[2] <= 239) self.assertTrue(236 <= gp(8, 6)[2] <= 239) self.assertTrue(236 <= gp(8, 7)[1] <= 239) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 3b7087d7a..e4b5b7f72 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import ImagePalette, Image @@ -134,7 +134,3 @@ class TestImagePalette(PillowTestCase): def test_invalid_palette(self): self.assertRaises(IOError, ImagePalette.load, "Tests/images/hopper.jpg") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index 8df71121d..8cf88b7c1 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import ImagePath, Image from PIL._util import py3 @@ -94,7 +94,3 @@ class evil: def __setitem__(self, i, x): self.corrupt[i] = struct.unpack("dd", x) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index cecb1b5ee..bd93828ef 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -1,7 +1,16 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper -from PIL import ImageQt +import warnings +deprecated = False +with warnings.catch_warnings(): + warnings.filterwarnings("error", category=DeprecationWarning) + try: + from PIL import ImageQt + except DeprecationWarning: + deprecated = True + warnings.filterwarnings("ignore", category=DeprecationWarning) + from PIL import ImageQt if ImageQt.qt_is_installed: from PIL.ImageQt import qRgba @@ -79,6 +88,5 @@ class TestImageQt(PillowQtTestCase, PillowTestCase): for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): ImageQt.ImageQt(hopper(mode)) - -if __name__ == '__main__': - unittest.main() + def test_deprecated(self): + self.assertEqual(ImageQt.qt_version in ["4", "side"], deprecated) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 500a2e60a..9fbf3fed8 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImageSequence, TiffImagePlugin @@ -69,7 +69,3 @@ class TestImageSequence(PillowTestCase): im.seek(0) color2 = im.getpalette()[0:3] self.assertEqual(color1, color2) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 244c18d91..899c057d6 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ImageShow @@ -44,7 +44,3 @@ class TestImageShow(PillowTestCase): def test_viewers(self): for viewer in ImageShow._viewers: viewer.get_command('test.jpg') - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py index 77eb0aac1..c2580a1b1 100644 --- a/Tests/test_imagestat.py +++ b/Tests/test_imagestat.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image from PIL import ImageStat @@ -55,7 +55,3 @@ class TestImageStat(PillowTestCase): self.assertEqual(st.rms[0], 128) self.assertEqual(st.var[0], 0) self.assertEqual(st.stddev[0], 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 667d18e8f..a6a4dd4ea 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import Image from PIL._util import py3 @@ -87,7 +87,3 @@ class TestImageTk(PillowTestCase): # reloaded = ImageTk.getimage(im_tk) # self.assert_image_equal(reloaded, im) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 70bf28247..16d681f2c 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import unittest, PillowTestCase, hopper from PIL import ImageWin import sys @@ -106,7 +106,3 @@ class TestImageWinDib(PillowTestCase): # Assert # Confirm they're the same self.assertEqual(dib1.tobytes(), dib2.tobytes()) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index dc78b655c..64f921916 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image, ImageWin import sys @@ -107,6 +107,3 @@ if sys.platform.startswith('win32'): DeleteDC(hdc) Image.open(BytesIO(bitmap)).save(opath) - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py index aefee2e08..466c43f88 100644 --- a/Tests/test_lib_image.py +++ b/Tests/test_lib_image.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 33590eeb4..543d151ac 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -1,6 +1,6 @@ import sys -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -620,7 +620,3 @@ class TestLibUnpack(PillowTestCase): self.assertRaises(ValueError, self.assert_unpack, "L", "L", 0, 0) self.assertRaises(ValueError, self.assert_unpack, "RGB", "RGB", 2, 0) self.assertRaises(ValueError, self.assert_unpack, "CMYK", "CMYK", 2, 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_locale.py b/Tests/test_locale.py index 5aef8427b..d40019e59 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -1,5 +1,5 @@ from __future__ import print_function -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import Image @@ -32,7 +32,3 @@ class TestLocale(PillowTestCase): except locale.Error: unittest.skip('Polish locale not available') Image.open(path) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_map.py b/Tests/test_map.py index 8e3916d27..2eeb1fc5f 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -1,4 +1,4 @@ -from helper import PillowTestCase, unittest +from .helper import PillowTestCase, unittest import sys from PIL import Image @@ -23,7 +23,3 @@ class TestMap(PillowTestCase): im.load() Image.MAX_IMAGE_PIXELS = max_pixels - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index d51847199..80730a312 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import Image @@ -105,7 +105,3 @@ class TestModeI16(PillowTestCase): self.verify(im.convert("I;16B")) self.verify(im.convert("I;16B").convert("L")) self.verify(im.convert("I;16B").convert("I")) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 03643ac1e..c9c3b0f1e 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,6 +1,6 @@ from __future__ import print_function -from helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper, unittest from PIL import Image try: @@ -208,7 +208,3 @@ class TestNumpy(PillowTestCase): # Act/Assert self.assert_warning(None, lambda: array(im)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_pdfparser.py b/Tests/test_pdfparser.py index ec849b17d..da69d258d 100644 --- a/Tests/test_pdfparser.py +++ b/Tests/test_pdfparser.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, \ PdfDict, PdfFormatError, PdfName, PdfParser, \ @@ -122,7 +122,3 @@ class TestPdfParser(PillowTestCase): self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]") self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index bdfd3582d..45ef0f1db 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image @@ -67,9 +67,13 @@ class TestPickle(PillowTestCase): "Tests/images/non_zero_bb.png", "Tests/images/non_zero_bb_scale2.png", "Tests/images/p_trns_single.png", - "Tests/images/pil123p.png" + "Tests/images/pil123p.png", + "Tests/images/itxt_chunks.png" ]: - self.helper_pickle_string(pickle, test_file=test_file) + for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): + self.helper_pickle_string(pickle, + protocol=protocol, + test_file=test_file) def test_pickle_l_mode(self): # Arrange @@ -91,7 +95,3 @@ class TestPickle(PillowTestCase): for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1): self.helper_pickle_string(cPickle, protocol, mode="L") self.helper_pickle_file(cPickle, protocol, mode="L") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py index 17fa3662b..73ef5e2b2 100644 --- a/Tests/test_psdraw.py +++ b/Tests/test_psdraw.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import PillowTestCase from PIL import Image, PSDraw import os @@ -61,7 +61,3 @@ class TestPsDraw(PillowTestCase): sys.stdout = old_stdout self.assertNotEqual(mystdout.getvalue(), "") - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_pyroma.py b/Tests/test_pyroma.py index 8d23d024b..3c4e3d9a2 100644 --- a/Tests/test_pyroma.py +++ b/Tests/test_pyroma.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import __version__ @@ -28,7 +28,3 @@ class TestPyroma(PillowTestCase): else: # Should have a perfect score self.assertEqual(rating, (10, [])) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_qt_image_fromqpixmap.py b/Tests/test_qt_image_fromqpixmap.py index 0127f77e2..358f1573d 100644 --- a/Tests/test_qt_image_fromqpixmap.py +++ b/Tests/test_qt_image_fromqpixmap.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQPixmapTestCase +from .helper import PillowTestCase, hopper +from .test_imageqt import PillowQPixmapTestCase from PIL import ImageQt @@ -25,7 +25,3 @@ class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase): def test_sanity_p(self): self.roundtrip(hopper('P')) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index 25391e09a..c1aa64b57 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQtTestCase +from .helper import PillowTestCase, hopper +from .test_imageqt import PillowQtTestCase from PIL import ImageQt, Image @@ -91,7 +91,3 @@ if ImageQt.qt_is_installed: lbl = QLabel(self) # Segfault in the problem lbl.setPixmap(pixmap1.copy()) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_qt_image_toqpixmap.py b/Tests/test_qt_image_toqpixmap.py index 5de7810f5..9bb7183b7 100644 --- a/Tests/test_qt_image_toqpixmap.py +++ b/Tests/test_qt_image_toqpixmap.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQPixmapTestCase +from .helper import PillowTestCase, hopper +from .test_imageqt import PillowQPixmapTestCase from PIL import ImageQt @@ -19,7 +19,3 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): # Test saving the file tempfile = self.tempfile('temp_{}.png'.format(mode)) data.save(tempfile) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_shell_injection.py b/Tests/test_shell_injection.py index 9e489f77c..77fd67f01 100644 --- a/Tests/test_shell_injection.py +++ b/Tests/test_shell_injection.py @@ -1,5 +1,5 @@ -from helper import unittest, PillowTestCase -from helper import djpeg_available, cjpeg_available, netpbm_available +from .helper import unittest, PillowTestCase +from .helper import djpeg_available, cjpeg_available, netpbm_available import sys import shutil @@ -51,7 +51,3 @@ class TestShellInjection(PillowTestCase): def test_save_netpbm_filename_l_mode(self): im = Image.open(TEST_GIF).convert("L") self.assert_save_filename_check(im, GifImagePlugin._save_netpbm) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index bd767b931..fae4d7ed6 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper from PIL import TiffImagePlugin, Image from PIL.TiffImagePlugin import IFDRational @@ -30,7 +30,7 @@ class Test_IFDRational(PillowTestCase): self._test_equal(1, 2, IFDRational(1, 2)) def test_nonetype(self): - " Fails if the _delegate function doesn't return a valid function" + # Fails if the _delegate function doesn't return a valid function xres = IFDRational(72) yres = IFDRational(72) @@ -58,7 +58,3 @@ class Test_IFDRational(PillowTestCase): reloaded = Image.open(out) self.assertEqual(float(IFDRational(301, 1)), float(reloaded.tag_v2[282])) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_uploader.py b/Tests/test_uploader.py index e157867cb..e40e7fb86 100644 --- a/Tests/test_uploader.py +++ b/Tests/test_uploader.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from .helper import PillowTestCase, hopper class TestUploader(PillowTestCase): @@ -11,7 +11,3 @@ class TestUploader(PillowTestCase): result = hopper('P').convert('RGB') target = hopper('RGB') self.assert_image_similar(result, target, 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_util.py b/Tests/test_util.py index 2316d3d65..08e9c1665 100644 --- a/Tests/test_util.py +++ b/Tests/test_util.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from .helper import unittest, PillowTestCase from PIL import _util @@ -30,7 +30,19 @@ class TestUtil(PillowTestCase): fp = "filename.ext" # Act - it_is = _util.isStringType(fp) + it_is = _util.isPath(fp) + + # Assert + self.assertTrue(it_is) + + @unittest.skipIf(not _util.py36, 'os.path support for Paths added in 3.6') + def test_path_obj_is_path(self): + # Arrange + from pathlib import Path + test_path = Path('filename.ext') + + # Act + it_is = _util.isPath(test_path) # Assert self.assertTrue(it_is) @@ -74,7 +86,3 @@ class TestUtil(PillowTestCase): # Assert self.assertRaises(ValueError, lambda: thing.some_attr) - - -if __name__ == '__main__': - unittest.main() diff --git a/Tests/test_webp_leaks.py b/Tests/test_webp_leaks.py index c542ed811..03befd507 100644 --- a/Tests/test_webp_leaks.py +++ b/Tests/test_webp_leaks.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowLeakTestCase +from .helper import unittest, PillowLeakTestCase from PIL import Image, features from io import BytesIO @@ -20,7 +20,3 @@ class TestWebPLeaks(PillowLeakTestCase): im.load() self._test_leak(core) - - -if __name__ == '__main__': - unittest.main() diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 6394d52be..ead5637ee 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.0.1 +archive=libwebp-1.0.2 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz diff --git a/docs/COPYING b/docs/COPYING index 754527885..a1e258129 100644 --- a/docs/COPYING +++ b/docs/COPYING @@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is Pillow is the friendly PIL fork. It is - Copyright © 2010-2018 by Alex Clark and contributors + Copyright © 2010-2019 by Alex Clark and contributors Like PIL, Pillow is licensed under the open source PIL Software License: diff --git a/docs/conf.py b/docs/conf.py index cd62f6227..2c25588a0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,7 +48,7 @@ master_doc = 'index' # General information about the project. project = u'Pillow (PIL Fork)' -copyright = u'1995-2011 Fredrik Lundh, 2010-2018 Alex Clark and Contributors' +copyright = u'1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors' author = u'Fredrik Lundh, Alex Clark and Contributors' # The version info for the project you're documenting, acts as replacement for @@ -140,7 +140,7 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ['_static', 'resources'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 8d0ccbf37..fb5acfa8e 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,6 +12,52 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. +Python 2.7 +~~~~~~~~~~ + +.. deprecated:: 6.0.0 + +Python 2.7 reaches end-of-life on 2020-01-01. + +Pillow 7.0.0 will be released on 2020-01-01 and will drop support for Python 2.7, making +Pillow 6.x the last series to support Python 2. + +PyQt4 and PySide +~~~~~~~~~~~~~~~~ + +.. deprecated:: 6.0.0 + +Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since +2018-08-31 and PySide since 2015-10-14. + +Support for PyQt4 and PySide has been deprecated from ``ImageQt`` and will be removed in +a future version. Please upgrade to PyQt5 or PySide2. + +PIL.*ImagePlugin.__version__ attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 6.0.0 + +The version constants of individual plugins have been deprecated and will be removed in +a future version. Use ``PIL.__version__`` instead. + +=============================== ================================= ================================== +Deprecated Deprecated Deprecated +=============================== ================================= ================================== +``BmpImagePlugin.__version__`` ``Jpeg2KImagePlugin.__version__`` ``PngImagePlugin.__version__`` +``CurImagePlugin.__version__`` ``JpegImagePlugin.__version__`` ``PpmImagePlugin.__version__`` +``DcxImagePlugin.__version__`` ``McIdasImagePlugin.__version__`` ``PsdImagePlugin.__version__`` +``EpsImagePlugin.__version__`` ``MicImagePlugin.__version__`` ``SgiImagePlugin.__version__`` +``FliImagePlugin.__version__`` ``MpegImagePlugin.__version__`` ``SunImagePlugin.__version__`` +``FpxImagePlugin.__version__`` ``MpoImagePlugin.__version__`` ``TgaImagePlugin.__version__`` +``GdImageFile.__version__`` ``MspImagePlugin.__version__`` ``TiffImagePlugin.__version__`` +``GifImagePlugin.__version__`` ``PalmImagePlugin.__version__`` ``WmfImagePlugin.__version__`` +``IcoImagePlugin.__version__`` ``PcdImagePlugin.__version__`` ``XbmImagePlugin.__version__`` +``ImImagePlugin.__version__`` ``PcxImagePlugin.__version__`` ``XpmImagePlugin.__version__`` +``ImtImagePlugin.__version__`` ``PdfImagePlugin.__version__`` ``XVThumbImagePlugin.__version__`` +``IptcImagePlugin.__version__`` ``PixarImagePlugin.__version__`` +=============================== ================================= ================================== + Setting the size of TIFF images ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -25,26 +71,38 @@ a ``DeprecationWarning``: Setting the size of a TIFF image directly is deprecated, and will be removed in a future version. Use the resize method instead. -PILLOW_VERSION and VERSION constants -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +PILLOW_VERSION constant +~~~~~~~~~~~~~~~~~~~~~~~ .. deprecated:: 5.2.0 -Two version constants – ``VERSION`` (the old PIL version, always 1.1.7) and -``PILLOW_VERSION`` – have been deprecated and will be removed in the next +``PILLOW_VERSION`` has been deprecated and will be removed in the next major release. Use ``__version__`` instead. +Removed features +---------------- + +Deprecated features are only removed in major releases after an appropriate +period of deprecation has passed. + +VERSION constant +~~~~~~~~~~~~~~~~ + +*Removed in version 6.0.0.* + +``VERSION`` (the old PIL version, always 1.1.7) has been removed. Use +``__version__`` instead. + Undocumented ImageOps functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. deprecated:: 4.3.0 +*Removed in version 6.0.0.* -Several undocumented functions in ``ImageOps`` have been deprecated. They issue -a ``DeprecationWarning`` informing which equivalent to use from ``ImageFilter`` -instead: +Several undocumented functions in ``ImageOps`` have been removed. Use the equivalents +in ``ImageFilter`` instead: ========================== ============================ -Deprecated Use instead +Removed Use instead ========================== ============================ ``ImageOps.box_blur`` ``ImageFilter.BoxBlur`` ``ImageOps.gaussian_blur`` ``ImageFilter.GaussianBlur`` @@ -56,27 +114,9 @@ Deprecated Use instead PIL.OleFileIO ~~~~~~~~~~~~~ -.. deprecated:: 4.0.0 +*Removed in version 6.0.0.* -The vendored version of olefile has been removed. Attempting to import -``PIL.OleFileIO`` issues a ``DeprecationWarning`` (from 4.0.0) or raises -``ImportError`` (from 5.0.0): - -.. code-block:: none - - PIL.OleFileIO is deprecated. Use the olefile Python package - instead. This module will be removed in a future version. - -Removed features ----------------- - -Deprecated features are only removed in major releases after an appropriate -period of deprecation has passed. - -Vendored olefile -~~~~~~~~~~~~~~~~ - -*Removed in version 4.0.0.* - -The vendored version of the olefile Python package was removed in favour of the -upstream package. Install if needed (eg. ``pip install olefile``). +PIL.OleFileIO was removed as a vendored file and in Pillow 4.0.0 (2017-01) in favour of +the upstream olefile Python package, and replaced with an ``ImportError`` in 5.0.0 +(2018-01). The deprecated file has now been removed from Pillow. If needed, install from +PyPI (eg. ``pip install olefile``). diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 469f89066..705438d4a 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -751,7 +751,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **method** Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 0. -**icc_procfile** +**icc_profile** The ICC Profile to include in the saved file. Only supported if the system WebP library was built with webpmux support. diff --git a/docs/installation.rst b/docs/installation.rst index 885cdcd67..44cd9af6f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -15,19 +15,23 @@ Notes .. note:: Pillow is supported on the following Python versions -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|**Python** |**2.4**|**2.5**|**2.6**|**2.7**|**3.2**|**3.3**|**3.4**|**3.5**|**3.6**|**3.7**| -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|Pillow < 2.0.0 | Yes | Yes | Yes | Yes | | | | | | | -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | | -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|Pillow 4.x | | | | Yes | | Yes | Yes | Yes | Yes | | -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|Pillow 5.0.x - 5.1.x| | | | Yes | | | Yes | Yes | Yes | | -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ -|Pillow >= 5.2.0 | | | | Yes | | | Yes | Yes | Yes | Yes | -+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|**Python** |**2.4**|**2.5**|**2.6**|**2.7**|**3.2**|**3.3**|**3.4**|**3.5**|**3.6**|**3.7**| ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow < 2.0.0 | Yes | Yes | Yes | Yes | | | | | | | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 4.x | | | | Yes | | Yes | Yes | Yes | Yes | | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 5.0.x - 5.1.x | | | | Yes | | | Yes | Yes | Yes | | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 5.2.x - 5.4.x | | | | Yes | | | Yes | Yes | Yes | Yes | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 6.x | | | | Yes | | | | Yes | Yes | Yes | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow >= 7.0.0 | | | | | | | | Yes | Yes | Yes | ++---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ Basic Installation ------------------ @@ -170,7 +174,7 @@ Many of Pillow's features require external libraries: the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. * Windows support: Libimagequant requires VS2013/MSVC 18 to compile, - so it is unlikely to work with any Python prior to 3.5 on Windows. + so it is unlikely to work with Python 2.7 on Windows. * **libraqm** provides complex text layout support. @@ -394,16 +398,16 @@ These platforms are built and tested for every change. +----------------------------------+-------------------------------+-----------------------+ | Fedora 29 | 2.7 |x86-64 | +----------------------------------+-------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite* | 2.7, 3.4, 3.5, 3.6, 3.7 |x86-64 | +| Mac OS X 10.10 Yosemite* | 2.7, 3.5, 3.6, 3.7 |x86-64 | +----------------------------------+-------------------------------+-----------------------+ | Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, |x86-64 | | | PyPy, PyPy3 | | +----------------------------------+-------------------------------+-----------------------+ -| Ubuntu Linux 14.04 LTS | 2.7, 3.4, 3.5, 3.6 |x86-64 | +| Ubuntu Linux 14.04 LTS | 2.7, 3.5, 3.6 |x86-64 | | +-------------------------------+-----------------------+ | | 2.7 |x86 | +----------------------------------+-------------------------------+-----------------------+ -| Windows Server 2012 R2 | 2.7, 3.4, 3.5, 3.6, 3.7 |x86, x86-64 | +| Windows Server 2012 R2 | 2.7, 3.5, 3.6, 3.7 |x86, x86-64 | | +-------------------------------+-----------------------+ | | PyPy, 3.7/MinGW |x86 | +----------------------------------+-------------------------------+-----------------------+ @@ -423,13 +427,13 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+------------------------------+--------------------------------+-----------------------+ |**Operating system** |**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | +----------------------------------+------------------------------+--------------------------------+-----------------------+ -| macOS 10.14 Mojave | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.3.0 |x86-64 | +| macOS 10.14 Mojave | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.4.1 |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ | macOS 10.13 High Sierra | 2.7, 3.4, 3.5, 3.6 | 4.2.1 |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ | macOS 10.12 Sierra | 2.7, 3.4, 3.5, 3.6 | 4.1.1 |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ -| Mac OS X 10.11 El Capitan | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.3.0 |x86-64 | +| Mac OS X 10.11 El Capitan | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.4.1 |x86-64 | | +------------------------------+--------------------------------+ + | | 3.3 | 4.1.0 | | +----------------------------------+------------------------------+--------------------------------+-----------------------+ @@ -478,8 +482,6 @@ These platforms have been reported to work at the versions mentioned. Old Versions ------------ -You can download old distributions from `PyPI -`_. Only the latest major -releases for Python 2.x and 3.x are visible, but all releases are -available by direct URL access -e.g. https://pypi.org/project/Pillow/1.0/. +You can download old distributions from the `release history at PyPI +`_ and by direct URL access +eg. https://pypi.org/project/Pillow/1.0/. diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index 2e4e21f19..6c8f11253 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -34,6 +34,7 @@ operations in this module). .. autofunction:: PIL.ImageChops.lighter .. autofunction:: PIL.ImageChops.logical_and .. autofunction:: PIL.ImageChops.logical_or +.. autofunction:: PIL.ImageChops.logical_xor .. autofunction:: PIL.ImageChops.multiply .. py:method:: PIL.ImageChops.offset(image, xoffset, yoffset=None) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 1f5e9b57e..7c24bae93 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -37,7 +37,8 @@ Coordinates ^^^^^^^^^^^ The graphics interface uses the same coordinate system as PIL itself, with (0, -0) in the upper left corner. +0) in the upper left corner. Any pixels drawn outside of the image bounds will +be discarded. Colors ^^^^^^ diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst index 386401075..5128f28fb 100644 --- a/docs/reference/ImageQt.rst +++ b/docs/reference/ImageQt.rst @@ -7,6 +7,12 @@ The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5, PySide or PySide2 QImage objects from PIL images. +Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since +2018-08-31 and PySide since 2015-10-14. + +Support for PyQt4 and PySide is deprecated since Pillow 6.0.0 and will be removed in a +future version. Please upgrade to PyQt5 or PySide2. + .. versionadded:: 1.1.6 .. py:class:: ImageQt.ImageQt(image) diff --git a/docs/reference/PixelAccess.rst b/docs/reference/PixelAccess.rst index 522e70937..8a8569922 100644 --- a/docs/reference/PixelAccess.rst +++ b/docs/reference/PixelAccess.rst @@ -65,7 +65,8 @@ Access using negative indexes is also possible. Modifies the pixel at x,y. The color is given as a single numerical value for single band images, and a tuple for - multi-band images + multi-band images. In addition to this, RGB and RGBA tuples + are accepted for P images. :param xy: The pixel coordinate, given as (x, y). :param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode) diff --git a/docs/reference/block_allocator.rst b/docs/reference/block_allocator.rst index e70f9667c..400f236dc 100644 --- a/docs/reference/block_allocator.rst +++ b/docs/reference/block_allocator.rst @@ -8,7 +8,7 @@ Historically there have been two image allocators in Pillow: ``ImagingAllocateBlock`` and ``ImagingAllocateArray``. The first works for images smaller than 16MB of data and allocates one large chunk of memory of ``im->linesize * im->ysize`` bytes. The second works for -large images and make one allocation for each scan line of size +large images and makes one allocation for each scan line of size ``im->linesize`` bytes. This makes for a very sharp transition between one allocation and potentially thousands of small allocations, leading to unpredictable performance penalties around the transition. diff --git a/docs/releasenotes/4.3.0.rst b/docs/releasenotes/4.3.0.rst index 827754230..6fa554e23 100644 --- a/docs/releasenotes/4.3.0.rst +++ b/docs/releasenotes/4.3.0.rst @@ -9,7 +9,7 @@ Deprecations Several undocumented functions in ImageOps have been deprecated: ``gaussian_blur``, ``gblur``, ``unsharp_mask``, ``usm`` and -``box_blur``. Use the equivalent operations in ImageFilter +``box_blur``. Use the equivalent operations in ``ImageFilter`` instead. These functions will be removed in a future release. TIFF Metadata Changes diff --git a/docs/releasenotes/5.4.0.rst b/docs/releasenotes/5.4.0.rst index 421ebbbf2..6d7277c70 100644 --- a/docs/releasenotes/5.4.0.rst +++ b/docs/releasenotes/5.4.0.rst @@ -1,9 +1,29 @@ -5.4.0 (unreleased) +5.4.0 ----- API Changes =========== +APNG extension to PNG plugin +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Animated Portable Network Graphics (APNG) images are not fully supported but +can be opened via the PNG plugin to get some basic info:: + + im = Image.open("image.apng") + print(im.mode) # "RGBA" + print(im.size) # (245, 245) + im.show() # Shows a single frame + +Check for libjpeg-turbo +^^^^^^^^^^^^^^^^^^^^^^^ + +You can check if Pillow has been built against the libjpeg-turbo version of the +libjpeg library:: + + from PIL import features + features.check_feature("libjpeg_turbo") # True or False + Negative indexes in pixel access ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,6 +35,26 @@ For example, to get or set the farthest pixel in the lower right of an image:: px[-1, -1] = (0, 0, 0) +New custom TIFF tags +^^^^^^^^^^^^^^^^^^^^ + +TIFF images can now be saved with custom integer, float and string TIFF tags:: + + im = Image.new("RGB", (200, 100)) + custom = { + 37000: 4, + 37001: 4.2, + 37002: "custom tag value", + 37003: u"custom tag value", + 37004: b"custom tag value", + } + im.save("output.tif", tiffinfo=custom) + + im2 = Image.open("output.tif") + print(im2.tag_v2[37000]) # 4 + print(im2.tag_v2[37002]) # "custom tag value" + print(im2.tag_v2[37004]) # b"custom tag value" + Other Changes ============= diff --git a/docs/releasenotes/5.4.1.rst b/docs/releasenotes/5.4.1.rst new file mode 100644 index 000000000..78f483db6 --- /dev/null +++ b/docs/releasenotes/5.4.1.rst @@ -0,0 +1,36 @@ +5.4.1 +----- + +This release fixes regressions in 5.4.0. + +Installation on Termux +^^^^^^^^^^^^^^^^^^^^^^ + +A change to the way Pillow detects libraries during installed prevented +installation on Termux, which does not have ``/sbin/ldconfig``. This is now +fixed. + +PNG: Handle IDAT chunks after image end +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some PNG images have multiple IDAT chunks. In some cases, Pillow will stop +reading image data before the IDAT chunks finish. A regression caused an +``EOFError`` exception when previously there was none. This is now fixed, and +file reading continues in case there are subsequent text chunks. + +PNG: MIME type +^^^^^^^^^^^^^^ + +The addition of limited APNG support to the PNG plugin also overwrote the MIME +type for PNG files, causing "image/apng" to be returned as the MIME type of +both APNG and PNG files. This has been fixed so the MIME type of PNG files is +"image/png". + +File closing +^^^^^^^^^^^^ + +A regression caused an unsupported image file to report a +``ValueError: seek of closed file`` exception instead of an ``OSError``. This +has been fixed by ensuring that image plugins only close their internal ``__fp`` +if they are not the same as ``ImageFile``'s ``fp``, allowing each to manage their own +file pointers. diff --git a/docs/releasenotes/6.0.0.rst b/docs/releasenotes/6.0.0.rst new file mode 100644 index 000000000..c9712ed8a --- /dev/null +++ b/docs/releasenotes/6.0.0.rst @@ -0,0 +1,116 @@ +6.0.0 +----- + +Backwards Incompatible Changes +============================== + +Python 3.4 dropped +^^^^^^^^^^^^^^^^^^ + +Python 3.4 is EOL since 2019-03-16 and no longer supported. We will not be creating +binaries, testing, or retaining compatibility with this version. The final version of +Pillow for Python 3.4 is 5.4.1. + +Removed deprecated PIL.OleFileIO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +PIL.OleFileIO was removed as a vendored file and in Pillow 4.0.0 (2017-01) in favour of +the upstream olefile Python package, and replaced with an ``ImportError``. The +deprecated file has now been removed from Pillow. If needed, install from PyPI (eg. +``pip install olefile``). + +Removed deprecated ImageOps functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Several undocumented functions in ``ImageOps`` were deprecated in Pillow 4.3.0 (2017-10) +and have now been removed: ``gaussian_blur``, ``gblur``, ``unsharp_mask``, ``usm`` and +``box_blur``. Use the equivalent operations in ``ImageFilter`` instead. + +Removed deprecated VERSION +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``VERSION`` (the old PIL version, always 1.1.7) has been removed. Use ``__version__`` +instead. + +API Changes +=========== + +Deprecations +^^^^^^^^^^^^ + +Python 2.7 +~~~~~~~~~~ + +Python 2.7 reaches end-of-life on 2020-01-01. + +Pillow 7.0.0 will be released on 2020-01-01 and will drop support for Python 2.7, making +Pillow 6.x the last series to support Python 2. + +PyQt4 and PySide +~~~~~~~~~~~~~~~~ + +Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since +2018-08-31 and PySide since 2015-10-14. + +Support for PyQt4 and PySide has been deprecated from ``ImageQt`` and will be removed in +a future version. Please upgrade to PyQt5 or PySide2. + +PIL.*ImagePlugin.__version__ attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These version constants have been deprecated and will be removed in a future +version. + +* ``BmpImagePlugin.__version__`` +* ``CurImagePlugin.__version__`` +* ``DcxImagePlugin.__version__`` +* ``EpsImagePlugin.__version__`` +* ``FliImagePlugin.__version__`` +* ``FpxImagePlugin.__version__`` +* ``GdImageFile.__version__`` +* ``GifImagePlugin.__version__`` +* ``IcoImagePlugin.__version__`` +* ``ImImagePlugin.__version__`` +* ``ImtImagePlugin.__version__`` +* ``IptcImagePlugin.__version__`` +* ``Jpeg2KImagePlugin.__version__`` +* ``JpegImagePlugin.__version__`` +* ``McIdasImagePlugin.__version__`` +* ``MicImagePlugin.__version__`` +* ``MpegImagePlugin.__version__`` +* ``MpoImagePlugin.__version__`` +* ``MspImagePlugin.__version__`` +* ``PalmImagePlugin.__version__`` +* ``PcdImagePlugin.__version__`` +* ``PcxImagePlugin.__version__`` +* ``PdfImagePlugin.__version__`` +* ``PixarImagePlugin.__version__`` +* ``PngImagePlugin.__version__`` +* ``PpmImagePlugin.__version__`` +* ``PsdImagePlugin.__version__`` +* ``SgiImagePlugin.__version__`` +* ``SunImagePlugin.__version__`` +* ``TgaImagePlugin.__version__`` +* ``TiffImagePlugin.__version__`` +* ``WmfImagePlugin.__version__`` +* ``XbmImagePlugin.__version__`` +* ``XpmImagePlugin.__version__`` +* ``XVThumbImagePlugin.__version__`` + +Use ``PIL.__version__`` instead. + +API Additions +============= + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index df4fd4930..9a088375b 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,8 @@ Release Notes .. toctree:: :maxdepth: 2 + 6.0.0 + 5.4.1 5.4.0 5.3.0 5.2.0 diff --git a/docs/_static/js/script.js b/docs/resources/js/script.js similarity index 98% rename from docs/_static/js/script.js rename to docs/resources/js/script.js index 2cd7335ec..3bc216c2d 100644 --- a/docs/_static/js/script.js +++ b/docs/resources/js/script.js @@ -5,7 +5,7 @@ jQuery(document).ready(function ($) { $section.children('.section, .function, .method').each(function () { if ($(this).hasClass('section')) { sectionID = $(this).attr('id'); - search($(this), $sidebarItem.parent().find('[href=#'+sectionID+']')); + search($(this), $sidebarItem.parent().find('[href="#'+sectionID+'"]')); } else { var $dt = $(this).children('dt'); var id = $dt.attr('id'); @@ -48,7 +48,7 @@ jQuery(document).ready(function ($) { } }); }; - search($('[itemprop=articleBody] > .section'), $('.wy-nav-side a[href=#]')); + search($('[itemprop=articleBody] > .section'), $('.wy-nav-side a[href="#"]')); }, 0); $(window).on('hashchange', function () { $('ul[data-sectionID]').each(function () { diff --git a/mp_compile.py b/mp_compile.py index 2d5287d77..f50210ba5 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -1,6 +1,6 @@ # A monkey patch of the base distutils.ccompiler to use parallel builds # Tested on 2.7, looks to be identical to 3.3. -# Only applied on Python < 3.5 because otherwise, it conflicts with Python's +# Only applied on Python 2.7 because otherwise, it conflicts with Python's # own newly-added support for parallel builds. from __future__ import print_function @@ -79,6 +79,6 @@ def install(): "%s processes" % MAX_PROCS) -# We monkeypatch only versions earlier than 3.5 -if sys.version_info < (3, 5): +# We monkeypatch Python 2.7 +if sys.version_info.major < 3: install() diff --git a/setup.cfg b/setup.cfg index 95900ff99..bcaf82f27 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,8 +4,5 @@ test=pytest [metadata] license_file = LICENSE -[tool:pytest] -addopts = -vx Tests - [flake8] max-line-length = 88 diff --git a/setup.py b/setup.py index 9be66e115..5d8159a12 100755 --- a/setup.py +++ b/setup.py @@ -111,7 +111,7 @@ def _find_library_dirs_ldconfig(): stdout=subprocess.PIPE, env=env) except OSError: # E.g. command not found - return None + return [] [data, _] = p.communicate() if isinstance(data, bytes): data = data.decode() @@ -178,12 +178,6 @@ def get_version(): return locals()['__version__'] -try: - import _tkinter -except (ImportError, OSError): - # pypy emits an oserror - _tkinter = None - NAME = 'Pillow' PILLOW_VERSION = get_version() JPEG_ROOT = None @@ -271,8 +265,8 @@ class pil_build_ext(build_ext): if self.debug: global DEBUG DEBUG = True - if sys.version_info >= (3, 5) and not self.parallel: - # For Python < 3.5, we monkeypatch distutils to have parallel + if sys.version_info.major >= 3 and not self.parallel: + # For Python 2.7, we monkeypatch distutils to have parallel # builds. If --parallel (or -j) wasn't specified, we want to # reproduce the same behavior as before, that is, auto-detect the # number of jobs. @@ -780,14 +774,13 @@ try: "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", cmdclass={"build_ext": pil_build_ext}, ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], include_package_data=True, diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 43801b1f6..5b655d757 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -29,6 +29,8 @@ from ._binary import i8, i16le as i16, i32le as i32, \ o8, o16le as o16, o32le as o32 import math +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.7" # diff --git a/src/PIL/ContainerIO.py b/src/PIL/ContainerIO.py index 682ad9031..e6c288c83 100644 --- a/src/PIL/ContainerIO.py +++ b/src/PIL/ContainerIO.py @@ -18,6 +18,8 @@ # A file object that provides read access to a part of an existing # file (for example a TAR file). +import io + class ContainerIO(object): @@ -39,9 +41,9 @@ class ContainerIO(object): # Always false. def isatty(self): - return 0 + return False - def seek(self, offset, mode=0): + def seek(self, offset, mode=io.SEEK_SET): """ Move file pointer. diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index 3e8f32102..e0a5fae62 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -21,6 +21,8 @@ from __future__ import print_function from . import Image, BmpImagePlugin from ._binary import i8, i16le as i16, i32le as i32 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" # diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index 1b72cbc99..3c8c2bc8a 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -25,6 +25,8 @@ from . import Image from ._binary import i32le as i32 from .PcxImagePlugin import PcxImageFile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? @@ -83,7 +85,8 @@ class DcxImageFile(PcxImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index cc2c1b1f8..7421189f8 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -27,6 +27,8 @@ import sys from . import Image, ImageFile from ._binary import i32le as i32 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.5" # @@ -102,7 +104,7 @@ def Ghostscript(tile, size, fp, scale=1): # Copy whole file to read in Ghostscript with open(infile_temp, 'wb') as f: # fetch length of fp - fp.seek(0, 2) + fp.seek(0, io.SEEK_END) fsize = fp.tell() # ensure start position # go back @@ -144,7 +146,7 @@ def Ghostscript(tile, size, fp, scale=1): if sys.platform.startswith('win'): startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - subprocess.check_call(command, stdin=devnull, stdout=devnull, + subprocess.check_call(command, stdout=devnull, startupinfo=startupinfo) im = Image.open(outfile) im.load() @@ -167,7 +169,7 @@ class PSFile(object): self.fp = fp self.char = None - def seek(self, offset, whence=0): + def seek(self, offset, whence=io.SEEK_SET): self.char = None self.fp.seek(offset, whence) @@ -310,7 +312,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:4] == b"%!PS": # for HEAD without binary preview - fp.seek(0, 2) + fp.seek(0, io.SEEK_END) length = fp.tell() offset = 0 elif i32(s[0:4]) == 0xC6D3D0C5: diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 1c8232fba..bbc1a1340 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -19,6 +19,8 @@ from . import Image, ImageFile, ImagePalette from ._binary import i8, i16le as i16, i32le as i32, o8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" @@ -159,7 +161,8 @@ class FliImageFile(ImageFile.ImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/FontFile.py b/src/PIL/FontFile.py index 305e8afa2..b43f44762 100644 --- a/src/PIL/FontFile.py +++ b/src/PIL/FontFile.py @@ -46,7 +46,7 @@ class FontFile(object): return self.glyph[ix] def compile(self): - "Create metrics and bitmap" + """Create metrics and bitmap""" if self.bitmap: return @@ -93,7 +93,7 @@ class FontFile(object): self.metrics[i] = d, dst, s def save(self, filename): - "Save font" + """Save font""" self.compile() diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 9f284fd85..5e8a814f2 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -22,6 +22,8 @@ from ._binary import i32le as i32, i8 import olefile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" # we map from colour field tuples to (mode, rawmode) descriptors diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 8b688381c..9f00b2fef 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -26,6 +26,8 @@ from . import ImageFile, ImagePalette from ._binary import i8, i16be as i16, i32be as i32 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 50b786a37..2ebd8b248 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -29,6 +29,8 @@ from ._binary import i8, i16le as i16, o8, o16le as o16 import itertools +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.9" @@ -304,7 +306,8 @@ class GifImageFile(ImageFile.ImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: @@ -601,16 +604,16 @@ def _save_netpbm(im, fp, filename): import os from subprocess import Popen, check_call, PIPE, CalledProcessError - file = im._dump() + tempfile = im._dump() with open(filename, 'wb') as f: if im.mode != "RGB": with open(os.devnull, 'wb') as devnull: - check_call(["ppmtogif", file], stdout=f, stderr=devnull) + check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull) else: # Pipe ppmquant output into ppmtogif - # "ppmquant 256 %s | ppmtogif > %s" % (file, filename) - quant_cmd = ["ppmquant", "256", file] + # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) + quant_cmd = ["ppmquant", "256", tempfile] togif_cmd = ["ppmtogif"] with open(os.devnull, 'wb') as devnull: quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull) @@ -629,7 +632,7 @@ def _save_netpbm(im, fp, filename): raise CalledProcessError(retcode, togif_cmd) try: - os.unlink(file) + os.unlink(tempfile) except OSError: pass diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 2ea66675f..4a10b24b8 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -195,7 +195,7 @@ class IcnsFile(object): i += HEADERSIZE blocksize -= HEADERSIZE dct[sig] = (i, blocksize) - fobj.seek(blocksize, 1) + fobj.seek(blocksize, io.SEEK_CUR) i += blocksize def itersizes(self): diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index 24c4bddca..c1c0775da 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -29,6 +29,8 @@ from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin from ._binary import i8, i16le as i16, i32le as i32 from math import log, ceil +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" # diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 1a4fdb65a..08250e959 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -30,6 +30,8 @@ import re from . import Image, ImageFile, ImagePalette from ._binary import i8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.7" @@ -292,7 +294,8 @@ class ImImageFile(ImageFile.ImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6e3a9719d..4e1cff312 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -24,10 +24,10 @@ # See the README file for information on usage and redistribution. # -# VERSION is deprecated and will be removed in Pillow 6.0.0. -# PILLOW_VERSION is deprecated and will be removed after that. +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0. # Use __version__ instead. -from . import VERSION, PILLOW_VERSION, __version__, _plugins +from . import PILLOW_VERSION, __version__, _plugins from ._util import py3 import logging @@ -60,8 +60,7 @@ except ImportError: from collections import Callable -# Silence warnings -assert VERSION +# Silence warning assert PILLOW_VERSION logger = logging.getLogger(__name__) @@ -594,10 +593,10 @@ class Image(object): :ref:`file-handling` for more information. """ try: - self.fp.close() - self.fp = None if hasattr(self, "_close__fp"): self._close__fp() + self.fp.close() + self.fp = None except Exception as msg: logger.debug("Error closing: %s", msg) @@ -611,12 +610,12 @@ class Image(object): if sys.version_info.major >= 3: def __del__(self): + if hasattr(self, "_close__fp"): + self._close__fp() if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp') and self.fp and self._exclusive_fp): self.fp.close() self.fp = None - if hasattr(self, "_close__fp"): - self._close__fp() def _copy(self): self.load() @@ -655,8 +654,7 @@ class Image(object): return filename def __eq__(self, other): - return (isinstance(other, Image) and - self.__class__.__name__ == other.__class__.__name__ and + return (self.__class__ is other.__class__ and self.mode == other.mode and self.size == other.size and self.info == other.info and @@ -681,8 +679,7 @@ class Image(object): :returns: png version of the image as bytes """ - from io import BytesIO - b = BytesIO() + b = io.BytesIO() self.save(b, 'PNG') return b.getvalue() @@ -958,7 +955,7 @@ class Image(object): # color to an alpha channel. new_im = self._new(self.im.convert_transparent( mode, self.info['transparency'])) - del(new_im.info['transparency']) + del new_im.info['transparency'] return new_im elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'): t = self.info['transparency'] @@ -1010,14 +1007,14 @@ class Image(object): if delete_trns: # This could possibly happen if we requantize to fewer colors. # The transparency would be totally off in that case. - del(new.info['transparency']) + del new.info['transparency'] if trns is not None: try: new.info['transparency'] = new.palette.getcolor(trns) except Exception: # if we can't make a transparent color, don't leave the old # transparency hanging around to mess us up. - del(new.info['transparency']) + del new.info['transparency'] warnings.warn("Couldn't allocate palette entry " + "for transparency") return new @@ -1039,13 +1036,13 @@ class Image(object): new_im = self._new(im) if delete_trns: # crash fail if we leave a bytes transparency in an rgb/l mode. - del(new_im.info['transparency']) + del new_im.info['transparency'] if trns is not None: if new_im.mode == 'P': try: new_im.info['transparency'] = new_im.palette.getcolor(trns) except Exception: - del(new_im.info['transparency']) + del new_im.info['transparency'] warnings.warn("Couldn't allocate palette entry " + "for transparency") else: @@ -1651,7 +1648,8 @@ class Image(object): """ Modifies the pixel at the given position. The color is given as a single numerical value for single-band images, and a tuple for - multi-band images. + multi-band images. In addition to this, RGB and RGBA tuples are + accepted for P images. Note that this method is relatively slow. For more extensive changes, use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` @@ -1674,6 +1672,11 @@ class Image(object): if self.pyaccess: return self.pyaccess.putpixel(xy, value) + + if self.mode == "P" and \ + isinstance(value, (list, tuple)) and len(value) in [3, 4]: + # RGB or RGBA value for a P image + value = self.palette.getcolor(value) return self.im.putpixel(xy, value) def remap_palette(self, dest_map, source_palette=None): @@ -2123,11 +2126,12 @@ class Image(object): self.draft(None, size) - im = self.resize(size, resample) + if self.size != size: + im = self.resize(size, resample) - self.im = im.im - self.mode = im.mode - self._size = size + self.im = im.im + self._size = size + self.mode = self.im.mode self.readonly = 0 self.pyaccess = None @@ -2657,7 +2661,7 @@ def open(fp, mode="r"): # opening failures that are entirely expected. # logger.debug("", exc_info=True) continue - except Exception: + except BaseException: if exclusive_fp: fp.close() raise diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index 89016730e..b1f71b5e7 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -54,7 +54,7 @@ def invert(image): def lighter(image1, image2): """ Compares the two images, pixel by pixel, and returns a new image containing - the lighter values. + the lighter values. At least one of the images must have mode "1". .. code-block:: python @@ -70,8 +70,8 @@ def lighter(image1, image2): def darker(image1, image2): """ - Compares the two images, pixel by pixel, and returns a new image - containing the darker values. + Compares the two images, pixel by pixel, and returns a new image containing + the darker values. At least one of the images must have mode "1". .. code-block:: python @@ -88,7 +88,7 @@ def darker(image1, image2): def difference(image1, image2): """ Returns the absolute value of the pixel-by-pixel difference between the two - images. + images. At least one of the images must have mode "1". .. code-block:: python @@ -107,7 +107,8 @@ def multiply(image1, image2): Superimposes two images on top of each other. If you multiply an image with a solid black image, the result is black. If - you multiply with a solid white image, the image is unaffected. + you multiply with a solid white image, the image is unaffected. At least + one of the images must have mode "1". .. code-block:: python @@ -123,7 +124,8 @@ def multiply(image1, image2): def screen(image1, image2): """ - Superimposes two inverted images on top of each other. + Superimposes two inverted images on top of each other. At least one of the + images must have mode "1". .. code-block:: python @@ -141,6 +143,7 @@ def add(image1, image2, scale=1.0, offset=0): """ Adds two images, dividing the result by scale and adding the offset. If omitted, scale defaults to 1.0, and offset to 0.0. + At least one of the images must have mode "1". .. code-block:: python @@ -156,8 +159,9 @@ def add(image1, image2, scale=1.0, offset=0): def subtract(image1, image2, scale=1.0, offset=0): """ - Subtracts two images, dividing the result by scale and adding the - offset. If omitted, scale defaults to 1.0, and offset to 0.0. + Subtracts two images, dividing the result by scale and adding the offset. + If omitted, scale defaults to 1.0, and offset to 0.0. At least one of the + images must have mode "1". .. code-block:: python @@ -172,7 +176,8 @@ def subtract(image1, image2, scale=1.0, offset=0): def add_modulo(image1, image2): - """Add two images, without clipping the result. + """Add two images, without clipping the result. At least one of the images + must have mode "1". .. code-block:: python @@ -187,7 +192,8 @@ def add_modulo(image1, image2): def subtract_modulo(image1, image2): - """Subtract two images, without clipping the result. + """Subtract two images, without clipping the result. At least one of the + images must have mode "1". .. code-block:: python @@ -202,7 +208,8 @@ def subtract_modulo(image1, image2): def logical_and(image1, image2): - """Logical AND between two images. + """Logical AND between two images. At least one of the images must have + mode "1". .. code-block:: python @@ -217,7 +224,8 @@ def logical_and(image1, image2): def logical_or(image1, image2): - """Logical OR between two images. + """Logical OR between two images. At least one of the images must have + mode "1". .. code-block:: python @@ -232,7 +240,8 @@ def logical_or(image1, image2): def logical_xor(image1, image2): - """Logical XOR between two images. + """Logical XOR between two images. At least one of the images must have + mode "1". .. code-block:: python diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index e8871bb1e..2cd6f33b5 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -274,13 +274,13 @@ class PyCMSError(Exception): def profileToProfile( im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, - outputMode=None, inPlace=0, flags=0): + outputMode=None, inPlace=False, flags=0): """ (pyCMS) Applies an ICC transformation to a given image, mapping from inputProfile to outputProfile. If the input or output profiles specified are not valid filenames, a - PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, + PyCMSError will be raised. If inPlace is True and outputMode != im.mode, a PyCMSError will be raised. If an error occurs during application of the profiles, a PyCMSError will be raised. If outputMode is not a mode supported by the outputProfile (or by pyCMS), a PyCMSError will be @@ -317,9 +317,9 @@ def profileToProfile( MUST be the same mode as the input, or omitted completely. If omitted, the outputMode will be the same as the mode of the input image (im.mode) - :param inPlace: Boolean (1 = True, None or 0 = False). If True, the - original image is modified in-place, and None is returned. If False - (default), a new Image object is returned with the transform applied. + :param inPlace: Boolean. If True, the original image is modified in-place, + and None is returned. If False (default), a new Image object is + returned with the transform applied. :param flags: Integer (0-...) specifying additional flags :returns: Either None or a new PIL image object, depending on value of inPlace @@ -559,13 +559,13 @@ buildTransformFromOpenProfiles = buildTransform buildProofTransformFromOpenProfiles = buildProofTransform -def applyTransform(im, transform, inPlace=0): +def applyTransform(im, transform, inPlace=False): """ (pyCMS) Applies a transform to a given image. If im.mode != transform.inMode, a PyCMSError is raised. - If inPlace == TRUE and transform.inMode != transform.outMode, a + If inPlace is True and transform.inMode != transform.outMode, a PyCMSError is raised. If im.mode, transfer.inMode, or transfer.outMode is not supported by @@ -581,7 +581,7 @@ def applyTransform(im, transform, inPlace=0): considerable calculation time if doing the same conversion multiple times. If you want to modify im in-place instead of receiving a new image as - the return value, set inPlace to TRUE. This can only be done if + the return value, set inPlace to True. This can only be done if transform.inMode and transform.outMode are the same, because we can't change the mode in-place (the buffer sizes for some modes are different). The default behavior is to return a new Image object of @@ -590,10 +590,9 @@ def applyTransform(im, transform, inPlace=0): :param im: A PIL Image object, and im.mode must be the same as the inMode supported by the transform. :param transform: A valid CmsTransform class object - :param inPlace: Bool (1 == True, 0 or None == False). If True, im is - modified in place and None is returned, if False, a new Image object - with the transform applied is returned (and im is not changed). The - default is False. + :param inPlace: Bool. If True, im is modified in place and None is + returned, if False, a new Image object with the transform applied is + returned (and im is not changed). The default is False. :returns: Either None, or a new PIL Image object, depending on the value of inPlace. The profile will be returned in the image's info['icc_profile']. @@ -951,5 +950,5 @@ def versions(): return ( VERSION, core.littlecms_version, - sys.version.split()[0], Image.VERSION + sys.version.split()[0], Image.__version__ ) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 915557a57..38838996b 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -79,6 +79,8 @@ class ImageFile(Image.Image): self._min_frame = 0 + self.custom_mimetype = None + self.tile = None self.readonly = 1 # until we know better @@ -113,17 +115,17 @@ class ImageFile(Image.Image): raise SyntaxError("not identified by this driver") def draft(self, mode, size): - "Set draft mode" + """Set draft mode""" pass def get_format_mimetype(self): if self.format is None: return - return Image.MIME.get(self.format.upper()) + return self.custom_mimetype or Image.MIME.get(self.format.upper()) def verify(self): - "Check file integrity" + """Check file integrity""" # raise exception if something's wrong. must be called # directly after open, and closes file when finished. @@ -132,7 +134,7 @@ class ImageFile(Image.Image): self.fp = None def load(self): - "Load image data based on tile list" + """Load image data based on tile list""" pixel = Image.Image.load(self) @@ -315,7 +317,7 @@ class StubImageFile(ImageFile): self.__dict__ = image.__dict__ def _load(self): - "(Hook) Find actual image loader." + """(Hook) Find actual image loader.""" raise NotImplementedError( "StubImageFile subclass must implement _load" ) @@ -489,7 +491,7 @@ def _save(im, fp, tile, bufsize=0): for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: - fp.seek(o, 0) + fp.seek(o) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) @@ -508,7 +510,7 @@ def _save(im, fp, tile, bufsize=0): for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: - fp.seek(o, 0) + fp.seek(o) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index d985877a6..68247c290 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -28,7 +28,7 @@ VERBOSE = 0 def _isconstant(v): - return isinstance(v, int) or isinstance(v, float) + return isinstance(v, (int, float)) class _Operand(object): diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 058447b68..b93f6f35d 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -21,7 +21,6 @@ from . import Image from ._util import isStringType import operator import functools -import warnings # @@ -523,98 +522,3 @@ def solarize(image, threshold=128): else: lut.append(255-i) return _lut(image, lut) - - -# -------------------------------------------------------------------- -# PIL USM components, from Kevin Cazabon. - -def gaussian_blur(im, radius=None): - """ PIL_usm.gblur(im, [radius])""" - - warnings.warn( - 'PIL.ImageOps.gaussian_blur is deprecated. ' - 'Use PIL.ImageFilter.GaussianBlur instead. ' - 'This function will be removed in a future version.', - DeprecationWarning - ) - - if radius is None: - radius = 5.0 - - im.load() - - return im.im.gaussian_blur(radius) - - -def gblur(im, radius=None): - """ PIL_usm.gblur(im, [radius])""" - - warnings.warn( - 'PIL.ImageOps.gblur is deprecated. ' - 'Use PIL.ImageFilter.GaussianBlur instead. ' - 'This function will be removed in a future version.', - DeprecationWarning - ) - - return gaussian_blur(im, radius) - - -def unsharp_mask(im, radius=None, percent=None, threshold=None): - """ PIL_usm.usm(im, [radius, percent, threshold])""" - - warnings.warn( - 'PIL.ImageOps.unsharp_mask is deprecated. ' - 'Use PIL.ImageFilter.UnsharpMask instead. ' - 'This function will be removed in a future version.', - DeprecationWarning - ) - - if radius is None: - radius = 5.0 - if percent is None: - percent = 150 - if threshold is None: - threshold = 3 - - im.load() - - return im.im.unsharp_mask(radius, percent, threshold) - - -def usm(im, radius=None, percent=None, threshold=None): - """ PIL_usm.usm(im, [radius, percent, threshold])""" - - warnings.warn( - 'PIL.ImageOps.usm is deprecated. ' - 'Use PIL.ImageFilter.UnsharpMask instead. ' - 'This function will be removed in a future version.', - DeprecationWarning - ) - - return unsharp_mask(im, radius, percent, threshold) - - -def box_blur(image, radius): - """ - Blur the image by setting each pixel to the average value of the pixels - in a square box extending radius pixels in each direction. - Supports float radius of arbitrary size. Uses an optimized implementation - which runs in linear time relative to the size of the image - for any radius value. - - :param image: The image to blur. - :param radius: Size of the box in one direction. Radius 0 does not blur, - returns an identical image. Radius 1 takes 1 pixel - in each direction, i.e. 9 pixels in total. - :return: An image. - """ - warnings.warn( - 'PIL.ImageOps.box_blur is deprecated. ' - 'Use PIL.ImageFilter.BoxBlur instead. ' - 'This function will be removed in a future version.', - DeprecationWarning - ) - - image.load() - - return image._new(image.im.box_blur(radius)) diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index b747781c5..02ce6354e 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -20,6 +20,7 @@ from . import Image from ._util import isPath, py3 from io import BytesIO import sys +import warnings qt_versions = [ ['5', 'PyQt5'], @@ -27,6 +28,12 @@ qt_versions = [ ['4', 'PyQt4'], ['side', 'PySide'] ] + +WARNING_TEXT = ( + "Support for EOL {} is deprecated and will be removed in a future version. " + "Please upgrade to PyQt5 or PySide2." +) + # If a version has already been imported, attempt it first qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True) @@ -41,9 +48,13 @@ for qt_version, qt_module in qt_versions: elif qt_module == 'PyQt4': from PyQt4.QtGui import QImage, qRgba, QPixmap from PyQt4.QtCore import QBuffer, QIODevice + + warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning) elif qt_module == 'PySide': from PySide.QtGui import QImage, qRgba, QPixmap from PySide.QtCore import QBuffer, QIODevice + + warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning) except (ImportError, RuntimeError): continue qt_is_installed = True @@ -67,7 +78,7 @@ def fromqimage(im): """ buffer = QBuffer() buffer.open(QIODevice.ReadWrite) - # preserve alha channel with png + # preserve alpha channel with png # otherwise ppm is more friendly with Image.open if im.hasAlphaChannel(): im.save(buffer, 'png') diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 7d758c737..bbd841db7 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -132,9 +132,9 @@ elif sys.platform == "darwin": def show_file(self, file, **options): """Display given file""" - f, path = tempfile.mkstemp() - f.write(file) - f.close() + fd, path = tempfile.mkstemp() + with os.fdopen(fd, 'w') as f: + f.write(file) with open(path, "r") as f: subprocess.Popen([ 'im=$(cat);' @@ -171,9 +171,9 @@ else: def show_file(self, file, **options): """Display given file""" - f, path = tempfile.mkstemp() - f.write(file) - f.close() + fd, path = tempfile.mkstemp() + with os.fdopen(fd, 'w') as f: + f.write(file) with open(path, "r") as f: command = self.get_command_ex(file, **options)[0] subprocess.Popen([ diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index d4b38d856..c926a7416 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -41,7 +41,7 @@ class Stat(object): self.bands = list(range(len(self.h) // 256)) def __getattr__(self, id): - "Calculate missing attribute" + """Calculate missing attribute""" if id[:4] == "_get": raise AttributeError(id) # calculate missing attribute @@ -50,7 +50,7 @@ class Stat(object): return v def _getextrema(self): - "Get min/max values for each band in the image" + """Get min/max values for each band in the image""" def minmax(histogram): n = 255 @@ -67,7 +67,7 @@ class Stat(object): return v def _getcount(self): - "Get total number of pixels in each layer" + """Get total number of pixels in each layer""" v = [] for i in range(0, len(self.h), 256): @@ -75,7 +75,7 @@ class Stat(object): return v def _getsum(self): - "Get sum of all pixels in each layer" + """Get sum of all pixels in each layer""" v = [] for i in range(0, len(self.h), 256): @@ -86,7 +86,7 @@ class Stat(object): return v def _getsum2(self): - "Get squared sum of all pixels in each layer" + """Get squared sum of all pixels in each layer""" v = [] for i in range(0, len(self.h), 256): @@ -97,7 +97,7 @@ class Stat(object): return v def _getmean(self): - "Get average pixel level for each layer" + """Get average pixel level for each layer""" v = [] for i in self.bands: @@ -105,7 +105,7 @@ class Stat(object): return v def _getmedian(self): - "Get median pixel level for each layer" + """Get median pixel level for each layer""" v = [] for i in self.bands: @@ -120,7 +120,7 @@ class Stat(object): return v def _getrms(self): - "Get RMS for each layer" + """Get RMS for each layer""" v = [] for i in self.bands: @@ -128,7 +128,7 @@ class Stat(object): return v def _getvar(self): - "Get variance for each layer" + """Get variance for each layer""" v = [] for i in self.bands: @@ -137,7 +137,7 @@ class Stat(object): return v def _getstddev(self): - "Get standard deviation for each layer" + """Get standard deviation for each layer""" v = [] for i in self.bands: diff --git a/src/PIL/ImtImagePlugin.py b/src/PIL/ImtImagePlugin.py index 5a6623ca5..18b7dd839 100644 --- a/src/PIL/ImtImagePlugin.py +++ b/src/PIL/ImtImagePlugin.py @@ -19,6 +19,8 @@ import re from . import Image, ImageFile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index b63e1ab50..5055d2a00 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -22,6 +22,8 @@ from ._binary import i8, i16be as i16, i32be as i32, o8 import os import tempfile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.3" COMPRESSION = { diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 172162c9a..9645f8ef0 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -17,6 +17,8 @@ import struct import os import io +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" @@ -57,10 +59,11 @@ def _parse_codestream(fp): def _parse_jp2_header(fp): """Parse the JP2 header box to extract size, component count and - color space information, returning a PIL (size, mode) tuple.""" + color space information, returning a (size, mode, mimetype) tuple.""" # Find the JP2 header box header = None + mimetype = None while True: lbox, tbox = struct.unpack('>I4s', fp.read(8)) if lbox == 1: @@ -75,6 +78,10 @@ def _parse_jp2_header(fp): if tbox == b'jp2h': header = fp.read(lbox - hlen) break + elif tbox == b'ftyp': + if fp.read(4) == b'jpx ': + mimetype = 'image/jpx' + fp.seek(lbox - hlen - 4, os.SEEK_CUR) else: fp.seek(lbox - hlen, os.SEEK_CUR) @@ -145,7 +152,7 @@ def _parse_jp2_header(fp): if size is None or mode is None: raise SyntaxError("Malformed jp2 header") - return (size, mode) + return (size, mode, mimetype) ## # Image plugin for JPEG2000 images. @@ -165,7 +172,8 @@ class Jpeg2KImageFile(ImageFile.ImageFile): if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': self.codec = "jp2" - self._size, self.mode = _parse_jp2_header(self.fp) + header = _parse_jp2_header(self.fp) + self._size, self.mode, self.custom_mimetype = header else: raise SyntaxError('not a JPEG 2000 file') @@ -185,9 +193,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile): fd = -1 try: pos = self.fp.tell() - self.fp.seek(0, 2) + self.fp.seek(0, io.SEEK_END) length = self.fp.tell() - self.fp.seek(pos, 0) + self.fp.seek(pos) except Exception: length = -1 @@ -281,4 +289,3 @@ Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]) Image.register_mime(Jpeg2KImageFile.format, 'image/jp2') -Image.register_mime(Jpeg2KImageFile.format, 'image/jpx') diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index e43bdea6b..2f76e9675 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -43,6 +43,8 @@ from ._binary import i8, o8, i16be as i16 from .JpegPresets import presets from ._util import isStringType +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.6" @@ -459,35 +461,36 @@ def _getexif(self): data = self.info["exif"] except KeyError: return None - file = io.BytesIO(data[6:]) - head = file.read(8) + fp = io.BytesIO(data[6:]) + head = fp.read(8) # process dictionary info = TiffImagePlugin.ImageFileDirectory_v1(head) - info.load(file) + fp.seek(info.next) + info.load(fp) exif = dict(_fixup_dict(info)) # get exif extension try: # exif field 0x8769 is an offset pointer to the location # of the nested embedded exif ifd. # It should be a long, but may be corrupted. - file.seek(exif[0x8769]) + fp.seek(exif[0x8769]) except (KeyError, TypeError): pass else: info = TiffImagePlugin.ImageFileDirectory_v1(head) - info.load(file) + info.load(fp) exif.update(_fixup_dict(info)) # get gpsinfo extension try: # exif field 0x8825 is an offset pointer to the location # of the nested embedded gps exif ifd. # It should be a long, but may be corrupted. - file.seek(exif[0x8825]) + fp.seek(exif[0x8825]) except (KeyError, TypeError): pass else: info = TiffImagePlugin.ImageFileDirectory_v1(head) - info.load(file) + info.load(fp) exif[0x8825] = _fixup_dict(info) return exif @@ -510,6 +513,7 @@ def _getmp(self): # process dictionary try: info = TiffImagePlugin.ImageFileDirectory_v2(head) + file_contents.seek(info.next) info.load(file_contents) mp = dict(info) except Exception: diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index 161fb5e50..2cdb6f828 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -19,6 +19,8 @@ import struct from . import Image, ImageFile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 6e29e456d..8c7707dae 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -21,6 +21,8 @@ from . import Image, TiffImagePlugin import olefile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" @@ -99,7 +101,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py index 15c7afccc..7f419c5dc 100644 --- a/src/PIL/MpegImagePlugin.py +++ b/src/PIL/MpegImagePlugin.py @@ -17,6 +17,8 @@ from . import Image, ImageFile from ._binary import i8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 06b93d921..64c0f57a4 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -20,6 +20,8 @@ from . import Image, JpegImagePlugin +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" @@ -86,7 +88,8 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 74c68172a..711f8f09a 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -28,6 +28,8 @@ from ._binary import i16le as i16, o16le as o16, i8 import struct import io +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" diff --git a/src/PIL/OleFileIO.py b/src/PIL/OleFileIO.py deleted file mode 100644 index b3caa10d5..000000000 --- a/src/PIL/OleFileIO.py +++ /dev/null @@ -1,4 +0,0 @@ -raise ImportError( - 'PIL.OleFileIO is deprecated. Use the olefile Python package ' - 'instead. This module will be removed in a future version.' -) diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index e4865b0de..096867207 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -10,6 +10,8 @@ from . import Image, ImageFile from ._binary import o8, o16be as o16b +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "1.0" _Palm8BitColormapValues = ( # noqa: E131 diff --git a/src/PIL/PcdImagePlugin.py b/src/PIL/PcdImagePlugin.py index 87e5792eb..7e8fa3128 100644 --- a/src/PIL/PcdImagePlugin.py +++ b/src/PIL/PcdImagePlugin.py @@ -18,6 +18,8 @@ from . import Image, ImageFile from ._binary import i8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index 471d6647a..b50fe72da 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -16,6 +16,7 @@ # See the README file for information on usage and redistribution. # +import io from . import Image, FontFile from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32 @@ -117,7 +118,7 @@ class PcfFontFile(FontFile.FontFile): for i in range(nprops): p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) if nprops & 3: - fp.seek(4 - (nprops & 3), 1) # pad + fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad data = fp.read(i32(fp.read(4))) diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index bf55a88a3..02dbe26e0 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -25,12 +25,15 @@ # See the README file for information on usage and redistribution. # +import io import logging from . import Image, ImageFile, ImagePalette from ._binary import i8, i16le as i16, o8, o16le as o16 logger = logging.getLogger(__name__) +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.6" @@ -80,7 +83,7 @@ class PcxImageFile(ImageFile.ImageFile): elif version == 5 and bits == 8 and planes == 1: mode = rawmode = "L" # FIXME: hey, this doesn't work with the incremental loader !!! - self.fp.seek(-769, 2) + self.fp.seek(-769, io.SEEK_END) s = self.fp.read(769) if len(s) == 769 and i8(s[0]) == 12: # check if the palette is linear greyscale diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index b42502762..702aaa392 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -25,6 +25,8 @@ import io import os import time +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.5" diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 7216e5b95..8f90b668d 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -269,18 +269,13 @@ class PdfDict(UserDict): else: self.__dict__[key] = value else: - if isinstance(key, str): - key = key.encode("us-ascii") - self[key] = value + self[key.encode("us-ascii")] = value def __getattr__(self, key): try: - value = self[key] + value = self[key.encode("us-ascii")] except KeyError: - try: - value = self[key.encode("us-ascii")] - except KeyError: - raise AttributeError(key) + raise AttributeError(key) if isinstance(value, bytes): value = decode_text(value) if key.endswith("Date"): @@ -361,8 +356,7 @@ def pdf_repr(x): return b"false" elif x is None: return b"null" - elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or - isinstance(x, PdfArray) or isinstance(x, PdfBinary)): + elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): return bytes(x) elif isinstance(x, int): return str(x).encode("us-ascii") @@ -393,8 +387,6 @@ class PdfParser: def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): - # type: (PdfParser, str, file, Union[bytes, bytearray], int, str) - # -> None if buf and f: raise RuntimeError( "specify buf or f or filename, but not both buf and f") diff --git a/src/PIL/PixarImagePlugin.py b/src/PIL/PixarImagePlugin.py index d07b28d07..b4f19a96c 100644 --- a/src/PIL/PixarImagePlugin.py +++ b/src/PIL/PixarImagePlugin.py @@ -22,6 +22,8 @@ from . import Image, ImageFile from ._binary import i16le as i16 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 04161a56c..e04ae2274 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -40,6 +40,8 @@ from . import Image, ImageFile, ImagePalette from ._binary import i8, i16be as i16, i32be as i32, o16be as o16, o32be as o32 from ._util import py3 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.9" logger = logging.getLogger(__name__) @@ -102,7 +104,7 @@ class ChunkStream(object): self.queue = [] def read(self): - "Fetch a new chunk. Returns header information." + """Fetch a new chunk. Returns header information.""" cid = None if self.queue: @@ -134,13 +136,13 @@ class ChunkStream(object): self.queue.append((cid, pos, length)) def call(self, cid, pos, length): - "Call the appropriate chunk handler" + """Call the appropriate chunk handler""" logger.debug("STREAM %r %s %s", cid, pos, length) return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) def crc(self, cid, data): - "Read and verify checksum" + """Read and verify checksum""" # Skip CRC checks for ancillary chunks if allowed to load truncated # images @@ -160,7 +162,7 @@ class ChunkStream(object): % cid) def crc_skip(self, cid, data): - "Read checksum. Used if the C module is not present" + """Read checksum. Used if the C module is not present""" self.fp.read(4) @@ -192,7 +194,7 @@ class iTXt(str): """ @staticmethod - def __new__(cls, text, lang, tkey): + def __new__(cls, text, lang=None, tkey=None): """ :param cls: the class to use when creating the instance :param text: value for this key @@ -296,6 +298,7 @@ class PngStream(ChunkStream): self.im_mode = None self.im_tile = None self.im_palette = None + self.im_custom_mimetype = None self.text_memory = 0 @@ -529,14 +532,7 @@ class PngStream(ChunkStream): # APNG chunks def chunk_acTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) - return s - - def chunk_fcTL(self, pos, length): - s = ImageFile._safe_read(self.fp, length) - return s - - def chunk_fdAT(self, pos, length): - s = ImageFile._safe_read(self.fp, length) + self.im_custom_mimetype = 'image/apng' return s @@ -594,6 +590,7 @@ class PngImageFile(ImageFile.ImageFile): self.info = self.png.im_info self._text = None self.tile = self.png.im_tile + self.custom_mimetype = self.png.im_custom_mimetype if self.png.im_palette: rawmode, data = self.png.im_palette @@ -611,7 +608,7 @@ class PngImageFile(ImageFile.ImageFile): return self._text def verify(self): - "Verify PNG file" + """Verify PNG file""" if self.fp is None: raise RuntimeError("verify must be called directly after open") @@ -627,7 +624,7 @@ class PngImageFile(ImageFile.ImageFile): self.fp = None def load_prepare(self): - "internal: prepare to read PNG file" + """internal: prepare to read PNG file""" if self.info.get("interlace"): self.decoderconfig = self.decoderconfig + (1,) @@ -635,7 +632,7 @@ class PngImageFile(ImageFile.ImageFile): ImageFile.ImageFile.load_prepare(self) def load_read(self, read_bytes): - "internal: read more image data" + """internal: read more image data""" while self.__idat == 0: # end of chunk, skip forward to next one @@ -661,7 +658,7 @@ class PngImageFile(ImageFile.ImageFile): return self.fp.read(read_bytes) def load_end(self): - "internal: finished reading image data" + """internal: finished reading image data""" while True: self.fp.read(4) # CRC @@ -677,6 +674,11 @@ class PngImageFile(ImageFile.ImageFile): self.png.call(cid, pos, length) except UnicodeDecodeError: break + except EOFError: + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + ImageFile._safe_read(self.fp, length) self._text = self.png.im_text self.png.close() self.png = None @@ -908,4 +910,3 @@ Image.register_save(PngImageFile.format, _save) Image.register_extensions(PngImageFile.format, [".png", ".apng"]) Image.register_mime(PngImageFile.format, "image/png") -Image.register_mime(PngImageFile.format, "image/apng") diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c4183d311..1d791abf0 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -17,6 +17,8 @@ from . import Image, ImageFile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" # diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 765895244..6623f8f87 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -16,8 +16,11 @@ # See the README file for information on usage and redistribution. # +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.4" +import io from . import Image, ImageFile, ImagePalette from ._binary import i8, i16be as i16, i32be as i32 @@ -214,12 +217,12 @@ def _layerinfo(file): if size: length = i32(read(4)) if length: - file.seek(length - 16, 1) + file.seek(length - 16, io.SEEK_CUR) combined += length + 4 length = i32(read(4)) if length: - file.seek(length, 1) + file.seek(length, io.SEEK_CUR) combined += length + 4 length = i8(read(1)) @@ -229,7 +232,7 @@ def _layerinfo(file): name = read(length).decode('latin-1', 'replace') combined += length + 1 - file.seek(size - combined, 1) + file.seek(size - combined, io.SEEK_CUR) layers.append((name, mode, (x0, y0, x1, y1))) # get tiles diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 35abfec81..5df1d400c 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -53,6 +53,8 @@ class PyAccess(object): # Keep pointer to im object to prevent dereferencing. self._im = img.im + if self._im.mode == "P": + self._palette = img.palette # Debugging is polluting test traces, only useful here # when hacking on PyAccess @@ -80,6 +82,12 @@ class PyAccess(object): if y < 0: y = self.ysize + y (x, y) = self.check_xy((x, y)) + + if self._im.mode == "P" and \ + isinstance(color, (list, tuple)) and len(color) in [3, 4]: + # RGB or RGBA value for a P image + color = self._palette.getcolor(color) + return self.set_pixel(x, y, color) def __getitem__(self, xy): diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 88df35135..37867bdb7 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -29,6 +29,8 @@ import struct import os +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.3" @@ -98,6 +100,8 @@ class SgiImageFile(ImageFile.ImageFile): self._size = xsize, ysize self.mode = rawmode.split(";")[0] + if self.mode == 'RGB': + self.custom_mimetype = 'image/rgb' # orientation -1 : scanlines begins at the bottom-left corner orientation = -1 @@ -220,7 +224,6 @@ Image.register_decoder("SGI16", SGI16Decoder) Image.register_open(SgiImageFile.format, SgiImageFile, _accept) Image.register_save(SgiImageFile.format, _save) Image.register_mime(SgiImageFile.format, "image/sgi") -Image.register_mime(SgiImageFile.format, "image/rgb") Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index dcde2f395..4d5aa3f0e 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -195,7 +195,8 @@ class SpiderImageFile(ImageFile.ImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index 898350e02..485099fd4 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -20,6 +20,8 @@ from . import Image, ImageFile, ImagePalette from ._binary import i32be as i32 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.3" diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py index 89957fba4..a421b12a5 100644 --- a/src/PIL/TarIO.py +++ b/src/PIL/TarIO.py @@ -14,6 +14,7 @@ # See the README file for information on usage and redistribution. # +import io import sys from . import ContainerIO @@ -51,7 +52,7 @@ class TarIO(ContainerIO.ContainerIO): if file == name: break - self.fh.seek((size + 511) & (~511), 1) + self.fh.seek((size + 511) & (~511), io.SEEK_CUR) # Open region ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 4c9bbc886..ae9697b29 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -22,6 +22,8 @@ from ._binary import i8, i16le as i16, o8, o16le as o16 import warnings +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.3" diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index be24624d6..aac630ea1 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -66,6 +66,8 @@ except ImportError: from collections import MutableMapping +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "1.3.5" DEBUG = False # Needs to be merged with the new logging approach. @@ -133,6 +135,9 @@ COMPRESSION_INFO = { 32946: "tiff_deflate", 34676: "tiff_sgilog", 34677: "tiff_sgilog24", + 34925: "lzma", + 50000: "zstd", + 50001: "webp", } COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} @@ -421,7 +426,7 @@ class ImageFileDirectory_v2(MutableMapping): ifd = ImageFileDirectory_v2() ifd[key] = 'Some Data' - ifd.tagtype[key] = 2 + ifd.tagtype[key] = TiffTags.ASCII print(ifd[key]) 'Some Data' @@ -555,7 +560,7 @@ class ImageFileDirectory_v2(MutableMapping): if info.type: self.tagtype[tag] = info.type else: - self.tagtype[tag] = 7 + self.tagtype[tag] = TiffTags.UNDEFINED if all(isinstance(v, IFDRational) for v in values): self.tagtype[tag] = TiffTags.RATIONAL elif all(isinstance(v, int) for v in values): @@ -870,7 +875,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): ifd = ImageFileDirectory_v1() ifd[key] = 'Some Data' - ifd.tagtype[key] = 2 + ifd.tagtype[key] = TiffTags.ASCII print(ifd[key]) ('Some Data',) @@ -963,7 +968,7 @@ class TiffImageFile(ImageFile.ImageFile): _close_exclusive_fp_after_loading = False def _open(self): - "Open the first image in a TIFF file" + """Open the first image in a TIFF file""" # Header ifh = self.fp.read(8) @@ -1020,7 +1025,7 @@ class TiffImageFile(ImageFile.ImageFile): return self._is_animated def seek(self, frame): - "Select a given frame as current image" + """Select a given frame as current image""" if not self._seek_check(frame): return self._seek(frame) @@ -1058,7 +1063,7 @@ class TiffImageFile(ImageFile.ImageFile): self._setup() def tell(self): - "Return the current frame number" + """Return the current frame number""" return self.__frame @property @@ -1173,7 +1178,7 @@ class TiffImageFile(ImageFile.ImageFile): return Image.Image.load(self) def _setup(self): - "Setup this image object based on current tags" + """Setup this image object based on current tags""" if 0xBC01 in self.tag_v2: raise IOError("Windows Media Photo files not yet supported") @@ -1361,7 +1366,8 @@ class TiffImageFile(ImageFile.ImageFile): def _close__fp(self): try: - self.__fp.close() + if self.__fp != self.fp: + self.__fp.close() except AttributeError: pass finally: @@ -1433,7 +1439,7 @@ def _save(im, fp, filename): try: ifd.tagtype[key] = info.tagtype[key] except Exception: - pass # might not be an IFD, Might not have populated type + pass # might not be an IFD. Might not have populated type # additions written by Greg Couch, gregc@cgl.ucsf.edu # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com @@ -1677,7 +1683,7 @@ class AppendingTiffWriter: def tell(self): return self.f.tell() - self.offsetOfNewPage - def seek(self, offset, whence): + def seek(self, offset, whence=io.SEEK_SET): if whence == os.SEEK_SET: offset += self.offsetOfNewPage diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 81699bda7..06eb851c9 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -27,6 +27,8 @@ from ._binary import i16le as word, si16le as short, \ from ._util import py3 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" _handler = None diff --git a/src/PIL/XVThumbImagePlugin.py b/src/PIL/XVThumbImagePlugin.py index 8cdd84817..ad913b2a8 100644 --- a/src/PIL/XVThumbImagePlugin.py +++ b/src/PIL/XVThumbImagePlugin.py @@ -20,6 +20,8 @@ from . import Image, ImageFile, ImagePalette from ._binary import i8, o8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.1" _MAGIC = b"P7 332" diff --git a/src/PIL/XbmImagePlugin.py b/src/PIL/XbmImagePlugin.py index 0cccda17c..af5adccd2 100644 --- a/src/PIL/XbmImagePlugin.py +++ b/src/PIL/XbmImagePlugin.py @@ -22,6 +22,8 @@ import re from . import Image, ImageFile +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.6" # XBM header diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 02bc28a17..9cecdbca2 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -19,6 +19,8 @@ import re from . import Image, ImageFile, ImagePalette from ._binary import i8, o8 +# __version__ is deprecated and will be removed in a future version. Use +# PIL.__version__ instead. __version__ = "0.2" # XPM header diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index bc8cfed8c..ec0611b68 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -16,10 +16,9 @@ PIL.VERSION is the old PIL version and will be removed in the future. from . import _version -# VERSION is deprecated and will be removed in Pillow 6.0.0. -# PILLOW_VERSION is deprecated and will be removed after that. +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0. # Use __version__ instead. -VERSION = '1.1.7' # PIL Version PILLOW_VERSION = __version__ = _version.__version__ del _version diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 5828c2c24..cb307050c 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -2,13 +2,20 @@ import os import sys py3 = sys.version_info.major >= 3 +py36 = sys.version_info[0:2] >= (3, 6) if py3: def isStringType(t): return isinstance(t, str) - def isPath(f): - return isinstance(f, (bytes, str)) + if py36: + from pathlib import Path + + def isPath(f): + return isinstance(f, (bytes, str, Path)) + else: + def isPath(f): + return isinstance(f, (bytes, str)) else: def isStringType(t): return isinstance(t, basestring) # noqa: F821 diff --git a/src/PIL/_version.py b/src/PIL/_version.py index 3bfc16ff4..0b0d90e80 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = '5.4.0.dev0' +__version__ = '6.0.0.dev0' diff --git a/src/_imaging.c b/src/_imaging.c index ed4702d55..7715a7995 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1420,7 +1420,7 @@ _putdata(ImagingObject* self, PyObject* args) image = self->image; n = PyObject_Length(data); - if (n > (Py_ssize_t) (image->xsize * image->ysize)) { + if (n > (Py_ssize_t)image->xsize * (Py_ssize_t)image->ysize) { PyErr_SetString(PyExc_TypeError, "too many data entries"); return NULL; } diff --git a/src/libImaging/Geometry.c b/src/libImaging/Geometry.c index 1d08728da..5358f6eeb 100644 --- a/src/libImaging/Geometry.c +++ b/src/libImaging/Geometry.c @@ -39,7 +39,11 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) { - FLIP_LEFT_RIGHT(UINT8, image8) + if (strncmp(imIn->mode, "I;16", 4) == 0) { + FLIP_LEFT_RIGHT(UINT16, image8) + } else { + FLIP_LEFT_RIGHT(UINT8, image8) + } } else { FLIP_LEFT_RIGHT(INT32, image32) } @@ -253,7 +257,11 @@ ImagingRotate180(Imaging imOut, Imaging imIn) yr = imIn->ysize-1; if (imIn->image8) { - ROTATE_180(UINT8, image8) + if (strncmp(imIn->mode, "I;16", 4) == 0) { + ROTATE_180(UINT16, image8) + } else { + ROTATE_180(UINT8, image8) + } } else { ROTATE_180(INT32, image32) } diff --git a/tox.ini b/tox.ini index 1226619db..fce4a4206 100644 --- a/tox.ini +++ b/tox.ini @@ -4,12 +4,29 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py34, py35, py36, py37 +envlist = + lint + py{27,35,36,37} +minversion = 1.9 [testenv] commands = {envpython} setup.py clean {envpython} setup.py build_ext --inplace {envpython} selftest.py - {envpython} -m pytest -qq {posargs} -deps = pytest + {envpython} -m pytest {posargs} +deps = + cffi + numpy + olefile + pyroma + pytest + +[testenv:lint] +commands = + flake8 --statistics --count + check-manifest +deps = + check-manifest + flake8 +skip_install = true diff --git a/winbuild/appveyor_install_pypy.cmd b/winbuild/appveyor_install_pypy.cmd index 8c254ad90..3092fc617 100644 --- a/winbuild/appveyor_install_pypy.cmd +++ b/winbuild/appveyor_install_pypy.cmd @@ -1,3 +1,3 @@ -curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-win32.zip +curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-win32.zip 7z x pypy2.zip -oc:\ -c:\Python37\Scripts\virtualenv.exe -p c:\pypy2-v6.0.0-win32\pypy.exe c:\vp\pypy2 +c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.0.0-win32\pypy.exe c:\vp\pypy2 diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 5cd5cb14d..13296dc5b 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -259,6 +259,30 @@ endlocal """ % compiler # noqa: E501 +def build_ghostscript(compiler, bit): + script = r""" +rem Build gs +setlocal +""" + vc_setup(compiler, bit) + r""" +set MSVC_VERSION=""" + { + "2008": "9", + "2015": "14" + }[compiler['vc_version']] + r""" +set RCOMP="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\RC.Exe" +cd /D %%GHOSTSCRIPT%% +""" + if bit == 64: + script += r""" +set WIN64="" +""" + script += r""" +nmake -f psi/msvc.mak +copy /Y /B bin\ C:\Python27\ +endlocal +""" + return script % compiler # noqa: E501 + + def add_compiler(compiler, bit): script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler, bit)) @@ -268,6 +292,7 @@ def add_compiler(compiler, bit): script.append(msbuild_freetype(compiler)) script.append(build_lcms2(compiler)) # script.append(nmake_openjpeg(compiler)) + script.append(build_ghostscript(compiler, bit)) script.append(end_compiler()) @@ -282,9 +307,8 @@ if 'PYTHON' in os.environ: add_compiler(compiler_from_env(), bit_from_env()) else: # for compiler in all_compilers(): - # add_compiler(compiler) + # add_compiler(compiler) add_compiler(compilers[7.0][2008][32], 32) - # add_compiler(compilers[7.1][2010][64]) with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) diff --git a/winbuild/config.py b/winbuild/config.py index 8edb7ea1a..da6f7855b 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -5,7 +5,6 @@ PILLOW_DEPENDS_DIR = 'C:\\pillow-depends\\' pythons = {'27': {'compiler': 7, 'vc': 2008}, 'pypy2': {'compiler': 7, 'vc': 2008}, - '34': {'compiler': 7.1, 'vc': 2010}, '35': {'compiler': 7.1, 'vc': 2015}, '36': {'compiler': 7.1, 'vc': 2015}, '37': {'compiler': 7.1, 'vc': 2015}} @@ -43,6 +42,11 @@ libs = { 'filename': PILLOW_DEPENDS_DIR + 'lcms2-2.7.zip', 'dir': 'lcms2-2.7', }, + 'ghostscript': { + 'url': 'https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs926/ghostscript-9.26.tar.gz', # noqa: E501 + 'filename': PILLOW_DEPENDS_DIR + 'ghostscript-9.26.tar.gz', + 'dir': 'ghostscript-9.26', + }, 'tcl-8.5': { 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.19/tcl8519-src.zip', 'filename': PILLOW_DEPENDS_DIR + 'tcl8519-src.zip', @@ -66,9 +70,9 @@ libs = { 'version': '8.6.9', }, 'webp': { - 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-1.0.1.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'libwebp-1.0.1.tar.gz', - 'dir': 'libwebp-1.0.1', + 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz', + 'filename': PILLOW_DEPENDS_DIR + 'libwebp-1.0.2.tar.gz', + 'dir': 'libwebp-1.0.2', }, 'openjpeg': { 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.3.0/openjpeg-2.3.0.tar.gz', @@ -99,24 +103,6 @@ compilers = { } }, 7.1: { - 2010: { - 64: { - 'env_version': 'v7.1', - 'vc_version': '2010', - 'env_flags': '/x64 /vista', - 'inc_dir': 'msvcr10-x64', - 'platform': 'x64', - 'webp_platform': 'x64', - }, - 32: { - 'env_version': 'v7.1', - 'vc_version': '2010', - 'env_flags': '/x86 /vista', - 'inc_dir': 'msvcr10-x32', - 'platform': 'Win32', - 'webp_platform': 'x86', - } - }, 2015: { 64: { 'env_version': 'v7.1',