diff --git a/.appveyor.yml b/.appveyor.yml index 8ab594134..2e3fd0a58 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,10 +15,6 @@ environment: matrix: - PYTHON: C:/Python38 - PYTHON: C:/Python38-x64 - - PYTHON: C:/Python37 - - PYTHON: C:/Python37-x64 - - PYTHON: C:/Python36 - - PYTHON: C:/Python36-x64 - PYTHON: C:/Python35 - PYTHON: C:/Python35-x64 - PYTHON: C:/msys64/mingw32 @@ -40,11 +36,6 @@ install: - xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\ - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images - cd c:\pillow\winbuild\ -- ps: | - if ($env:PYTHON -eq "c:/vp/pypy2") - { - c:\pillow\winbuild\appveyor_install_pypy2.cmd - } - ps: | if ($env:PYTHON -eq "c:/vp/pypy3") { @@ -61,6 +52,9 @@ install: c:\pillow\winbuild\build_deps.cmd $host.SetShouldExit(0) } +- curl -fsSL -o gs950.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs950/gs950w32.exe +- gs950.exe /S +- path %path%;C:\Program Files (x86)\gs\gs9.50\bin build_script: - ps: | @@ -82,7 +76,7 @@ test_script: - cd c:\pillow - '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov' - c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE% -- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests' +- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests' #- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest? after_test: diff --git a/.codecov.yml b/.codecov.yml index a93095486..d348345dc 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -9,3 +9,10 @@ codecov: token: 6dafc396-e7f5-4221-a38a-8b07a49fbdae comment: off + +# Matches 'omit:' in .coveragerc +ignore: + - "Tests/32bit_segfault_check.py" + - "Tests/bench_cffi_access.py" + - "Tests/check_*.py" + - "Tests/createfontdatachunk.py" diff --git a/.coveragerc b/.coveragerc index ea79190ae..f1f095806 100644 --- a/.coveragerc +++ b/.coveragerc @@ -12,3 +12,10 @@ exclude_lines = # Don't complain about debug code if Image.DEBUG: if DEBUG: + +[run] +omit = + Tests/32bit_segfault_check.py + Tests/bench_cffi_access.py + Tests/check_*.py + Tests/createfontdatachunk.py diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b3d456659..3d27b5d88 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,14 +9,14 @@ Please send a pull request to the master branch. Please include [documentation]( - Fork the Pillow repository. - Create a branch from master. - Develop bug fixes, features, tests, etc. -- Run the test suite on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests. +- Run the test suite. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests. - Create a pull request to pull the changes from your branch to the Pillow master. ### Guidelines - Separate code commits from reformatting commits. - Provide tests for any newly added code. -- Follow PEP8. +- Follow PEP 8. - When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor. ## Reporting Issues diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 075707bf6..00cae9bc2 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.5", "3.6", "3.7"] + python-version: ["3.5", "3.6", "3.7", "3.8", "pypy3.6"] architecture: ["x86", "x64"] include: - architecture: "x86" @@ -31,6 +31,21 @@ jobs: steps: - uses: actions/checkout@v1 + - uses: actions/checkout@v1 + with: + repository: python-pillow/pillow-depends + ref: master + + - name: Cache + uses: actions/cache@v1 + with: + path: ~\AppData\Local\pip\Cache + key: + ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.architecture }}-${{ hashFiles('**/.github/workflows/test-windows.yml') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.architecture }}- + ${{ runner.os }}-${{ matrix.python-version }}- + - name: Install PyPy if: "contains(matrix.python-version, 'pypy')" run: | @@ -55,32 +70,25 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - - name: pip install wheel pytest pytest-cov codecov + - name: pip install wheel pytest pytest-cov run: | "%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov - pip install codecov shell: cmd - name: Fetch dependencies run: | - curl -fsSL -o nasm.zip https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/win64/nasm-2.14.02-win64.zip - 7z x nasm.zip "-o$env:RUNNER_WORKSPACE\" + 7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\" Write-Host "`#`#[add-path]$env:RUNNER_WORKSPACE\nasm-2.14.02" Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02" - # 32-bit should work on both platforms - curl -fsSL -o gs950.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs950/gs950w32.exe - ./gs950.exe /S + ..\pillow-depends\gs950w32.exe /S Write-Host "`#`#[add-path]C:\Program Files (x86)\gs\gs9.50\bin" Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" $env:PYTHON=$env:pythonLocation - curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/master.zip - 7z x pillow-depends.zip -oc:\ - mv c:\pillow-depends-master c:\pillow-depends - xcopy c:\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\ - xcopy c:\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\ - xcopy /s c:\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ + xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\ + xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\ + xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ cd $env:GITHUB_WORKSPACE/winbuild/ python.exe $env:GITHUB_WORKSPACE\winbuild\build_dep.py env: @@ -253,7 +261,6 @@ jobs: # for Raqm - name: Build dependencies / HarfBuzz - if: "!contains(matrix.python-version, 'pypy')" run: | set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 @@ -274,7 +281,6 @@ jobs: # for Raqm - name: Build dependencies / FriBidi - if: "!contains(matrix.python-version, 'pypy')" run: | set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 @@ -292,9 +298,7 @@ jobs: copy /Y /B *.lib %INCLIB% shell: cmd - # failing with PyPy3 - name: Build dependencies / Raqm - if: "!contains(matrix.python-version, 'pypy')" run: | set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 @@ -324,6 +328,7 @@ jobs: set LIB=%INCLIB%;%PYTHON%\tcl set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE% call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} + set MSSdk=1 set DISTUTILS_USE_SDK=1 set py_vcruntime_redist=true %PYTHON%\python.exe setup.py build_ext install @@ -346,7 +351,7 @@ jobs: rem Add libraqm.dll (copied to INCLIB) to PATH. path %INCLIB%;%PATH% cd /D %GITHUB_WORKSPACE% - %PYTHON%\python.exe -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests + %PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests shell: cmd - name: Upload errors @@ -357,8 +362,11 @@ jobs: path: Tests/errors - name: Upload coverage - run: 'codecov --file "%GITHUB_WORKSPACE%\coverage.xml" --name "%pythonLocation%"' - shell: cmd + if: success() + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + name: ${{ runner.os }} Python ${{ matrix.python-version }} - name: Build wheel id: wheel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 163250248..69ed445a6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,26 @@ jobs: steps: - uses: actions/checkout@v1 + - name: Ubuntu cache + uses: actions/cache@v1 + if: startsWith(matrix.os, 'ubuntu') + with: + path: ~/.cache/pip + key: + ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.travis/*.sh') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.python-version }}- + + - name: macOS cache + uses: actions/cache@v1 + if: startsWith(matrix.os, 'macOS') + with: + path: ~/Library/Caches/pip + key: + ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.travis/*.sh') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.python-version }}- + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -71,5 +91,13 @@ jobs: run: | .travis/after_success.sh env: + MATRIX_OS: ${{ matrix.os }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + + - name: Upload coverage + if: success() + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + name: ${{ matrix.os }} Python ${{ matrix.python-version }} diff --git a/.travis.yml b/.travis.yml index 56542996b..9dea5fb14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ notifications: irc: "chat.freenode.net#pil" # Run fast lint first to get fast feedback. -# Run slow PyPy* next, to give them a headstart and reduce waiting time. -# Run latest 3.x and 2.x next, to get quick compatibility results. +# Run slow PyPy next, to give it a headstart and reduce waiting time. +# Run latest 3.x next, to get quick compatibility results. # Then run the remainder, with fastest Docker jobs last. matrix: diff --git a/.travis/after_success.sh b/.travis/after_success.sh index 1dca2ccb9..ec4f027b4 100755 --- a/.travis/after_success.sh +++ b/.travis/after_success.sh @@ -1,7 +1,12 @@ #!/bin/bash # gather the coverage data -sudo apt-get -qq install lcov +if [[ "$MATRIX_OS" == "macOS-latest" ]]; then + brew install lcov +else + sudo apt-get -qq install lcov +fi + lcov --capture --directory . -b . --output-file coverage.info # filter to remove system headers lcov --remove coverage.info '/usr/*' -o coverage.filtered.info @@ -11,16 +16,14 @@ coveralls-lcov -v -n coverage.filtered.info > coverage.c.json coverage report pip install codecov -if [[ $TRAVIS_PYTHON_VERSION != "2.7_with_system_site_packages" ]]; then - # Not working here. Just skip it, it's being removed soon. - pip install coveralls-merge - coveralls-merge coverage.c.json +pip install coveralls-merge +coveralls-merge coverage.c.json +if [[ $TRAVIS ]]; then + codecov fi -codecov -if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then +if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ] && [ "$DOCKER" == "" ]; then # Coverage and quality reports on just the latest diff. - # (Installation is very slow on Py3, so just do it for Py2.) depends/diffcover-install.sh depends/diffcover-run.sh fi diff --git a/.travis/install.sh b/.travis/install.sh index 6ad89402e..747acb448 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -20,8 +20,8 @@ if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then pip install pyqt5 fi -# docs only on Python 2.7 -if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi +# docs only on Python 3.7 +if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then pip install -r requirements.txt ; fi # webp pushd depends && ./install_webp.sh && popd diff --git a/.travis/test.sh b/.travis/test.sh index 67c390ebd..91dae9f8d 100755 --- a/.travis/test.sh +++ b/.travis/test.sh @@ -2,7 +2,7 @@ set -e -python -m pytest -v -x --cov PIL --cov-report term Tests +python -m pytest -v -x --cov PIL --cov Tests --cov-report term Tests # Docs -if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make doccheck; fi +if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then make doccheck; fi diff --git a/CHANGES.rst b/CHANGES.rst index eb96600ed..5af03f281 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,24 @@ Changelog (Pillow) 7.0.0 (unreleased) ------------------ +- Handle broken Photoshop data #4239 + [radarhere] + +- Raise a specific exception if no data is found for an MPO frame #4240 + [radarhere] + +- Fix Unicode support for PyPy #4145 + [nulano] + +- Drop support for EOL Python 2.7 #4109 + [hugovk, radarhere, jdufresne] + +- Added UnidentifiedImageError #4182 + [radarhere, hugovk] + +- Remove deprecated __version__ from plugins #4197 + [hugovk, radarhere] + - Fixed freeing unallocated pointer when resizing with height too large #4116 [radarhere] @@ -14,7 +32,7 @@ Changelog (Pillow) - Corrected DdsImagePlugin setting info gamma #4171 [radarhere] -- Depends: Update libtiff to 4.1.0 #4195 +- Depends: Update libtiff to 4.1.0 #4195, Tk Tcl to 8.6.10 #4229 [radarhere] - Improve handling of file resources #3577 diff --git a/Makefile b/Makefile index 1803e617d..fdde3416b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ .DEFAULT_GOAL := release-test clean: - python setup.py clean + python3 setup.py clean rm src/PIL/*.so || true rm -r build || true find . -name __pycache__ | xargs rm -r || true @@ -15,8 +15,8 @@ co: done coverage: - python selftest.py - python setup.py test + python3 selftest.py + python3 setup.py test rm -r htmlcov || true coverage report @@ -30,7 +30,7 @@ doccheck: $(MAKE) -C docs linkcheck || true docserve: - cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& + cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null& help: @echo "Welcome to Pillow development. Please use \`make \` where is one of" @@ -50,22 +50,22 @@ help: @echo " upload-test build and upload sdists to test.pythonpackages.com" inplace: clean - python setup.py develop build_ext --inplace + python3 setup.py develop build_ext --inplace install: - python setup.py install - python selftest.py + python3 setup.py install + python3 selftest.py install-coverage: - CFLAGS="-coverage" python setup.py build_ext install - python selftest.py + CFLAGS="-coverage" python3 setup.py build_ext install + python3 selftest.py debug: # make a debug version if we don't have a -dbg python. Leaves in symbols # for our stuff, kills optimization, and redirects to dev null so we # see any build failures. make clean > /dev/null - CFLAGS='-g -O0' python setup.py build_ext install > /dev/null + CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null install-req: pip install -r requirements.txt @@ -76,17 +76,17 @@ install-venv: release-test: $(MAKE) install-req - python setup.py develop - python selftest.py - python -m pytest Tests - python setup.py install - python -m pytest -qq + python3 setup.py develop + python3 selftest.py + python3 -m pytest Tests + python3 setup.py install + python3 -m pytest -qq check-manifest pyroma . viewdoc sdist: - python setup.py sdist --format=gztar + python3 setup.py sdist --format=gztar test: pytest -qq @@ -97,10 +97,10 @@ upload-test: # username: # password: # repository = http://test.pythonpackages.com - python setup.py sdist --format=gztar upload -r test + python3 setup.py sdist --format=gztar upload -r test upload: - python setup.py sdist --format=gztar upload + python3 setup.py sdist --format=gztar upload readme: viewdoc diff --git a/Tests/README.rst b/Tests/README.rst index da3297bce..9c959a4bf 100644 --- a/Tests/README.rst +++ b/Tests/README.rst @@ -27,6 +27,6 @@ Run all the tests from the root of the Pillow source distribution:: Or with coverage:: - pytest --cov PIL --cov-report term + pytest --cov PIL --cov Tests --cov-report term coverage html open htmlcov/index.html diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 99c7006fa..1797d34fc 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -1,8 +1,9 @@ import time +import unittest from PIL import PyAccess -from .helper import PillowTestCase, hopper, unittest +from .helper import 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 deleted file mode 100644 index 8a54ff921..000000000 --- a/Tests/bench_get.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys -import timeit - -from . import helper - -sys.path.insert(0, ".") - - -def bench(mode): - im = helper.hopper(mode) - get = im.im.getpixel - xy = 50, 50 # position shouldn't really matter - t0 = timeit.default_timer() - for _ in range(1000000): - get(xy) - print(mode, timeit.default_timer() - t0, "us") - - -bench("L") -bench("I") -bench("I;16") -bench("F") -bench("RGB") diff --git a/Tests/check_fli_overflow.py b/Tests/check_fli_overflow.py index db6559f1e..206a86007 100644 --- a/Tests/check_fli_overflow.py +++ b/Tests/check_fli_overflow.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase TEST_FILE = "Tests/images/fli_overflow.fli" @@ -9,8 +11,8 @@ class TestFliOverflow(PillowTestCase): def test_fli_overflow(self): # this should not crash with a malloc error or access violation - im = Image.open(TEST_FILE) - im.load() + with Image.open(TEST_FILE) as im: + im.load() if __name__ == "__main__": diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index a109b5cd5..2c1793a4f 100755 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -1,10 +1,9 @@ #!/usr/bin/env python - -from __future__ import division +import unittest from PIL import Image -from .helper import PillowTestCase, is_win32, unittest +from .helper import PillowTestCase, is_win32 min_iterations = 100 max_iterations = 10000 diff --git a/Tests/check_j2k_leaks.py b/Tests/check_j2k_leaks.py index d9c6c68b7..1635f1001 100755 --- a/Tests/check_j2k_leaks.py +++ b/Tests/check_j2k_leaks.py @@ -1,8 +1,9 @@ +import unittest from io import BytesIO from PIL import Image -from .helper import PillowTestCase, is_win32, unittest +from .helper import PillowTestCase, is_win32 # Limits for testing the leak mem_limit = 1024 * 1048576 diff --git a/Tests/check_j2k_overflow.py b/Tests/check_j2k_overflow.py index 3e6cf8d34..d5b6e455f 100644 --- a/Tests/check_j2k_overflow.py +++ b/Tests/check_j2k_overflow.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase class TestJ2kEncodeOverflow(PillowTestCase): diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 8cb93f2dd..6b2801a21 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -1,6 +1,7 @@ +import unittest from io import BytesIO -from .helper import PillowTestCase, hopper, is_win32, unittest +from .helper import PillowTestCase, hopper, is_win32 iterations = 5000 diff --git a/Tests/check_large_memory.py b/Tests/check_large_memory.py index 5df476e0a..7fcaa4cf9 100644 --- a/Tests/check_large_memory.py +++ b/Tests/check_large_memory.py @@ -1,8 +1,9 @@ import sys +import unittest from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase # This test is not run automatically. # diff --git a/Tests/check_large_memory_numpy.py b/Tests/check_large_memory_numpy.py index 4653a6013..8e65dc1cb 100644 --- a/Tests/check_large_memory_numpy.py +++ b/Tests/check_large_memory_numpy.py @@ -1,8 +1,9 @@ import sys +import unittest from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase # This test is not run automatically. # diff --git a/Tests/check_libtiff_segfault.py b/Tests/check_libtiff_segfault.py index ae9a46d1b..b272c601c 100644 --- a/Tests/check_libtiff_segfault.py +++ b/Tests/check_libtiff_segfault.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase TEST_FILE = "Tests/images/libtiff_segfault.tif" @@ -12,8 +14,8 @@ class TestLibtiffSegfault(PillowTestCase): """ with self.assertRaises(IOError): - im = Image.open(TEST_FILE) - im.load() + with Image.open(TEST_FILE) as im: + im.load() if __name__ == "__main__": diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 5c78ce122..f133b2695 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -1,9 +1,10 @@ +import unittest import zlib from io import BytesIO from PIL import Image, ImageFile, PngImagePlugin -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase TEST_FILE = "Tests/images/png_decompression_dos.png" diff --git a/Tests/createfontdatachunk.py b/Tests/createfontdatachunk.py index 4d189dbad..c7055995e 100755 --- a/Tests/createfontdatachunk.py +++ b/Tests/createfontdatachunk.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -from __future__ import print_function - import base64 import os diff --git a/Tests/helper.py b/Tests/helper.py index 57bbab0c9..0f8e05c19 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -1,7 +1,6 @@ """ Helper functions. """ -from __future__ import print_function import logging import os @@ -12,7 +11,6 @@ import unittest from io import BytesIO from PIL import Image, ImageMath -from PIL._util import py3 logger = logging.getLogger(__name__) @@ -76,10 +74,13 @@ class PillowTestCase(unittest.TestCase): def assert_deep_equal(self, a, b, msg=None): try: self.assertEqual( - len(a), len(b), msg or "got length %s, expected %s" % (len(a), len(b)) + len(a), + len(b), + msg or "got length {}, expected {}".format(len(a), len(b)), ) self.assertTrue( - all(x == y for x, y in zip(a, b)), msg or "got %s, expected %s" % (a, b) + all(x == y for x, y in zip(a, b)), + msg or "got {}, expected {}".format(a, b), ) except Exception: self.assertEqual(a, b, msg) @@ -87,20 +88,24 @@ class PillowTestCase(unittest.TestCase): def assert_image(self, im, mode, size, msg=None): if mode is not None: self.assertEqual( - im.mode, mode, msg or "got mode %r, expected %r" % (im.mode, mode) + im.mode, + mode, + msg or "got mode {!r}, expected {!r}".format(im.mode, mode), ) if size is not None: self.assertEqual( - im.size, size, msg or "got size %r, expected %r" % (im.size, size) + im.size, + size, + msg or "got size {!r}, expected {!r}".format(im.size, size), ) def assert_image_equal(self, a, b, msg=None): self.assertEqual( - a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode) + a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode) ) self.assertEqual( - a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) + a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size) ) if a.tobytes() != b.tobytes(): if HAS_UPLOADER: @@ -121,10 +126,10 @@ class PillowTestCase(unittest.TestCase): def assert_image_similar(self, a, b, epsilon, msg=None): epsilon = float(epsilon) self.assertEqual( - a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode) + a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode) ) self.assertEqual( - a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) + a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size) ) a, b = convert_to_comparable(a, b) @@ -216,12 +221,12 @@ class PillowTestCase(unittest.TestCase): def open_withImagemagick(self, f): if not imagemagick_available(): - raise IOError() + raise OSError() outfile = self.tempfile("temp.png") if command_succeeds([IMCONVERT, f, outfile]): return Image.open(outfile) - raise IOError() + raise OSError() @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") @@ -264,11 +269,6 @@ class PillowLeakTestCase(PillowTestCase): # helpers -if not py3: - # Remove DeprecationWarning in Python 3 - PillowTestCase.assertRaisesRegex = PillowTestCase.assertRaisesRegexp - PillowTestCase.assertRegex = PillowTestCase.assertRegexpMatches - def fromstring(data): return Image.open(BytesIO(data)) @@ -306,11 +306,10 @@ def command_succeeds(cmd): Runs the command, which must be a list of strings. Returns True if the command succeeds, or False if an OSError was raised by subprocess.Popen. """ - with open(os.devnull, "wb") as f: - try: - subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT) - except OSError: - return False + try: + subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) + except OSError: + return False return True @@ -373,7 +372,7 @@ def distro(): return line.strip().split("=")[1] -class cached_property(object): +class cached_property: def __init__(self, func): self.func = func diff --git a/Tests/images/photoshop-200dpi-broken.jpg b/Tests/images/photoshop-200dpi-broken.jpg new file mode 100644 index 000000000..a574872f2 Binary files /dev/null and b/Tests/images/photoshop-200dpi-broken.jpg differ diff --git a/Tests/images/sugarshack_no_data.mpo b/Tests/images/sugarshack_no_data.mpo new file mode 100644 index 000000000..d94bad53b Binary files /dev/null and b/Tests/images/sugarshack_no_data.mpo differ diff --git a/Tests/import_all.py b/Tests/import_all.py deleted file mode 100644 index 4dfacb291..000000000 --- a/Tests/import_all.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import print_function - -import glob -import os -import sys -import traceback - -sys.path.insert(0, ".") - -for file in glob.glob("src/PIL/*.py"): - module = os.path.basename(file)[:-3] - try: - exec("from PIL import " + module) - except (ImportError, SyntaxError): - print("===", "failed to import", module) - traceback.print_exc() diff --git a/Tests/make_hash.py b/Tests/make_hash.py deleted file mode 100644 index bacb391fa..000000000 --- a/Tests/make_hash.py +++ /dev/null @@ -1,68 +0,0 @@ -# brute-force search for access descriptor hash table - -from __future__ import print_function - -modes = [ - "1", - "L", - "LA", - "La", - "I", - "I;16", - "I;16L", - "I;16B", - "I;32L", - "I;32B", - "F", - "P", - "PA", - "RGB", - "RGBA", - "RGBa", - "RGBX", - "CMYK", - "YCbCr", - "LAB", - "HSV", -] - - -def hash(s, i): - # djb2 hash: multiply by 33 and xor character - for c in s: - i = (((i << 5) + i) ^ ord(c)) & 0xFFFFFFFF - return i - - -def check(size, i0): - h = [None] * size - for m in modes: - i = hash(m, i0) - i = i % size - if h[i]: - return 0 - h[i] = m - return h - - -min_start = 0 - -# 1) find the smallest table size with no collisions -for min_size in range(len(modes), 16384): - if check(min_size, 0): - print(len(modes), "modes fit in", min_size, "slots") - break - -# 2) see if we can do better with a different initial value -for i0 in range(65556): - for size in range(1, min_size): - if check(size, i0): - if size < min_size: - print(len(modes), "modes fit in", size, "slots with start", i0) - min_size = size - min_start = i0 - -print() - -print("#define ACCESS_TABLE_SIZE", min_size) -print("#define ACCESS_TABLE_HASH", min_start) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index f7a60cfd7..3a33d2c8a 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import os from PIL import Image @@ -109,4 +107,4 @@ class TestBmpReference(PillowTestCase): os.path.join(base, "g", "pal4rle.bmp"), ) if f not in unsupported: - self.fail("Unsupported Image %s: %s" % (f, msg)) + self.fail("Unsupported Image {}: {}".format(f, msg)) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index ca82209c2..96461ca3a 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -1,10 +1,9 @@ -from __future__ import division - +import unittest from array import array from PIL import Image, ImageFilter -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase try: import numpy diff --git a/Tests/test_core_resources.py b/Tests/test_core_resources.py index 0aedb91c9..ac2970de2 100644 --- a/Tests/test_core_resources.py +++ b/Tests/test_core_resources.py @@ -1,10 +1,9 @@ -from __future__ import division, print_function - import sys +import unittest from PIL import Image -from .helper import PillowTestCase, is_pypy, unittest +from .helper import PillowTestCase, is_pypy class TestCoreStats(PillowTestCase): diff --git a/Tests/test_features.py b/Tests/test_features.py index 06da35ee4..eb51407a1 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -1,10 +1,9 @@ -from __future__ import unicode_literals - import io +import unittest from PIL import features -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase try: from PIL import _webp diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index 8dbd82986..1e8dff184 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -15,6 +15,6 @@ class TestFileBlp(PillowTestCase): self.assert_image_equal(im, target) def test_load_blp2_dxt1a(self): - im = Image.open("Tests/images/blp/blp2_dxt1a.blp") - target = Image.open("Tests/images/blp/blp2_dxt1a.png") - self.assert_image_equal(im, target) + with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im: + with Image.open("Tests/images/blp/blp2_dxt1a.png") as target: + self.assert_image_equal(im, target) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index e7f2c9315..76cd98aba 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -11,12 +11,12 @@ class TestFileBmp(PillowTestCase): im.save(outfile, "BMP") - reloaded = Image.open(outfile) - reloaded.load() - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "BMP") - self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") + with Image.open(outfile) as reloaded: + reloaded.load() + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual(im.size, reloaded.size) + self.assertEqual(reloaded.format, "BMP") + self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") def test_sanity(self): self.roundtrip(hopper()) @@ -36,11 +36,10 @@ class TestFileBmp(PillowTestCase): im.save(output, "BMP") output.seek(0) - reloaded = Image.open(output) - - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "BMP") + with Image.open(output) as reloaded: + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual(im.size, reloaded.size) + self.assertEqual(reloaded.format, "BMP") def test_dpi(self): dpi = (72, 72) @@ -57,17 +56,17 @@ class TestFileBmp(PillowTestCase): # Test for #1301 # Arrange outfile = self.tempfile("temp.jpg") - im = Image.open("Tests/images/hopper.bmp") + with Image.open("Tests/images/hopper.bmp") as im: - # Act - im.save(outfile, "JPEG", dpi=im.info["dpi"]) + # Act + im.save(outfile, "JPEG", dpi=im.info["dpi"]) - # Assert - reloaded = Image.open(outfile) - reloaded.load() - self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) - self.assertEqual(im.size, reloaded.size) - self.assertEqual(reloaded.format, "JPEG") + # Assert + with Image.open(outfile) as reloaded: + reloaded.load() + self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) + self.assertEqual(im.size, reloaded.size) + self.assertEqual(reloaded.format, "JPEG") def test_load_dpi_rounding(self): # Round up @@ -80,11 +79,10 @@ class TestFileBmp(PillowTestCase): def test_save_dpi_rounding(self): outfile = self.tempfile("temp.bmp") - im = Image.open("Tests/images/hopper.bmp") - - im.save(outfile, dpi=(72.2, 72.2)) - with Image.open(outfile) as reloaded: - self.assertEqual(reloaded.info["dpi"], (72, 72)) + with Image.open("Tests/images/hopper.bmp") as im: + im.save(outfile, dpi=(72.2, 72.2)) + with Image.open(outfile) as reloaded: + self.assertEqual(reloaded.info["dpi"], (72, 72)) im.save(outfile, dpi=(72.8, 72.8)) with Image.open(outfile) as reloaded: @@ -92,32 +90,32 @@ class TestFileBmp(PillowTestCase): def test_load_dib(self): # test for #1293, Imagegrab returning Unsupported Bitfields Format - im = Image.open("Tests/images/clipboard.dib") - self.assertEqual(im.format, "DIB") - self.assertEqual(im.get_format_mimetype(), "image/bmp") + with Image.open("Tests/images/clipboard.dib") as im: + self.assertEqual(im.format, "DIB") + self.assertEqual(im.get_format_mimetype(), "image/bmp") - target = Image.open("Tests/images/clipboard_target.png") - self.assert_image_equal(im, target) + with Image.open("Tests/images/clipboard_target.png") as target: + self.assert_image_equal(im, target) def test_save_dib(self): outfile = self.tempfile("temp.dib") - im = Image.open("Tests/images/clipboard.dib") - im.save(outfile) + with Image.open("Tests/images/clipboard.dib") as im: + im.save(outfile) - reloaded = Image.open(outfile) - self.assertEqual(reloaded.format, "DIB") - self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") - self.assert_image_equal(im, reloaded) + with Image.open(outfile) as reloaded: + self.assertEqual(reloaded.format, "DIB") + self.assertEqual(reloaded.get_format_mimetype(), "image/bmp") + self.assert_image_equal(im, reloaded) def test_rgba_bitfields(self): # This test image has been manually hexedited # to change the bitfield compression in the header from XBGR to RGBA - im = Image.open("Tests/images/rgb32bf-rgba.bmp") + with Image.open("Tests/images/rgb32bf-rgba.bmp") as im: - # So before the comparing the image, swap the channels - b, g, r = im.split()[1:] - im = Image.merge("RGB", (r, g, b)) + # So before the comparing the image, swap the channels + b, g, r = im.split()[1:] + im = Image.merge("RGB", (r, g, b)) - target = Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") - self.assert_image_equal(im, target) + with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target: + self.assert_image_equal(im, target) diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index 0b2f7a98c..246404bab 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -7,14 +7,13 @@ TEST_FILE = "Tests/images/deerstalker.cur" class TestFileCur(PillowTestCase): def test_sanity(self): - im = Image.open(TEST_FILE) - - self.assertEqual(im.size, (32, 32)) - self.assertIsInstance(im, CurImagePlugin.CurImageFile) - # Check some pixel colors to ensure image is loaded properly - self.assertEqual(im.getpixel((10, 1)), (0, 0, 0, 0)) - self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1)) - self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) + with Image.open(TEST_FILE) as im: + self.assertEqual(im.size, (32, 32)) + self.assertIsInstance(im, CurImagePlugin.CurImageFile) + # Check some pixel colors to ensure image is loaded properly + self.assertEqual(im.getpixel((10, 1)), (0, 0, 0, 0)) + self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1)) + self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 8ef90e86e..053d72568 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -17,73 +17,71 @@ class TestFileDds(PillowTestCase): def test_sanity_dxt1(self): """Check DXT1 images can be opened""" - target = Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) + with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target: + target = target.convert("RGBA") + with Image.open(TEST_FILE_DXT1) as im: + im.load() - im = Image.open(TEST_FILE_DXT1) - im.load() + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (256, 256)) - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) - - self.assert_image_equal(target.convert("RGBA"), im) + self.assert_image_equal(im, target) def test_sanity_dxt5(self): """Check DXT5 images can be opened""" - target = Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) - - im = Image.open(TEST_FILE_DXT5) - im.load() + with Image.open(TEST_FILE_DXT5) as im: + im.load() self.assertEqual(im.format, "DDS") self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) - self.assert_image_equal(target, im) + with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target: + self.assert_image_equal(target, im) def test_sanity_dxt3(self): """Check DXT3 images can be opened""" - target = Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) + with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target: + with Image.open(TEST_FILE_DXT3) as im: + im.load() - im = Image.open(TEST_FILE_DXT3) - im.load() + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (256, 256)) - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) - - self.assert_image_equal(target, im) + self.assert_image_equal(target, im) def test_dx10_bc7(self): """Check DX10 images can be opened""" - target = Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) + with Image.open(TEST_FILE_DX10_BC7) as im: + im.load() - im = Image.open(TEST_FILE_DX10_BC7) - im.load() + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (256, 256)) - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) - - self.assert_image_equal(target, im) + with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target: + self.assert_image_equal(target, im) def test_dx10_bc7_unorm_srgb(self): """Check DX10 unsigned normalized integer images can be opened""" - target = Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")) + with Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) as im: + im.load() - im = Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) - im.load() + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (16, 16)) + self.assertEqual(im.info["gamma"], 1 / 2.2) - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (16, 16)) - self.assertEqual(im.info["gamma"], 1 / 2.2) - - self.assert_image_equal(target, im) + with Image.open( + TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png") + ) as target: + self.assert_image_equal(target, im) def test_unimplemented_dxgi_format(self): self.assertRaises( @@ -95,16 +93,17 @@ class TestFileDds(PillowTestCase): def test_uncompressed_rgb(self): """Check uncompressed RGB images can be opened""" - target = Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")) + with Image.open(TEST_FILE_UNCOMPRESSED_RGB) as im: + im.load() - im = Image.open(TEST_FILE_UNCOMPRESSED_RGB) - im.load() + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (800, 600)) - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (800, 600)) - - self.assert_image_equal(target, im) + with Image.open( + TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png") + ) as target: + self.assert_image_equal(target, im) def test__validate_true(self): """Check valid prefix""" @@ -145,8 +144,8 @@ class TestFileDds(PillowTestCase): img_file = f.read() def short_file(): - im = Image.open(BytesIO(img_file[:-100])) - im.load() + with Image.open(BytesIO(img_file[:-100])) as im: + im.load() self.assertRaises(IOError, short_file) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 9b1a1ec40..6fd201d48 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,8 +1,9 @@ import io +import unittest from PIL import EpsImagePlugin, Image -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript() @@ -67,8 +68,8 @@ class TestFileEps(PillowTestCase): self.assertEqual(cmyk_image.mode, "RGB") if "jpeg_decoder" in dir(Image.core): - target = Image.open("Tests/images/pil_sample_rgb.jpg") - self.assert_image_similar(cmyk_image, target, 10) + with Image.open("Tests/images/pil_sample_rgb.jpg") as target: + self.assert_image_similar(cmyk_image, target, 10) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") def test_showpage(self): @@ -91,7 +92,7 @@ class TestFileEps(PillowTestCase): def test_iobase_object(self): # issue 479 with Image.open(file1) as image1: - with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh: + with open(self.tempfile("temp_iobase.eps"), "wb") as fh: image1.save(fh, "EPS") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @@ -99,12 +100,13 @@ class TestFileEps(PillowTestCase): with open(file1, "rb") as f: img_bytes = io.BytesIO(f.read()) - img = Image.open(img_bytes) - img.load() + with Image.open(img_bytes) as img: + img.load() - image1_scale1_compare = Image.open(file1_compare).convert("RGB") - image1_scale1_compare.load() - self.assert_image_similar(img, image1_scale1_compare, 5) + with Image.open(file1_compare) as image1_scale1_compare: + image1_scale1_compare = image1_scale1_compare.convert("RGB") + image1_scale1_compare.load() + self.assert_image_similar(img, image1_scale1_compare, 5) def test_image_mode_not_supported(self): im = hopper("RGBA") @@ -121,14 +123,16 @@ class TestFileEps(PillowTestCase): # Zero bounding box with Image.open(file1) as image1_scale1: image1_scale1.load() - image1_scale1_compare = Image.open(file1_compare).convert("RGB") + with Image.open(file1_compare) as image1_scale1_compare: + image1_scale1_compare = image1_scale1_compare.convert("RGB") image1_scale1_compare.load() self.assert_image_similar(image1_scale1, image1_scale1_compare, 5) # Non-Zero bounding box with Image.open(file2) as image2_scale1: image2_scale1.load() - image2_scale1_compare = Image.open(file2_compare).convert("RGB") + with Image.open(file2_compare) as image2_scale1_compare: + image2_scale1_compare = image2_scale1_compare.convert("RGB") image2_scale1_compare.load() self.assert_image_similar(image2_scale1, image2_scale1_compare, 10) @@ -142,14 +146,16 @@ class TestFileEps(PillowTestCase): # Zero bounding box with Image.open(file1) as image1_scale2: image1_scale2.load(scale=2) - image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB") + with Image.open(file1_compare_scale2) as image1_scale2_compare: + image1_scale2_compare = image1_scale2_compare.convert("RGB") image1_scale2_compare.load() self.assert_image_similar(image1_scale2, image1_scale2_compare, 5) # Non-Zero bounding box with Image.open(file2) as image2_scale2: image2_scale2.load(scale=2) - image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB") + with Image.open(file2_compare_scale2) as image2_scale2_compare: + image2_scale2_compare = image2_scale2_compare.convert("RGB") image2_scale2_compare.load() self.assert_image_similar(image2_scale2, image2_scale2_compare, 10) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 68412c8ca..7c985be30 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -1,4 +1,6 @@ -from .helper import PillowTestCase, unittest +import unittest + +from .helper import PillowTestCase try: from PIL import FpxImagePlugin diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index 7d30042ca..335a96e83 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -5,12 +5,11 @@ from .helper import PillowTestCase class TestFileFtex(PillowTestCase): def test_load_raw(self): - im = Image.open("Tests/images/ftex_uncompressed.ftu") - target = Image.open("Tests/images/ftex_uncompressed.png") - - self.assert_image_equal(im, target) + with Image.open("Tests/images/ftex_uncompressed.ftu") as im: + with Image.open("Tests/images/ftex_uncompressed.png") as target: + self.assert_image_equal(im, target) def test_load_dxt1(self): - 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) + with Image.open("Tests/images/ftex_dxt1.ftc") as im: + with Image.open("Tests/images/ftex_dxt1.png") as target: + self.assert_image_similar(im, target.convert("RGBA"), 15) diff --git a/Tests/test_file_gd.py b/Tests/test_file_gd.py index 9208dcbff..0b7543a31 100644 --- a/Tests/test_file_gd.py +++ b/Tests/test_file_gd.py @@ -1,4 +1,4 @@ -from PIL import GdImageFile +from PIL import GdImageFile, UnidentifiedImageError from .helper import PillowTestCase @@ -17,4 +17,4 @@ class TestFileGd(PillowTestCase): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(IOError, GdImageFile.open, invalid_file) + self.assertRaises(UnidentifiedImageError, GdImageFile.open, invalid_file) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 7c1de5891..b17459a8b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -1,8 +1,9 @@ +import unittest from io import BytesIO from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette -from .helper import PillowTestCase, hopper, is_pypy, netpbm_available, unittest +from .helper import PillowTestCase, hopper, is_pypy, netpbm_available try: from PIL import _webp @@ -87,21 +88,20 @@ class TestFileGif(PillowTestCase): def check(colors, size, expected_palette_length): # make an image with empty colors in the start of the palette range im = Image.frombytes( - "P", - (colors, colors), - bytes(bytearray(range(256 - colors, 256)) * colors), + "P", (colors, colors), bytes(range(256 - colors, 256)) * colors ) im = im.resize((size, size)) outfile = BytesIO() im.save(outfile, "GIF") outfile.seek(0) - reloaded = Image.open(outfile) + with Image.open(outfile) as reloaded: + # check palette length + palette_length = max( + i + 1 for i, v in enumerate(reloaded.histogram()) if v + ) + self.assertEqual(expected_palette_length, palette_length) - # check palette length - palette_length = max(i + 1 for i, v in enumerate(reloaded.histogram()) if v) - self.assertEqual(expected_palette_length, palette_length) - - self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) + self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) # These do optimize the palette check(128, 511, 128) @@ -119,7 +119,7 @@ class TestFileGif(PillowTestCase): check(256, 511, 256) def test_optimize_full_l(self): - im = Image.frombytes("L", (16, 16), bytes(bytearray(range(256)))) + im = Image.frombytes("L", (16, 16), bytes(range(256))) test_file = BytesIO() im.save(test_file, "GIF", optimize=True) self.assertEqual(im.mode, "L") @@ -555,9 +555,9 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.info["background"], im.info["background"]) if HAVE_WEBP and _webp.HAVE_WEBPANIM: - im = Image.open("Tests/images/hopper.webp") - self.assertIsInstance(im.info["background"], tuple) - im.save(out) + with Image.open("Tests/images/hopper.webp") as im: + self.assertIsInstance(im.info["background"], tuple) + im.save(out) def test_comment(self): with Image.open(TEST_GIF) as im: @@ -632,8 +632,7 @@ class TestFileGif(PillowTestCase): # Tests appending using a generator def imGenerator(ims): - for im in ims: - yield im + yield from ims im.save(out, save_all=True, append_images=imGenerator(ims)) @@ -655,7 +654,7 @@ class TestFileGif(PillowTestCase): # that's > 128 items where the transparent color is actually # the top palette entry to trigger the bug. - data = bytes(bytearray(range(1, 254))) + data = bytes(range(1, 254)) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) im = Image.new("L", (253, 1)) @@ -703,7 +702,7 @@ class TestFileGif(PillowTestCase): im = hopper("P") im_l = Image.frombytes("L", im.size, im.tobytes()) - palette = bytes(bytearray(im.getpalette())) + palette = bytes(im.getpalette()) out = self.tempfile("temp.gif") im_l.save(out, palette=palette) @@ -718,7 +717,7 @@ class TestFileGif(PillowTestCase): # Forcing a non-straight grayscale palette. im = hopper("P") - palette = bytes(bytearray([255 - i // 3 for i in range(768)])) + palette = bytes([255 - i // 3 for i in range(768)]) out = self.tempfile("temp.gif") im.save(out, palette=palette) @@ -759,7 +758,7 @@ class TestFileGif(PillowTestCase): im.putpalette(ImagePalette.ImagePalette("RGB")) im.info = {"background": 0} - passed_palette = bytes(bytearray([255 - i // 3 for i in range(768)])) + passed_palette = bytes([255 - i // 3 for i in range(768)]) GifImagePlugin._FORCE_OPTIMIZE = True try: diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 4d7f95ec3..6b6543d8d 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -1,9 +1,10 @@ import io import sys +import unittest from PIL import IcnsImagePlugin, Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase # sample icon file TEST_FILE = "Tests/images/pillow.icns" @@ -26,32 +27,31 @@ class TestFileIcns(PillowTestCase): @unittest.skipIf(sys.platform != "darwin", "requires macOS") def test_save(self): - im = Image.open(TEST_FILE) - temp_file = self.tempfile("temp.icns") - im.save(temp_file) - reread = Image.open(temp_file) + with Image.open(TEST_FILE) as im: + im.save(temp_file) - self.assertEqual(reread.mode, "RGBA") - self.assertEqual(reread.size, (1024, 1024)) - self.assertEqual(reread.format, "ICNS") + with Image.open(temp_file) as reread: + self.assertEqual(reread.mode, "RGBA") + self.assertEqual(reread.size, (1024, 1024)) + self.assertEqual(reread.format, "ICNS") @unittest.skipIf(sys.platform != "darwin", "requires macOS") def test_save_append_images(self): - im = Image.open(TEST_FILE) - temp_file = self.tempfile("temp.icns") provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128)) - im.save(temp_file, append_images=[provided_im]) - reread = Image.open(temp_file) - self.assert_image_similar(reread, im, 1) + with Image.open(TEST_FILE) as im: + im.save(temp_file, append_images=[provided_im]) - reread = Image.open(temp_file) - reread.size = (16, 16, 2) - reread.load() - self.assert_image_equal(reread, provided_im) + with Image.open(temp_file) as reread: + self.assert_image_similar(reread, im, 1) + + with Image.open(temp_file) as reread: + reread.size = (16, 16, 2) + reread.load() + self.assert_image_equal(reread, provided_im) def test_sizes(self): # Check that we can load all of the sizes, and that the final pixel diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index ac6b19041..d8bb9630f 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -27,23 +27,23 @@ class TestFileIco(PillowTestCase): # the default image output.seek(0) - reloaded = Image.open(output) - self.assertEqual(reloaded.info["sizes"], {(32, 32), (64, 64)}) + with Image.open(output) as reloaded: + self.assertEqual(reloaded.info["sizes"], {(32, 32), (64, 64)}) - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual((64, 64), reloaded.size) - self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual((64, 64), reloaded.size) + self.assertEqual(reloaded.format, "ICO") + self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) # the other one output.seek(0) - reloaded = Image.open(output) - reloaded.size = (32, 32) + with Image.open(output) as reloaded: + reloaded.size = (32, 32) - self.assertEqual(im.mode, reloaded.mode) - self.assertEqual((32, 32), reloaded.size) - self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual((32, 32), reloaded.size) + self.assertEqual(reloaded.format, "ICO") + self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) def test_incorrect_size(self): with Image.open(TEST_ICO_FILE) as im: diff --git a/Tests/test_file_iptc.py b/Tests/test_file_iptc.py index 1dd18a759..b6cc4c225 100644 --- a/Tests/test_file_iptc.py +++ b/Tests/test_file_iptc.py @@ -1,3 +1,6 @@ +import sys +from io import StringIO + from PIL import Image, IptcImagePlugin from .helper import PillowTestCase, hopper @@ -52,12 +55,6 @@ class TestFileIptc(PillowTestCase): # Arrange c = b"abc" # Temporarily redirect stdout - try: - from cStringIO import StringIO - except ImportError: - from io import StringIO - import sys - old_stdout = sys.stdout sys.stdout = mystdout = StringIO() diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 35f2c0940..72adbe537 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -44,12 +44,12 @@ class TestFileJpeg(PillowTestCase): # internal version number self.assertRegex(Image.core.jpeglib_version, r"\d+\.\d+$") - im = Image.open(TEST_FILE) - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "JPEG") - self.assertEqual(im.get_format_mimetype(), "image/jpeg") + with Image.open(TEST_FILE) as im: + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "JPEG") + self.assertEqual(im.get_format_mimetype(), "image/jpeg") def test_app(self): # Test APP/COM reader (@PIL135) @@ -66,30 +66,34 @@ class TestFileJpeg(PillowTestCase): # Test CMYK handling. Thanks to Tim and Charlie for test data, # Michael for getting me to look one more time. f = "Tests/images/pil_sample_cmyk.jpg" - im = Image.open(f) - # the source image has red pixels in the upper left corner. - c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] - self.assertEqual(c, 0.0) - self.assertGreater(m, 0.8) - self.assertGreater(y, 0.8) - self.assertEqual(k, 0.0) - # the opposite corner is black - c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))] - self.assertGreater(k, 0.9) - # roundtrip, and check again - im = self.roundtrip(im) - c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] - self.assertEqual(c, 0.0) - self.assertGreater(m, 0.8) - self.assertGreater(y, 0.8) - self.assertEqual(k, 0.0) - c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))] - self.assertGreater(k, 0.9) + with Image.open(f) as im: + # the source image has red pixels in the upper left corner. + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + self.assertEqual(c, 0.0) + self.assertGreater(m, 0.8) + self.assertGreater(y, 0.8) + self.assertEqual(k, 0.0) + # the opposite corner is black + c, m, y, k = [ + x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1)) + ] + self.assertGreater(k, 0.9) + # roundtrip, and check again + im = self.roundtrip(im) + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + self.assertEqual(c, 0.0) + self.assertGreater(m, 0.8) + self.assertGreater(y, 0.8) + self.assertEqual(k, 0.0) + c, m, y, k = [ + x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1)) + ] + self.assertGreater(k, 0.9) def test_dpi(self): def test(xdpi, ydpi=None): - im = Image.open(TEST_FILE) - im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + with Image.open(TEST_FILE) as im: + im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) return im.info.get("dpi") self.assertEqual(test(72), (72, 72)) @@ -140,18 +144,18 @@ class TestFileJpeg(PillowTestCase): # https://github.com/python-pillow/Pillow/issues/148 # Sometimes the meta data on the icc_profile block is bigger than # Image.MAXBLOCK or the image size. - im = Image.open("Tests/images/icc_profile_big.jpg") - f = self.tempfile("temp.jpg") - icc_profile = im.info["icc_profile"] - # Should not raise IOError for image with icc larger than image size. - im.save( - f, - format="JPEG", - progressive=True, - quality=95, - icc_profile=icc_profile, - optimize=True, - ) + with Image.open("Tests/images/icc_profile_big.jpg") as im: + f = self.tempfile("temp.jpg") + icc_profile = im.info["icc_profile"] + # Should not raise IOError for image with icc larger than image size. + im.save( + f, + format="JPEG", + progressive=True, + quality=95, + icc_profile=icc_profile, + optimize=True, + ) def test_optimize(self): im1 = self.roundtrip(hopper()) @@ -339,17 +343,17 @@ class TestFileJpeg(PillowTestCase): def test_quality_keep(self): # RGB - im = Image.open("Tests/images/hopper.jpg") - f = self.tempfile("temp.jpg") - im.save(f, quality="keep") + with Image.open("Tests/images/hopper.jpg") as im: + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") # Grayscale - im = Image.open("Tests/images/hopper_gray.jpg") - f = self.tempfile("temp.jpg") - im.save(f, quality="keep") + with Image.open("Tests/images/hopper_gray.jpg") as im: + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") # CMYK - im = Image.open("Tests/images/pil_sample_cmyk.jpg") - f = self.tempfile("temp.jpg") - im.save(f, quality="keep") + with Image.open("Tests/images/pil_sample_cmyk.jpg") as im: + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") def test_junk_jpeg_header(self): # https://github.com/python-pillow/Pillow/issues/630 @@ -365,10 +369,10 @@ class TestFileJpeg(PillowTestCase): def test_truncated_jpeg_should_read_all_the_data(self): filename = "Tests/images/truncated_jpeg.jpg" ImageFile.LOAD_TRUNCATED_IMAGES = True - im = Image.open(filename) - im.load() - ImageFile.LOAD_TRUNCATED_IMAGES = False - self.assertIsNotNone(im.getbbox()) + with Image.open(filename) as im: + im.load() + ImageFile.LOAD_TRUNCATED_IMAGES = False + self.assertIsNotNone(im.getbbox()) def test_truncated_jpeg_throws_IOError(self): filename = "Tests/images/truncated_jpeg.jpg" @@ -381,106 +385,106 @@ class TestFileJpeg(PillowTestCase): im.load() def _n_qtables_helper(self, n, test_file): - im = Image.open(test_file) - f = self.tempfile("temp.jpg") - im.save(f, qtables=[[n] * 64] * n) - im = Image.open(f) - self.assertEqual(len(im.quantization), n) - reloaded = self.roundtrip(im, qtables="keep") - self.assertEqual(im.quantization, reloaded.quantization) + with Image.open(test_file) as im: + f = self.tempfile("temp.jpg") + im.save(f, qtables=[[n] * 64] * n) + with Image.open(f) as im: + self.assertEqual(len(im.quantization), n) + reloaded = self.roundtrip(im, qtables="keep") + self.assertEqual(im.quantization, reloaded.quantization) def test_qtables(self): - im = Image.open("Tests/images/hopper.jpg") - qtables = im.quantization - reloaded = self.roundtrip(im, qtables=qtables, subsampling=0) - self.assertEqual(im.quantization, reloaded.quantization) - self.assert_image_similar(im, self.roundtrip(im, qtables="web_low"), 30) - self.assert_image_similar(im, self.roundtrip(im, qtables="web_high"), 30) - self.assert_image_similar(im, self.roundtrip(im, qtables="keep"), 30) + with Image.open("Tests/images/hopper.jpg") as im: + qtables = im.quantization + reloaded = self.roundtrip(im, qtables=qtables, subsampling=0) + self.assertEqual(im.quantization, reloaded.quantization) + self.assert_image_similar(im, self.roundtrip(im, qtables="web_low"), 30) + self.assert_image_similar(im, self.roundtrip(im, qtables="web_high"), 30) + self.assert_image_similar(im, self.roundtrip(im, qtables="keep"), 30) - # valid bounds for baseline qtable - bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)] - self.roundtrip(im, qtables=[bounds_qtable]) + # valid bounds for baseline qtable + bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)] + self.roundtrip(im, qtables=[bounds_qtable]) - # values from wizard.txt in jpeg9-a src package. - standard_l_qtable = [ - int(s) - for s in """ - 16 11 10 16 24 40 51 61 - 12 12 14 19 26 58 60 55 - 14 13 16 24 40 57 69 56 - 14 17 22 29 51 87 80 62 - 18 22 37 56 68 109 103 77 - 24 35 55 64 81 104 113 92 - 49 64 78 87 103 121 120 101 - 72 92 95 98 112 100 103 99 - """.split( - None + # values from wizard.txt in jpeg9-a src package. + standard_l_qtable = [ + int(s) + for s in """ + 16 11 10 16 24 40 51 61 + 12 12 14 19 26 58 60 55 + 14 13 16 24 40 57 69 56 + 14 17 22 29 51 87 80 62 + 18 22 37 56 68 109 103 77 + 24 35 55 64 81 104 113 92 + 49 64 78 87 103 121 120 101 + 72 92 95 98 112 100 103 99 + """.split( + None + ) + ] + + standard_chrominance_qtable = [ + int(s) + for s in """ + 17 18 24 47 99 99 99 99 + 18 21 26 66 99 99 99 99 + 24 26 56 99 99 99 99 99 + 47 66 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + """.split( + None + ) + ] + # list of qtable lists + self.assert_image_similar( + im, + self.roundtrip( + im, qtables=[standard_l_qtable, standard_chrominance_qtable] + ), + 30, ) - ] - standard_chrominance_qtable = [ - int(s) - for s in """ - 17 18 24 47 99 99 99 99 - 18 21 26 66 99 99 99 99 - 24 26 56 99 99 99 99 99 - 47 66 99 99 99 99 99 99 - 99 99 99 99 99 99 99 99 - 99 99 99 99 99 99 99 99 - 99 99 99 99 99 99 99 99 - 99 99 99 99 99 99 99 99 - """.split( - None + # tuple of qtable lists + self.assert_image_similar( + im, + self.roundtrip( + im, qtables=(standard_l_qtable, standard_chrominance_qtable) + ), + 30, ) - ] - # list of qtable lists - self.assert_image_similar( - im, - self.roundtrip( - im, qtables=[standard_l_qtable, standard_chrominance_qtable] - ), - 30, - ) - # tuple of qtable lists - self.assert_image_similar( - im, - self.roundtrip( - im, qtables=(standard_l_qtable, standard_chrominance_qtable) - ), - 30, - ) + # dict of qtable lists + self.assert_image_similar( + im, + self.roundtrip( + im, qtables={0: standard_l_qtable, 1: standard_chrominance_qtable} + ), + 30, + ) - # dict of qtable lists - self.assert_image_similar( - im, - self.roundtrip( - im, qtables={0: standard_l_qtable, 1: standard_chrominance_qtable} - ), - 30, - ) + self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg") + self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg") + self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg") + self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg") + self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg") + self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg") + self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg") + self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg") - self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg") - self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg") - self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg") - self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg") - self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg") - self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg") - self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg") - self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg") + # not a sequence + self.assertRaises(ValueError, self.roundtrip, im, qtables="a") + # sequence wrong length + self.assertRaises(ValueError, self.roundtrip, im, qtables=[]) + # sequence wrong length + self.assertRaises(ValueError, self.roundtrip, im, qtables=[1, 2, 3, 4, 5]) - # not a sequence - self.assertRaises(ValueError, self.roundtrip, im, qtables="a") - # sequence wrong length - self.assertRaises(ValueError, self.roundtrip, im, qtables=[]) - # sequence wrong length - self.assertRaises(ValueError, self.roundtrip, im, qtables=[1, 2, 3, 4, 5]) - - # qtable entry not a sequence - self.assertRaises(ValueError, self.roundtrip, im, qtables=[1]) - # qtable entry has wrong number of items - self.assertRaises(ValueError, self.roundtrip, im, qtables=[[1, 2, 3, 4]]) + # qtable entry not a sequence + self.assertRaises(ValueError, self.roundtrip, im, qtables=[1]) + # qtable entry has wrong number of items + self.assertRaises(ValueError, self.roundtrip, im, qtables=[[1, 2, 3, 4]]) @unittest.skipUnless(djpeg_available(), "djpeg not available") def test_load_djpeg(self): @@ -490,12 +494,11 @@ class TestFileJpeg(PillowTestCase): @unittest.skipUnless(cjpeg_available(), "cjpeg not available") def test_save_cjpeg(self): - img = Image.open(TEST_FILE) - - tempfile = self.tempfile("temp.jpg") - JpegImagePlugin._save_cjpeg(img, 0, tempfile) - # Default save quality is 75%, so a tiny bit of difference is alright - self.assert_image_similar(img, Image.open(tempfile), 17) + with Image.open(TEST_FILE) as img: + tempfile = self.tempfile("temp.jpg") + JpegImagePlugin._save_cjpeg(img, 0, tempfile) + # Default save quality is 75%, so a tiny bit of difference is alright + self.assert_image_similar(img, Image.open(tempfile), 17) def test_no_duplicate_0x1001_tag(self): # Arrange @@ -512,12 +515,11 @@ class TestFileJpeg(PillowTestCase): f = self.tempfile("temp.jpeg") im.save(f, quality=100, optimize=True) - reloaded = Image.open(f) - - # none of these should crash - reloaded.save(f, quality="keep") - reloaded.save(f, quality="keep", progressive=True) - reloaded.save(f, quality="keep", optimize=True) + with Image.open(f) as reloaded: + # none of these should crash + reloaded.save(f, quality="keep") + reloaded.save(f, quality="keep", progressive=True) + reloaded.save(f, quality="keep", optimize=True) def test_bad_mpo_header(self): """ Treat unknown MPO as JPEG """ @@ -547,15 +549,15 @@ class TestFileJpeg(PillowTestCase): def test_save_tiff_with_dpi(self): # Arrange outfile = self.tempfile("temp.tif") - im = Image.open("Tests/images/hopper.tif") + with Image.open("Tests/images/hopper.tif") as im: - # Act - im.save(outfile, "JPEG", dpi=im.info["dpi"]) + # Act + im.save(outfile, "JPEG", dpi=im.info["dpi"]) - # Assert - reloaded = Image.open(outfile) - reloaded.load() - self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) + # Assert + with Image.open(outfile) as reloaded: + reloaded.load() + self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) def test_load_dpi_rounding(self): # Round up @@ -568,13 +570,14 @@ class TestFileJpeg(PillowTestCase): def test_save_dpi_rounding(self): outfile = self.tempfile("temp.jpg") - im = Image.open("Tests/images/hopper.jpg") + with Image.open("Tests/images/hopper.jpg") as im: + im.save(outfile, dpi=(72.2, 72.2)) - im.save(outfile, dpi=(72.2, 72.2)) - with Image.open(outfile) as reloaded: - self.assertEqual(reloaded.info["dpi"], (72, 72)) + with Image.open(outfile) as reloaded: + self.assertEqual(reloaded.info["dpi"], (72, 72)) im.save(outfile, dpi=(72.8, 72.8)) + with Image.open(outfile) as reloaded: self.assertEqual(reloaded.info["dpi"], (73, 73)) @@ -656,6 +659,11 @@ class TestFileJpeg(PillowTestCase): }, ) + # Test that the image can still load, even with broken Photoshop data + # This image had the APP13 length hexedited to be smaller + with Image.open("Tests/images/photoshop-200dpi-broken.jpg") as im_broken: + self.assert_image_equal(im_broken, im) + # This image does not contain a Photoshop header string with Image.open("Tests/images/app13.jpg") as im: self.assertNotIn("photoshop", im.info) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index dac1d0ec0..37ce726db 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -33,13 +33,13 @@ class TestFileJpeg2k(PillowTestCase): # Internal version number self.assertRegex(Image.core.jp2klib_version, r"\d+\.\d+\.\d+$") - im = Image.open("Tests/images/test-card-lossless.jp2") - px = im.load() - self.assertEqual(px[0, 0], (0, 0, 0)) - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (640, 480)) - self.assertEqual(im.format, "JPEG2000") - self.assertEqual(im.get_format_mimetype(), "image/jp2") + with Image.open("Tests/images/test-card-lossless.jp2") as im: + px = im.load() + self.assertEqual(px[0, 0], (0, 0, 0)) + 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): with Image.open("Tests/images/balloon.jpf") as im: @@ -54,24 +54,24 @@ class TestFileJpeg2k(PillowTestCase): def test_bytesio(self): with open("Tests/images/test-card-lossless.jp2", "rb") as f: data = BytesIO(f.read()) - im = Image.open(data) - im.load() - self.assert_image_similar(im, test_card, 1.0e-3) + with Image.open(data) as im: + im.load() + self.assert_image_similar(im, test_card, 1.0e-3) # These two test pre-written JPEG 2000 files that were not written with # PIL (they were made using Adobe Photoshop) def test_lossless(self): - im = Image.open("Tests/images/test-card-lossless.jp2") - im.load() - outfile = self.tempfile("temp_test-card.png") - im.save(outfile) + with Image.open("Tests/images/test-card-lossless.jp2") as im: + im.load() + outfile = self.tempfile("temp_test-card.png") + im.save(outfile) self.assert_image_similar(im, test_card, 1.0e-3) def test_lossy_tiled(self): - im = Image.open("Tests/images/test-card-lossy-tiled.jp2") - im.load() - self.assert_image_similar(im, test_card, 2.0) + with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im: + im.load() + self.assert_image_similar(im, test_card, 2.0) def test_lossless_rt(self): im = self.roundtrip(test_card) @@ -110,10 +110,10 @@ class TestFileJpeg2k(PillowTestCase): self.assert_image_equal(im, test_card) def test_reduce(self): - im = Image.open("Tests/images/test-card-lossless.jp2") - im.reduce = 2 - im.load() - self.assertEqual(im.size, (160, 120)) + with Image.open("Tests/images/test-card-lossless.jp2") as im: + im.reduce = 2 + im.load() + self.assertEqual(im.size, (160, 120)) def test_layers_type(self): outfile = self.tempfile("temp_layers.jp2") @@ -132,64 +132,58 @@ class TestFileJpeg2k(PillowTestCase): ) out.seek(0) - im = Image.open(out) - im.layers = 1 - im.load() - self.assert_image_similar(im, test_card, 13) + with Image.open(out) as im: + im.layers = 1 + im.load() + self.assert_image_similar(im, test_card, 13) out.seek(0) - im = Image.open(out) - im.layers = 3 - im.load() - self.assert_image_similar(im, test_card, 0.4) + with Image.open(out) as im: + im.layers = 3 + im.load() + self.assert_image_similar(im, test_card, 0.4) def test_rgba(self): # Arrange - j2k = Image.open("Tests/images/rgb_trns_ycbc.j2k") - jp2 = Image.open("Tests/images/rgb_trns_ycbc.jp2") + with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k: + with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2: - # Act - j2k.load() - jp2.load() + # Act + j2k.load() + jp2.load() - # Assert - self.assertEqual(j2k.mode, "RGBA") - self.assertEqual(jp2.mode, "RGBA") + # Assert + self.assertEqual(j2k.mode, "RGBA") + self.assertEqual(jp2.mode, "RGBA") def test_16bit_monochrome_has_correct_mode(self): + with Image.open("Tests/images/16bit.cropped.j2k") as j2k: + j2k.load() + self.assertEqual(j2k.mode, "I;16") - j2k = Image.open("Tests/images/16bit.cropped.j2k") - jp2 = Image.open("Tests/images/16bit.cropped.jp2") - - j2k.load() - jp2.load() - - self.assertEqual(j2k.mode, "I;16") - self.assertEqual(jp2.mode, "I;16") + with Image.open("Tests/images/16bit.cropped.jp2") as jp2: + jp2.load() + self.assertEqual(jp2.mode, "I;16") def test_16bit_monochrome_jp2_like_tiff(self): - - tiff_16bit = Image.open("Tests/images/16bit.cropped.tif") - jp2 = Image.open("Tests/images/16bit.cropped.jp2") - self.assert_image_similar(jp2, tiff_16bit, 1e-3) + with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit: + with Image.open("Tests/images/16bit.cropped.jp2") as jp2: + self.assert_image_similar(jp2, tiff_16bit, 1e-3) def test_16bit_monochrome_j2k_like_tiff(self): - - tiff_16bit = Image.open("Tests/images/16bit.cropped.tif") - j2k = Image.open("Tests/images/16bit.cropped.j2k") - self.assert_image_similar(j2k, tiff_16bit, 1e-3) + with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit: + with Image.open("Tests/images/16bit.cropped.j2k") as j2k: + self.assert_image_similar(j2k, tiff_16bit, 1e-3) def test_16bit_j2k_roundtrips(self): - - j2k = Image.open("Tests/images/16bit.cropped.j2k") - im = self.roundtrip(j2k) - self.assert_image_equal(im, j2k) + with Image.open("Tests/images/16bit.cropped.j2k") as j2k: + im = self.roundtrip(j2k) + self.assert_image_equal(im, j2k) def test_16bit_jp2_roundtrips(self): - - jp2 = Image.open("Tests/images/16bit.cropped.jp2") - im = self.roundtrip(jp2) - self.assert_image_equal(im, jp2) + with Image.open("Tests/images/16bit.cropped.jp2") as jp2: + im = self.roundtrip(jp2) + self.assert_image_equal(im, jp2) def test_unbound_local(self): # prepatch, a malformed jp2 file could cause an UnboundLocalError diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index e7c3615c2..c26c503c4 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import base64 import distutils.version import io @@ -10,7 +8,6 @@ from collections import namedtuple from ctypes import c_float from PIL import Image, TiffImagePlugin, TiffTags, features -from PIL._util import py3 from .helper import PillowTestCase, hopper @@ -50,25 +47,23 @@ class TestFileLibTiff(LibTiffTestCase): """Test the ordinary file path load path""" test_file = "Tests/images/hopper_g4_500.tif" - im = Image.open(test_file) - - self.assertEqual(im.size, (500, 500)) - self._assert_noerr(im) + with Image.open(test_file) as im: + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) def test_g4_large(self): test_file = "Tests/images/pport_g4.tif" - im = Image.open(test_file) - self._assert_noerr(im) + with Image.open(test_file) as im: + self._assert_noerr(im) def test_g4_tiff_file(self): """Testing the string load path""" test_file = "Tests/images/hopper_g4_500.tif" with open(test_file, "rb") as f: - im = Image.open(f) - - self.assertEqual(im.size, (500, 500)) - self._assert_noerr(im) + with Image.open(f) as im: + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) def test_g4_tiff_bytesio(self): """Testing the stringio loading code path""" @@ -77,10 +72,9 @@ class TestFileLibTiff(LibTiffTestCase): with open(test_file, "rb") as f: s.write(f.read()) s.seek(0) - im = Image.open(s) - - self.assertEqual(im.size, (500, 500)) - self._assert_noerr(im) + with Image.open(s) as im: + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) def test_g4_non_disk_file_object(self): """Testing loading from non-disk non-BytesIO file object""" @@ -90,56 +84,51 @@ class TestFileLibTiff(LibTiffTestCase): s.write(f.read()) s.seek(0) r = io.BufferedReader(s) - im = Image.open(r) - - self.assertEqual(im.size, (500, 500)) - self._assert_noerr(im) + with Image.open(r) as im: + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) def test_g4_eq_png(self): """ Checking that we're actually getting the data that we expect""" - png = Image.open("Tests/images/hopper_bw_500.png") - g4 = Image.open("Tests/images/hopper_g4_500.tif") - - self.assert_image_equal(g4, png) + with Image.open("Tests/images/hopper_bw_500.png") as png: + with Image.open("Tests/images/hopper_g4_500.tif") as g4: + self.assert_image_equal(g4, png) # see https://github.com/python-pillow/Pillow/issues/279 def test_g4_fillorder_eq_png(self): """ Checking that we're actually getting the data that we expect""" - png = Image.open("Tests/images/g4-fillorder-test.png") - g4 = Image.open("Tests/images/g4-fillorder-test.tif") - - self.assert_image_equal(g4, png) + with Image.open("Tests/images/g4-fillorder-test.png") as png: + with Image.open("Tests/images/g4-fillorder-test.tif") as g4: + self.assert_image_equal(g4, png) def test_g4_write(self): """Checking to see that the saved image is the same as what we wrote""" test_file = "Tests/images/hopper_g4_500.tif" - orig = Image.open(test_file) + with Image.open(test_file) as orig: + out = self.tempfile("temp.tif") + rot = orig.transpose(Image.ROTATE_90) + self.assertEqual(rot.size, (500, 500)) + rot.save(out) - out = self.tempfile("temp.tif") - rot = orig.transpose(Image.ROTATE_90) - self.assertEqual(rot.size, (500, 500)) - rot.save(out) + with Image.open(out) as reread: + self.assertEqual(reread.size, (500, 500)) + self._assert_noerr(reread) + self.assert_image_equal(reread, rot) + self.assertEqual(reread.info["compression"], "group4") - reread = Image.open(out) - self.assertEqual(reread.size, (500, 500)) - self._assert_noerr(reread) - self.assert_image_equal(reread, rot) - self.assertEqual(reread.info["compression"], "group4") + self.assertEqual(reread.info["compression"], orig.info["compression"]) - self.assertEqual(reread.info["compression"], orig.info["compression"]) - - self.assertNotEqual(orig.tobytes(), reread.tobytes()) + self.assertNotEqual(orig.tobytes(), reread.tobytes()) def test_adobe_deflate_tiff(self): test_file = "Tests/images/tiff_adobe_deflate.tif" - im = Image.open(test_file) + with Image.open(test_file) as im: + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (278, 374)) + self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0)) + im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (278, 374)) - self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0)) - im.load() - - self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") def test_write_metadata(self): """ Test metadata writing through libtiff """ @@ -207,44 +196,44 @@ class TestFileLibTiff(LibTiffTestCase): # Exclude ones that have special meaning # that we're already testing them - im = Image.open("Tests/images/hopper_g4.tif") - for tag in im.tag_v2: - try: - del core_items[tag] - except KeyError: - pass + with Image.open("Tests/images/hopper_g4.tif") as im: + for tag in im.tag_v2: + try: + del core_items[tag] + except KeyError: + pass - # Type codes: - # 2: "ascii", - # 3: "short", - # 4: "long", - # 5: "rational", - # 12: "double", - # Type: dummy value - values = { - 2: "test", - 3: 1, - 4: 2 ** 20, - 5: TiffImagePlugin.IFDRational(100, 1), - 12: 1.05, - } + # Type codes: + # 2: "ascii", + # 3: "short", + # 4: "long", + # 5: "rational", + # 12: "double", + # Type: dummy value + values = { + 2: "test", + 3: 1, + 4: 2 ** 20, + 5: TiffImagePlugin.IFDRational(100, 1), + 12: 1.05, + } - new_ifd = TiffImagePlugin.ImageFileDirectory_v2() - for tag, info in core_items.items(): - if info.length == 1: - new_ifd[tag] = values[info.type] - if info.length == 0: - new_ifd[tag] = tuple(values[info.type] for _ in range(3)) - else: - new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) + new_ifd = TiffImagePlugin.ImageFileDirectory_v2() + for tag, info in core_items.items(): + if info.length == 1: + new_ifd[tag] = values[info.type] + if info.length == 0: + new_ifd[tag] = tuple(values[info.type] for _ in range(3)) + else: + new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) - # Extra samples really doesn't make sense in this application. - del new_ifd[338] + # Extra samples really doesn't make sense in this application. + del new_ifd[338] - out = self.tempfile("temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + out = self.tempfile("temp.tif") + TiffImagePlugin.WRITE_LIBTIFF = True - im.save(out, tiffinfo=new_ifd) + im.save(out, tiffinfo=new_ifd) TiffImagePlugin.WRITE_LIBTIFF = False @@ -263,7 +252,6 @@ class TestFileLibTiff(LibTiffTestCase): tc(4.25, TiffTags.FLOAT, True), tc(4.25, TiffTags.DOUBLE, True), tc("custom tag value", TiffTags.ASCII, True), - tc(u"custom tag value", TiffTags.ASCII, True), tc(b"custom tag value", TiffTags.BYTE, True), tc((4, 5, 6), TiffTags.SHORT, True), tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True), @@ -346,70 +334,58 @@ class TestFileLibTiff(LibTiffTestCase): self.assertEqual(reloaded.info["dpi"], (72.0, 72.0)) def test_g3_compression(self): - i = Image.open("Tests/images/hopper_g4_500.tif") - out = self.tempfile("temp.tif") - i.save(out, compression="group3") + with Image.open("Tests/images/hopper_g4_500.tif") as i: + out = self.tempfile("temp.tif") + i.save(out, compression="group3") - reread = Image.open(out) - self.assertEqual(reread.info["compression"], "group3") - self.assert_image_equal(reread, i) + with Image.open(out) as reread: + self.assertEqual(reread.info["compression"], "group3") + self.assert_image_equal(reread, i) def test_little_endian(self): - im = Image.open("Tests/images/16bit.deflate.tif") - self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, "I;16") + with Image.open("Tests/images/16bit.deflate.tif") as im: + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, "I;16") - b = im.tobytes() - # Bytes are in image native order (little endian) - if py3: + b = im.tobytes() + # Bytes are in image native order (little endian) self.assertEqual(b[0], ord(b"\xe0")) self.assertEqual(b[1], ord(b"\x01")) - else: - self.assertEqual(b[0], b"\xe0") - self.assertEqual(b[1], b"\x01") - out = self.tempfile("temp.tif") - # out = "temp.le.tif" - im.save(out) - reread = Image.open(out) - - self.assertEqual(reread.info["compression"], im.info["compression"]) - self.assertEqual(reread.getpixel((0, 0)), 480) + out = self.tempfile("temp.tif") + # out = "temp.le.tif" + im.save(out) + with Image.open(out) as reread: + self.assertEqual(reread.info["compression"], im.info["compression"]) + self.assertEqual(reread.getpixel((0, 0)), 480) # UNDONE - libtiff defaults to writing in native endian, so # on big endian, we'll get back mode = 'I;16B' here. def test_big_endian(self): - im = Image.open("Tests/images/16bit.MM.deflate.tif") + with Image.open("Tests/images/16bit.MM.deflate.tif") as im: + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, "I;16B") - self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, "I;16B") + b = im.tobytes() - b = im.tobytes() - - # Bytes are in image native order (big endian) - if py3: + # Bytes are in image native order (big endian) self.assertEqual(b[0], ord(b"\x01")) self.assertEqual(b[1], ord(b"\xe0")) - else: - self.assertEqual(b[0], b"\x01") - self.assertEqual(b[1], b"\xe0") - out = self.tempfile("temp.tif") - im.save(out) - reread = Image.open(out) - - self.assertEqual(reread.info["compression"], im.info["compression"]) - self.assertEqual(reread.getpixel((0, 0)), 480) + out = self.tempfile("temp.tif") + im.save(out) + with Image.open(out) as reread: + self.assertEqual(reread.info["compression"], im.info["compression"]) + self.assertEqual(reread.getpixel((0, 0)), 480) def test_g4_string_info(self): """Tests String data in info directory""" test_file = "Tests/images/hopper_g4_500.tif" - orig = Image.open(test_file) + with Image.open(test_file) as orig: + out = self.tempfile("temp.tif") - out = self.tempfile("temp.tif") - - orig.tag[269] = "temp.tif" - orig.save(out) + orig.tag[269] = "temp.tif" + orig.save(out) with Image.open(out) as reread: self.assertEqual("temp.tif", reread.tag_v2[269]) @@ -419,16 +395,16 @@ class TestFileLibTiff(LibTiffTestCase): """ Are we generating the same interpretation of the image as Imagemagick is? """ TiffImagePlugin.READ_LIBTIFF = True - im = Image.open("Tests/images/12bit.cropped.tif") - im.load() - TiffImagePlugin.READ_LIBTIFF = False - # to make the target -- - # convert 12bit.cropped.tif -depth 16 tmp.tif - # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif - # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, - # so we need to unshift so that the integer values are the same. + with Image.open("Tests/images/12bit.cropped.tif") as im: + im.load() + TiffImagePlugin.READ_LIBTIFF = False + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. - self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") + self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") def test_blur(self): # test case from irc, how to do blur on b/w image @@ -436,16 +412,16 @@ class TestFileLibTiff(LibTiffTestCase): from PIL import ImageFilter out = self.tempfile("temp.tif") - im = Image.open("Tests/images/pport_g4.tif") - im = im.convert("L") + with Image.open("Tests/images/pport_g4.tif") as im: + im = im.convert("L") im = im.filter(ImageFilter.GaussianBlur(4)) im.save(out, compression="tiff_adobe_deflate") - im2 = Image.open(out) - im2.load() + with Image.open(out) as im2: + im2.load() - self.assert_image_equal(im, im2) + self.assert_image_equal(im, im2) def test_compressions(self): # Test various tiff compressions and assert similar image content but reduced @@ -458,18 +434,18 @@ class TestFileLibTiff(LibTiffTestCase): for compression in ("packbits", "tiff_lzw"): im.save(out, compression=compression) size_compressed = os.path.getsize(out) - im2 = Image.open(out) - self.assert_image_equal(im, im2) + with Image.open(out) as im2: + self.assert_image_equal(im, im2) im.save(out, compression="jpeg") size_jpeg = os.path.getsize(out) - im2 = Image.open(out) - self.assert_image_similar(im, im2, 30) + with Image.open(out) as im2: + self.assert_image_similar(im, im2, 30) im.save(out, compression="jpeg", quality=30) size_jpeg_30 = os.path.getsize(out) - im3 = Image.open(out) - self.assert_image_similar(im2, im3, 30) + with Image.open(out) as im3: + self.assert_image_similar(im2, im3, 30) self.assertGreater(size_raw, size_compressed) self.assertGreater(size_compressed, size_jpeg) @@ -491,8 +467,8 @@ class TestFileLibTiff(LibTiffTestCase): out = self.tempfile("temp.tif") im.save(out, compression="tiff_adobe_deflate") - im2 = Image.open(out) - self.assert_image_equal(im, im2) + with Image.open(out) as im2: + self.assert_image_equal(im, im2) def xtest_bw_compression_w_rgb(self): """ This test passes, but when running all tests causes a failure due @@ -555,10 +531,10 @@ class TestFileLibTiff(LibTiffTestCase): def test__next(self): TiffImagePlugin.READ_LIBTIFF = True - im = Image.open("Tests/images/hopper.tif") - self.assertFalse(im.tag.next) - im.load() - self.assertFalse(im.tag.next) + with Image.open("Tests/images/hopper.tif") as im: + self.assertFalse(im.tag.next) + im.load() + self.assertFalse(im.tag.next) def test_4bit(self): # Arrange @@ -567,13 +543,13 @@ class TestFileLibTiff(LibTiffTestCase): # Act TiffImagePlugin.READ_LIBTIFF = True - im = Image.open(test_file) - TiffImagePlugin.READ_LIBTIFF = False + with Image.open(test_file) as im: + TiffImagePlugin.READ_LIBTIFF = False - # Assert - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.mode, "L") - self.assert_image_similar(im, original, 7.3) + # Assert + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, 7.3) def test_gray_semibyte_per_pixel(self): test_files = ( @@ -598,15 +574,15 @@ class TestFileLibTiff(LibTiffTestCase): ) original = hopper("L") for epsilon, group in test_files: - im = Image.open(group[0]) - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.mode, "L") - self.assert_image_similar(im, original, epsilon) + with Image.open(group[0]) as im: + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, epsilon) for file in group[1:]: - im2 = Image.open(file) - self.assertEqual(im2.size, (128, 128)) - self.assertEqual(im2.mode, "L") - self.assert_image_equal(im, im2) + with Image.open(file) as im2: + self.assertEqual(im2.size, (128, 128)) + self.assertEqual(im2.mode, "L") + self.assert_image_equal(im, im2) def test_save_bytesio(self): # PR 1011 @@ -624,8 +600,8 @@ class TestFileLibTiff(LibTiffTestCase): pilim.save(buffer_io, format="tiff", compression=compression) buffer_io.seek(0) - pilim_load = Image.open(buffer_io) - self.assert_image_similar(pilim, pilim_load, 0) + with Image.open(buffer_io) as pilim_load: + self.assert_image_similar(pilim, pilim_load, 0) save_bytesio() save_bytesio("raw") @@ -637,12 +613,12 @@ class TestFileLibTiff(LibTiffTestCase): def test_crashing_metadata(self): # issue 1597 - im = Image.open("Tests/images/rdf.tif") - out = self.tempfile("temp.tif") + with Image.open("Tests/images/rdf.tif") as im: + out = self.tempfile("temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True - # this shouldn't crash - im.save(out, format="TIFF") + TiffImagePlugin.WRITE_LIBTIFF = True + # this shouldn't crash + im.save(out, format="TIFF") TiffImagePlugin.WRITE_LIBTIFF = False def test_page_number_x_0(self): @@ -708,44 +684,50 @@ class TestFileLibTiff(LibTiffTestCase): # Created with ImageMagick: convert hopper.jpg hopper_jpg.tif # Contains JPEGTables (347) tag infile = "Tests/images/hopper_jpg.tif" - im = Image.open(infile) - - # Act / Assert - # Should not raise UnicodeDecodeError or anything else - im.save(outfile) + with Image.open(infile) as im: + # Act / Assert + # Should not raise UnicodeDecodeError or anything else + im.save(outfile) def test_16bit_RGB_tiff(self): - im = Image.open("Tests/images/tiff_16bit_RGB.tiff") + with Image.open("Tests/images/tiff_16bit_RGB.tiff") as im: + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (100, 40)) + self.assertEqual( + im.tile, + [ + ( + "libtiff", + (0, 0, 100, 40), + 0, + ("RGB;16N", "tiff_adobe_deflate", False, 8), + ) + ], + ) + im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (100, 40)) - self.assertEqual( - im.tile, - [ - ( - "libtiff", - (0, 0, 100, 40), - 0, - ("RGB;16N", "tiff_adobe_deflate", False, 8), - ) - ], - ) - im.load() - - self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png") + self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png") def test_16bit_RGBa_tiff(self): - im = Image.open("Tests/images/tiff_16bit_RGBa.tiff") + with Image.open("Tests/images/tiff_16bit_RGBa.tiff") as im: + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (100, 40)) + self.assertEqual( + im.tile, + [ + ( + "libtiff", + (0, 0, 100, 40), + 0, + ("RGBa;16N", "tiff_lzw", False, 38236), + ) + ], + ) + im.load() - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (100, 40)) - self.assertEqual( - im.tile, - [("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236))], - ) - im.load() - - self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png") + self.assert_image_equal_tofile( + im, "Tests/images/tiff_16bit_RGBa_target.png" + ) def test_gimp_tiff(self): # Read TIFF JPEG images from GIMP [@PIL168] @@ -755,82 +737,79 @@ class TestFileLibTiff(LibTiffTestCase): self.skipTest("jpeg support not available") filename = "Tests/images/pil168.tif" - im = Image.open(filename) + with Image.open(filename) as im: + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (256, 256)) + self.assertEqual( + im.tile, + [("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))], + ) + im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (256, 256)) - self.assertEqual( - im.tile, [("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))] - ) - im.load() - - self.assert_image_equal_tofile(im, "Tests/images/pil168.png") + self.assert_image_equal_tofile(im, "Tests/images/pil168.png") def test_sampleformat(self): # https://github.com/python-pillow/Pillow/issues/1466 - im = Image.open("Tests/images/copyleft.tiff") - self.assertEqual(im.mode, "RGB") + with Image.open("Tests/images/copyleft.tiff") as im: + self.assertEqual(im.mode, "RGB") - self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") + self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") def test_lzw(self): - im = Image.open("Tests/images/hopper_lzw.tif") - - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "TIFF") - im2 = hopper() - self.assert_image_similar(im, im2, 5) + with Image.open("Tests/images/hopper_lzw.tif") as im: + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "TIFF") + im2 = hopper() + self.assert_image_similar(im, im2, 5) def test_strip_cmyk_jpeg(self): infile = "Tests/images/tiff_strip_cmyk_jpeg.tif" - im = Image.open(infile) - - self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5) + with Image.open(infile) as im: + self.assert_image_similar_tofile( + im, "Tests/images/pil_sample_cmyk.jpg", 0.5 + ) def test_strip_cmyk_16l_jpeg(self): infile = "Tests/images/tiff_strip_cmyk_16l_jpeg.tif" - im = Image.open(infile) - - self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5) + with Image.open(infile) as im: + self.assert_image_similar_tofile( + im, "Tests/images/pil_sample_cmyk.jpg", 0.5 + ) def test_strip_ycbcr_jpeg_2x2_sampling(self): infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif" - im = Image.open(infile) - - self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) + with Image.open(infile) as im: + self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) def test_strip_ycbcr_jpeg_1x1_sampling(self): infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg") + with Image.open(infile) as im: + self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg") def test_tiled_cmyk_jpeg(self): infile = "Tests/images/tiff_tiled_cmyk_jpeg.tif" - im = Image.open(infile) - - self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5) + with Image.open(infile) as im: + self.assert_image_similar_tofile( + im, "Tests/images/pil_sample_cmyk.jpg", 0.5 + ) def test_tiled_ycbcr_jpeg_1x1_sampling(self): infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg") + with Image.open(infile) as im: + self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg") def test_tiled_ycbcr_jpeg_2x2_sampling(self): infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif" - im = Image.open(infile) - - self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) + with Image.open(infile) as im: + self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) def test_old_style_jpeg(self): infile = "Tests/images/old-style-jpeg-compression.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile( - im, "Tests/images/old-style-jpeg-compression.png" - ) + with Image.open(infile) as im: + self.assert_image_equal_tofile( + im, "Tests/images/old-style-jpeg-compression.png" + ) def test_no_rows_per_strip(self): # This image does not have a RowsPerStrip TIFF tag @@ -840,13 +819,12 @@ class TestFileLibTiff(LibTiffTestCase): self.assertEqual(im.size, (950, 975)) def test_orientation(self): - base_im = Image.open("Tests/images/g4_orientation_1.tif") + with Image.open("Tests/images/g4_orientation_1.tif") as base_im: + for i in range(2, 9): + with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im: + im.load() - for i in range(2, 9): - im = Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") - im.load() - - self.assert_image_similar(base_im, im, 0.7) + self.assert_image_similar(base_im, im, 0.7) def test_sampleformat_not_corrupted(self): # Assert that a TIFF image with SampleFormat=UINT tag is not corrupted diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index 2eabc60fd..a4e74896f 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -20,10 +20,9 @@ class TestFileLibTiffSmall(LibTiffTestCase): test_file = "Tests/images/hopper_g4.tif" with open(test_file, "rb") as f: - im = Image.open(f) - - self.assertEqual(im.size, (128, 128)) - self._assert_noerr(im) + with Image.open(f) as im: + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) def test_g4_hopper_bytesio(self): """Testing the bytesio loading code path""" @@ -32,16 +31,14 @@ class TestFileLibTiffSmall(LibTiffTestCase): with open(test_file, "rb") as f: s.write(f.read()) s.seek(0) - im = Image.open(s) - - self.assertEqual(im.size, (128, 128)) - self._assert_noerr(im) + with Image.open(s) as im: + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) def test_g4_hopper(self): """The 128x128 lena image failed for some reason.""" test_file = "Tests/images/hopper_g4.tif" - im = Image.open(test_file) - - self.assertEqual(im.size, (128, 128)) - self._assert_noerr(im) + with Image.open(test_file) as im: + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index acc4ddb91..ed2653cd3 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -17,12 +17,12 @@ class TestFileMcIdas(PillowTestCase): saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png" # Act - im = Image.open(test_file) - im.load() + with Image.open(test_file) as im: + im.load() - # Assert - self.assertEqual(im.format, "MCIDAS") - self.assertEqual(im.mode, "I") - self.assertEqual(im.size, (1800, 400)) - im2 = Image.open(saved_file) - self.assert_image_equal(im, im2) + # Assert + self.assertEqual(im.format, "MCIDAS") + self.assertEqual(im.mode, "I") + self.assertEqual(im.size, (1800, 400)) + with Image.open(saved_file) as im2: + self.assert_image_equal(im, im2) diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index f9ea7712c..00f42fa4a 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image, ImagePalette, features -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper try: from PIL import MicImagePlugin diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 9c8a2b468..cdcf2b041 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -113,6 +113,13 @@ class TestFileMpo(PillowTestCase): self.assertEqual(mpinfo[45056], b"0100") self.assertEqual(mpinfo[45057], 2) + def test_mp_no_data(self): + # This image has been manually hexedited to have the second frame + # beyond the end of the file + with Image.open("Tests/images/sugarshack_no_data.mpo") as im: + with self.assertRaises(ValueError): + im.seek(1) + def test_mp_attribute(self): for test_file in test_files: with Image.open(test_file) as im: diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 5d512047b..51189b83b 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -1,8 +1,9 @@ import os +import unittest from PIL import Image, MspImagePlugin -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper TEST_FILE = "Tests/images/hopper.msp" EXTRA_DIR = "Tests/images/picins" @@ -15,11 +16,11 @@ class TestFileMsp(PillowTestCase): hopper("1").save(test_file) - im = Image.open(test_file) - im.load() - self.assertEqual(im.mode, "1") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "MSP") + with Image.open(test_file) as im: + im.load() + self.assertEqual(im.mode, "1") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "MSP") def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -37,16 +38,16 @@ class TestFileMsp(PillowTestCase): def test_open_windows_v1(self): # Arrange # Act - im = Image.open(TEST_FILE) + with Image.open(TEST_FILE) as im: - # Assert - self.assert_image_equal(im, hopper("1")) - self.assertIsInstance(im, MspImagePlugin.MspImageFile) + # Assert + self.assert_image_equal(im, hopper("1")) + self.assertIsInstance(im, MspImagePlugin.MspImageFile) def _assert_file_image_equal(self, source_path, target_path): with Image.open(source_path) as im: - target = Image.open(target_path) - self.assert_image_equal(im, target) + with Image.open(target_path) as target: + self.assert_image_equal(im, target) @unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed") def test_open_windows_v2(self): diff --git a/Tests/test_file_pcd.py b/Tests/test_file_pcd.py index b23328ba5..55d753fc3 100644 --- a/Tests/test_file_pcd.py +++ b/Tests/test_file_pcd.py @@ -5,8 +5,8 @@ from .helper import PillowTestCase class TestFilePcd(PillowTestCase): def test_load_raw(self): - im = Image.open("Tests/images/hopper.pcd") - im.load() # should not segfault. + with Image.open("Tests/images/hopper.pcd") as im: + im.load() # should not segfault. # Note that this image was created with a resized hopper # image, which was then converted to pcd with imagemagick diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index eb2c7d611..780739422 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -7,13 +7,12 @@ class TestFilePcx(PillowTestCase): def _roundtrip(self, im): f = self.tempfile("temp.pcx") im.save(f) - im2 = Image.open(f) - - 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) + with Image.open(f) as im2: + 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): for mode in ("1", "L", "P", "RGB"): @@ -42,13 +41,12 @@ class TestFilePcx(PillowTestCase): # Check reading of files where xmin/xmax is not zero. test_file = "Tests/images/pil184.pcx" - im = Image.open(test_file) + with Image.open(test_file) as im: + self.assertEqual(im.size, (447, 144)) + self.assertEqual(im.tile[0][1], (0, 0, 447, 144)) - self.assertEqual(im.size, (447, 144)) - self.assertEqual(im.tile[0][1], (0, 0, 447, 144)) - - # Make sure all pixels are either 0 or 255. - self.assertEqual(im.histogram()[0] + im.histogram()[255], 447 * 144) + # Make sure all pixels are either 0 or 255. + self.assertEqual(im.histogram()[0] + im.histogram()[255], 447 * 144) def test_1px_width(self): im = Image.new("L", (1, 256)) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index c4f64a0a0..9b8b789b9 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -99,8 +99,7 @@ class TestFilePdf(PillowTestCase): # Test appending using a generator def imGenerator(ims): - for im in ims: - yield im + yield from ims im.save(outfile, save_all=True, append_images=imGenerator(ims)) @@ -108,8 +107,8 @@ class TestFilePdf(PillowTestCase): self.assertGreater(os.path.getsize(outfile), 0) # Append JPEG images - jpeg = Image.open("Tests/images/flower.jpg") - jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()]) + with Image.open("Tests/images/flower.jpg") as jpeg: + jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()]) self.assertTrue(os.path.isfile(outfile)) self.assertGreater(os.path.getsize(outfile), 0) @@ -163,13 +162,10 @@ class TestFilePdf(PillowTestCase): def test_pdf_append_fails_on_nonexistent_file(self): im = hopper("RGB") - temp_dir = tempfile.mkdtemp() - try: + with tempfile.TemporaryDirectory() as temp_dir: self.assertRaises( IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True ) - finally: - os.rmdir(temp_dir) def check_pdf_pages_consistency(self, pdf): pages_info = pdf.read_indirect(pdf.pages_ref) @@ -207,7 +203,7 @@ class TestFilePdf(PillowTestCase): # append some info pdf.info.Title = "abc" pdf.info.Author = "def" - pdf.info.Subject = u"ghi\uABCD" + pdf.info.Subject = "ghi\uABCD" pdf.info.Keywords = "qw)e\\r(ty" pdf.info.Creator = "hopper()" pdf.start_writing() @@ -235,7 +231,7 @@ class TestFilePdf(PillowTestCase): self.assertEqual(pdf.info.Title, "abc") self.assertEqual(pdf.info.Producer, "PdfParser") self.assertEqual(pdf.info.Keywords, "qw)e\\r(ty") - self.assertEqual(pdf.info.Subject, u"ghi\uABCD") + self.assertEqual(pdf.info.Subject, "ghi\uABCD") self.assertIn(b"CreationDate", pdf.info) self.assertIn(b"ModDate", pdf.info) self.check_pdf_pages_consistency(pdf) diff --git a/Tests/test_file_pixar.py b/Tests/test_file_pixar.py index c744932d4..eae2fabba 100644 --- a/Tests/test_file_pixar.py +++ b/Tests/test_file_pixar.py @@ -7,15 +7,15 @@ TEST_FILE = "Tests/images/hopper.pxr" class TestFilePixar(PillowTestCase): def test_sanity(self): - im = Image.open(TEST_FILE) - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "PIXAR") - self.assertIsNone(im.get_format_mimetype()) + with Image.open(TEST_FILE) as im: + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PIXAR") + self.assertIsNone(im.get_format_mimetype()) - im2 = hopper() - self.assert_image_similar(im, im2, 4.8) + im2 = hopper() + self.assert_image_similar(im, im2, 4.8) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index ee2a95255..4cd613785 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -1,10 +1,10 @@ +import unittest import zlib from io import BytesIO from PIL import Image, ImageFile, PngImagePlugin -from PIL._util import py3 -from .helper import PillowLeakTestCase, PillowTestCase, hopper, is_win32, unittest +from .helper import PillowLeakTestCase, PillowTestCase, hopper, is_win32 try: from PIL import _webp @@ -91,10 +91,10 @@ class TestFilePng(PillowTestCase): for mode in ["1", "L", "P", "RGB", "I", "I;16"]: im = hopper(mode) im.save(test_file) - reloaded = Image.open(test_file) - if mode == "I;16": - reloaded = reloaded.convert(mode) - self.assert_image_equal(reloaded, im) + with Image.open(test_file) as reloaded: + if mode == "I;16": + reloaded = reloaded.convert(mode) + self.assert_image_equal(reloaded, im) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -195,27 +195,24 @@ class TestFilePng(PillowTestCase): def test_interlace(self): test_file = "Tests/images/pil123p.png" - im = Image.open(test_file) + with Image.open(test_file) as im: + self.assert_image(im, "P", (162, 150)) + self.assertTrue(im.info.get("interlace")) - self.assert_image(im, "P", (162, 150)) - self.assertTrue(im.info.get("interlace")) - - im.load() + im.load() test_file = "Tests/images/pil123rgba.png" - im = Image.open(test_file) + with Image.open(test_file) as im: + self.assert_image(im, "RGBA", (162, 150)) + self.assertTrue(im.info.get("interlace")) - self.assert_image(im, "RGBA", (162, 150)) - self.assertTrue(im.info.get("interlace")) - - im.load() + im.load() def test_load_transparent_p(self): test_file = "Tests/images/pil123p.png" - im = Image.open(test_file) - - self.assert_image(im, "P", (162, 150)) - im = im.convert("RGBA") + with Image.open(test_file) as im: + self.assert_image(im, "P", (162, 150)) + im = im.convert("RGBA") self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values @@ -223,11 +220,11 @@ class TestFilePng(PillowTestCase): def test_load_transparent_rgb(self): test_file = "Tests/images/rgb_trns.png" - im = Image.open(test_file) - self.assertEqual(im.info["transparency"], (0, 255, 52)) + with Image.open(test_file) as im: + self.assertEqual(im.info["transparency"], (0, 255, 52)) - self.assert_image(im, "RGB", (64, 64)) - im = im.convert("RGBA") + self.assert_image(im, "RGB", (64, 64)) + im = im.convert("RGBA") self.assert_image(im, "RGBA", (64, 64)) # image has 876 transparent pixels @@ -235,21 +232,20 @@ class TestFilePng(PillowTestCase): def test_save_p_transparent_palette(self): in_file = "Tests/images/pil123p.png" - im = Image.open(in_file) + with Image.open(in_file) as im: + # 'transparency' contains a byte string with the opacity for + # each palette entry + self.assertEqual(len(im.info["transparency"]), 256) - # 'transparency' contains a byte string with the opacity for - # each palette entry - self.assertEqual(len(im.info["transparency"]), 256) - - test_file = self.tempfile("temp.png") - im.save(test_file) + test_file = self.tempfile("temp.png") + im.save(test_file) # check if saved image contains same transparency - im = Image.open(test_file) - self.assertEqual(len(im.info["transparency"]), 256) + with Image.open(test_file) as im: + self.assertEqual(len(im.info["transparency"]), 256) - self.assert_image(im, "P", (162, 150)) - im = im.convert("RGBA") + self.assert_image(im, "P", (162, 150)) + im = im.convert("RGBA") self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values @@ -257,21 +253,20 @@ class TestFilePng(PillowTestCase): def test_save_p_single_transparency(self): in_file = "Tests/images/p_trns_single.png" - im = Image.open(in_file) + with Image.open(in_file) as im: + # pixel value 164 is full transparent + self.assertEqual(im.info["transparency"], 164) + self.assertEqual(im.getpixel((31, 31)), 164) - # pixel value 164 is full transparent - self.assertEqual(im.info["transparency"], 164) - self.assertEqual(im.getpixel((31, 31)), 164) - - test_file = self.tempfile("temp.png") - im.save(test_file) + test_file = self.tempfile("temp.png") + im.save(test_file) # check if saved image contains same transparency - im = Image.open(test_file) - self.assertEqual(im.info["transparency"], 164) - self.assertEqual(im.getpixel((31, 31)), 164) - self.assert_image(im, "P", (64, 64)) - im = im.convert("RGBA") + with Image.open(test_file) as im: + self.assertEqual(im.info["transparency"], 164) + self.assertEqual(im.getpixel((31, 31)), 164) + self.assert_image(im, "P", (64, 64)) + im = im.convert("RGBA") self.assert_image(im, "RGBA", (64, 64)) self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0)) @@ -290,30 +285,30 @@ class TestFilePng(PillowTestCase): im.save(test_file) # check if saved image contains same transparency - im = Image.open(test_file) - self.assertEqual(len(im.info["transparency"]), 256) - self.assert_image(im, "P", (10, 10)) - im = im.convert("RGBA") + with Image.open(test_file) as im: + self.assertEqual(len(im.info["transparency"]), 256) + self.assert_image(im, "P", (10, 10)) + im = im.convert("RGBA") self.assert_image(im, "RGBA", (10, 10)) self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))]) def test_save_greyscale_transparency(self): for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items(): in_file = "Tests/images/" + mode.lower() + "_trns.png" - im = Image.open(in_file) - self.assertEqual(im.mode, mode) - self.assertEqual(im.info["transparency"], 255) + with Image.open(in_file) as im: + self.assertEqual(im.mode, mode) + self.assertEqual(im.info["transparency"], 255) - im_rgba = im.convert("RGBA") + im_rgba = im.convert("RGBA") self.assertEqual(im_rgba.getchannel("A").getcolors()[0][0], num_transparent) test_file = self.tempfile("temp.png") im.save(test_file) - test_im = Image.open(test_file) - self.assertEqual(test_im.mode, mode) - self.assertEqual(test_im.info["transparency"], 255) - self.assert_image_equal(im, test_im) + with Image.open(test_file) as test_im: + self.assertEqual(test_im.mode, mode) + self.assertEqual(test_im.info["transparency"], 255) + self.assert_image_equal(im, test_im) test_im_rgba = test_im.convert("RGBA") self.assertEqual( @@ -322,22 +317,20 @@ class TestFilePng(PillowTestCase): def test_save_rgb_single_transparency(self): in_file = "Tests/images/caption_6_33_22.png" - im = Image.open(in_file) - - test_file = self.tempfile("temp.png") - im.save(test_file) + with Image.open(in_file) as im: + test_file = self.tempfile("temp.png") + im.save(test_file) def test_load_verify(self): # Check open/load/verify exception (@PIL150) - im = Image.open(TEST_PNG_FILE) + with Image.open(TEST_PNG_FILE) as im: + # Assert that there is no unclosed file warning + self.assert_warning(None, im.verify) - # Assert that there is no unclosed file warning - self.assert_warning(None, im.verify) - - im = Image.open(TEST_PNG_FILE) - im.load() - self.assertRaises(RuntimeError, im.verify) + with Image.open(TEST_PNG_FILE) as im: + im.load() + self.assertRaises(RuntimeError, im.verify) def test_verify_struct_error(self): # Check open/load/verify exception (#1755) @@ -350,9 +343,9 @@ class TestFilePng(PillowTestCase): with open(TEST_PNG_FILE, "rb") as f: test_file = f.read()[:offset] - im = Image.open(BytesIO(test_file)) - self.assertIsNotNone(im.fp) - self.assertRaises((IOError, SyntaxError), im.verify) + with Image.open(BytesIO(test_file)) as im: + self.assertIsNotNone(im.fp) + self.assertRaises((IOError, SyntaxError), im.verify) def test_verify_ignores_crc_error(self): # check ignores crc errors in ancillary chunks @@ -386,9 +379,8 @@ class TestFilePng(PillowTestCase): def test_roundtrip_dpi(self): # Check dpi roundtripping - im = Image.open(TEST_PNG_FILE) - - im = roundtrip(im, dpi=(100, 100)) + with Image.open(TEST_PNG_FILE) as im: + im = roundtrip(im, dpi=(100, 100)) self.assertEqual(im.info["dpi"], (100, 100)) def test_load_dpi_rounding(self): @@ -401,9 +393,8 @@ class TestFilePng(PillowTestCase): self.assertEqual(im.info["dpi"], (72, 72)) def test_save_dpi_rounding(self): - im = Image.open(TEST_PNG_FILE) - - im = roundtrip(im, dpi=(72.2, 72.2)) + with Image.open(TEST_PNG_FILE) as im: + im = roundtrip(im, dpi=(72.2, 72.2)) self.assertEqual(im.info["dpi"], (72, 72)) im = roundtrip(im, dpi=(72.8, 72.8)) @@ -412,13 +403,12 @@ class TestFilePng(PillowTestCase): def test_roundtrip_text(self): # Check text roundtripping - im = Image.open(TEST_PNG_FILE) + with Image.open(TEST_PNG_FILE) as im: + info = PngImagePlugin.PngInfo() + info.add_text("TXT", "VALUE") + info.add_text("ZIP", "VALUE", zip=True) - info = PngImagePlugin.PngInfo() - info.add_text("TXT", "VALUE") - info.add_text("ZIP", "VALUE", zip=True) - - im = roundtrip(im, pnginfo=info) + im = roundtrip(im, pnginfo=info) self.assertEqual(im.info, {"TXT": "VALUE", "ZIP": "VALUE"}) self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"}) @@ -448,9 +438,7 @@ class TestFilePng(PillowTestCase): self.assertIsInstance(im.info["Text"], str) def test_unicode_text(self): - # Check preservation of non-ASCII characters on Python 3 - # This cannot really be meaningfully tested on Python 2, - # since it didn't preserve charsets to begin with. + # Check preservation of non-ASCII characters def rt_text(value): im = Image.new("RGB", (32, 32)) @@ -459,17 +447,11 @@ class TestFilePng(PillowTestCase): im = roundtrip(im, pnginfo=info) self.assertEqual(im.info, {"Text": value}) - if py3: - rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1 - rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic - rt_text( - chr(0x4E00) - + chr(0x66F0) - + chr(0x9FBA) # CJK - + chr(0x3042) - + chr(0xAC00) - ) - rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined + rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1 + rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic + # CJK: + rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00)) + rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined def test_scary(self): # Check reading of evil PNG file. For information, see: @@ -488,11 +470,11 @@ class TestFilePng(PillowTestCase): # Independent file sample provided by Sebastian Spaeth. test_file = "Tests/images/caption_6_33_22.png" - im = Image.open(test_file) - self.assertEqual(im.info["transparency"], (248, 248, 248)) + with Image.open(test_file) as im: + self.assertEqual(im.info["transparency"], (248, 248, 248)) - # check saving transparency by default - im = roundtrip(im) + # check saving transparency by default + im = roundtrip(im) self.assertEqual(im.info["transparency"], (248, 248, 248)) im = roundtrip(im, transparency=(0, 1, 2)) @@ -506,10 +488,10 @@ class TestFilePng(PillowTestCase): f = self.tempfile("temp.png") im.save(f) - im2 = Image.open(f) - self.assertIn("transparency", im2.info) + with Image.open(f) as im2: + self.assertIn("transparency", im2.info) - self.assert_image_equal(im2.convert("RGBA"), im.convert("RGBA")) + self.assert_image_equal(im2.convert("RGBA"), im.convert("RGBA")) def test_trns_null(self): # Check reading images with null tRNS value, issue #1239 @@ -529,36 +511,35 @@ class TestFilePng(PillowTestCase): self.assertEqual(im.info["icc_profile"], expected_icc) def test_discard_icc_profile(self): - im = Image.open("Tests/images/icc_profile.png") - - im = roundtrip(im, icc_profile=None) + with Image.open("Tests/images/icc_profile.png") as im: + im = roundtrip(im, icc_profile=None) self.assertNotIn("icc_profile", im.info) def test_roundtrip_icc_profile(self): - im = Image.open("Tests/images/icc_profile.png") - expected_icc = im.info["icc_profile"] + with Image.open("Tests/images/icc_profile.png") as im: + expected_icc = im.info["icc_profile"] - im = roundtrip(im) + im = roundtrip(im) self.assertEqual(im.info["icc_profile"], expected_icc) def test_roundtrip_no_icc_profile(self): - im = Image.open("Tests/images/icc_profile_none.png") - self.assertIsNone(im.info["icc_profile"]) + with Image.open("Tests/images/icc_profile_none.png") as im: + self.assertIsNone(im.info["icc_profile"]) - im = roundtrip(im) + im = roundtrip(im) self.assertNotIn("icc_profile", im.info) def test_repr_png(self): im = hopper() - repr_png = Image.open(BytesIO(im._repr_png_())) - self.assertEqual(repr_png.format, "PNG") - self.assert_image_equal(im, repr_png) + with Image.open(BytesIO(im._repr_png_())) as repr_png: + self.assertEqual(repr_png.format, "PNG") + self.assert_image_equal(im, repr_png) def test_chunk_order(self): - im = Image.open("Tests/images/icc_profile.png") - test_file = self.tempfile("temp.png") - im.convert("P").save(test_file, dpi=(100, 100)) + with Image.open("Tests/images/icc_profile.png") as im: + test_file = self.tempfile("temp.png") + im.convert("P").save(test_file, dpi=(100, 100)) chunks = self.get_chunks(test_file) @@ -583,61 +564,58 @@ class TestFilePng(PillowTestCase): self.assertEqual(len(chunks), 3) def test_textual_chunks_after_idat(self): - im = Image.open("Tests/images/hopper.png") - self.assertIn("comment", im.text.keys()) - for k, v in { - "date:create": "2014-09-04T09:37:08+03:00", - "date:modify": "2014-09-04T09:37:08+03:00", - }.items(): - self.assertEqual(im.text[k], v) + with Image.open("Tests/images/hopper.png") as im: + self.assertIn("comment", im.text.keys()) + for k, v in { + "date:create": "2014-09-04T09:37:08+03:00", + "date:modify": "2014-09-04T09:37:08+03:00", + }.items(): + self.assertEqual(im.text[k], v) # Raises a SyntaxError in load_end - im = Image.open("Tests/images/broken_data_stream.png") - with self.assertRaises(IOError): - self.assertIsInstance(im.text, dict) + with Image.open("Tests/images/broken_data_stream.png") as im: + with self.assertRaises(IOError): + self.assertIsInstance(im.text, dict) # Raises a UnicodeDecodeError in load_end - im = Image.open("Tests/images/truncated_image.png") - # The file is truncated - self.assertRaises(IOError, lambda: im.text) - ImageFile.LOAD_TRUNCATED_IMAGES = True - self.assertIsInstance(im.text, dict) - ImageFile.LOAD_TRUNCATED_IMAGES = False + with Image.open("Tests/images/truncated_image.png") as im: + # The file is truncated + self.assertRaises(IOError, lambda: im.text) + ImageFile.LOAD_TRUNCATED_IMAGES = True + 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"}) + with Image.open("Tests/images/hopper_idat_after_image_end.png") as im: + self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"}) def test_exif(self): - im = Image.open("Tests/images/exif.png") - exif = im._getexif() + with Image.open("Tests/images/exif.png") as im: + exif = im._getexif() self.assertEqual(exif[274], 1) def test_exif_save(self): - im = Image.open("Tests/images/exif.png") - - test_file = self.tempfile("temp.png") - im.save(test_file) + with Image.open("Tests/images/exif.png") as im: + test_file = self.tempfile("temp.png") + im.save(test_file) with Image.open(test_file) as reloaded: exif = reloaded._getexif() self.assertEqual(exif[274], 1) def test_exif_from_jpg(self): - im = Image.open("Tests/images/pil_sample_rgb.jpg") - - test_file = self.tempfile("temp.png") - im.save(test_file) + with Image.open("Tests/images/pil_sample_rgb.jpg") as im: + test_file = self.tempfile("temp.png") + im.save(test_file) with Image.open(test_file) as reloaded: exif = reloaded._getexif() self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh") def test_exif_argument(self): - im = Image.open(TEST_PNG_FILE) - - test_file = self.tempfile("temp.png") - im.save(test_file, exif=b"exifstring") + with Image.open(TEST_PNG_FILE) as im: + test_file = self.tempfile("temp.png") + im.save(test_file, exif=b"exifstring") with Image.open(test_file) as reloaded: self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring") @@ -646,12 +624,12 @@ class TestFilePng(PillowTestCase): 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") + with Image.open("Tests/images/iss634.apng") as im: + 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) + # This also tests reading unknown PNG chunks (fcTL and fdAT) in load_end + with Image.open("Tests/images/iss634.webp") as expected: + self.assert_image_similar(im, expected, 0.23) @unittest.skipIf(is_win32(), "requires Unix or macOS") diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 226687fa3..cd3719c18 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -8,42 +8,42 @@ test_file = "Tests/images/hopper.ppm" class TestFilePpm(PillowTestCase): def test_sanity(self): - im = Image.open(test_file) - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "PPM") - self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap") + with Image.open(test_file) as im: + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PPM") + self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap") def test_16bit_pgm(self): - im = Image.open("Tests/images/16_bit_binary.pgm") - im.load() - self.assertEqual(im.mode, "I") - self.assertEqual(im.size, (20, 100)) - self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap") + with Image.open("Tests/images/16_bit_binary.pgm") as im: + im.load() + self.assertEqual(im.mode, "I") + self.assertEqual(im.size, (20, 100)) + self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap") - tgt = Image.open("Tests/images/16_bit_binary_pgm.png") - self.assert_image_equal(im, tgt) + with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt: + self.assert_image_equal(im, tgt) def test_16bit_pgm_write(self): - im = Image.open("Tests/images/16_bit_binary.pgm") - im.load() + with Image.open("Tests/images/16_bit_binary.pgm") as im: + im.load() - f = self.tempfile("temp.pgm") - im.save(f, "PPM") + f = self.tempfile("temp.pgm") + im.save(f, "PPM") - reloaded = Image.open(f) - self.assert_image_equal(im, reloaded) + with Image.open(f) as reloaded: + 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) + with Image.open("Tests/images/hopper.pnm") as im: + self.assert_image_similar(im, hopper(), 0.0001) - f = self.tempfile("temp.pnm") - im.save(f) + f = self.tempfile("temp.pnm") + im.save(f) - reloaded = Image.open(f) - self.assert_image_equal(im, reloaded) + with Image.open(f) as reloaded: + self.assert_image_equal(im, reloaded) def test_truncated_file(self): path = self.tempfile("temp.pgm") diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index ff3aea1d5..2cb510c6f 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -9,50 +9,50 @@ class TestFileSgi(PillowTestCase): # convert hopper.ppm -compress None sgi:hopper.rgb test_file = "Tests/images/hopper.rgb" - im = Image.open(test_file) - self.assert_image_equal(im, hopper()) - self.assertEqual(im.get_format_mimetype(), "image/rgb") + with Image.open(test_file) as im: + self.assert_image_equal(im, hopper()) + self.assertEqual(im.get_format_mimetype(), "image/rgb") def test_rgb16(self): test_file = "Tests/images/hopper16.rgb" - im = Image.open(test_file) - self.assert_image_equal(im, hopper()) + with Image.open(test_file) as im: + self.assert_image_equal(im, hopper()) def test_l(self): # Created with ImageMagick # convert hopper.ppm -monochrome -compress None sgi:hopper.bw test_file = "Tests/images/hopper.bw" - im = Image.open(test_file) - self.assert_image_similar(im, hopper("L"), 2) - self.assertEqual(im.get_format_mimetype(), "image/sgi") + with Image.open(test_file) as im: + self.assert_image_similar(im, hopper("L"), 2) + self.assertEqual(im.get_format_mimetype(), "image/sgi") def test_rgba(self): # Created with ImageMagick: # convert transparent.png -compress None transparent.sgi test_file = "Tests/images/transparent.sgi" - 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") + with Image.open(test_file) as im: + with Image.open("Tests/images/transparent.png") as target: + self.assert_image_equal(im, target) + self.assertEqual(im.get_format_mimetype(), "image/sgi") def test_rle(self): # Created with ImageMagick: # convert hopper.ppm hopper.sgi test_file = "Tests/images/hopper.sgi" - im = Image.open(test_file) - target = Image.open("Tests/images/hopper.rgb") - self.assert_image_equal(im, target) + with Image.open(test_file) as im: + with Image.open("Tests/images/hopper.rgb") as target: + self.assert_image_equal(im, target) def test_rle16(self): test_file = "Tests/images/tv16.sgi" - im = Image.open(test_file) - target = Image.open("Tests/images/tv.rgb") - self.assert_image_equal(im, target) + with Image.open(test_file) as im: + with Image.open("Tests/images/tv.rgb") as target: + self.assert_image_equal(im, target) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -63,8 +63,8 @@ class TestFileSgi(PillowTestCase): def roundtrip(img): out = self.tempfile("temp.sgi") img.save(out, format="sgi") - reloaded = Image.open(out) - self.assert_image_equal(img, reloaded) + with Image.open(out) as reloaded: + self.assert_image_equal(img, reloaded) for mode in ("L", "RGB", "RGBA"): roundtrip(hopper(mode)) @@ -75,12 +75,12 @@ class TestFileSgi(PillowTestCase): def test_write16(self): test_file = "Tests/images/hopper16.rgb" - im = Image.open(test_file) - out = self.tempfile("temp.sgi") - im.save(out, format="sgi", bpc=2) + with Image.open(test_file) as im: + out = self.tempfile("temp.sgi") + im.save(out, format="sgi", bpc=2) - reloaded = Image.open(out) - self.assert_image_equal(im, reloaded) + with Image.open(out) as reloaded: + self.assert_image_equal(im, reloaded) def test_unsupported_mode(self): im = hopper("LA") diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 5940c2ff2..4f617ce51 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -64,10 +64,10 @@ class TestImageSpider(PillowTestCase): # Assert fp.seek(0) - reloaded = Image.open(fp) - self.assertEqual(reloaded.mode, "F") - self.assertEqual(reloaded.size, (128, 128)) - self.assertEqual(reloaded.format, "SPIDER") + with Image.open(fp) as reloaded: + self.assertEqual(reloaded.mode, "F") + self.assertEqual(reloaded.size, (128, 128)) + self.assertEqual(reloaded.format, "SPIDER") def test_isSpiderImage(self): self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE)) @@ -143,5 +143,5 @@ class TestImageSpider(PillowTestCase): im.save(data, format="SPIDER") data.seek(0) - im2 = Image.open(data) - self.assert_image_equal(im, im2) + with Image.open(data) as im2: + self.assert_image_equal(im, im2) diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index 84d59e0c7..835aead79 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -1,8 +1,9 @@ import os +import unittest from PIL import Image, SunImagePlugin -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper EXTRA_DIR = "Tests/images/sunraster" @@ -14,20 +15,20 @@ class TestFileSun(PillowTestCase): test_file = "Tests/images/hopper.ras" # Act - im = Image.open(test_file) + with Image.open(test_file) as im: - # Assert - self.assertEqual(im.size, (128, 128)) + # Assert + self.assertEqual(im.size, (128, 128)) - self.assert_image_similar(im, hopper(), 5) # visually verified + self.assert_image_similar(im, hopper(), 5) # visually verified invalid_file = "Tests/images/flower.jpg" self.assertRaises(SyntaxError, SunImagePlugin.SunImageFile, invalid_file) def test_im1(self): - im = Image.open("Tests/images/sunraster.im1") - target = Image.open("Tests/images/sunraster.im1.png") - self.assert_image_equal(im, target) + with Image.open("Tests/images/sunraster.im1") as im: + with Image.open("Tests/images/sunraster.im1.png") as target: + self.assert_image_equal(im, target) @unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed") def test_others(self): diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index f381eef7e..a77e4e84c 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -22,11 +22,11 @@ class TestFileTar(PillowTestCase): ]: if codec in codecs: with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar: - im = Image.open(tar) - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, format) + with Image.open(tar) as im: + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, format) @unittest.skipIf(is_pypy(), "Requires CPython") def test_unclosed_file(self): diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index bd5c32c5b..01664cd6e 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -24,52 +24,57 @@ class TestFileTga(PillowTestCase): ) for png_path in png_paths: - reference_im = Image.open(png_path) - self.assertEqual(reference_im.mode, mode) + with Image.open(png_path) as reference_im: + self.assertEqual(reference_im.mode, mode) - path_no_ext = os.path.splitext(png_path)[0] - for origin, rle in product(self._ORIGINS, (True, False)): - tga_path = "{}_{}_{}.tga".format( - 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") - self.assertEqual( - original_im.info["orientation"], - self._ORIGIN_TO_ORIENTATION[origin], - ) - if mode == "P": - self.assertEqual( - original_im.getpalette(), reference_im.getpalette() + path_no_ext = os.path.splitext(png_path)[0] + for origin, rle in product(self._ORIGINS, (True, False)): + tga_path = "{}_{}_{}.tga".format( + path_no_ext, origin, "rle" if rle else "raw" ) - self.assert_image_equal(original_im, reference_im) + with Image.open(tga_path) as original_im: + 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" + ) + self.assertEqual( + original_im.info["orientation"], + self._ORIGIN_TO_ORIENTATION[origin], + ) + if mode == "P": + self.assertEqual( + original_im.getpalette(), reference_im.getpalette() + ) - # Generate a new test name every time so the - # test will not fail with permission error - # on Windows. - out = self.tempfile("temp.tga") + self.assert_image_equal(original_im, reference_im) - original_im.save(out, rle=rle) - saved_im = Image.open(out) - if rle: - self.assertEqual( - saved_im.info["compression"], - original_im.info["compression"], - ) - self.assertEqual( - saved_im.info["orientation"], original_im.info["orientation"] - ) - if mode == "P": - self.assertEqual( - saved_im.getpalette(), original_im.getpalette() - ) + # Generate a new test name every time so the + # test will not fail with permission error + # on Windows. + out = self.tempfile("temp.tga") - self.assert_image_equal(saved_im, original_im) + original_im.save(out, rle=rle) + with Image.open(out) as saved_im: + if rle: + self.assertEqual( + saved_im.info["compression"], + original_im.info["compression"], + ) + self.assertEqual( + saved_im.info["orientation"], + original_im.info["orientation"], + ) + if mode == "P": + self.assertEqual( + saved_im.getpalette(), original_im.getpalette() + ) + + self.assert_image_equal(saved_im, original_im) def test_id_field(self): # tga file with id field @@ -93,29 +98,27 @@ class TestFileTga(PillowTestCase): def test_save(self): test_file = "Tests/images/tga_id_field.tga" - im = Image.open(test_file) + with Image.open(test_file) as im: + out = self.tempfile("temp.tga") - out = self.tempfile("temp.tga") + # Save + im.save(out) + with Image.open(out) as test_im: + self.assertEqual(test_im.size, (100, 100)) + self.assertEqual(test_im.info["id_section"], im.info["id_section"]) - # Save - im.save(out) - with Image.open(out) as test_im: - self.assertEqual(test_im.size, (100, 100)) - self.assertEqual(test_im.info["id_section"], im.info["id_section"]) - - # RGBA save - im.convert("RGBA").save(out) + # RGBA save + im.convert("RGBA").save(out) with Image.open(out) as test_im: self.assertEqual(test_im.size, (100, 100)) def test_save_id_section(self): test_file = "Tests/images/rgb32rle.tga" - im = Image.open(test_file) + with Image.open(test_file) as im: + out = self.tempfile("temp.tga") - out = self.tempfile("temp.tga") - - # Check there is no id section - im.save(out) + # Check there is no id section + im.save(out) with Image.open(out) as test_im: self.assertNotIn("id_section", test_im.info) @@ -140,24 +143,23 @@ class TestFileTga(PillowTestCase): def test_save_orientation(self): test_file = "Tests/images/rgb32rle.tga" - im = Image.open(test_file) - self.assertEqual(im.info["orientation"], -1) - out = self.tempfile("temp.tga") + with Image.open(test_file) as im: + self.assertEqual(im.info["orientation"], -1) - im.save(out, orientation=1) + im.save(out, orientation=1) with Image.open(out) as test_im: self.assertEqual(test_im.info["orientation"], 1) def test_save_rle(self): test_file = "Tests/images/rgb32rle.tga" - im = Image.open(test_file) - self.assertEqual(im.info["compression"], "tga_rle") + with Image.open(test_file) as im: + self.assertEqual(im.info["compression"], "tga_rle") - out = self.tempfile("temp.tga") + out = self.tempfile("temp.tga") - # Save - im.save(out) + # Save + im.save(out) with Image.open(out) as test_im: self.assertEqual(test_im.size, (199, 199)) self.assertEqual(test_im.info["compression"], "tga_rle") @@ -173,11 +175,11 @@ class TestFileTga(PillowTestCase): self.assertEqual(test_im.size, (199, 199)) test_file = "Tests/images/tga_id_field.tga" - im = Image.open(test_file) - self.assertNotIn("compression", im.info) + with Image.open(test_file) as im: + self.assertNotIn("compression", im.info) - # Save with compression - im.save(out, compression="tga_rle") + # Save with compression + im.save(out, compression="tga_rle") with Image.open(out) as test_im: self.assertEqual(test_im.info["compression"], "tga_rle") @@ -186,15 +188,15 @@ class TestFileTga(PillowTestCase): num_transparent = 559 in_file = "Tests/images/la.tga" - im = Image.open(in_file) - self.assertEqual(im.mode, "LA") - self.assertEqual(im.getchannel("A").getcolors()[0][0], num_transparent) + with Image.open(in_file) as im: + self.assertEqual(im.mode, "LA") + self.assertEqual(im.getchannel("A").getcolors()[0][0], num_transparent) - out = self.tempfile("temp.tga") - im.save(out) + out = self.tempfile("temp.tga") + im.save(out) - test_im = Image.open(out) - self.assertEqual(test_im.mode, "LA") - self.assertEqual(test_im.getchannel("A").getcolors()[0][0], num_transparent) + with Image.open(out) as test_im: + self.assertEqual(test_im.mode, "LA") + self.assertEqual(test_im.getchannel("A").getcolors()[0][0], num_transparent) - self.assert_image_equal(im, test_im) + self.assert_image_equal(im, test_im) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 146bf1851..2353fb34e 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -1,13 +1,13 @@ import logging import os +import unittest from io import BytesIO import pytest from PIL import Image, TiffImagePlugin -from PIL._util import py3 from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION -from .helper import PillowTestCase, hopper, is_pypy, is_win32, unittest +from .helper import PillowTestCase, hopper, is_pypy, is_win32 logger = logging.getLogger(__name__) @@ -72,22 +72,20 @@ class TestFileTiff(PillowTestCase): # Read RGBa images from macOS [@PIL136] filename = "Tests/images/pil136.tiff" - im = Image.open(filename) + with Image.open(filename) as im: + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (55, 43)) + self.assertEqual(im.tile, [("raw", (0, 0, 55, 43), 8, ("RGBa", 0, 1))]) + im.load() - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (55, 43)) - self.assertEqual(im.tile, [("raw", (0, 0, 55, 43), 8, ("RGBa", 0, 1))]) - im.load() - - self.assert_image_similar_tofile(im, "Tests/images/pil136.png", 1) + self.assert_image_similar_tofile(im, "Tests/images/pil136.png", 1) def test_wrong_bits_per_sample(self): - im = Image.open("Tests/images/tiff_wrong_bits_per_sample.tiff") - - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (52, 53)) - self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))]) - im.load() + with Image.open("Tests/images/tiff_wrong_bits_per_sample.tiff") as im: + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (52, 53)) + self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))]) + im.load() def test_set_legacy_api(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() @@ -149,23 +147,22 @@ class TestFileTiff(PillowTestCase): def test_save_dpi_rounding(self): outfile = self.tempfile("temp.tif") - im = Image.open("Tests/images/hopper.tif") + with Image.open("Tests/images/hopper.tif") as im: + for dpi in (72.2, 72.8): + im.save(outfile, dpi=(dpi, dpi)) - for dpi in (72.2, 72.8): - im.save(outfile, dpi=(dpi, dpi)) - - reloaded = Image.open(outfile) - reloaded.load() - self.assertEqual((round(dpi), round(dpi)), reloaded.info["dpi"]) + with Image.open(outfile) as reloaded: + reloaded.load() + self.assertEqual((round(dpi), round(dpi)), reloaded.info["dpi"]) def test_save_setting_missing_resolution(self): b = BytesIO() Image.open("Tests/images/10ct_32bit_128.tiff").save( b, format="tiff", resolution=123.45 ) - im = Image.open(b) - self.assertEqual(float(im.tag_v2[X_RESOLUTION]), 123.45) - self.assertEqual(float(im.tag_v2[Y_RESOLUTION]), 123.45) + with Image.open(b) as im: + self.assertEqual(float(im.tag_v2[X_RESOLUTION]), 123.45) + self.assertEqual(float(im.tag_v2[Y_RESOLUTION]), 123.45) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -192,63 +189,53 @@ class TestFileTiff(PillowTestCase): self.assertRaises(IOError, im.save, outfile) def test_little_endian(self): - im = Image.open("Tests/images/16bit.cropped.tif") - self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, "I;16") + with Image.open("Tests/images/16bit.cropped.tif") as im: + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, "I;16") - b = im.tobytes() + b = im.tobytes() # Bytes are in image native order (little endian) - if py3: - self.assertEqual(b[0], ord(b"\xe0")) - self.assertEqual(b[1], ord(b"\x01")) - else: - self.assertEqual(b[0], b"\xe0") - self.assertEqual(b[1], b"\x01") + self.assertEqual(b[0], ord(b"\xe0")) + self.assertEqual(b[1], ord(b"\x01")) def test_big_endian(self): - im = Image.open("Tests/images/16bit.MM.cropped.tif") - self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, "I;16B") - - b = im.tobytes() + with Image.open("Tests/images/16bit.MM.cropped.tif") as im: + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, "I;16B") + b = im.tobytes() # Bytes are in image native order (big endian) - if py3: - self.assertEqual(b[0], ord(b"\x01")) - self.assertEqual(b[1], ord(b"\xe0")) - else: - self.assertEqual(b[0], b"\x01") - self.assertEqual(b[1], b"\xe0") + self.assertEqual(b[0], ord(b"\x01")) + self.assertEqual(b[1], ord(b"\xe0")) def test_16bit_s(self): - im = Image.open("Tests/images/16bit.s.tif") - im.load() - self.assertEqual(im.mode, "I") - self.assertEqual(im.getpixel((0, 0)), 32767) - self.assertEqual(im.getpixel((0, 1)), 0) + with Image.open("Tests/images/16bit.s.tif") as im: + im.load() + self.assertEqual(im.mode, "I") + self.assertEqual(im.getpixel((0, 0)), 32767) + self.assertEqual(im.getpixel((0, 1)), 0) def test_12bit_rawmode(self): """ Are we generating the same interpretation of the image as Imagemagick is? """ - im = Image.open("Tests/images/12bit.cropped.tif") + with Image.open("Tests/images/12bit.cropped.tif") as im: + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. - # to make the target -- - # convert 12bit.cropped.tif -depth 16 tmp.tif - # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif - # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, - # so we need to unshift so that the integer values are the same. - - self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") + self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") def test_32bit_float(self): # Issue 614, specific 32-bit float format path = "Tests/images/10ct_32bit_128.tiff" - im = Image.open(path) - im.load() + with Image.open(path) as im: + im.load() - self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343) - self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617)) + self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343) + self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617)) def test_unknown_pixel_mode(self): self.assertRaises( @@ -300,10 +287,10 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) def test_multipage_last_frame(self): - im = Image.open("Tests/images/multipage-lastframe.tif") - im.load() - self.assertEqual(im.size, (20, 20)) - self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) + with Image.open("Tests/images/multipage-lastframe.tif") as im: + im.load() + self.assertEqual(im.size, (20, 20)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) def test___str__(self): filename = "Tests/images/pil136.tiff" @@ -419,10 +406,10 @@ class TestFileTiff(PillowTestCase): def test_4bit(self): test_file = "Tests/images/hopper_gray_4bpp.tif" original = hopper("L") - im = Image.open(test_file) - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.mode, "L") - self.assert_image_similar(im, original, 7.3) + with Image.open(test_file) as im: + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, 7.3) def test_gray_semibyte_per_pixel(self): test_files = ( @@ -447,15 +434,15 @@ class TestFileTiff(PillowTestCase): ) original = hopper("L") for epsilon, group in test_files: - im = Image.open(group[0]) - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.mode, "L") - self.assert_image_similar(im, original, epsilon) - for file in group[1:]: - im2 = Image.open(file) - self.assertEqual(im2.size, (128, 128)) - self.assertEqual(im2.mode, "L") - self.assert_image_equal(im, im2) + with Image.open(group[0]) as im: + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, epsilon) + for file in group[1:]: + with Image.open(file) as im2: + self.assertEqual(im2.size, (128, 128)) + self.assertEqual(im2.mode, "L") + self.assert_image_equal(im, im2) def test_with_underscores(self): kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36} @@ -475,29 +462,26 @@ class TestFileTiff(PillowTestCase): # Test an image of all '0' values pixel_value = 0x1234 infile = "Tests/images/uint16_1_4660.tif" - im = Image.open(infile) - self.assertEqual(im.getpixel((0, 0)), pixel_value) + with Image.open(infile) as im: + self.assertEqual(im.getpixel((0, 0)), pixel_value) - tmpfile = self.tempfile("temp.tif") - im.save(tmpfile) + tmpfile = self.tempfile("temp.tif") + im.save(tmpfile) - reloaded = Image.open(tmpfile) - - self.assert_image_equal(im, reloaded) + with Image.open(tmpfile) as reloaded: + self.assert_image_equal(im, reloaded) def test_strip_raw(self): infile = "Tests/images/tiff_strip_raw.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + with Image.open(infile) as im: + self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") def test_strip_planar_raw(self): # gdal_translate -of GTiff -co INTERLEAVE=BAND \ # tiff_strip_raw.tif tiff_strip_planar_raw.tiff infile = "Tests/images/tiff_strip_planar_raw.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + with Image.open(infile) as im: + self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") def test_strip_planar_raw_with_overviews(self): # gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16 @@ -510,9 +494,8 @@ class TestFileTiff(PillowTestCase): # -co BLOCKYSIZE=32 -co INTERLEAVE=BAND \ # tiff_tiled_raw.tif tiff_tiled_planar_raw.tiff infile = "Tests/images/tiff_tiled_planar_raw.tif" - im = Image.open(infile) - - self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + with Image.open(infile) as im: + self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") def test_palette(self): for mode in ["P", "PA"]: @@ -521,8 +504,8 @@ class TestFileTiff(PillowTestCase): im = hopper(mode) im.save(outfile) - reloaded = Image.open(outfile) - self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) + with Image.open(outfile) as reloaded: + self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) def test_tiff_save_all(self): mp = BytesIO() @@ -540,20 +523,19 @@ class TestFileTiff(PillowTestCase): im.copy().save(mp, format="TIFF", save_all=True, append_images=ims) mp.seek(0, os.SEEK_SET) - reread = Image.open(mp) - self.assertEqual(reread.n_frames, 3) + with Image.open(mp) as reread: + self.assertEqual(reread.n_frames, 3) # Test appending using a generator def imGenerator(ims): - for im in ims: - yield im + yield from ims mp = BytesIO() im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) mp.seek(0, os.SEEK_SET) - reread = Image.open(mp) - self.assertEqual(reread.n_frames, 3) + with Image.open(mp) as reread: + self.assertEqual(reread.n_frames, 3) def test_saving_icc_profile(self): # Tests saving TIFF with icc_profile set. diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 09b510511..bf8d6e933 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -130,14 +130,13 @@ class TestFileTiffMetadata(PillowTestCase): def test_write_metadata(self): """ Test metadata writing through the python code """ - img = Image.open("Tests/images/hopper.tif") - - f = self.tempfile("temp.tiff") - img.save(f, tiffinfo=img.tag) - - with Image.open(f) as loaded: + with Image.open("Tests/images/hopper.tif") as img: + f = self.tempfile("temp.tiff") + img.save(f, tiffinfo=img.tag) original = img.tag_v2.named() + + with Image.open(f) as loaded: reloaded = loaded.tag_v2.named() for k, v in original.items(): @@ -161,13 +160,13 @@ class TestFileTiffMetadata(PillowTestCase): self.assert_deep_equal( original[tag], value, - "%s didn't roundtrip, %s, %s" % (tag, original[tag], value), + "{} didn't roundtrip, {}, {}".format(tag, original[tag], value), ) else: self.assertEqual( original[tag], value, - "%s didn't roundtrip, %s, %s" % (tag, original[tag], value), + "{} didn't roundtrip, {}, {}".format(tag, original[tag], value), ) for tag, value in original.items(): @@ -187,10 +186,10 @@ class TestFileTiffMetadata(PillowTestCase): def test_iccprofile(self): # https://github.com/python-pillow/Pillow/issues/1462 - im = Image.open("Tests/images/hopper.iccprofile.tif") out = self.tempfile("temp.tiff") + with Image.open("Tests/images/hopper.iccprofile.tif") as im: + im.save(out) - im.save(out) with Image.open(out) as reloaded: self.assertNotIsInstance(im.info["icc_profile"], tuple) self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"]) @@ -205,14 +204,14 @@ class TestFileTiffMetadata(PillowTestCase): self.assertTrue(im.info["icc_profile"]) def test_iccprofile_save_png(self): - im = Image.open("Tests/images/hopper.iccprofile.tif") - outfile = self.tempfile("temp.png") - im.save(outfile) + with Image.open("Tests/images/hopper.iccprofile.tif") as im: + outfile = self.tempfile("temp.png") + im.save(outfile) def test_iccprofile_binary_save_png(self): - im = Image.open("Tests/images/hopper.iccprofile_binary.tif") - outfile = self.tempfile("temp.png") - im.save(outfile) + with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im: + outfile = self.tempfile("temp.png") + im.save(outfile) def test_exif_div_zero(self): im = hopper() @@ -241,12 +240,11 @@ class TestFileTiffMetadata(PillowTestCase): self.assertIn(33432, info) def test_PhotoshopInfo(self): - im = Image.open("Tests/images/issue_2278.tif") - - self.assertEqual(len(im.tag_v2[34377]), 1) - self.assertIsInstance(im.tag_v2[34377][0], bytes) - out = self.tempfile("temp.tiff") - im.save(out) + with Image.open("Tests/images/issue_2278.tif") as im: + self.assertEqual(len(im.tag_v2[34377]), 1) + self.assertIsInstance(im.tag_v2[34377][0], bytes) + out = self.tempfile("temp.tiff") + im.save(out) with Image.open(out) as reloaded: self.assertEqual(len(reloaded.tag_v2[34377]), 1) self.assertIsInstance(reloaded.tag_v2[34377][0], bytes) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 35ce3dba7..a2fdbc333 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image, WebPImagePlugin -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper try: from PIL import _webp @@ -39,19 +41,18 @@ class TestFileWebp(PillowTestCase): Does it have the bits we expect? """ - image = Image.open("Tests/images/hopper.webp") + with Image.open("Tests/images/hopper.webp") as image: + self.assertEqual(image.mode, self.rgb_mode) + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - self.assertEqual(image.mode, self.rgb_mode) - self.assertEqual(image.size, (128, 128)) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() - - # generated with: - # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm - self.assert_image_similar_tofile( - image, "Tests/images/hopper_webp_bits.ppm", 1.0 - ) + # generated with: + # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm + self.assert_image_similar_tofile( + image, "Tests/images/hopper_webp_bits.ppm", 1.0 + ) def test_write_rgb(self): """ @@ -62,26 +63,25 @@ class TestFileWebp(PillowTestCase): temp_file = self.tempfile("temp.webp") hopper(self.rgb_mode).save(temp_file) - image = Image.open(temp_file) + with Image.open(temp_file) as image: + self.assertEqual(image.mode, self.rgb_mode) + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - self.assertEqual(image.mode, self.rgb_mode) - self.assertEqual(image.size, (128, 128)) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() + # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm + self.assert_image_similar_tofile( + image, "Tests/images/hopper_webp_write.ppm", 12.0 + ) - # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm - self.assert_image_similar_tofile( - image, "Tests/images/hopper_webp_write.ppm", 12.0 - ) - - # This test asserts that the images are similar. If the average pixel - # difference between the two images is less than the epsilon value, - # then we're going to accept that it's a reasonable lossy version of - # the image. The old lena images for WebP are showing ~16 on - # Ubuntu, the jpegs are showing ~18. - target = hopper(self.rgb_mode) - self.assert_image_similar(image, target, 12.0) + # This test asserts that the images are similar. If the average pixel + # difference between the two images is less than the epsilon value, + # then we're going to accept that it's a reasonable lossy version of + # the image. The old lena images for WebP are showing ~16 on + # Ubuntu, the jpegs are showing ~18. + target = hopper(self.rgb_mode) + self.assert_image_similar(image, target, 12.0) def test_write_unsupported_mode_L(self): """ @@ -91,17 +91,16 @@ class TestFileWebp(PillowTestCase): temp_file = self.tempfile("temp.webp") hopper("L").save(temp_file) - image = Image.open(temp_file) + with Image.open(temp_file) as image: + self.assertEqual(image.mode, self.rgb_mode) + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") - self.assertEqual(image.mode, self.rgb_mode) - self.assertEqual(image.size, (128, 128)) - self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + target = hopper("L").convert(self.rgb_mode) - image.load() - image.getdata() - target = hopper("L").convert(self.rgb_mode) - - self.assert_image_similar(image, target, 10.0) + self.assert_image_similar(image, target, 10.0) def test_write_unsupported_mode_P(self): """ @@ -111,17 +110,16 @@ class TestFileWebp(PillowTestCase): temp_file = self.tempfile("temp.webp") hopper("P").save(temp_file) - image = Image.open(temp_file) + with Image.open(temp_file) as image: + self.assertEqual(image.mode, self.rgb_mode) + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") - self.assertEqual(image.mode, self.rgb_mode) - self.assertEqual(image.size, (128, 128)) - self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + target = hopper("P").convert(self.rgb_mode) - image.load() - image.getdata() - target = hopper("P").convert(self.rgb_mode) - - self.assert_image_similar(image, target, 50.0) + self.assert_image_similar(image, target, 50.0) def test_WebPEncode_with_invalid_args(self): """ @@ -143,10 +141,9 @@ class TestFileWebp(PillowTestCase): def test_no_resource_warning(self): file_path = "Tests/images/hopper.webp" - image = Image.open(file_path) - - temp_file = self.tempfile("temp.webp") - self.assert_warning(None, image.save, temp_file) + with Image.open(file_path) as image: + temp_file = self.tempfile("temp.webp") + self.assert_warning(None, image.save, temp_file) def test_file_pointer_could_be_reused(self): file_path = "Tests/images/hopper.webp" diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 9693f691f..85a1e3d2f 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper try: from PIL import _webp @@ -24,18 +26,17 @@ class TestFileWebpAlpha(PillowTestCase): # Generated with `cwebp transparent.png -o transparent.webp` file_path = "Tests/images/transparent.webp" - image = Image.open(file_path) + with Image.open(file_path) as image: + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, (200, 150)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - self.assertEqual(image.mode, "RGBA") - self.assertEqual(image.size, (200, 150)) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() + image.tobytes() - image.tobytes() - - target = Image.open("Tests/images/transparent.png") - self.assert_image_similar(image, target, 20.0) + with Image.open("Tests/images/transparent.png") as target: + self.assert_image_similar(image, target, 20.0) def test_write_lossless_rgb(self): """ @@ -54,16 +55,16 @@ class TestFileWebpAlpha(PillowTestCase): pil_image.save(temp_file, lossless=True) - image = Image.open(temp_file) - image.load() + with Image.open(temp_file) as image: + image.load() - self.assertEqual(image.mode, "RGBA") - self.assertEqual(image.size, pil_image.size) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, pil_image.size) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - self.assert_image_equal(image, pil_image) + self.assert_image_equal(image, pil_image) def test_write_rgba(self): """ @@ -79,21 +80,21 @@ class TestFileWebpAlpha(PillowTestCase): if _webp.WebPDecoderBuggyAlpha(self): return - image = Image.open(temp_file) - image.load() + with Image.open(temp_file) as image: + image.load() - self.assertEqual(image.mode, "RGBA") - self.assertEqual(image.size, (10, 10)) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, (10, 10)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - # early versions of webp are known to produce higher deviations: - # deal with it - if _webp.WebPDecoderVersion(self) <= 0x201: - self.assert_image_similar(image, pil_image, 3.0) - else: - self.assert_image_similar(image, pil_image, 1.0) + # early versions of webp are known to produce higher deviations: + # deal with it + if _webp.WebPDecoderVersion(self) <= 0x201: + self.assert_image_similar(image, pil_image, 3.0) + else: + self.assert_image_similar(image, pil_image, 1.0) def test_write_unsupported_mode_PA(self): """ @@ -105,15 +106,14 @@ class TestFileWebpAlpha(PillowTestCase): file_path = "Tests/images/transparent.gif" with Image.open(file_path) as im: im.save(temp_file) - image = Image.open(temp_file) + with Image.open(temp_file) as image: + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, (200, 150)) + self.assertEqual(image.format, "WEBP") - self.assertEqual(image.mode, "RGBA") - self.assertEqual(image.size, (200, 150)) - self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + with Image.open(file_path) as im: + target = im.convert("RGBA") - image.load() - image.getdata() - with Image.open(file_path) as im: - target = im.convert("RGBA") - - self.assert_image_similar(image, target, 25.0) + self.assert_image_similar(image, target, 25.0) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index a5d71093d..bf425d079 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -48,18 +48,18 @@ class TestFileWebpAnimation(PillowTestCase): temp_file = self.tempfile("temp.webp") orig.save(temp_file, save_all=True) - im = Image.open(temp_file) - self.assertEqual(im.n_frames, orig.n_frames) + with Image.open(temp_file) as im: + self.assertEqual(im.n_frames, orig.n_frames) - # Compare first and last frames to the original animated GIF - orig.load() - im.load() - self.assert_image_similar(im, orig.convert("RGBA"), 25.0) - orig.seek(orig.n_frames - 1) - im.seek(im.n_frames - 1) - orig.load() - im.load() - self.assert_image_similar(im, orig.convert("RGBA"), 25.0) + # Compare first and last frames to the original animated GIF + orig.load() + im.load() + self.assert_image_similar(im, orig.convert("RGBA"), 25.0) + orig.seek(orig.n_frames - 1) + im.seek(im.n_frames - 1) + orig.load() + im.load() + self.assert_image_similar(im, orig.convert("RGBA"), 25.0) def test_write_animation_RGB(self): """ @@ -68,40 +68,38 @@ class TestFileWebpAnimation(PillowTestCase): """ def check(temp_file): - im = Image.open(temp_file) - self.assertEqual(im.n_frames, 2) + with Image.open(temp_file) as im: + self.assertEqual(im.n_frames, 2) - # Compare first frame to original - im.load() - self.assert_image_equal(im, frame1.convert("RGBA")) + # Compare first frame to original + im.load() + self.assert_image_equal(im, frame1.convert("RGBA")) - # Compare second frame to original - im.seek(1) - im.load() - self.assert_image_equal(im, frame2.convert("RGBA")) + # Compare second frame to original + im.seek(1) + im.load() + self.assert_image_equal(im, frame2.convert("RGBA")) - frame1 = Image.open("Tests/images/anim_frame1.webp") - frame2 = Image.open("Tests/images/anim_frame2.webp") + with Image.open("Tests/images/anim_frame1.webp") as frame1: + with Image.open("Tests/images/anim_frame2.webp") as frame2: + temp_file1 = self.tempfile("temp.webp") + frame1.copy().save( + temp_file1, save_all=True, append_images=[frame2], lossless=True + ) + check(temp_file1) - temp_file1 = self.tempfile("temp.webp") - frame1.copy().save( - temp_file1, save_all=True, append_images=[frame2], lossless=True - ) - check(temp_file1) + # Tests appending using a generator + def imGenerator(ims): + yield from ims - # Tests appending using a generator - def imGenerator(ims): - for im in ims: - yield im - - temp_file2 = self.tempfile("temp_generator.webp") - frame1.copy().save( - temp_file2, - save_all=True, - append_images=imGenerator([frame2]), - lossless=True, - ) - check(temp_file2) + temp_file2 = self.tempfile("temp_generator.webp") + frame1.copy().save( + temp_file2, + save_all=True, + append_images=imGenerator([frame2]), + lossless=True, + ) + check(temp_file2) def test_timestamp_and_duration(self): """ @@ -111,27 +109,27 @@ class TestFileWebpAnimation(PillowTestCase): durations = [0, 10, 20, 30, 40] temp_file = self.tempfile("temp.webp") - frame1 = Image.open("Tests/images/anim_frame1.webp") - frame2 = Image.open("Tests/images/anim_frame2.webp") - frame1.save( - temp_file, - save_all=True, - append_images=[frame2, frame1, frame2, frame1], - duration=durations, - ) + with Image.open("Tests/images/anim_frame1.webp") as frame1: + with Image.open("Tests/images/anim_frame2.webp") as frame2: + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2, frame1], + duration=durations, + ) - im = Image.open(temp_file) - self.assertEqual(im.n_frames, 5) - self.assertTrue(im.is_animated) + with Image.open(temp_file) as im: + self.assertEqual(im.n_frames, 5) + self.assertTrue(im.is_animated) - # Check that timestamps and durations match original values specified - ts = 0 - for frame in range(im.n_frames): - im.seek(frame) - im.load() - self.assertEqual(im.info["duration"], durations[frame]) - self.assertEqual(im.info["timestamp"], ts) - ts += durations[frame] + # Check that timestamps and durations match original values specified + ts = 0 + for frame in range(im.n_frames): + im.seek(frame) + im.load() + self.assertEqual(im.info["duration"], durations[frame]) + self.assertEqual(im.info["timestamp"], ts) + ts += durations[frame] def test_seeking(self): """ @@ -142,24 +140,24 @@ class TestFileWebpAnimation(PillowTestCase): dur = 33 temp_file = self.tempfile("temp.webp") - frame1 = Image.open("Tests/images/anim_frame1.webp") - frame2 = Image.open("Tests/images/anim_frame2.webp") - frame1.save( - temp_file, - save_all=True, - append_images=[frame2, frame1, frame2, frame1], - duration=dur, - ) + with Image.open("Tests/images/anim_frame1.webp") as frame1: + with Image.open("Tests/images/anim_frame2.webp") as frame2: + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2, frame1], + duration=dur, + ) - im = Image.open(temp_file) - self.assertEqual(im.n_frames, 5) - self.assertTrue(im.is_animated) + with Image.open(temp_file) as im: + self.assertEqual(im.n_frames, 5) + self.assertTrue(im.is_animated) - # Traverse frames in reverse, checking timestamps and durations - ts = dur * (im.n_frames - 1) - for frame in reversed(range(im.n_frames)): - im.seek(frame) - im.load() - self.assertEqual(im.info["duration"], dur) - self.assertEqual(im.info["timestamp"], ts) - ts -= dur + # Traverse frames in reverse, checking timestamps and durations + ts = dur * (im.n_frames - 1) + for frame in reversed(range(im.n_frames)): + im.seek(frame) + im.load() + self.assertEqual(im.info["duration"], dur) + self.assertEqual(im.info["timestamp"], ts) + ts -= dur diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index 2eff41529..5d184a766 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -26,13 +26,13 @@ class TestFileWebpLossless(PillowTestCase): hopper(self.rgb_mode).save(temp_file, lossless=True) - image = Image.open(temp_file) - image.load() + with Image.open(temp_file) as image: + image.load() - self.assertEqual(image.mode, self.rgb_mode) - self.assertEqual(image.size, (128, 128)) - self.assertEqual(image.format, "WEBP") - image.load() - image.getdata() + self.assertEqual(image.mode, self.rgb_mode) + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() - self.assert_image_equal(image, hopper(self.rgb_mode)) + self.assert_image_equal(image, hopper(self.rgb_mode)) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 6a25ea261..06c780299 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -42,17 +42,15 @@ class TestFileWebpMetadata(PillowTestCase): def test_write_exif_metadata(self): file_path = "Tests/images/flower.jpg" - image = Image.open(file_path) - expected_exif = image.info["exif"] - test_buffer = BytesIO() + with Image.open(file_path) as image: + expected_exif = image.info["exif"] - image.save(test_buffer, "webp", exif=expected_exif) + image.save(test_buffer, "webp", exif=expected_exif) test_buffer.seek(0) - webp_image = Image.open(test_buffer) - - webp_exif = webp_image.info.get("exif", None) + with Image.open(test_buffer) as webp_image: + webp_exif = webp_image.info.get("exif", None) self.assertTrue(webp_exif) if webp_exif: self.assertEqual(webp_exif, expected_exif, "WebP EXIF didn't match") @@ -74,17 +72,15 @@ class TestFileWebpMetadata(PillowTestCase): def test_write_icc_metadata(self): file_path = "Tests/images/flower2.jpg" - image = Image.open(file_path) - expected_icc_profile = image.info["icc_profile"] - test_buffer = BytesIO() + with Image.open(file_path) as image: + expected_icc_profile = image.info["icc_profile"] - image.save(test_buffer, "webp", icc_profile=expected_icc_profile) + image.save(test_buffer, "webp", icc_profile=expected_icc_profile) test_buffer.seek(0) - webp_image = Image.open(test_buffer) - - webp_icc_profile = webp_image.info.get("icc_profile", None) + with Image.open(test_buffer) as webp_image: + webp_icc_profile = webp_image.info.get("icc_profile", None) self.assertTrue(webp_icc_profile) if webp_icc_profile: @@ -94,37 +90,35 @@ class TestFileWebpMetadata(PillowTestCase): def test_read_no_exif(self): file_path = "Tests/images/flower.jpg" - image = Image.open(file_path) - self.assertIn("exif", image.info) - test_buffer = BytesIO() + with Image.open(file_path) as image: + self.assertIn("exif", image.info) - image.save(test_buffer, "webp") + image.save(test_buffer, "webp") test_buffer.seek(0) - webp_image = Image.open(test_buffer) - - self.assertFalse(webp_image._getexif()) + with Image.open(test_buffer) as webp_image: + self.assertFalse(webp_image._getexif()) def test_write_animated_metadata(self): if not _webp.HAVE_WEBPANIM: self.skipTest("WebP animation support not available") - iccp_data = "".encode("utf-8") - exif_data = "".encode("utf-8") - xmp_data = "".encode("utf-8") + iccp_data = b"" + exif_data = b"" + xmp_data = b"" temp_file = self.tempfile("temp.webp") - frame1 = Image.open("Tests/images/anim_frame1.webp") - frame2 = Image.open("Tests/images/anim_frame2.webp") - frame1.save( - temp_file, - save_all=True, - append_images=[frame2, frame1, frame2], - icc_profile=iccp_data, - exif=exif_data, - xmp=xmp_data, - ) + with Image.open("Tests/images/anim_frame1.webp") as frame1: + with Image.open("Tests/images/anim_frame2.webp") as frame2: + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2], + icc_profile=iccp_data, + exif=exif_data, + xmp=xmp_data, + ) with Image.open(temp_file) as image: self.assertIn("icc_profile", image.info) diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index c07bbf60a..01a802bfb 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -12,9 +12,9 @@ class TestFileWmf(PillowTestCase): # Currently, support for WMF/EMF is Windows-only im.load() # Compare to reference rendering - imref = Image.open("Tests/images/drawing_emf_ref.png") - imref.load() - self.assert_image_similar(im, imref, 0) + with Image.open("Tests/images/drawing_emf_ref.png") as imref: + imref.load() + self.assert_image_similar(im, imref, 0) # Test basic WMF open and rendering with Image.open("Tests/images/drawing.wmf") as im: @@ -22,9 +22,9 @@ class TestFileWmf(PillowTestCase): # Currently, support for WMF/EMF is Windows-only im.load() # Compare to reference rendering - imref = Image.open("Tests/images/drawing_wmf_ref.png") - imref.load() - self.assert_image_similar(im, imref, 2.0) + with Image.open("Tests/images/drawing_wmf_ref.png") as imref: + imref.load() + self.assert_image_similar(im, imref, 2.0) def test_register_handler(self): class TestHandler: diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index 5a1eb54bc..972c5abe0 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -30,11 +30,10 @@ static char basic_bits[] = { class TestFileXbm(PillowTestCase): def test_pil151(self): - im = Image.open(BytesIO(PIL151)) - - im.load() - self.assertEqual(im.mode, "1") - self.assertEqual(im.size, (32, 32)) + with Image.open(BytesIO(PIL151)) as im: + im.load() + self.assertEqual(im.mode, "1") + self.assertEqual(im.size, (32, 32)) def test_open(self): # Arrange diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index a4ea8c491..38ecbc3de 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -7,14 +7,14 @@ TEST_FILE = "Tests/images/hopper.xpm" class TestFileXpm(PillowTestCase): def test_sanity(self): - im = Image.open(TEST_FILE) - im.load() - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "XPM") + with Image.open(TEST_FILE) as im: + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "XPM") - # large error due to quantization->44 colors. - self.assert_image_similar(im.convert("RGB"), hopper("RGB"), 60) + # large error due to quantization->44 colors. + self.assert_image_similar(im.convert("RGB"), hopper("RGB"), 60) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_xvthumb.py b/Tests/test_file_xvthumb.py index f8b6d3531..3b63ab36f 100644 --- a/Tests/test_file_xvthumb.py +++ b/Tests/test_file_xvthumb.py @@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.p7" class TestFileXVThumb(PillowTestCase): def test_open(self): # Act - im = Image.open(TEST_FILE) + with Image.open(TEST_FILE) as im: - # Assert - self.assertEqual(im.format, "XVThumb") + # Assert + self.assertEqual(im.format, "XVThumb") - # Create a Hopper image with a similar XV palette - im_hopper = hopper().quantize(palette=im) - self.assert_image_similar(im, im_hopper, 9) + # Create a Hopper image with a similar XV palette + im_hopper = hopper().quantize(palette=im) + self.assert_image_similar(im, im_hopper, 9) def test_unexpected_eof(self): # Test unexpected EOF reading XV thumbnail file diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 9b5fe2055..393ddb0fb 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -1,8 +1,8 @@ -from __future__ import division +import unittest from PIL import Image, ImageDraw, ImageFont, features -from .helper import PillowLeakTestCase, is_win32, unittest +from .helper import PillowLeakTestCase, is_win32 @unittest.skipIf(is_win32(), "requires Unix or macOS") diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index a2b4ef27e..e37f43207 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,5 +1,4 @@ from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile -from PIL._util import py3 from .helper import PillowTestCase @@ -74,6 +73,5 @@ class TestFontPcf(PillowTestCase): def test_high_characters(self): message = "".join(chr(i + 1) for i in range(140, 232)) self._test_high_characters(message) - # accept bytes instances in Py3. - if py3: - self._test_high_characters(message.encode("latin1")) + # accept bytes instances. + self._test_high_characters(message.encode("latin1")) diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index ce0524e1e..e80e16ffe 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -2,7 +2,6 @@ import colorsys import itertools from PIL import Image -from PIL._util import py3 from .helper import PillowTestCase, hopper @@ -49,27 +48,18 @@ class TestFormatHSV(PillowTestCase): (r, g, b) = im.split() - if py3: - conv_func = self.int_to_float - else: - conv_func = self.str_to_float - - if hasattr(itertools, "izip"): - iter_helper = itertools.izip - else: - iter_helper = itertools.zip_longest + conv_func = self.int_to_float converted = [ self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b))) - for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), b.tobytes()) + for (_r, _g, _b) in itertools.zip_longest( + r.tobytes(), g.tobytes(), b.tobytes() + ) ] - if py3: - new_bytes = b"".join( - bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted - ) - else: - new_bytes = b"".join(chr(h) + chr(s) + chr(v) for (h, s, v) in converted) + new_bytes = b"".join( + bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted + ) hsv = Image.frombytes(mode, r.size, new_bytes) diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py index a98c20579..08e9ad068 100644 --- a/Tests/test_format_lab.py +++ b/Tests/test_format_lab.py @@ -5,21 +5,21 @@ from .helper import PillowTestCase class TestFormatLab(PillowTestCase): def test_white(self): - i = Image.open("Tests/images/lab.tif") + with Image.open("Tests/images/lab.tif") as i: + i.load() - i.load() + self.assertEqual(i.mode, "LAB") - self.assertEqual(i.mode, "LAB") + self.assertEqual(i.getbands(), ("L", "A", "B")) - self.assertEqual(i.getbands(), ("L", "A", "B")) + k = i.getpixel((0, 0)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) - k = i.getpixel((0, 0)) self.assertEqual(k, (255, 128, 128)) - L = i.getdata(0) - a = i.getdata(1) - b = i.getdata(2) - self.assertEqual(list(L), [255] * 100) self.assertEqual(list(a), [128] * 100) self.assertEqual(list(b), [128] * 100) @@ -27,15 +27,13 @@ class TestFormatLab(PillowTestCase): def test_green(self): # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS # == RGB: 0, 152, 117 - i = Image.open("Tests/images/lab-green.tif") - - k = i.getpixel((0, 0)) + with Image.open("Tests/images/lab-green.tif") as i: + k = i.getpixel((0, 0)) self.assertEqual(k, (128, 28, 128)) def test_red(self): # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS # == RGB: 255, 0, 124 - i = Image.open("Tests/images/lab-red.tif") - - k = i.getpixel((0, 0)) + with Image.open("Tests/images/lab-red.tif") as i: + k = i.getpixel((0, 0)) self.assertEqual(k, (128, 228, 128)) diff --git a/Tests/test_image.py b/Tests/test_image.py index b494b17dc..83da76b96 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,11 +1,11 @@ import os import shutil import tempfile +import unittest -from PIL import Image -from PIL._util import py3 +from PIL import Image, UnidentifiedImageError -from .helper import PillowTestCase, hopper, is_win32, unittest +from .helper import PillowTestCase, hopper, is_win32 class TestImage(PillowTestCase): @@ -48,6 +48,9 @@ class TestImage(PillowTestCase): Image.new(mode, (1, 1)) self.assertEqual(str(e.exception), "unrecognized image mode") + def test_exception_inheritance(self): + self.assertTrue(issubclass(UnidentifiedImageError, IOError)) + def test_sanity(self): im = Image.new("L", (100, 100)) @@ -80,20 +83,14 @@ class TestImage(PillowTestCase): im.size = (3, 4) def test_invalid_image(self): - if py3: - import io + import io - im = io.BytesIO(b"") - else: - import StringIO - - im = StringIO.StringIO("") - self.assertRaises(IOError, Image.open, im) + im = io.BytesIO(b"") + self.assertRaises(UnidentifiedImageError, Image.open, im) def test_bad_mode(self): self.assertRaises(ValueError, Image.open, "filename", "bad mode") - @unittest.skipUnless(Image.HAS_PATHLIB, "requires pathlib/pathlib2") def test_pathlib(self): from PIL.Image import Path @@ -101,19 +98,19 @@ class TestImage(PillowTestCase): self.assertEqual(im.mode, "P") self.assertEqual(im.size, (10, 10)) - im = Image.open(Path("Tests/images/hopper.jpg")) - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) + with Image.open(Path("Tests/images/hopper.jpg")) as im: + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) - temp_file = self.tempfile("temp.jpg") - if os.path.exists(temp_file): - os.remove(temp_file) - im.save(Path(temp_file)) + temp_file = self.tempfile("temp.jpg") + if os.path.exists(temp_file): + os.remove(temp_file) + im.save(Path(temp_file)) def test_fp_name(self): temp_file = self.tempfile("temp.jpg") - class FP(object): + class FP: def write(a, b): pass @@ -130,8 +127,8 @@ class TestImage(PillowTestCase): with tempfile.TemporaryFile() as fp: im.save(fp, "JPEG") fp.seek(0) - reloaded = Image.open(fp) - self.assert_image_similar(im, reloaded, 20) + with Image.open(fp) as reloaded: + self.assert_image_similar(im, reloaded, 20) def test_unknown_extension(self): im = hopper() @@ -153,9 +150,9 @@ class TestImage(PillowTestCase): temp_file = self.tempfile("temp.bmp") shutil.copy("Tests/images/rgb32bf-rgba.bmp", temp_file) - im = Image.open(temp_file) - self.assertTrue(im.readonly) - im.save(temp_file) + with Image.open(temp_file) as im: + self.assertTrue(im.readonly) + im.save(temp_file) def test_dump(self): im = Image.new("L", (10, 10)) @@ -368,8 +365,8 @@ class TestImage(PillowTestCase): # Assert self.assertEqual(im.size, (512, 512)) - im2 = Image.open("Tests/images/effect_mandelbrot.png") - self.assert_image_equal(im, im2) + with Image.open("Tests/images/effect_mandelbrot.png") as im2: + self.assert_image_equal(im, im2) def test_effect_mandelbrot_bad_arguments(self): # Arrange @@ -410,8 +407,8 @@ class TestImage(PillowTestCase): # Assert self.assertEqual(im.size, (128, 128)) - im3 = Image.open("Tests/images/effect_spread.png") - self.assert_image_similar(im2, im3, 110) + with Image.open("Tests/images/effect_spread.png") as im3: + self.assert_image_similar(im2, im3, 110) def test_check_size(self): # Checking that the _check_size function throws value errors @@ -478,7 +475,8 @@ class TestImage(PillowTestCase): self.assertEqual(im.mode, mode) self.assertEqual(im.getpixel((0, 0)), 0) self.assertEqual(im.getpixel((255, 255)), 255) - target = Image.open(target_file).convert(mode) + with Image.open(target_file) as target: + target = target.convert(mode) self.assert_image_equal(im, target) def test_radial_gradient_wrong_mode(self): @@ -502,7 +500,8 @@ class TestImage(PillowTestCase): self.assertEqual(im.mode, mode) self.assertEqual(im.getpixel((0, 0)), 255) self.assertEqual(im.getpixel((128, 128)), 0) - target = Image.open(target_file).convert(mode) + with Image.open(target_file) as target: + target = target.convert(mode) self.assert_image_equal(im, target) def test_register_extensions(self): @@ -592,11 +591,11 @@ class TestImage(PillowTestCase): try: im.load() self.assertFail() - except IOError as e: + except OSError as e: self.assertEqual(str(e), "buffer overrun when reading image file") -class MockEncoder(object): +class MockEncoder: pass diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index d7bd828e9..57bac753d 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -2,11 +2,12 @@ import ctypes import os import subprocess import sys +import unittest from distutils import ccompiler, sysconfig from PIL import Image -from .helper import PillowTestCase, hopper, is_win32, on_ci, unittest +from .helper import PillowTestCase, hopper, is_win32, on_ci # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 @@ -125,7 +126,7 @@ class TestImageGetPixel(AccessTest): self.assertEqual( im.getpixel((0, 0)), c, - "put/getpixel roundtrip failed for mode %s, color %s" % (mode, c), + "put/getpixel roundtrip failed for mode {}, color {}".format(mode, c), ) # check putpixel negative index @@ -154,7 +155,7 @@ class TestImageGetPixel(AccessTest): self.assertEqual( im.getpixel((0, 0)), c, - "initial color failed for mode %s, color %s " % (mode, c), + "initial color failed for mode {}, color {} ".format(mode, c), ) # check initial color negative index self.assertEqual( @@ -348,12 +349,8 @@ class TestEmbeddable(unittest.TestCase): int main(int argc, char* argv[]) { char *home = "%s"; -#if PY_MAJOR_VERSION >= 3 wchar_t *whome = Py_DecodeLocale(home, NULL); Py_SetPythonHome(whome); -#else - Py_SetPythonHome(home); -#endif Py_InitializeEx(0); Py_DECREF(PyImport_ImportModule("PIL.Image")); @@ -363,9 +360,7 @@ int main(int argc, char* argv[]) Py_DECREF(PyImport_ImportModule("PIL.Image")); Py_Finalize(); -#if PY_MAJOR_VERSION >= 3 PyMem_RawFree(whome); -#endif return 0; } diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 02e5c80f2..12c3b34cb 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -25,7 +25,7 @@ class TestImageArray(PillowTestCase): self.assertEqual(test("RGBX"), (3, (100, 128, 4), "|u1", 51200)) def test_fromarray(self): - class Wrapper(object): + class Wrapper: """ Class with API matching Image.fromarray """ def __init__(self, img, arr_params): @@ -52,3 +52,8 @@ 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)) + + # Test mode is None with no "typestr" in the array interface + with self.assertRaises(TypeError): + wrapped = Wrapper(test("L"), {"shape": (100, 128)}) + Image.fromarray(wrapped) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 80fa9f513..5bbe3abfa 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -53,16 +53,16 @@ class TestImageConvert(PillowTestCase): self.assertEqual(orig, converted) def test_8bit(self): - im = Image.open("Tests/images/hopper.jpg") - self._test_float_conversion(im.convert("L")) + with Image.open("Tests/images/hopper.jpg") as im: + self._test_float_conversion(im.convert("L")) def test_16bit(self): - im = Image.open("Tests/images/16bit.cropped.tif") - self._test_float_conversion(im) + with Image.open("Tests/images/16bit.cropped.tif") as im: + self._test_float_conversion(im) def test_16bit_workaround(self): - im = Image.open("Tests/images/16bit.cropped.tif") - self._test_float_conversion(im.convert("I")) + with Image.open("Tests/images/16bit.cropped.tif") as im: + self._test_float_conversion(im.convert("I")) def test_rgba_p(self): im = hopper("RGBA") @@ -210,13 +210,13 @@ class TestImageConvert(PillowTestCase): # Assert self.assertEqual(converted_im.mode, mode) self.assertEqual(converted_im.size, im.size) - target = Image.open("Tests/images/hopper-XYZ.png") - if converted_im.mode == "RGB": - self.assert_image_similar(converted_im, target, 3) - self.assertEqual(converted_im.info["transparency"], (105, 54, 4)) - else: - self.assert_image_similar(converted_im, target.getchannel(0), 1) - self.assertEqual(converted_im.info["transparency"], 105) + with Image.open("Tests/images/hopper-XYZ.png") as target: + if converted_im.mode == "RGB": + self.assert_image_similar(converted_im, target, 3) + self.assertEqual(converted_im.info["transparency"], (105, 54, 4)) + else: + self.assert_image_similar(converted_im, target.getchannel(0), 1) + self.assertEqual(converted_im.info["transparency"], 105) matrix_convert("RGB") matrix_convert("L") diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index 6a604f494..480ab2b8e 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -76,13 +76,13 @@ class TestImageCrop(PillowTestCase): test_img = "Tests/images/bmp/g/pal8-0.bmp" extents = (1, 1, 10, 10) # works prepatch - img = Image.open(test_img) - img2 = img.crop(extents) + with Image.open(test_img) as img: + img2 = img.crop(extents) img2.load() # fail prepatch - img = Image.open(test_img) - img = img.crop(extents) + with Image.open(test_img) as img: + img = img.crop(extents) img.load() def test_crop_zero(self): diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index bbd10e6e5..50d2ebbe8 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -99,45 +99,45 @@ class TestImageFilter(PillowTestCase): self.assertRaises(ValueError, lambda: ImageFilter.Kernel((3, 3), (0, 0))) def test_consistency_3x3(self): - source = Image.open("Tests/images/hopper.bmp") - reference = Image.open("Tests/images/hopper_emboss.bmp") - kernel = ImageFilter.Kernel( # noqa: E127 - (3, 3), - # fmt: off - (-1, -1, 0, - -1, 0, 1, - 0, 1, 1), - # fmt: on - 0.3, - ) - source = source.split() * 2 - reference = reference.split() * 2 + with Image.open("Tests/images/hopper.bmp") as source: + with Image.open("Tests/images/hopper_emboss.bmp") as reference: + kernel = ImageFilter.Kernel( # noqa: E127 + (3, 3), + # fmt: off + (-1, -1, 0, + -1, 0, 1, + 0, 1, 1), + # fmt: on + 0.3, + ) + source = source.split() * 2 + reference = reference.split() * 2 - for mode in ["L", "LA", "RGB", "CMYK"]: - self.assert_image_equal( - Image.merge(mode, source[: len(mode)]).filter(kernel), - Image.merge(mode, reference[: len(mode)]), - ) + for mode in ["L", "LA", "RGB", "CMYK"]: + self.assert_image_equal( + Image.merge(mode, source[: len(mode)]).filter(kernel), + Image.merge(mode, reference[: len(mode)]), + ) def test_consistency_5x5(self): - source = Image.open("Tests/images/hopper.bmp") - reference = Image.open("Tests/images/hopper_emboss_more.bmp") - kernel = ImageFilter.Kernel( # noqa: E127 - (5, 5), - # fmt: off - (-1, -1, -1, -1, 0, - -1, -1, -1, 0, 1, - -1, -1, 0, 1, 1, - -1, 0, 1, 1, 1, - 0, 1, 1, 1, 1), - # fmt: on - 0.3, - ) - source = source.split() * 2 - reference = reference.split() * 2 + with Image.open("Tests/images/hopper.bmp") as source: + with Image.open("Tests/images/hopper_emboss_more.bmp") as reference: + kernel = ImageFilter.Kernel( # noqa: E127 + (5, 5), + # fmt: off + (-1, -1, -1, -1, 0, + -1, -1, -1, 0, 1, + -1, -1, 0, 1, 1, + -1, 0, 1, 1, 1, + 0, 1, 1, 1, 1), + # fmt: on + 0.3, + ) + source = source.split() * 2 + reference = reference.split() * 2 - for mode in ["L", "LA", "RGB", "CMYK"]: - self.assert_image_equal( - Image.merge(mode, source[: len(mode)]).filter(kernel), - Image.merge(mode, reference[: len(mode)]), - ) + for mode in ["L", "LA", "RGB", "CMYK"]: + self.assert_image_equal( + Image.merge(mode, source[: len(mode)]).filter(kernel), + Image.merge(mode, reference[: len(mode)]), + ) diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index bf3a250f3..0d961572c 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -6,7 +6,7 @@ from .test_imageqt import PillowQtTestCase class TestFromQImage(PillowQtTestCase, PillowTestCase): def setUp(self): - super(TestFromQImage, self).setUp() + super().setUp() self.files_to_test = [ hopper(), Image.open("Tests/images/transparent.png"), diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py index 1944b041c..228eb82c8 100644 --- a/Tests/test_image_getextrema.py +++ b/Tests/test_image_getextrema.py @@ -19,7 +19,7 @@ class TestImageGetExtrema(PillowTestCase): self.assertEqual(extrema("I;16"), (0, 255)) def test_true_16(self): - im = Image.open("Tests/images/16_bit_noise.tif") - self.assertEqual(im.mode, "I;16") - extrema = im.getextrema() + with Image.open("Tests/images/16_bit_noise.tif") as im: + self.assertEqual(im.mode, "I;16") + extrema = im.getextrema() self.assertEqual(extrema, (106, 285)) diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index 3f0c46c46..f6908af4b 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -1,5 +1,3 @@ -from PIL._util import py3 - from .helper import PillowTestCase, hopper @@ -8,7 +6,5 @@ class TestImageGetIm(PillowTestCase): im = hopper() type_repr = repr(type(im.getim())) - if py3: - self.assertIn("PyCapsule", type_repr) - + self.assertIn("PyCapsule", type_repr) self.assertIsInstance(im.im.id, int) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 2be5b74fc..ee5a09899 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -42,21 +42,24 @@ class TestImageQuantize(PillowTestCase): self.assertEqual(image.quantize().convert().mode, "RGBA") def test_quantize(self): - image = Image.open("Tests/images/caption_6_33_22.png").convert("RGB") + with Image.open("Tests/images/caption_6_33_22.png") as image: + image = image.convert("RGB") converted = image.quantize() self.assert_image(converted, "P", converted.size) self.assert_image_similar(converted.convert("RGB"), image, 1) def test_quantize_no_dither(self): image = hopper() - palette = Image.open("Tests/images/caption_6_33_22.png").convert("P") + with Image.open("Tests/images/caption_6_33_22.png") as palette: + palette = palette.convert("P") converted = image.quantize(dither=0, palette=palette) self.assert_image(converted, "P", converted.size) def test_quantize_dither_diff(self): image = hopper() - palette = Image.open("Tests/images/caption_6_33_22.png").convert("P") + with Image.open("Tests/images/caption_6_33_22.png") as palette: + palette = palette.convert("P") dither = image.quantize(dither=1, palette=palette) nodither = image.quantize(dither=0, palette=palette) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 854a64d01..3bb941438 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -1,10 +1,9 @@ -from __future__ import division, print_function - +import unittest from contextlib import contextmanager from PIL import Image, ImageDraw -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper class TestImagingResampleVulnerability(PillowTestCase): @@ -451,25 +450,25 @@ class CoreResampleBoxTest(PillowTestCase): return tiled def test_tiles(self): - im = Image.open("Tests/images/flower.jpg") - self.assertEqual(im.size, (480, 360)) - dst_size = (251, 188) - reference = im.resize(dst_size, Image.BICUBIC) + with Image.open("Tests/images/flower.jpg") as im: + self.assertEqual(im.size, (480, 360)) + dst_size = (251, 188) + reference = im.resize(dst_size, Image.BICUBIC) - for tiles in [(1, 1), (3, 3), (9, 7), (100, 100)]: - tiled = self.resize_tiled(im, dst_size, *tiles) - self.assert_image_similar(reference, tiled, 0.01) + for tiles in [(1, 1), (3, 3), (9, 7), (100, 100)]: + tiled = self.resize_tiled(im, dst_size, *tiles) + self.assert_image_similar(reference, tiled, 0.01) def test_subsample(self): # This test shows advantages of the subpixel resizing # after supersampling (e.g. during JPEG decoding). - im = Image.open("Tests/images/flower.jpg") - self.assertEqual(im.size, (480, 360)) - dst_size = (48, 36) - # Reference is cropped image resized to destination - reference = im.crop((0, 0, 473, 353)).resize(dst_size, Image.BICUBIC) - # Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) - supersampled = im.resize((60, 45), Image.BOX) + with Image.open("Tests/images/flower.jpg") as im: + self.assertEqual(im.size, (480, 360)) + dst_size = (48, 36) + # Reference is cropped image resized to destination + reference = im.crop((0, 0, 473, 353)).resize(dst_size, Image.BICUBIC) + # Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) + supersampled = im.resize((60, 45), Image.BOX) with_box = supersampled.resize(dst_size, Image.BICUBIC, (0, 0, 59.125, 44.125)) without_box = supersampled.resize(dst_size, Image.BICUBIC) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 9c62e7362..b758531d4 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -24,8 +24,8 @@ class TestImageRotate(PillowTestCase): def test_angle(self): for angle in (0, 90, 180, 270): - im = Image.open("Tests/images/test-card.png") - self.rotate(im, im.mode, angle) + with Image.open("Tests/images/test-card.png") as im: + self.rotate(im, im.mode, angle) def test_zero(self): for angle in (0, 45, 90, 180, 270): @@ -38,43 +38,43 @@ class TestImageRotate(PillowTestCase): # >>> im = im.rotate(45, resample=Image.BICUBIC, expand=True) # >>> im.save('Tests/images/hopper_45.png') - target = Image.open("Tests/images/hopper_45.png") - for (resample, epsilon) in ( - (Image.NEAREST, 10), - (Image.BILINEAR, 5), - (Image.BICUBIC, 0), - ): - im = hopper() - im = im.rotate(45, resample=resample, expand=True) - self.assert_image_similar(im, target, epsilon) + with Image.open("Tests/images/hopper_45.png") as target: + for (resample, epsilon) in ( + (Image.NEAREST, 10), + (Image.BILINEAR, 5), + (Image.BICUBIC, 0), + ): + im = hopper() + im = im.rotate(45, resample=resample, expand=True) + self.assert_image_similar(im, target, epsilon) def test_center_0(self): im = hopper() - target = Image.open("Tests/images/hopper_45.png") - target_origin = target.size[1] / 2 - target = target.crop((0, target_origin, 128, target_origin + 128)) - im = im.rotate(45, center=(0, 0), resample=Image.BICUBIC) + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = target.size[1] / 2 + target = target.crop((0, target_origin, 128, target_origin + 128)) + self.assert_image_similar(im, target, 15) def test_center_14(self): im = hopper() - target = Image.open("Tests/images/hopper_45.png") - target_origin = target.size[1] / 2 - 14 - target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) - im = im.rotate(45, center=(14, 14), resample=Image.BICUBIC) - self.assert_image_similar(im, target, 10) + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = target.size[1] / 2 - 14 + target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) + + self.assert_image_similar(im, target, 10) def test_translate(self): im = hopper() - target = Image.open("Tests/images/hopper_45.png") - target_origin = (target.size[1] / 2 - 64) - 5 - target = target.crop( - (target_origin, target_origin, target_origin + 128, target_origin + 128) - ) + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = (target.size[1] / 2 - 64) - 5 + target = target.crop( + (target_origin, target_origin, target_origin + 128, target_origin + 128) + ) im = im.rotate(45, translate=(5, 5), resample=Image.BICUBIC) @@ -102,15 +102,15 @@ class TestImageRotate(PillowTestCase): def test_rotate_no_fill(self): im = Image.new("RGB", (100, 100), "green") - target = Image.open("Tests/images/rotate_45_no_fill.png") im = im.rotate(45) - self.assert_image_equal(im, target) + with Image.open("Tests/images/rotate_45_no_fill.png") as target: + self.assert_image_equal(im, target) def test_rotate_with_fill(self): im = Image.new("RGB", (100, 100), "green") - target = Image.open("Tests/images/rotate_45_with_fill.png") im = im.rotate(45, fillcolor="white") - self.assert_image_equal(im, target) + with Image.open("Tests/images/rotate_45_with_fill.png") as target: + self.assert_image_equal(im, target) def test_alpha_rotate_no_fill(self): # Alpha images are handled differently internally diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index a19878aae..63918d073 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -53,8 +53,8 @@ class TestImageSplit(PillowTestCase): def split_open(mode): hopper(mode).save(test_file) - im = Image.open(test_file) - return len(im.split()) + with Image.open(test_file) as im: + return len(im.split()) self.assertEqual(split_open("1"), 1) self.assertEqual(split_open("L"), 1) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 35f64bcc9..781ba7923 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -23,11 +23,11 @@ class TestImageTransform(PillowTestCase): def test_info(self): comment = b"File written by Adobe Photoshop\xa8 4.0" - im = Image.open("Tests/images/hopper.gif") - self.assertEqual(im.info["comment"], comment) + with Image.open("Tests/images/hopper.gif") as im: + self.assertEqual(im.info["comment"], comment) - transform = ImageTransform.ExtentTransform((0, 0, 0, 0)) - new_im = im.transform((100, 100), transform) + transform = ImageTransform.ExtentTransform((0, 0, 0, 0)) + new_im = im.transform((100, 100), transform) self.assertEqual(new_im.info["comment"], comment) def test_extent(self): diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 6f42a28df..ad1fee3e5 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -45,11 +45,11 @@ class TestImageChops(PillowTestCase): def test_add(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") - im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add(im1, im2) + # Act + new = ImageChops.add(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -57,11 +57,11 @@ class TestImageChops(PillowTestCase): def test_add_scale_offset(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") - im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add(im1, im2, scale=2.5, offset=100) + # Act + new = ImageChops.add(im1, im2, scale=2.5, offset=100) # Assert self.assertEqual(new.getbbox(), (0, 0, 100, 100)) @@ -79,11 +79,11 @@ class TestImageChops(PillowTestCase): def test_add_modulo(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") - im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add_modulo(im1, im2) + # Act + new = ImageChops.add_modulo(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -101,11 +101,11 @@ class TestImageChops(PillowTestCase): def test_blend(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") - im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.blend(im1, im2, 0.5) + # Act + new = ImageChops.blend(im1, im2, 0.5) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -125,33 +125,33 @@ class TestImageChops(PillowTestCase): def test_darker_image(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") - im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.darker(im1, im2) + # Act + new = ImageChops.darker(im1, im2) - # Assert - self.assert_image_equal(new, im2) + # Assert + self.assert_image_equal(new, im2) def test_darker_pixel(self): # Arrange im1 = hopper() - im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - # Act - new = ImageChops.darker(im1, im2) + # Act + new = ImageChops.darker(im1, im2) # Assert self.assertEqual(new.getpixel((50, 50)), (240, 166, 0)) def test_difference(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_arc_end_le_start.png") - im2 = Image.open("Tests/images/imagedraw_arc_no_loops.png") + with Image.open("Tests/images/imagedraw_arc_end_le_start.png") as im1: + with Image.open("Tests/images/imagedraw_arc_no_loops.png") as im2: - # Act - new = ImageChops.difference(im1, im2) + # Act + new = ImageChops.difference(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -159,10 +159,10 @@ class TestImageChops(PillowTestCase): def test_difference_pixel(self): # Arrange im1 = hopper() - im2 = Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") + with Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") as im2: - # Act - new = ImageChops.difference(im1, im2) + # Act + new = ImageChops.difference(im1, im2) # Assert self.assertEqual(new.getpixel((50, 50)), (240, 166, 128)) @@ -179,10 +179,10 @@ class TestImageChops(PillowTestCase): def test_invert(self): # Arrange - im = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: - # Act - new = ImageChops.invert(im) + # Act + new = ImageChops.invert(im) # Assert self.assertEqual(new.getbbox(), (0, 0, 100, 100)) @@ -191,11 +191,11 @@ class TestImageChops(PillowTestCase): def test_lighter_image(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") - im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.lighter(im1, im2) + # Act + new = ImageChops.lighter(im1, im2) # Assert self.assert_image_equal(new, im1) @@ -203,10 +203,10 @@ class TestImageChops(PillowTestCase): def test_lighter_pixel(self): # Arrange im1 = hopper() - im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - # Act - new = ImageChops.lighter(im1, im2) + # Act + new = ImageChops.lighter(im1, im2) # Assert self.assertEqual(new.getpixel((50, 50)), (255, 255, 127)) @@ -226,11 +226,11 @@ class TestImageChops(PillowTestCase): def test_multiply_green(self): # Arrange - im = Image.open("Tests/images/imagedraw_floodfill_RGB.png") - green = Image.new("RGB", im.size, "green") + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: + green = Image.new("RGB", im.size, "green") - # Act - new = ImageChops.multiply(im, green) + # Act + new = ImageChops.multiply(im, green) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -252,12 +252,12 @@ class TestImageChops(PillowTestCase): def test_offset(self): # Arrange - im = Image.open("Tests/images/imagedraw_ellipse_RGB.png") xoffset = 45 yoffset = 20 + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im: - # Act - new = ImageChops.offset(im, xoffset, yoffset) + # Act + new = ImageChops.offset(im, xoffset, yoffset) # Assert self.assertEqual(new.getbbox(), (0, 45, 100, 96)) @@ -271,11 +271,11 @@ class TestImageChops(PillowTestCase): def test_screen(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") - im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png") + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.screen(im1, im2) + # Act + new = ImageChops.screen(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) @@ -283,11 +283,11 @@ class TestImageChops(PillowTestCase): def test_subtract(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") - im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract(im1, im2) + # Act + new = ImageChops.subtract(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 50, 76, 76)) @@ -296,11 +296,11 @@ class TestImageChops(PillowTestCase): def test_subtract_scale_offset(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") - im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) + # Act + new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) # Assert self.assertEqual(new.getbbox(), (0, 0, 100, 100)) @@ -309,21 +309,21 @@ class TestImageChops(PillowTestCase): def test_subtract_clip(self): # Arrange im1 = hopper() - im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract(im1, im2) + # Act + new = ImageChops.subtract(im1, im2) # Assert self.assertEqual(new.getpixel((50, 50)), (0, 0, 127)) def test_subtract_modulo(self): # Arrange - im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") - im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract_modulo(im1, im2) + # Act + new = ImageChops.subtract_modulo(im1, im2) # Assert self.assertEqual(new.getbbox(), (25, 50, 76, 76)) @@ -333,10 +333,10 @@ class TestImageChops(PillowTestCase): def test_subtract_modulo_no_clip(self): # Arrange im1 = hopper() - im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract_modulo(im1, im2) + # Act + new = ImageChops.subtract_modulo(im1, im2) # Assert self.assertEqual(new.getpixel((50, 50)), (241, 167, 127)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 97f81fb3f..98b1a80ab 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -229,18 +229,16 @@ class TestImageCms(PillowTestCase): # i.save('temp.lab.tif') # visually verified vs PS. - target = Image.open("Tests/images/hopper.Lab.tif") - - self.assert_image_similar(i, target, 3.5) + with Image.open("Tests/images/hopper.Lab.tif") as target: + self.assert_image_similar(i, target, 3.5) def test_lab_srgb(self): psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - img = Image.open("Tests/images/hopper.Lab.tif") - - img_srgb = ImageCms.applyTransform(img, t) + with Image.open("Tests/images/hopper.Lab.tif") as img: + img_srgb = ImageCms.applyTransform(img, t) # img_srgb.save('temp.srgb.tif') # visually verified vs ps. diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 75e658d65..d3791e0b1 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,8 +1,9 @@ import os.path +import unittest from PIL import Image, ImageColor, ImageDraw, ImageFont, features -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper BLACK = (0, 0, 0) WHITE = (255, 255, 255) @@ -156,12 +157,12 @@ class TestImageDraw(PillowTestCase): def test_bitmap(self): # Arrange - small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) + with Image.open("Tests/images/pil123rgba.png").resize((50, 50)) as small: - # Act - draw.bitmap((10, 10), small) + # Act + draw.bitmap((10, 10), small) # Assert self.assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) @@ -522,8 +523,8 @@ class TestImageDraw(PillowTestCase): # Assert expected = "Tests/images/imagedraw_floodfill_" + mode + ".png" - im_floodfill = Image.open(expected) - self.assert_image_equal(im, im_floodfill) + with Image.open(expected) as im_floodfill: + self.assert_image_equal(im, im_floodfill) # Test that using the same colour does not change the image ImageDraw.floodfill(im, centre_point, red) @@ -602,152 +603,166 @@ class TestImageDraw(PillowTestCase): return img, ImageDraw.Draw(img) def test_square(self): - expected = Image.open(os.path.join(IMAGES_PATH, "square.png")) - expected.load() - img, draw = self.create_base_image_draw((10, 10)) - draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK) - self.assert_image_equal(img, expected, "square as normal polygon failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK) - self.assert_image_equal(img, expected, "square as inverted polygon failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.rectangle((2, 2, 7, 7), BLACK) - self.assert_image_equal(img, expected, "square as normal rectangle failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.rectangle((7, 7, 2, 2), BLACK) - self.assert_image_equal(img, expected, "square as inverted rectangle failed") + with Image.open(os.path.join(IMAGES_PATH, "square.png")) as expected: + expected.load() + img, draw = self.create_base_image_draw((10, 10)) + draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK) + self.assert_image_equal(img, expected, "square as normal polygon failed") + img, draw = self.create_base_image_draw((10, 10)) + draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK) + self.assert_image_equal(img, expected, "square as inverted polygon failed") + img, draw = self.create_base_image_draw((10, 10)) + draw.rectangle((2, 2, 7, 7), BLACK) + self.assert_image_equal(img, expected, "square as normal rectangle failed") + img, draw = self.create_base_image_draw((10, 10)) + draw.rectangle((7, 7, 2, 2), BLACK) + self.assert_image_equal( + img, expected, "square as inverted rectangle failed" + ) def test_triangle_right(self): - expected = Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK) - self.assert_image_equal(img, expected, "triangle right failed") + with Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK) + self.assert_image_equal(img, expected, "triangle right failed") def test_line_horizontal(self): - expected = Image.open( + with Image.open( os.path.join(IMAGES_PATH, "line_horizontal_w2px_normal.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 5), BLACK, 2) - self.assert_image_equal( - img, expected, "line straight horizontal normal 2px wide failed" - ) - expected = Image.open( + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 5), BLACK, 2) + self.assert_image_equal( + img, expected, "line straight horizontal normal 2px wide failed" + ) + with Image.open( os.path.join(IMAGES_PATH, "line_horizontal_w2px_inverted.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 5), BLACK, 2) - self.assert_image_equal( - img, expected, "line straight horizontal inverted 2px wide failed" - ) - expected = Image.open(os.path.join(IMAGES_PATH, "line_horizontal_w3px.png")) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 5), BLACK, 3) - self.assert_image_equal( - img, expected, "line straight horizontal normal 3px wide failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 5), BLACK, 3) - self.assert_image_equal( - img, expected, "line straight horizontal inverted 3px wide failed" - ) - expected = Image.open(os.path.join(IMAGES_PATH, "line_horizontal_w101px.png")) - expected.load() - img, draw = self.create_base_image_draw((200, 110)) - draw.line((5, 55, 195, 55), BLACK, 101) - self.assert_image_equal( - img, expected, "line straight horizontal 101px wide failed" - ) + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 5), BLACK, 2) + self.assert_image_equal( + img, expected, "line straight horizontal inverted 2px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w3px.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 5), BLACK, 3) + self.assert_image_equal( + img, expected, "line straight horizontal normal 3px wide failed" + ) + img, draw = self.create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 5), BLACK, 3) + self.assert_image_equal( + img, expected, "line straight horizontal inverted 3px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w101px.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((200, 110)) + draw.line((5, 55, 195, 55), BLACK, 101) + self.assert_image_equal( + img, expected, "line straight horizontal 101px wide failed" + ) def test_line_h_s1_w2(self): self.skipTest("failing") - expected = Image.open( + with Image.open( os.path.join(IMAGES_PATH, "line_horizontal_slope1px_w2px.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 6), BLACK, 2) - self.assert_image_equal( - img, expected, "line horizontal 1px slope 2px wide failed" - ) + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 6), BLACK, 2) + self.assert_image_equal( + img, expected, "line horizontal 1px slope 2px wide failed" + ) def test_line_vertical(self): - expected = Image.open( + with Image.open( os.path.join(IMAGES_PATH, "line_vertical_w2px_normal.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 5, 14), BLACK, 2) - self.assert_image_equal( - img, expected, "line straight vertical normal 2px wide failed" - ) - expected = Image.open( + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 5, 14), BLACK, 2) + self.assert_image_equal( + img, expected, "line straight vertical normal 2px wide failed" + ) + with Image.open( os.path.join(IMAGES_PATH, "line_vertical_w2px_inverted.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 5, 5), BLACK, 2) - self.assert_image_equal( - img, expected, "line straight vertical inverted 2px wide failed" - ) - expected = Image.open(os.path.join(IMAGES_PATH, "line_vertical_w3px.png")) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 5, 14), BLACK, 3) - self.assert_image_equal( - img, expected, "line straight vertical normal 3px wide failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 5, 5), BLACK, 3) - self.assert_image_equal( - img, expected, "line straight vertical inverted 3px wide failed" - ) - expected = Image.open(os.path.join(IMAGES_PATH, "line_vertical_w101px.png")) - expected.load() - img, draw = self.create_base_image_draw((110, 200)) - draw.line((55, 5, 55, 195), BLACK, 101) - self.assert_image_equal( - img, expected, "line straight vertical 101px wide failed" - ) - expected = Image.open( + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 14, 5, 5), BLACK, 2) + self.assert_image_equal( + img, expected, "line straight vertical inverted 2px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w3px.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 5, 14), BLACK, 3) + self.assert_image_equal( + img, expected, "line straight vertical normal 3px wide failed" + ) + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 14, 5, 5), BLACK, 3) + self.assert_image_equal( + img, expected, "line straight vertical inverted 3px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w101px.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((110, 200)) + draw.line((55, 5, 55, 195), BLACK, 101) + self.assert_image_equal( + img, expected, "line straight vertical 101px wide failed" + ) + with Image.open( os.path.join(IMAGES_PATH, "line_vertical_slope1px_w2px.png") - ) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 6, 14), BLACK, 2) - self.assert_image_equal( - img, expected, "line vertical 1px slope 2px wide failed" - ) + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 6, 14), BLACK, 2) + self.assert_image_equal( + img, expected, "line vertical 1px slope 2px wide failed" + ) def test_line_oblique_45(self): - expected = Image.open(os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png")) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 14), BLACK, 3) - self.assert_image_equal( - img, expected, "line oblique 45 normal 3px wide A failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 14, 5, 5), BLACK, 3) - self.assert_image_equal( - img, expected, "line oblique 45 inverted 3px wide A failed" - ) - expected = Image.open(os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png")) - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 14), BLACK, 3) - self.assert_image_equal( - img, expected, "line oblique 45 normal 3px wide B failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 14, 5), BLACK, 3) - self.assert_image_equal( - img, expected, "line oblique 45 inverted 3px wide B failed" - ) + with Image.open( + os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 14), BLACK, 3) + self.assert_image_equal( + img, expected, "line oblique 45 normal 3px wide A failed" + ) + img, draw = self.create_base_image_draw((20, 20)) + draw.line((14, 14, 5, 5), BLACK, 3) + self.assert_image_equal( + img, expected, "line oblique 45 inverted 3px wide A failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png") + ) as expected: + expected.load() + img, draw = self.create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 14), BLACK, 3) + self.assert_image_equal( + img, expected, "line oblique 45 normal 3px wide B failed" + ) + img, draw = self.create_base_image_draw((20, 20)) + draw.line((5, 14, 14, 5), BLACK, 3) + self.assert_image_equal( + img, expected, "line oblique 45 inverted 3px wide B failed" + ) def test_wide_line_dot(self): # Test drawing a wide "line" from one point to another just draws diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 9ce472dd0..577727664 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -1,8 +1,9 @@ import os.path +import unittest from PIL import Image, ImageDraw2, features -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper BLACK = (0, 0, 0) WHITE = (255, 255, 255) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index b2235853a..d0d994eee 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -35,7 +35,7 @@ class TestImageEnhance(PillowTestCase): self.assert_image_equal( im.getchannel("A"), original.getchannel("A"), - "Diff on %s: %s" % (op, amount), + "Diff on {}: {}".format(op, amount), ) def test_alpha(self): diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index dad408e93..50c7d337b 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -1,8 +1,9 @@ +import unittest from io import BytesIO from PIL import EpsImagePlugin, Image, ImageFile -from .helper import PillowTestCase, fromstring, hopper, tostring, unittest +from .helper import PillowTestCase, fromstring, hopper, tostring try: from PIL import _webp @@ -127,33 +128,31 @@ class TestImageFile(PillowTestCase): if "zip_encoder" not in codecs: self.skipTest("PNG (zlib) encoder not available") - im = Image.open("Tests/images/truncated_image.png") - - ImageFile.LOAD_TRUNCATED_IMAGES = True - try: - im.load() - finally: - ImageFile.LOAD_TRUNCATED_IMAGES = False + with Image.open("Tests/images/truncated_image.png") as im: + ImageFile.LOAD_TRUNCATED_IMAGES = True + try: + im.load() + finally: + ImageFile.LOAD_TRUNCATED_IMAGES = False def test_broken_datastream_with_errors(self): if "zip_encoder" not in codecs: self.skipTest("PNG (zlib) encoder not available") - im = Image.open("Tests/images/broken_data_stream.png") - with self.assertRaises(IOError): - im.load() + with Image.open("Tests/images/broken_data_stream.png") as im: + with self.assertRaises(IOError): + im.load() def test_broken_datastream_without_errors(self): if "zip_encoder" not in codecs: self.skipTest("PNG (zlib) encoder not available") - im = Image.open("Tests/images/broken_data_stream.png") - - ImageFile.LOAD_TRUNCATED_IMAGES = True - try: - im.load() - finally: - ImageFile.LOAD_TRUNCATED_IMAGES = False + with Image.open("Tests/images/broken_data_stream.png") as im: + ImageFile.LOAD_TRUNCATED_IMAGES = True + try: + im.load() + finally: + ImageFile.LOAD_TRUNCATED_IMAGES = False class MockPyDecoder(ImageFile.PyDecoder): @@ -245,19 +244,19 @@ class TestPyDecoder(PillowTestCase): self.assertIsNone(im.get_format_mimetype()) def test_exif_jpeg(self): - im = Image.open("Tests/images/exif-72dpi-int.jpg") # Little endian - exif = im.getexif() - self.assertNotIn(258, exif) - self.assertIn(40960, exif) - self.assertEqual(exif[40963], 450) - self.assertEqual(exif[11], "gThumb 3.0.1") + with Image.open("Tests/images/exif-72dpi-int.jpg") as im: # Little endian + exif = im.getexif() + self.assertNotIn(258, exif) + self.assertIn(40960, exif) + self.assertEqual(exif[40963], 450) + self.assertEqual(exif[11], "gThumb 3.0.1") - out = self.tempfile("temp.jpg") - exif[258] = 8 - del exif[40960] - exif[40963] = 455 - exif[11] = "Pillow test" - im.save(out, exif=exif) + out = self.tempfile("temp.jpg") + exif[258] = 8 + del exif[40960] + exif[40963] = 455 + exif[11] = "Pillow test" + im.save(out, exif=exif) with Image.open(out) as reloaded: reloaded_exif = reloaded.getexif() self.assertEqual(reloaded_exif[258], 8) @@ -265,19 +264,19 @@ class TestPyDecoder(PillowTestCase): self.assertEqual(reloaded_exif[40963], 455) self.assertEqual(exif[11], "Pillow test") - im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian - exif = im.getexif() - self.assertNotIn(258, exif) - self.assertIn(40962, exif) - self.assertEqual(exif[40963], 200) - self.assertEqual(exif[305], "Adobe Photoshop CC 2017 (Macintosh)") + with Image.open("Tests/images/no-dpi-in-exif.jpg") as im: # Big endian + exif = im.getexif() + self.assertNotIn(258, exif) + self.assertIn(40962, exif) + self.assertEqual(exif[40963], 200) + self.assertEqual(exif[305], "Adobe Photoshop CC 2017 (Macintosh)") - out = self.tempfile("temp.jpg") - exif[258] = 8 - del exif[34665] - exif[40963] = 455 - exif[305] = "Pillow test" - im.save(out, exif=exif) + out = self.tempfile("temp.jpg") + exif[258] = 8 + del exif[34665] + exif[40963] = 455 + exif[305] = "Pillow test" + im.save(out, exif=exif) with Image.open(out) as reloaded: reloaded_exif = reloaded.getexif() self.assertEqual(reloaded_exif[258], 8) @@ -290,38 +289,38 @@ class TestPyDecoder(PillowTestCase): "WebP support not installed with animation", ) def test_exif_webp(self): - im = Image.open("Tests/images/hopper.webp") - exif = im.getexif() - self.assertEqual(exif, {}) + with Image.open("Tests/images/hopper.webp") as im: + exif = im.getexif() + self.assertEqual(exif, {}) - out = self.tempfile("temp.webp") - exif[258] = 8 - exif[40963] = 455 - exif[305] = "Pillow test" + out = self.tempfile("temp.webp") + exif[258] = 8 + exif[40963] = 455 + exif[305] = "Pillow test" - def check_exif(): - with Image.open(out) as reloaded: - reloaded_exif = reloaded.getexif() - self.assertEqual(reloaded_exif[258], 8) - self.assertEqual(reloaded_exif[40963], 455) - self.assertEqual(exif[305], "Pillow test") + def check_exif(): + with Image.open(out) as reloaded: + reloaded_exif = reloaded.getexif() + self.assertEqual(reloaded_exif[258], 8) + self.assertEqual(reloaded_exif[40963], 455) + self.assertEqual(exif[305], "Pillow test") - im.save(out, exif=exif) - check_exif() - im.save(out, exif=exif, save_all=True) - check_exif() + im.save(out, exif=exif) + check_exif() + im.save(out, exif=exif, save_all=True) + check_exif() def test_exif_png(self): - im = Image.open("Tests/images/exif.png") - exif = im.getexif() - self.assertEqual(exif, {274: 1}) + with Image.open("Tests/images/exif.png") as im: + exif = im.getexif() + self.assertEqual(exif, {274: 1}) - out = self.tempfile("temp.png") - exif[258] = 8 - del exif[274] - exif[40963] = 455 - exif[305] = "Pillow test" - im.save(out, exif=exif) + out = self.tempfile("temp.png") + exif[258] = 8 + del exif[274] + exif[40963] = 455 + exif[305] = "Pillow test" + im.save(out, exif=exif) with Image.open(out) as reloaded: reloaded_exif = reloaded.getexif() diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 6944404c1..1777e2978 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- import copy import distutils.version import os import re import shutil import sys +import unittest from io import BytesIO from PIL import Image, ImageDraw, ImageFont, features -from .helper import PillowTestCase, is_pypy, is_win32, unittest +from .helper import PillowTestCase, is_pypy, is_win32 FONT_PATH = "Tests/fonts/FreeMono.ttf" FONT_SIZE = 20 @@ -20,7 +20,7 @@ HAS_FREETYPE = features.check("freetype2") HAS_RAQM = features.check("raqm") -class SimplePatcher(object): +class SimplePatcher: def __init__(self, parent_obj, attr_name, value): self._parent_obj = parent_obj self._attr_name = attr_name @@ -179,10 +179,10 @@ class TestImageFont(PillowTestCase): draw.rectangle((10, 10, 10 + size[0], 10 + size[1])) target = "Tests/images/rectangle_surrounding_text.png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics["textsize"]) + # Epsilon ~.5 fails with FreeType 2.7 + self.assert_image_similar(im, target_img, self.metrics["textsize"]) def test_render_multiline(self): im = Image.new(mode="RGB", size=(300, 100)) @@ -196,12 +196,12 @@ class TestImageFont(PillowTestCase): y += line_spacing target = "Tests/images/multiline_text.png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # some versions of freetype have different horizontal spacing. - # setting a tight epsilon, I'm showing the original test failure - # at epsilon = ~38. - self.assert_image_similar(im, target_img, self.metrics["multiline"]) + # some versions of freetype have different horizontal spacing. + # setting a tight epsilon, I'm showing the original test failure + # at epsilon = ~38. + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_render_multiline_text(self): ttf = self.get_font() @@ -213,10 +213,10 @@ class TestImageFont(PillowTestCase): draw.text((0, 0), TEST_TEXT, font=ttf) target = "Tests/images/multiline_text.png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics["multiline"]) + # Epsilon ~.5 fails with FreeType 2.7 + self.assert_image_similar(im, target_img, self.metrics["multiline"]) # Test that text() can pass on additional arguments # to multiline_text() @@ -232,10 +232,10 @@ class TestImageFont(PillowTestCase): draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) target = "Tests/images/multiline_text" + ext + ".png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics["multiline"]) + # Epsilon ~.5 fails with FreeType 2.7 + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_unknown_align(self): im = Image.new(mode="RGB", size=(300, 100)) @@ -297,10 +297,10 @@ class TestImageFont(PillowTestCase): draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) target = "Tests/images/multiline_text_spacing.png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics["multiline"]) + # Epsilon ~.5 fails with FreeType 2.7 + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_rotated_transposed_font(self): img_grey = Image.new("L", (100, 100)) @@ -432,14 +432,14 @@ class TestImageFont(PillowTestCase): draw = ImageDraw.Draw(im) target = "Tests/images/default_font.png" - target_img = Image.open(target) + with Image.open(target) as target_img: - # Act - default_font = ImageFont.load_default() - draw.text((10, 10), txt, font=default_font) + # Act + default_font = ImageFont.load_default() + draw.text((10, 10), txt, font=default_font) - # Assert - self.assert_image_equal(im, target_img) + # Assert + self.assert_image_equal(im, target_img) def test_getsize_empty(self): # issue #2614 @@ -462,12 +462,12 @@ class TestImageFont(PillowTestCase): # issue #2826 font = ImageFont.load_default() with self.assertRaises(UnicodeEncodeError): - font.getsize(u"’") + font.getsize("’") - @unittest.skipIf(sys.version.startswith("2") or is_pypy(), "requires CPython 3.3+") + @unittest.skipIf(is_pypy(), "failing on PyPy") def test_unicode_extended(self): # issue #3777 - text = u"A\u278A\U0001F12B" + text = "A\u278A\U0001F12B" target = "Tests/images/unicode_extended.png" ttf = ImageFont.truetype( @@ -703,8 +703,8 @@ class TestImageFont(PillowTestCase): d = ImageDraw.Draw(im) d.text((10, 10), "Text", font=font, fill="black") - expected = Image.open(path) - self.assert_image_similar(im, expected, epsilon) + with Image.open(path) as expected: + self.assert_image_similar(im, expected, epsilon) font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) _check_text(font, "Tests/images/variation_adobe.png", 11) @@ -733,8 +733,8 @@ class TestImageFont(PillowTestCase): d = ImageDraw.Draw(im) d.text((10, 10), "Text", font=font, fill="black") - expected = Image.open(path) - self.assert_image_similar(im, expected, epsilon) + with Image.open(path) as expected: + self.assert_image_similar(im, expected, epsilon) font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) font.set_variation_by_axes([500, 50]) diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index b7be8f723..eed0c70b6 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image, ImageDraw, ImageFont -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase image_font_installed = True try: diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 5b88f94cc..c1f976dfa 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -1,7 +1,8 @@ -# -*- coding: utf-8 -*- +import unittest + from PIL import Image, ImageDraw, ImageFont, features -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase FONT_SIZE = 20 FONT_PATH = "Tests/fonts/DejaVuSans.ttf" @@ -24,9 +25,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "اهلا عمان", font=ttf, fill=500) target = "Tests/images/test_text.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_y_offset(self): ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE) @@ -36,9 +36,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "العالم العربي", font=ttf, fill=500) target = "Tests/images/test_y_offset.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 1.7) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 1.7) def test_complex_unicode_text(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -48,9 +47,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "السلام عليكم", font=ttf, fill=500) target = "Tests/images/test_complex_unicode_text.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf", FONT_SIZE) @@ -59,9 +57,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "លោកុប្បត្តិ", font=ttf, fill=500) target = "Tests/images/test_complex_unicode_text2.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 2.3) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 2.3) def test_text_direction_rtl(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -71,9 +68,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "English عربي", font=ttf, fill=500, direction="rtl") target = "Tests/images/test_direction_rtl.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_text_direction_ltr(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -83,9 +79,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "سلطنة عمان Oman", font=ttf, fill=500, direction="ltr") target = "Tests/images/test_direction_ltr.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_text_direction_rtl2(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -95,9 +90,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "Oman سلطنة عمان", font=ttf, fill=500, direction="rtl") target = "Tests/images/test_direction_ltr.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_text_direction_ttb(self): ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE) @@ -111,9 +105,8 @@ class TestImagecomplextext(PillowTestCase): self.skipTest("libraqm 0.7 or greater not available") target = "Tests/images/test_direction_ttb.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 1.15) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 1.15) def test_text_direction_ttb_stroke(self): ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50) @@ -135,9 +128,8 @@ class TestImagecomplextext(PillowTestCase): self.skipTest("libraqm 0.7 or greater not available") target = "Tests/images/test_direction_ttb_stroke.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 12.4) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 12.4) def test_ligature_features(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -146,9 +138,8 @@ class TestImagecomplextext(PillowTestCase): draw = ImageDraw.Draw(im) draw.text((0, 0), "filling", font=ttf, fill=500, features=["-liga"]) target = "Tests/images/test_ligature_features.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) liga_size = ttf.getsize("fi", features=["-liga"]) self.assertEqual(liga_size, (13, 19)) @@ -161,9 +152,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "TeToAV", font=ttf, fill=500, features=["-kern"]) target = "Tests/images/test_kerning_features.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_arabictext_features(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -179,9 +169,8 @@ class TestImagecomplextext(PillowTestCase): ) target = "Tests/images/test_arabictext_features.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_x_max_and_y_offset(self): ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40) @@ -191,9 +180,8 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "لح", font=ttf, fill=500) target = "Tests/images/test_x_max_and_y_offset.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) def test_language(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -203,6 +191,5 @@ class TestImagecomplextext(PillowTestCase): draw.text((0, 0), "абвг", font=ttf, fill=500, language="sr") target = "Tests/images/test_language.png" - target_img = Image.open(target) - - self.assert_image_similar(im, target_img, 0.5) + with Image.open(target) as target_img: + self.assert_image_similar(im, target_img, 0.5) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index da41b3a12..8d2b94226 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -1,5 +1,3 @@ -from __future__ import print_function - from PIL import Image, ImageMath from .helper import PillowTestCase @@ -7,7 +5,7 @@ from .helper import PillowTestCase def pixel(im): if hasattr(im, "im"): - return "%s %r" % (im.mode, im.getpixel((0, 0))) + return "{} {!r}".format(im.mode, im.getpixel((0, 0))) else: if isinstance(im, int): return int(im) # hack to deal with booleans diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 1492872b6..c9fd37c54 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -51,8 +51,8 @@ class MorphTests(PillowTestCase): self.assertEqual(self.img_to_string(A), self.img_string_normalize(Bstring)) def test_str_to_img(self): - im = Image.open("Tests/images/morph_a.png") - self.assert_image_equal(self.A, im) + with Image.open("Tests/images/morph_a.png") as im: + self.assert_image_equal(self.A, im) def create_lut(self): for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"): diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 2cdbbe02f..ea50a4c76 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -11,7 +11,7 @@ except ImportError: class TestImageOps(PillowTestCase): - class Deformer(object): + class Deformer: def getmesh(self, im): x, y = im.size return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] @@ -106,10 +106,10 @@ class TestImageOps(PillowTestCase): new_im = ImageOps.pad(im, new_size, color=color, centering=centering) self.assertEqual(new_im.size, new_size) - target = Image.open( + with Image.open( "Tests/images/imageops_pad_" + label + "_" + str(i) + ".jpg" - ) - self.assert_image_similar(new_im, target, 6) + ) as target: + self.assert_image_similar(new_im, target, 6) def test_pil163(self): # Division by zero in equalize if < 255 pixels in image (@PIL163) @@ -140,8 +140,8 @@ class TestImageOps(PillowTestCase): # Test the colorizing function with 2-color functionality # Open test image (256px by 10px, black to white) - im = Image.open("Tests/images/bw_gradient.png") - im = im.convert("L") + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") # Create image with original 2-color functionality im_test = ImageOps.colorize(im, "red", "green") @@ -173,8 +173,8 @@ class TestImageOps(PillowTestCase): # Test the colorizing function with 2-color functionality and offset # Open test image (256px by 10px, black to white) - im = Image.open("Tests/images/bw_gradient.png") - im = im.convert("L") + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") # Create image with original 2-color functionality with offsets im_test = ImageOps.colorize( @@ -208,8 +208,8 @@ class TestImageOps(PillowTestCase): # Test the colorizing function with 3-color functionality and offset # Open test image (256px by 10px, black to white) - im = Image.open("Tests/images/bw_gradient.png") - im = im.convert("L") + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") # Create image with new three color functionality with offsets im_test = ImageOps.colorize( @@ -261,27 +261,36 @@ class TestImageOps(PillowTestCase): if HAVE_WEBP and _webp.HAVE_WEBPANIM: exts.append(".webp") for ext in exts: - base_im = Image.open("Tests/images/hopper" + ext) + with Image.open("Tests/images/hopper" + ext) as base_im: - orientations = [base_im] - for i in range(2, 9): - im = Image.open("Tests/images/hopper_orientation_" + str(i) + ext) - orientations.append(im) - for i, orientation_im in enumerate(orientations): - for im in [orientation_im, orientation_im.copy()]: # ImageFile # Image - if i == 0: - self.assertNotIn("exif", im.info) - else: - original_exif = im.info["exif"] - transposed_im = ImageOps.exif_transpose(im) - self.assert_image_similar(base_im, transposed_im, 17) - if i == 0: - self.assertNotIn("exif", im.info) - else: - self.assertNotEqual(transposed_im.info["exif"], original_exif) + def check(orientation_im): + for im in [ + orientation_im, + orientation_im.copy(), + ]: # ImageFile # Image + if orientation_im is base_im: + self.assertNotIn("exif", im.info) + else: + original_exif = im.info["exif"] + transposed_im = ImageOps.exif_transpose(im) + self.assert_image_similar(base_im, transposed_im, 17) + if orientation_im is base_im: + self.assertNotIn("exif", im.info) + else: + self.assertNotEqual( + transposed_im.info["exif"], original_exif + ) - self.assertNotIn(0x0112, transposed_im.getexif()) + self.assertNotIn(0x0112, transposed_im.getexif()) - # Repeat the operation, to test that it does not keep transposing - transposed_im2 = ImageOps.exif_transpose(transposed_im) - self.assert_image_equal(transposed_im2, transposed_im) + # Repeat the operation + # to test that it does not keep transposing + transposed_im2 = ImageOps.exif_transpose(transposed_im) + self.assert_image_equal(transposed_im2, transposed_im) + + check(base_im) + for i in range(2, 9): + with Image.open( + "Tests/images/hopper_orientation_" + str(i) + ext + ) as orientation_im: + check(orientation_im) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 4ed8a83a7..71e858681 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -5,7 +5,7 @@ from .helper import PillowTestCase class TestImageOpsUsm(PillowTestCase): def setUp(self): - super(TestImageOpsUsm, self).setUp() + super().setUp() self.im = Image.open("Tests/images/hopper.ppm") self.addCleanup(self.im.close) self.snakes = Image.open("Tests/images/color_snakes.png") diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 1297712ef..933fc9923 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -128,9 +128,8 @@ class TestImagePalette(PillowTestCase): img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB img.save(outfile, format="PNG") - reloaded = Image.open(outfile) - - self.assert_image_equal(img, reloaded) + with Image.open(outfile) as reloaded: + self.assert_image_equal(img, reloaded) def test_invalid_palette(self): self.assertRaises(IOError, ImagePalette.load, "Tests/images/hopper.jpg") diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index ed65d47c1..35c78cd3b 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -2,7 +2,6 @@ import array import struct from PIL import Image, ImagePath -from PIL._util import py3 from .helper import PillowTestCase @@ -75,10 +74,7 @@ class TestImagePath(PillowTestCase): # This fails due to the invalid malloc above, # and segfaults for i in range(200000): - if py3: - x[i] = b"0" * 16 - else: - x[i] = "0" * 16 + x[i] = b"0" * 16 class evil: diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 5cf14adda..f69a21d2a 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -15,7 +15,7 @@ else: test_case.skipTest("Qt bindings are not installed") -class PillowQtTestCase(object): +class PillowQtTestCase: def setUp(self): skip_if_qt_is_not_installed(self) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 225c8a6a9..858714e57 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image, ImageShow -from .helper import PillowTestCase, hopper, is_win32, on_ci, on_github_actions, unittest +from .helper import PillowTestCase, hopper, is_win32, on_ci, on_github_actions class TestImageShow(PillowTestCase): diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index c397c84be..5ee994dc7 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,15 +1,14 @@ -from PIL import Image -from PIL._util import py3 +import unittest -from .helper import PillowTestCase, hopper, unittest +from PIL import Image + +from .helper import PillowTestCase, hopper try: from PIL import ImageTk - if py3: - import tkinter as tk - else: - import Tkinter as tk + import tkinter as tk + dir(ImageTk) HAS_TK = True except (OSError, ImportError): @@ -32,19 +31,19 @@ class TestImageTk(PillowTestCase): def test_kw(self): TEST_JPG = "Tests/images/hopper.jpg" TEST_PNG = "Tests/images/hopper.png" - im1 = Image.open(TEST_JPG) - im2 = Image.open(TEST_PNG) - with open(TEST_PNG, "rb") as fp: - data = fp.read() - kw = {"file": TEST_JPG, "data": data} + with Image.open(TEST_JPG) as im1: + with Image.open(TEST_PNG) as im2: + with open(TEST_PNG, "rb") as fp: + data = fp.read() + kw = {"file": TEST_JPG, "data": data} - # Test "file" - im = ImageTk._get_image_from_kw(kw) - self.assert_image_equal(im, im1) + # Test "file" + im = ImageTk._get_image_from_kw(kw) + self.assert_image_equal(im, im1) - # Test "data" - im = ImageTk._get_image_from_kw(kw) - self.assert_image_equal(im, im2) + # Test "data" + im = ImageTk._get_image_from_kw(kw) + self.assert_image_equal(im, im2) # Test no relevant entry im = ImageTk._get_image_from_kw(kw) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 2b2034187..1cd8c674e 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -1,6 +1,8 @@ +import unittest + from PIL import ImageWin -from .helper import PillowTestCase, hopper, is_win32, unittest +from .helper import PillowTestCase, hopper, is_win32 class TestImageWin(PillowTestCase): diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py index 68e72bc4e..240783960 100644 --- a/Tests/test_lib_image.py +++ b/Tests/test_lib_image.py @@ -1,6 +1,8 @@ +import unittest + from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase class TestLibImage(PillowTestCase): diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 6178184bc..91479e343 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -18,7 +18,7 @@ class TestLibPack(PillowTestCase): if isinstance(data, int): data_len = data * len(pixels) - data = bytes(bytearray(range(1, data_len + 1))) + data = bytes(range(1, data_len + 1)) self.assertEqual(data, im.tobytes("raw", rawmode)) @@ -44,6 +44,9 @@ class TestLibPack(PillowTestCase): self.assert_pack("LA", "LA", 2, (1, 2), (3, 4), (5, 6)) self.assert_pack("LA", "LA;L", 2, (1, 4), (2, 5), (3, 6)) + def test_La(self): + self.assert_pack("La", "La", 2, (1, 2), (3, 4), (5, 6)) + def test_P(self): self.assert_pack("P", "P;1", b"\xe4", 1, 1, 1, 0, 0, 255, 0, 0) self.assert_pack("P", "P;2", b"\xe4", 3, 2, 1, 0) @@ -226,7 +229,7 @@ class TestLibUnpack(PillowTestCase): """ if isinstance(data, int): data_len = data * len(pixels) - data = bytes(bytearray(range(1, data_len + 1))) + data = bytes(range(1, data_len + 1)) im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1) @@ -269,6 +272,9 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("LA", "LA", 2, (1, 2), (3, 4), (5, 6)) self.assert_unpack("LA", "LA;L", 2, (1, 4), (2, 5), (3, 6)) + def test_La(self): + self.assert_unpack("La", "La", 2, (1, 2), (3, 4), (5, 6)) + def test_P(self): self.assert_unpack("P", "P;1", b"\xe4", 1, 1, 1, 0, 0, 1, 0, 0) self.assert_unpack("P", "P;2", b"\xe4", 3, 2, 1, 0) diff --git a/Tests/test_locale.py b/Tests/test_locale.py index e4b2806e3..0b1f330ac 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -1,10 +1,9 @@ -from __future__ import print_function - import locale +import unittest from PIL import Image -from .helper import PillowTestCase, unittest +from .helper import PillowTestCase # ref https://github.com/python-pillow/Pillow/issues/272 # on windows, in polish locale: diff --git a/Tests/test_main.py b/Tests/test_main.py index 56e3ccadd..4f52149b6 100644 --- a/Tests/test_main.py +++ b/Tests/test_main.py @@ -1,11 +1,10 @@ -from __future__ import unicode_literals - import os import subprocess import sys +import unittest from unittest import TestCase -from .helper import is_pypy, is_win32, on_github_actions, unittest +from .helper import is_pypy, is_win32, on_github_actions class TestMain(TestCase): diff --git a/Tests/test_map.py b/Tests/test_map.py index 25e24e2fb..bdc3a7e2c 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -1,8 +1,9 @@ import sys +import unittest from PIL import Image -from .helper import PillowTestCase, is_win32, unittest +from .helper import PillowTestCase, is_win32 try: import numpy diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 649148699..fcd45b3c1 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -20,7 +20,11 @@ class TestModeI16(PillowTestCase): self.assertEqual( p1, p2, - ("got %r from mode %s at %s, expected %r" % (p1, im1.mode, xy, p2)), + ( + "got {!r} from mode {} at {}, expected {!r}".format( + p1, im1.mode, xy, p2 + ) + ), ) def test_basic(self): diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 872ecdbb6..d3c4ce11d 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,8 +1,8 @@ -from __future__ import print_function +import unittest from PIL import Image -from .helper import PillowTestCase, hopper, unittest +from .helper import PillowTestCase, hopper try: import numpy @@ -101,9 +101,9 @@ class TestNumpy(PillowTestCase): self.assert_deep_equal(px[x, y], np[y, x]) def test_16bit(self): - img = Image.open("Tests/images/16bit.cropped.tif") - np_img = numpy.array(img) - self._test_img_equals_nparray(img, np_img) + with Image.open("Tests/images/16bit.cropped.tif") as img: + np_img = numpy.array(img) + self._test_img_equals_nparray(img, np_img) self.assertEqual(np_img.dtype, numpy.dtype(" 1 else "PNG" - -im = Image.open("Tests/images/hopper.ppm") -im.load() - -queue = queue.Queue() - -result = [] - - -class Worker(threading.Thread): - def run(self): - while True: - im = queue.get() - if im is None: - queue.task_done() - sys.stdout.write("x") - break - f = io.BytesIO() - im.save(f, test_format, optimize=1) - data = f.getvalue() - result.append(len(data)) - im = Image.open(io.BytesIO(data)) - im.load() - sys.stdout.write(".") - queue.task_done() - - -t0 = time.time() - -threads = 20 -jobs = 100 - -for i in range(threads): - w = Worker() - w.start() - -for i in range(jobs): - queue.put(im) - -for i in range(threads): - queue.put(None) - -queue.join() - -print() -print(time.time() - t0) -print(len(result), sum(result)) -print(result) diff --git a/Tests/versions.py b/Tests/versions.py deleted file mode 100644 index 1ac226c9d..000000000 --- a/Tests/versions.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import print_function - -from PIL import Image - - -def version(module, version): - v = getattr(module.core, version + "_version", None) - if v: - print(version, v) - - -version(Image, "jpeglib") -version(Image, "zlib") -version(Image, "libtiff") - -try: - from PIL import ImageFont -except ImportError: - pass -else: - version(ImageFont, "freetype2") - -try: - from PIL import ImageCms -except ImportError: - pass -else: - version(ImageCms, "littlecms") diff --git a/depends/README.rst b/depends/README.rst index 069d2b81f..ce88fa47b 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -7,24 +7,3 @@ build & install non-packaged dependencies; useful for testing with Travis CI. ``install_extra_test_images.sh`` can be used to install additional test images that are used for Travis CI and AppVeyor. - -The other scripts can be used to install all of the dependencies for -the listed operating systems/distros. The ``ubuntu_14.04.sh`` and -``debian_8.2.sh`` scripts have been tested on bare AWS images and will -install all required dependencies for the system Python 2.7 and 3.4 -for all of the optional dependencies. Git may also be required prior -to running the script to actually download Pillow. - -e.g.:: - - $ sudo apt-get install git - $ git clone https://github.com/python-pillow/Pillow.git - $ cd Pillow/depends - $ ./debian_8.2.sh - $ cd .. - $ git checkout [branch or tag] - $ virtualenv -p /usr/bin/python2.7 ~/vpy27 - $ source ~/vpy27/bin/activate - $ make install - $ make test - diff --git a/depends/alpine_Dockerfile b/depends/alpine_Dockerfile deleted file mode 100644 index 69bdf84f6..000000000 --- a/depends/alpine_Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# This is a sample Dockerfile to build Pillow on Alpine Linux -# with all/most of the dependencies working. -# -# Tcl/Tk isn't detecting -# Freetype has different metrics so tests are failing. -# sudo and bash are required for the webp build script. - -FROM alpine -USER root - -RUN apk --no-cache add python \ - build-base \ - python-dev \ - py-pip \ - # Pillow dependencies - jpeg-dev \ - zlib-dev \ - freetype-dev \ - lcms2-dev \ - openjpeg-dev \ - tiff-dev \ - tk-dev \ - tcl-dev - -# install from pip, without webp -#RUN LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "pip install Pillow" - -# install from git, run tests, including webp -RUN apk --no-cache add git \ - bash \ - sudo - -RUN git clone https://github.com/python-pillow/Pillow.git /Pillow -RUN pip install virtualenv && virtualenv /vpy && source /vpy/bin/activate && pip install nose - -RUN echo "#!/bin/bash" >> /test && \ - echo "source /vpy/bin/activate && cd /Pillow " >> test && \ - echo "pushd depends && ./install_webp.sh && ./install_imagequant.sh && popd" >> test && \ - echo "LIBRARY_PATH=/lib:/usr/lib make install && make test" >> test - -RUN chmod +x /test - -CMD ["/test"] diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh deleted file mode 100755 index c4f72bf8e..000000000 --- a/depends/debian_8.2.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# -# Installs all of the dependencies for Pillow for Debian 8.2 -# for both system Pythons 2.7 and 3.4 -# -# Also works for Raspbian Jessie -# - -sudo apt-get -y install python-dev python-setuptools \ - python3-dev python-virtualenv cmake -sudo apt-get -y install libtiff5-dev libjpeg62-turbo-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \ - python-tk python3-tk libharfbuzz-dev libfribidi-dev - -./install_openjpeg.sh -./install_imagequant.sh -./install_raqm.sh diff --git a/depends/fedora_23.sh b/depends/fedora_23.sh deleted file mode 100755 index 5bdcf7f17..000000000 --- a/depends/fedora_23.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# -# Installs all of the dependencies for Pillow for Fedora 23 -# for both system Pythons 2.7 and 3.4 -# -# note that Fedora does ship packages for Pillow as python-pillow - -# this is a workaround for -# "gcc: error: /usr/lib/rpm/redhat/redhat-hardened-cc1: No such file or directory" -# errors when compiling. -sudo dnf install redhat-rpm-config - -sudo dnf install python-devel python3-devel python-virtualenv make gcc - -sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \ - lcms2-devel libwebp-devel openjpeg2-devel tkinter python3-tkinter \ - tcl-devel tk-devel harfbuzz-devel fribidi-devel libraqm-devel \ No newline at end of file diff --git a/depends/freebsd_10.sh b/depends/freebsd_10.sh deleted file mode 100755 index 36d9c1069..000000000 --- a/depends/freebsd_10.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -# -# Installs all of the dependencies for Pillow for Freebsd 10.x -# for both system Pythons 2.7 and 3.4 -# -sudo pkg install python2 python3 py27-pip py27-virtualenv wget cmake - -# Openjpeg fails badly using the openjpeg package. -# I can't find a python3.4 version of tkinter -sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 harfbuzz fribidi py27-tkinter - -./install_raqm_cmake.sh diff --git a/depends/ubuntu_12.04.sh b/depends/ubuntu_12.04.sh deleted file mode 100755 index 9bfae43b0..000000000 --- a/depends/ubuntu_12.04.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# -# Installs all of the dependencies for Pillow for Ubuntu 12.04 -# for both system Pythons 2.7 and 3.2 -# - -sudo apt-get -y install python-dev python-setuptools \ - python3-dev python-virtualenv cmake -sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev tcl8.5-dev \ - tk8.5-dev python-tk python3-tk - - -./install_openjpeg.sh -./install_webp.sh -./install_imagequant.sh diff --git a/depends/ubuntu_14.04.sh b/depends/ubuntu_14.04.sh deleted file mode 100755 index f7d28fba7..000000000 --- a/depends/ubuntu_14.04.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# -# Installs all of the dependencies for Pillow for Ubuntu 14.04 -# for both system Pythons 2.7 and 3.4 -# -sudo apt-get update -sudo apt-get -y install python-dev python-setuptools \ - python3-dev python-virtualenv cmake -sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \ - python-tk python3-tk libharfbuzz-dev libfribidi-dev - -./install_openjpeg.sh -./install_imagequant.sh -./install_raqm.sh diff --git a/docs/conf.py b/docs/conf.py index a9ca91de7..66effdfb2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Pillow (PIL Fork) documentation build configuration file, created by # sphinx-quickstart on Sat Apr 4 07:54:11 2015. @@ -42,9 +41,9 @@ source_suffix = ".rst" master_doc = "index" # General information about the project. -project = u"Pillow (PIL Fork)" -copyright = u"1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors" -author = u"Fredrik Lundh, Alex Clark and Contributors" +project = "Pillow (PIL Fork)" +copyright = "1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors" +author = "Fredrik Lundh, Alex Clark and Contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -220,8 +219,8 @@ latex_documents = [ ( master_doc, "PillowPILFork.tex", - u"Pillow (PIL Fork) Documentation", - u"Alex Clark", + "Pillow (PIL Fork) Documentation", + "Alex Clark", "manual", ) ] @@ -252,7 +251,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, "pillowpilfork", u"Pillow (PIL Fork) Documentation", [author], 1) + (master_doc, "pillowpilfork", "Pillow (PIL Fork) Documentation", [author], 1) ] # If true, show URL addresses after external links. @@ -268,7 +267,7 @@ texinfo_documents = [ ( master_doc, "PillowPILFork", - u"Pillow (PIL Fork) Documentation", + "Pillow (PIL Fork) Documentation", author, "PillowPILFork", "Pillow is the friendly PIL fork by Alex Clark and Contributors.", diff --git a/docs/example/DdsImagePlugin.py b/docs/example/DdsImagePlugin.py index be493a316..45f63f164 100644 --- a/docs/example/DdsImagePlugin.py +++ b/docs/example/DdsImagePlugin.py @@ -212,10 +212,10 @@ class DdsImageFile(ImageFile.ImageFile): def _open(self): magic, header_size = struct.unpack(">> from __future__ import print_function >>> print(im.format, im.size, im.mode) PPM (512, 512) RGB @@ -67,7 +66,6 @@ Convert files to JPEG :: - from __future__ import print_function import os, sys from PIL import Image @@ -89,7 +87,6 @@ Create JPEG thumbnails :: - from __future__ import print_function import os, sys from PIL import Image @@ -120,7 +117,6 @@ Identify Image Files :: - from __future__ import print_function import sys from PIL import Image diff --git a/docs/installation.rst b/docs/installation.rst index 39005db8e..56ab8f0a3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -99,14 +99,13 @@ Pillow can be installed on FreeBSD via the official Ports or Packages systems: **Packages**:: - pkg install py27-pillow + pkg install py36-pillow .. note:: The `Pillow FreeBSD port `_ and packages - are tested by the ports team with all supported FreeBSD versions - and against Python 2.7 and 3.x. + are tested by the ports team with all supported FreeBSD versions. Building From Source @@ -129,10 +128,9 @@ External Libraries .. note:: - There are scripts to install the dependencies for some operating - systems included in the ``depends`` directory. Also see the - Dockerfiles in our `docker images repo - `_. + There are Dockerfiles in our `Docker images repo + `_ to install the + dependencies for some operating systems. Many of Pillow's features require external libraries: @@ -179,8 +177,6 @@ Many of Pillow's features require external libraries: * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. - * Windows support: Libimagequant requires VS2015/MSVC 19 to compile, - so it is unlikely to work with Python 2.7 on Windows. * **libraqm** provides complex text layout support. @@ -388,40 +384,40 @@ Continuous Integration Targets These platforms are built and tested for every change. -+----------------------------------+-------------------------------+-----------------------+ -|**Operating system** |**Tested Python versions** |**Tested Architecture**| -+----------------------------------+-------------------------------+-----------------------+ -| Alpine | 2.7, 3.7 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Arch | 2.7, 3.7 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Amazon Linux 1 | 2.7, 3.6 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Amazon Linux 2 | 2.7, 3.6 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| CentOS 6 | 2.7, 3.6 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| CentOS 7 | 2.7, 3.6 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Debian 9 Stretch | 2.7, 3.5 |x86 | -+----------------------------------+-------------------------------+-----------------------+ -| Debian 10 Buster | 2.7, 3.7 |x86 | -+----------------------------------+-------------------------------+-----------------------+ -| Fedora 30 | 2.7, 3.7 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Fedora 31 | 3.7 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7, 3.8 |x86-64 | -+----------------------------------+-------------------------------+-----------------------+ -| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, 3.8, |x86-64 | -| | PyPy, PyPy3 | | -+----------------------------------+-------------------------------+-----------------------+ -| Windows Server 2012 R2 | 2.7, 3.5, 3.6, 3.7, 3.8 |x86, x86-64 | -| +-------------------------------+-----------------------+ -| | PyPy, 3.7/MinGW |x86 | -+----------------------------------+-------------------------------+-----------------------+ ++----------------------------------+--------------------------+-----------------------+ +|**Operating system** |**Tested Python versions**|**Tested architecture**| ++----------------------------------+--------------------------+-----------------------+ +| Alpine | 3.7 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Arch | 3.7 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Amazon Linux 1 | 3.6 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Amazon Linux 2 | 3.6 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| CentOS 6 | 3.6 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| CentOS 7 | 3.6 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Debian 9 Stretch | 3.5 |x86 | ++----------------------------------+--------------------------+-----------------------+ +| Debian 10 Buster | 3.7 |x86 | ++----------------------------------+--------------------------+-----------------------+ +| Fedora 30 | 3.7 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Fedora 31 | 3.7 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| macOS 10.13 High Sierra | 3.5, 3.6, 3.7, 3.8 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| macOS 10.15 Catalina | PyPy3 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Ubuntu Linux 16.04 LTS | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Windows Server 2012 R2 | 3.5, 3.6, 3.7, 3.8 |x86, x86-64 | +| +--------------------------+-----------------------+ +| | PyPy3, 3.7/MinGW |x86 | ++----------------------------------+--------------------------+-----------------------+ -\* macOS CI is not run for every commit, but is run for every release. Other Platforms ^^^^^^^^^^^^^^^ diff --git a/docs/releasenotes/7.0.0.rst b/docs/releasenotes/7.0.0.rst index 374a8d2dd..386a26757 100644 --- a/docs/releasenotes/7.0.0.rst +++ b/docs/releasenotes/7.0.0.rst @@ -63,11 +63,11 @@ TODO API Additions ============= -TODO -^^^^ - -TODO +Custom unidentified image error +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Pillow will now throw a custom ``UnidentifiedImageError`` when an image cannot be +identified. For backwards compatibility, this will inherit from ``IOError``. Other Changes ============= diff --git a/mp_compile.py b/mp_compile.py deleted file mode 100644 index ec73e927e..000000000 --- a/mp_compile.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 2.7 because otherwise, it conflicts with Python's -# own newly-added support for parallel builds. - -from __future__ import print_function - -import os -import sys -from distutils.ccompiler import CCompiler -from multiprocessing import Pool, cpu_count - -try: - MAX_PROCS = int(os.environ.get("MAX_CONCURRENCY", min(4, cpu_count()))) -except NotImplementedError: - MAX_PROCS = None - - -# hideous monkeypatching. but. but. but. -def _mp_compile_one(tp): - (self, obj, build, cc_args, extra_postargs, pp_opts) = tp - try: - src, ext = build[obj] - except KeyError: - return - self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) - return - - -def _mp_compile( - self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - depends=None, -): - """Compile one or more source files. - - see distutils.ccompiler.CCompiler.compile for comments. - """ - # A concrete compiler class can either override this method - # entirely or implement _compile(). - - macros, objects, extra_postargs, pp_opts, build = self._setup_compile( - output_dir, macros, include_dirs, sources, depends, extra_postargs - ) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - pool = Pool(MAX_PROCS) - try: - print("Building using %d processes" % pool._processes) - except Exception: - pass - arr = [(self, obj, build, cc_args, extra_postargs, pp_opts) for obj in objects] - pool.map_async(_mp_compile_one, arr) - pool.close() - pool.join() - # Return *all* object filenames, not just the ones we just built. - return objects - - -def install(): - - fl_win = sys.platform.startswith("win") - fl_cygwin = sys.platform.startswith("cygwin") - - if fl_win or fl_cygwin: - # Windows barfs on multiprocessing installs - print("Single threaded build for Windows") - return - - if MAX_PROCS != 1: - # explicitly don't enable if environment says 1 processor - try: - # bug, only enable if we can make a Pool. see issue #790 and - # https://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing - Pool(2) - CCompiler.compile = _mp_compile - except Exception as msg: - print("Exception installing mp_compile, proceeding without: %s" % msg) - else: - print( - "Single threaded build, not installing mp_compile: %s processes" % MAX_PROCS - ) - - -# We monkeypatch Python 2.7 -if sys.version_info.major < 3: - install() diff --git a/selftest.py b/selftest.py index a8937b77e..ea52256f7 100755 --- a/selftest.py +++ b/selftest.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # minimal sanity check -from __future__ import print_function import sys diff --git a/setup.py b/setup.py index 339132ff3..f5024990f 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,6 @@ # Final rating: 10/10 # Your cheese is so fresh most people think it's a cream: Mascarpone # ------------------------------ -from __future__ import print_function import os import re @@ -20,10 +19,6 @@ from distutils.command.build_ext import build_ext from setuptools import Extension, setup -# monkey patch import hook. Even though flake8 says it's not used, it is. -# comment this out to disable multi threaded builds. -import mp_compile - def get_version(): version_file = "src/PIL/_version.py" @@ -189,10 +184,10 @@ def _find_library_dirs_ldconfig(): expr = r".* => (.*)" env = {} - null = open(os.devnull, "wb") try: - with null: - p = subprocess.Popen(args, stderr=null, stdout=subprocess.PIPE, env=env) + p = subprocess.Popen( + args, stderr=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env + ) except OSError: # E.g. command not found return [] [data, _] = p.communicate() @@ -305,8 +300,7 @@ class pil_build_ext(build_ext): return getattr(self, feat) is None def __iter__(self): - for x in self.features: - yield x + yield from self.features feature = feature() @@ -334,12 +328,15 @@ class pil_build_ext(build_ext): if self.debug: global DEBUG DEBUG = True - 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. - self.parallel = mp_compile.MAX_PROCS + if not self.parallel: + # If --parallel (or -j) wasn't specified, we want to reproduce the same + # behavior as before, that is, auto-detect the number of jobs. + try: + self.parallel = int( + os.environ.get("MAX_CONCURRENCY", min(4, os.cpu_count())) + ) + except TypeError: + self.parallel = None for x in self.feature: if getattr(self, "disable_%s" % x): setattr(self.feature, x, False) @@ -347,7 +344,7 @@ class pil_build_ext(build_ext): _dbg("Disabling %s", x) if getattr(self, "enable_%s" % x): raise ValueError( - "Conflicting options: --enable-%s and --disable-%s" % (x, x) + "Conflicting options: --enable-{} and --disable-{}".format(x, x) ) if getattr(self, "enable_%s" % x): _dbg("Requiring %s", x) @@ -719,7 +716,7 @@ class pil_build_ext(build_ext): defs.append(("HAVE_LIBTIFF", None)) if sys.platform == "win32": libs.extend(["kernel32", "user32", "gdi32"]) - if struct.unpack("h", "\0\1".encode("ascii"))[0] == 1: + if struct.unpack("h", b"\0\1")[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) if sys.platform == "win32" and not (PLATFORM_PYPY or PLATFORM_MINGW): @@ -800,7 +797,7 @@ class pil_build_ext(build_ext): print("-" * 68) print("version Pillow %s" % PILLOW_VERSION) v = sys.version.split("[") - print("platform %s %s" % (sys.platform, v[0].strip())) + print("platform {} {}".format(sys.platform, v[0].strip())) for v in v[1:]: print(" [%s" % v.strip()) print("-" * 68) @@ -823,7 +820,7 @@ class pil_build_ext(build_ext): version = "" if len(option) >= 3 and option[2]: version = " (%s)" % option[2] - print("--- %s support available%s" % (option[1], version)) + print("--- {} support available{}".format(option[1], version)) else: print("*** %s support not available" % option[1]) all = 0 @@ -868,13 +865,12 @@ try: classifiers=[ "Development Status :: 6 - Mature", "License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501 - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Multimedia :: Graphics", @@ -883,7 +879,7 @@ try: "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Viewers", ], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", + python_requires=">=3.5", cmdclass={"build_ext": pil_build_ext}, ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], include_package_data=True, diff --git a/src/PIL/BdfFontFile.py b/src/PIL/BdfFontFile.py index 53a890fb6..7a485cf80 100644 --- a/src/PIL/BdfFontFile.py +++ b/src/PIL/BdfFontFile.py @@ -17,7 +17,6 @@ # See the README file for information on usage and redistribution. # -from __future__ import print_function from . import FontFile, Image diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index dd1745158..5ccba37db 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -283,7 +283,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder): self._read_blp_header() self._load() except struct.error: - raise IOError("Truncated Blp file") + raise OSError("Truncated Blp file") return 0, 0 def _read_palette(self): diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index e9c2afd10..cdef77ced 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -144,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info["a_mask"], ) else: - raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"]) + raise OSError("Unsupported BMP header type (%d)" % file_info["header_size"]) # ------------------ Special case : header is reported 40, which # ---------------------- is shorter than real size for bpp >= 16 @@ -159,12 +159,12 @@ class BmpImageFile(ImageFile.ImageFile): # ------------------------------- Check abnormal values for DOS attacks if file_info["width"] * file_info["height"] > 2 ** 31: - raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) + raise OSError("Unsupported BMP Size: (%dx%d)" % self.size) # ---------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None)) if self.mode is None: - raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"]) + raise OSError("Unsupported BMP pixel depth (%d)" % file_info["bits"]) # ---------------- Process BMP with Bitfields compression (not palette) if file_info["compression"] == self.BITFIELDS: @@ -202,21 +202,21 @@ class BmpImageFile(ImageFile.ImageFile): ): raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] else: - raise IOError("Unsupported BMP bitfields layout") + raise OSError("Unsupported BMP bitfields layout") else: - raise IOError("Unsupported BMP bitfields layout") + raise OSError("Unsupported BMP bitfields layout") elif file_info["compression"] == self.RAW: if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" else: - raise IOError("Unsupported BMP compression (%d)" % file_info["compression"]) + raise OSError("Unsupported BMP compression (%d)" % file_info["compression"]) # --------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images # ---------------------------------------------------- 1-bit images if not (0 < file_info["colors"] <= 65536): - raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"]) + raise OSError("Unsupported BMP Palette size (%d)" % file_info["colors"]) else: padding = file_info["palette_padding"] palette = read(padding * file_info["colors"]) @@ -305,7 +305,7 @@ def _save(im, fp, filename, bitmap_header=True): try: rawmode, bits, colors = SAVE[im.mode] except KeyError: - raise IOError("cannot write mode %s as BMP" % im.mode) + raise OSError("cannot write mode %s as BMP" % im.mode) info = im.encoderinfo diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 56cac3bb1..48f21e1b3 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -60,7 +60,7 @@ class BufrStubImageFile(ImageFile.StubImageFile): def _save(im, fp, filename): if _handler is None or not hasattr("_handler", "save"): - raise IOError("BUFR save handler not installed") + raise OSError("BUFR save handler not installed") _handler.save(im, fp, filename) diff --git a/src/PIL/ContainerIO.py b/src/PIL/ContainerIO.py index 3cf9d82d2..9727601ab 100644 --- a/src/PIL/ContainerIO.py +++ b/src/PIL/ContainerIO.py @@ -21,7 +21,7 @@ import io -class ContainerIO(object): +class ContainerIO: def __init__(self, file, offset, length): """ Create file object. diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index 50bf1a356..3a1b6d2e5 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -15,9 +15,6 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import print_function - from . import BmpImagePlugin, Image from ._binary import i8, i16le as i16, i32le as i32 diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 28a582ec4..9ba6e0ff8 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -106,10 +106,10 @@ class DdsImageFile(ImageFile.ImageFile): def _open(self): magic, header_size = struct.unpack(" 2: - fp = io.TextIOWrapper(fp, encoding="latin-1") - wrapped_fp = True + fp = io.TextIOWrapper(fp, encoding="latin-1") + wrapped_fp = True try: if eps: diff --git a/src/PIL/FitsStubImagePlugin.py b/src/PIL/FitsStubImagePlugin.py index 7e6d35ee5..c2ce8651c 100644 --- a/src/PIL/FitsStubImagePlugin.py +++ b/src/PIL/FitsStubImagePlugin.py @@ -63,7 +63,7 @@ class FITSStubImageFile(ImageFile.StubImageFile): def _save(im, fp, filename): if _handler is None or not hasattr("_handler", "save"): - raise IOError("FITS save handler not installed") + raise OSError("FITS save handler not installed") _handler.save(im, fp, filename) diff --git a/src/PIL/FontFile.py b/src/PIL/FontFile.py index e57c2f3fd..979a1e33c 100644 --- a/src/PIL/FontFile.py +++ b/src/PIL/FontFile.py @@ -14,7 +14,6 @@ # See the README file for information on usage and redistribution. # -from __future__ import print_function import os @@ -35,7 +34,7 @@ def puti16(fp, values): # Base class for raster font file handlers. -class FontFile(object): +class FontFile: bitmap = None diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index dfb509e15..3938f0f09 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -14,9 +14,6 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import print_function - import olefile from . import Image, ImageFile @@ -62,7 +59,7 @@ class FpxImageFile(ImageFile.ImageFile): try: self.ole = olefile.OleFileIO(self.fp) - except IOError: + except OSError: raise SyntaxError("not an FPX file; invalid OLE file") if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": @@ -141,7 +138,7 @@ class FpxImageFile(ImageFile.ImageFile): length = i32(s, 32) if size != self.size: - raise IOError("subimage mismatch") + raise OSError("subimage mismatch") # get tile descriptors fp.seek(28 + offset) @@ -214,7 +211,7 @@ class FpxImageFile(ImageFile.ImageFile): self.tile_prefix = self.jpeg[jpeg_tables] else: - raise IOError("unknown/invalid compression") + raise OSError("unknown/invalid compression") x = x + xtile if x >= xsize: diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 2de56aadf..292de435c 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # # -# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for +# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for # format documentation. # # This code Interprets version 1 and 2 .gbr files. diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 80235bba6..54c88712c 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -23,7 +23,7 @@ # purposes only. -from . import ImageFile, ImagePalette +from . import ImageFile, ImagePalette, UnidentifiedImageError from ._binary import i8, i16be as i16, i32be as i32 ## @@ -82,4 +82,4 @@ def open(fp, mode="r"): try: return GdImageFile(fp) except SyntaxError: - raise IOError("cannot identify this image file") + raise UnidentifiedImageError("cannot identify this image file") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index ec0f1ee71..7717fc707 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -618,20 +618,20 @@ def _save_netpbm(im, fp, filename): with open(filename, "wb") as f: if im.mode != "RGB": - with open(os.devnull, "wb") as devnull: - subprocess.check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull) + subprocess.check_call( + ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL + ) else: # Pipe ppmquant output into ppmtogif # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) quant_cmd = ["ppmquant", "256", tempfile] togif_cmd = ["ppmtogif"] - with open(os.devnull, "wb") as devnull: - quant_proc = subprocess.Popen( - quant_cmd, stdout=subprocess.PIPE, stderr=devnull - ) - togif_proc = subprocess.Popen( - togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull - ) + quant_proc = subprocess.Popen( + quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + togif_proc = subprocess.Popen( + togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=subprocess.DEVNULL + ) # Allow ppmquant to receive SIGPIPE if ppmtogif exits quant_proc.stdout.close() @@ -848,7 +848,7 @@ def getdata(im, offset=(0, 0), **params): """ - class Collector(object): + class Collector: data = [] def write(self, data): diff --git a/src/PIL/GimpGradientFile.py b/src/PIL/GimpGradientFile.py index f48e7f76e..851e24d62 100644 --- a/src/PIL/GimpGradientFile.py +++ b/src/PIL/GimpGradientFile.py @@ -60,7 +60,7 @@ def sphere_decreasing(middle, pos): SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] -class GradientFile(object): +class GradientFile: gradient = None @@ -132,7 +132,7 @@ class GimpGradientFile(GradientFile): cspace = int(s[12]) if cspace != 0: - raise IOError("cannot handle HSV colour space") + raise OSError("cannot handle HSV colour space") gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index 2994bbeab..e3060ab8a 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -22,7 +22,7 @@ from ._binary import o8 # File handler for GIMP's palette format. -class GimpPaletteFile(object): +class GimpPaletteFile: rawmode = "RGB" diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index 8a24a9829..515c272f7 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -61,7 +61,7 @@ class GribStubImageFile(ImageFile.StubImageFile): def _save(im, fp, filename): if _handler is None or not hasattr("_handler", "save"): - raise IOError("GRIB save handler not installed") + raise OSError("GRIB save handler not installed") _handler.save(im, fp, filename) diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index a3ea12f99..362f2d399 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -60,7 +60,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile): def _save(im, fp, filename): if _handler is None or not hasattr("_handler", "save"): - raise IOError("HDF5 save handler not installed") + raise OSError("HDF5 save handler not installed") _handler.save(im, fp, filename) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 9cf7a90cd..18e8403d1 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -129,7 +129,7 @@ def read_png_or_jpeg2000(fobj, start_length, size): raise ValueError("Unsupported icon subimage format") -class IcnsFile(object): +class IcnsFile: SIZES = { (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], @@ -314,42 +314,40 @@ def _save(im, fp, filename): fp.flush() # create the temporary set of pngs - iconset = tempfile.mkdtemp(".iconset") - provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} - last_w = None - second_path = None - for w in [16, 32, 128, 256, 512]: - prefix = "icon_{}x{}".format(w, w) + with tempfile.TemporaryDirectory(".iconset") as iconset: + provided_images = { + im.width: im for im in im.encoderinfo.get("append_images", []) + } + last_w = None + second_path = None + for w in [16, 32, 128, 256, 512]: + prefix = "icon_{}x{}".format(w, w) - first_path = os.path.join(iconset, prefix + ".png") - if last_w == w: - shutil.copyfile(second_path, first_path) - else: - im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS)) - im_w.save(first_path) + first_path = os.path.join(iconset, prefix + ".png") + if last_w == w: + shutil.copyfile(second_path, first_path) + else: + im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS)) + im_w.save(first_path) - second_path = os.path.join(iconset, prefix + "@2x.png") - im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS)) - im_w2.save(second_path) - last_w = w * 2 + second_path = os.path.join(iconset, prefix + "@2x.png") + im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS)) + im_w2.save(second_path) + last_w = w * 2 - # iconutil -c icns -o {} {} + # iconutil -c icns -o {} {} - convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] - with open(os.devnull, "wb") as devnull: + convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] convert_proc = subprocess.Popen( - convert_cmd, stdout=subprocess.PIPE, stderr=devnull + convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) - convert_proc.stdout.close() + convert_proc.stdout.close() - retcode = convert_proc.wait() + retcode = convert_proc.wait() - # remove the temporary files - shutil.rmtree(iconset) - - if retcode: - raise subprocess.CalledProcessError(retcode, convert_cmd) + if retcode: + raise subprocess.CalledProcessError(retcode, convert_cmd) Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index 2e4ad1d65..19cb548a6 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -82,7 +82,7 @@ def _accept(prefix): return prefix[:4] == _MAGIC -class IcoFile(object): +class IcoFile: def __init__(self, buf): """ Parse image from file-like object containing ico file data diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f50b319d2..96ec314bc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -25,6 +25,7 @@ # import atexit +import builtins import io import logging import math @@ -34,29 +35,15 @@ import struct import sys import tempfile import warnings +from collections.abc import Callable, MutableMapping +from pathlib import Path # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 7.0.0. # Use __version__ instead. -from . import ImageMode, TiffTags, __version__, _plugins +from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins from ._binary import i8, i32le -from ._util import deferred_error, isPath, isStringType, py3 - -try: - import builtins -except ImportError: - import __builtin__ - - builtins = __builtin__ - - -try: - # Python 3 - from collections.abc import Callable, MutableMapping -except ImportError: - # Python 2.7 - from collections import Callable, MutableMapping - +from ._util import deferred_error, isPath logger = logging.getLogger(__name__) @@ -101,22 +88,6 @@ except ImportError as v: ) elif str(v).startswith("The _imaging extension"): warnings.warn(str(v), RuntimeWarning) - elif "Symbol not found: _PyUnicodeUCS2_" in str(v): - # should match _PyUnicodeUCS2_FromString and - # _PyUnicodeUCS2_AsLatin1String - warnings.warn( - "The _imaging extension was built for Python with UCS2 support; " - "recompile Pillow or build Python --without-wide-unicode. ", - RuntimeWarning, - ) - elif "Symbol not found: _PyUnicodeUCS4_" in str(v): - # should match _PyUnicodeUCS4_FromString and - # _PyUnicodeUCS4_AsLatin1String - warnings.warn( - "The _imaging extension was built for Python with UCS4 support; " - "recompile Pillow or build Python --with-wide-unicode. ", - RuntimeWarning, - ) # Fail here anyway. Don't let people run with a mostly broken Pillow. # see docs/porting.rst raise @@ -129,18 +100,6 @@ try: except ImportError: cffi = None -try: - from pathlib import Path - - HAS_PATHLIB = True -except ImportError: - try: - from pathlib2 import Path - - HAS_PATHLIB = True - except ImportError: - HAS_PATHLIB = False - def isImageType(t): """ @@ -447,7 +406,7 @@ def _getdecoder(mode, decoder_name, args, extra=()): decoder = getattr(core, decoder_name + "_decoder") return decoder(mode, *args + extra) except AttributeError: - raise IOError("decoder %s not available" % decoder_name) + raise OSError("decoder %s not available" % decoder_name) def _getencoder(mode, encoder_name, args, extra=()): @@ -468,7 +427,7 @@ def _getencoder(mode, encoder_name, args, extra=()): encoder = getattr(core, encoder_name + "_encoder") return encoder(mode, *args + extra) except AttributeError: - raise IOError("encoder %s not available" % encoder_name) + raise OSError("encoder %s not available" % encoder_name) # -------------------------------------------------------------------- @@ -479,7 +438,7 @@ def coerce_e(value): return value if isinstance(value, _E) else _E(value) -class _E(object): +class _E: def __init__(self, data): self.data = data @@ -520,7 +479,7 @@ def _getscaleoffset(expr): # Implementation wrapper -class Image(object): +class Image: """ This class represents an image object. To create :py:class:`~PIL.Image.Image` objects, use the appropriate factory @@ -662,10 +621,6 @@ class Image(object): and self.tobytes() == other.tobytes() ) - def __ne__(self, other): - eq = self == other - return not eq - def __repr__(self): return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( self.__class__.__module__, @@ -1335,10 +1290,7 @@ class Image(object): self.load() try: - if py3: - return list(self.im.getpalette()) - else: - return [i8(c) for c in self.im.getpalette()] + return list(self.im.getpalette()) except ValueError: return None # no palette @@ -1489,7 +1441,7 @@ class Image(object): raise ValueError("cannot determine region size; use 4-item box") box += (box[0] + size[0], box[1] + size[1]) - if isStringType(im): + if isinstance(im, str): from . import ImageColor im = ImageColor.getcolor(im, self.mode) @@ -1689,10 +1641,7 @@ class Image(object): palette = ImagePalette.raw(data.rawmode, data.palette) else: if not isinstance(data, bytes): - if py3: - data = bytes(data) - else: - data = "".join(chr(x) for x in data) + data = bytes(data) palette = ImagePalette.raw(rawmode, data) self.mode = "PA" if "A" in self.mode else "P" self.palette = palette @@ -2024,7 +1973,7 @@ class Image(object): if isPath(fp): filename = fp open_fp = True - elif HAS_PATHLIB and isinstance(fp, Path): + elif isinstance(fp, Path): filename = str(fp) open_fp = True if not filename and hasattr(fp, "name") and isPath(fp.name): @@ -2146,7 +2095,7 @@ class Image(object): """ self.load() - if isStringType(channel): + if isinstance(channel, str): try: channel = self.getbands().index(channel) except ValueError: @@ -2411,12 +2360,12 @@ class Image(object): # Abstract handlers. -class ImagePointHandler(object): +class ImagePointHandler: # used as a mixin by point transforms (for use with im.point) pass -class ImageTransformHandler(object): +class ImageTransformHandler: # used as a mixin by geometry transforms (for use with im.transform) pass @@ -2474,7 +2423,7 @@ def new(mode, size, color=0): # don't initialize return Image()._new(core.new(mode, size)) - if isStringType(color): + if isinstance(color, str): # css3-style specifier from . import ImageColor @@ -2621,9 +2570,12 @@ def fromarray(obj, mode=None): if mode is None: try: typekey = (1, 1) + shape[2:], arr["typestr"] - mode, rawmode = _fromarray_typemap[typekey] except KeyError: raise TypeError("Cannot handle this data type") + try: + mode, rawmode = _fromarray_typemap[typekey] + except KeyError: + raise TypeError("Cannot handle this data type: %s, %s" % typekey) else: rawmode = mode if mode in ["1", "L", "I", "P", "F"]: @@ -2736,7 +2688,7 @@ def open(fp, mode="r"): exclusive_fp = False filename = "" - if HAS_PATHLIB and isinstance(fp, Path): + if isinstance(fp, Path): filename = str(fp.resolve()) elif isPath(fp): filename = fp @@ -2794,7 +2746,9 @@ def open(fp, mode="r"): fp.close() for message in accept_warnings: warnings.warn(message) - raise IOError("cannot identify image file %r" % (filename if filename else fp)) + raise UnidentifiedImageError( + "cannot identify image file %r" % (filename if filename else fp) + ) # @@ -3300,11 +3254,6 @@ class Exif(MutableMapping): def __contains__(self, tag): return tag in self._data or (self._info is not None and tag in self._info) - if not py3: - - def has_key(self, tag): - return tag in self - def __setitem__(self, tag, value): if self._info is not None and tag in self._info: del self._info[tag] diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ed4eefc0d..d05b3102c 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -15,12 +15,9 @@ # See the README file for information on usage and redistribution. See # below for the original description. -from __future__ import print_function - import sys from PIL import Image -from PIL._util import isStringType try: from PIL import _imagingcms @@ -152,7 +149,7 @@ for flag in FLAGS.values(): # Profile. -class ImageCmsProfile(object): +class ImageCmsProfile: def __init__(self, profile): """ :param profile: Either a string representing a filename, @@ -161,7 +158,7 @@ class ImageCmsProfile(object): """ - if isStringType(profile): + if isinstance(profile, str): self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): self._set(core.profile_frombytes(profile.read())) @@ -374,7 +371,7 @@ def profileToProfile( imOut = None else: imOut = transform.apply(im) - except (IOError, TypeError, ValueError) as v: + except (OSError, TypeError, ValueError) as v: raise PyCMSError(v) return imOut @@ -398,7 +395,7 @@ def getOpenProfile(profileFilename): try: return ImageCmsProfile(profileFilename) - except (IOError, TypeError, ValueError) as v: + except (OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -479,7 +476,7 @@ def buildTransform( return ImageCmsTransform( inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags ) - except (IOError, TypeError, ValueError) as v: + except (OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -590,7 +587,7 @@ def buildProofTransform( proofRenderingIntent, flags, ) - except (IOError, TypeError, ValueError) as v: + except (OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -733,9 +730,9 @@ def getProfileName(profile): return (profile.profile.profile_description or "") + "\n" if not manufacturer or len(model) > 30: return model + "\n" - return "%s - %s\n" % (model, manufacturer) + return "{} - {}\n".format(model, manufacturer) - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -775,7 +772,7 @@ def getProfileInfo(profile): arr.append(elt) return "\r\n\r\n".join(arr) + "\r\n\r\n" - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -803,7 +800,7 @@ def getProfileCopyright(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return (profile.profile.copyright or "") + "\n" - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -831,7 +828,7 @@ def getProfileManufacturer(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return (profile.profile.manufacturer or "") + "\n" - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -860,7 +857,7 @@ def getProfileModel(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return (profile.profile.model or "") + "\n" - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -889,7 +886,7 @@ def getProfileDescription(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return (profile.profile.profile_description or "") + "\n" - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -928,7 +925,7 @@ def getDefaultIntent(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.rendering_intent - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -979,7 +976,7 @@ def isIntentSupported(profile, intent, direction): return 1 else: return -1 - except (AttributeError, IOError, TypeError, ValueError) as v: + except (AttributeError, OSError, TypeError, ValueError) as v: raise PyCMSError(v) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index ed3383f05..c6e12150e 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -34,7 +34,6 @@ import math import numbers from . import Image, ImageColor -from ._util import isStringType """ @@ -45,7 +44,7 @@ directly. """ -class ImageDraw(object): +class ImageDraw: def __init__(self, im, mode=None): """ Create a drawing instance. @@ -107,13 +106,13 @@ class ImageDraw(object): ink = self.ink else: if ink is not None: - if isStringType(ink): + if isinstance(ink, str): ink = ImageColor.getcolor(ink, self.mode) if self.palette and not isinstance(ink, numbers.Number): ink = self.palette.getcolor(ink) ink = self.draw.draw_ink(ink) if fill is not None: - if isStringType(fill): + if isinstance(fill, str): fill = ImageColor.getcolor(fill, self.mode) if self.palette and not isinstance(fill, numbers.Number): fill = self.palette.getcolor(fill) @@ -314,7 +313,7 @@ class ImageDraw(object): language=language, stroke_width=stroke_width, *args, - **kwargs + **kwargs, ) coord = coord[0] + offset[0], coord[1] + offset[1] except AttributeError: @@ -327,7 +326,7 @@ class ImageDraw(object): language, stroke_width, *args, - **kwargs + **kwargs, ) except TypeError: mask = font.getmask(text) diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 324d869f0..20b5fe4c4 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -19,25 +19,25 @@ from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath -class Pen(object): +class Pen: def __init__(self, color, width=1, opacity=255): self.color = ImageColor.getrgb(color) self.width = width -class Brush(object): +class Brush: def __init__(self, color, opacity=255): self.color = ImageColor.getrgb(color) -class Font(object): +class Font: def __init__(self, color, file, size=12): # FIXME: add support for bitmap fonts self.color = ImageColor.getrgb(color) self.font = ImageFont.truetype(file, size) -class Draw(object): +class Draw: def __init__(self, image, size=None, color=None): if not hasattr(image, "im"): image = Image.new(image, size, color) diff --git a/src/PIL/ImageEnhance.py b/src/PIL/ImageEnhance.py index 534eb4f16..3b79d5c46 100644 --- a/src/PIL/ImageEnhance.py +++ b/src/PIL/ImageEnhance.py @@ -21,7 +21,7 @@ from . import Image, ImageFilter, ImageStat -class _Enhance(object): +class _Enhance: def enhance(self, factor): """ Returns an enhanced image. diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 7d5229b8e..a343bd43c 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -56,7 +56,7 @@ def raise_ioerror(error): message = ERRORS.get(error) if not message: message = "decoder error %d" % error - raise IOError(message + " when reading image file") + raise OSError(message + " when reading image file") # @@ -148,7 +148,7 @@ class ImageFile(Image.Image): pixel = Image.Image.load(self) if self.tile is None: - raise IOError("cannot load this image") + raise OSError("cannot load this image") if not self.tile: return pixel @@ -206,7 +206,7 @@ class ImageFile(Image.Image): # we might need to reload the palette data. if self.palette: self.palette.dirty = 1 - except (AttributeError, EnvironmentError, ImportError): + except (AttributeError, OSError, ImportError): self.map = None self.load_prepare() @@ -241,13 +241,13 @@ class ImageFile(Image.Image): if LOAD_TRUNCATED_IMAGES: break else: - raise IOError("image file is truncated") + raise OSError("image file is truncated") if not s: # truncated jpeg if LOAD_TRUNCATED_IMAGES: break else: - raise IOError( + raise OSError( "image file is truncated " "(%d bytes not processed)" % len(b) ) @@ -325,7 +325,7 @@ class StubImageFile(ImageFile): def load(self): loader = self._load() if loader is None: - raise IOError("cannot find loader for this %s file" % self.format) + raise OSError("cannot find loader for this %s file" % self.format) image = loader.load(self) assert image is not None # become the other object (!) @@ -337,7 +337,7 @@ class StubImageFile(ImageFile): raise NotImplementedError("StubImageFile subclass must implement _load") -class Parser(object): +class Parser: """ Incremental image parser. This class implements the standard feed/close consumer interface. @@ -414,7 +414,7 @@ class Parser(object): try: with io.BytesIO(self.data) as fp: im = Image.open(fp) - except IOError: + except OSError: # traceback.print_exc() pass # not enough data else: @@ -459,9 +459,9 @@ class Parser(object): self.feed(b"") self.data = self.decoder = None if not self.finished: - raise IOError("image was incomplete") + raise OSError("image was incomplete") if not self.image: - raise IOError("cannot parse this image") + raise OSError("cannot parse this image") if self.data: # incremental parsing not possible; reopen the file # not that we have all data @@ -517,7 +517,7 @@ def _save(im, fp, tile, bufsize=0): if s: break if s < 0: - raise IOError("encoder error %d when writing image file" % s) + raise OSError("encoder error %d when writing image file" % s) e.cleanup() else: # slight speedup: compress to real file object @@ -532,7 +532,7 @@ def _save(im, fp, tile, bufsize=0): else: s = e.encode_to_file(fh, bufsize) if s < 0: - raise IOError("encoder error %d when writing image file" % s) + raise OSError("encoder error %d when writing image file" % s) e.cleanup() if hasattr(fp, "flush"): fp.flush() @@ -562,7 +562,7 @@ def _safe_read(fp, size): return b"".join(data) -class PyCodecState(object): +class PyCodecState: def __init__(self): self.xsize = 0 self.ysize = 0 @@ -573,7 +573,7 @@ class PyCodecState(object): return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize) -class PyDecoder(object): +class PyDecoder: """ Python implementation of a format decoder. Override this class and add the decoding logic in the `decode` method. diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index fa4162b61..6b0f5eb37 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -14,9 +14,6 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import division - import functools try: @@ -25,7 +22,7 @@ except ImportError: # pragma: no cover numpy = None -class Filter(object): +class Filter: pass @@ -498,7 +495,7 @@ class Color3DLUT(MultibandFilter): r / (size1D - 1), g / (size2D - 1), b / (size3D - 1), - *values + *values, ) else: values = callback(*values) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 2546d2aa8..619800829 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -31,13 +31,13 @@ import sys from io import BytesIO from . import Image -from ._util import isDirectory, isPath, py3 +from ._util import isDirectory, isPath LAYOUT_BASIC = 0 LAYOUT_RAQM = 1 -class _imagingft_not_installed(object): +class _imagingft_not_installed: # module placeholder def __getattr__(self, id): raise ImportError("The _imagingft C module is not installed") @@ -65,7 +65,7 @@ except ImportError: # -------------------------------------------------------------------- -class ImageFont(object): +class ImageFont: "PIL font wrapper" def _load_pilfont(self, filename): @@ -81,7 +81,7 @@ class ImageFont(object): if image and image.mode in ("1", "L"): break else: - raise IOError("cannot find glyph data file") + raise OSError("cannot find glyph data file") self.file = fullname @@ -147,7 +147,7 @@ class ImageFont(object): # truetype factory function to create font objects. -class FreeTypeFont(object): +class FreeTypeFont: "FreeType font wrapper (requires _imagingft service)" def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): @@ -544,7 +544,7 @@ class FreeTypeFont(object): raise NotImplementedError("FreeType 2.9.1 or greater is required") -class TransposedFont(object): +class TransposedFont: "Wrapper for writing rotated or mirrored text" def __init__(self, font, orientation=None): @@ -640,7 +640,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): try: return freetype(font) - except IOError: + except OSError: if not isPath(font): raise ttf_filename = os.path.basename(font) @@ -697,15 +697,12 @@ def load_path(filename): for directory in sys.path: if isDirectory(directory): if not isinstance(filename, str): - if py3: - filename = filename.decode("utf-8") - else: - filename = filename.encode("utf-8") + filename = filename.decode("utf-8") try: return load(os.path.join(directory, filename)) - except IOError: + except OSError: pass - raise IOError("cannot find font file") + raise OSError("cannot find font file") def load_default(): diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 392151c10..adbb94000 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -15,15 +15,9 @@ # See the README file for information on usage and redistribution. # +import builtins + from . import Image, _imagingmath -from ._util import py3 - -try: - import builtins -except ImportError: - import __builtin__ - - builtins = __builtin__ VERBOSE = 0 @@ -32,7 +26,7 @@ def _isconstant(v): return isinstance(v, (int, float)) -class _Operand(object): +class _Operand: """Wraps an image operand, providing standard operators""" def __init__(self, im): @@ -101,11 +95,6 @@ class _Operand(object): # an image is "true" if it contains at least one non-zero pixel return self.im.getbbox() is not None - if not py3: - # Provide __nonzero__ for pre-Py3k - __nonzero__ = __bool__ - del __bool__ - def __abs__(self): return self.apply("abs", self) @@ -152,13 +141,6 @@ class _Operand(object): def __rpow__(self, other): return self.apply("pow", other, self) - if not py3: - # Provide __div__ and __rdiv__ for pre-Py3k - __div__ = __truediv__ - __rdiv__ = __rtruediv__ - del __truediv__ - del __rtruediv__ - # bitwise def __invert__(self): return self.apply("invert", self) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 596be7b9d..988288329 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -17,7 +17,7 @@ _modes = None -class ModeDescriptor(object): +class ModeDescriptor: """Wrapper for mode strings.""" def __init__(self, mode, bands, basemode, basetype): diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index 61199234b..d1ec09eac 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -5,8 +5,6 @@ # # Copyright (c) 2014 Dov Grobgeld -from __future__ import print_function - import re from . import Image, _imagingmorph @@ -27,7 +25,7 @@ MIRROR_MATRIX = [ # fmt: on -class LutBuilder(object): +class LutBuilder: """A class for building a MorphLut from a descriptive language The input patterns is a list of a strings sequences like these:: @@ -178,7 +176,7 @@ class LutBuilder(object): return self.lut -class MorphOp(object): +class MorphOp: """A class for binary morphological operators""" def __init__(self, lut=None, op_name=None, patterns=None): diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 5052cb74d..3ffe50806 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -21,7 +21,6 @@ import functools import operator from . import Image -from ._util import isStringType # # helpers @@ -39,7 +38,7 @@ def _border(border): def _color(color, mode): - if isStringType(color): + if isinstance(color, str): from . import ImageColor color = ImageColor.getcolor(color, mode) @@ -55,7 +54,7 @@ def _lut(image, lut): lut = lut + lut + lut return image.point(lut) else: - raise IOError("not supported for this image mode") + raise OSError("not supported for this image mode") # diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 2d4f5cb6b..e0d439c98 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -21,7 +21,7 @@ import array from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile -class ImagePalette(object): +class ImagePalette: """ Color palette for palette mapped images @@ -216,6 +216,6 @@ def load(filename): # traceback.print_exc() pass else: - raise IOError("cannot load palette") + raise OSError("cannot load palette") return lut # data, rawmode diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index f49725b5f..dfe2f80bd 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -20,7 +20,7 @@ import sys from io import BytesIO from . import Image -from ._util import isPath, py3 +from ._util import isPath qt_versions = [["5", "PyQt5"], ["side2", "PySide2"]] @@ -65,11 +65,7 @@ def fromqimage(im): im.save(buffer, "ppm") b = BytesIO() - try: - b.write(buffer.data()) - except TypeError: - # workaround for Python 2 - b.write(str(buffer.data())) + b.write(buffer.data()) buffer.close() b.seek(0) @@ -125,10 +121,7 @@ def _toqclass_helper(im): # handle filename, if given instead of image name if hasattr(im, "toUtf8"): # FIXME - is this really the best way to do this? - if py3: - im = str(im.toUtf8(), "utf-8") - else: - im = unicode(im.toUtf8(), "utf-8") # noqa: F821 + im = str(im.toUtf8(), "utf-8") if isPath(im): im = Image.open(im) diff --git a/src/PIL/ImageSequence.py b/src/PIL/ImageSequence.py index f9be92d48..4e9f5c210 100644 --- a/src/PIL/ImageSequence.py +++ b/src/PIL/ImageSequence.py @@ -16,7 +16,7 @@ ## -class Iterator(object): +class Iterator: """ This class implements an iterator object that can be used to loop over an image sequence. @@ -52,9 +52,6 @@ class Iterator(object): except EOFError: raise StopIteration - def next(self): - return self.__next__() - def all_frames(im, func=None): """ diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index ca622c525..f7e809279 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -11,21 +11,15 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import print_function - import os +import shutil import subprocess import sys import tempfile +from shlex import quote from PIL import Image -if sys.version_info.major >= 3: - from shlex import quote -else: - from pipes import quote - _viewers = [] @@ -56,7 +50,7 @@ def show(image, title=None, **options): return 0 -class Viewer(object): +class Viewer: """Base class for viewers.""" # main api @@ -127,10 +121,8 @@ elif sys.platform == "darwin": # on darwin open returns immediately resulting in the temp # file removal while app is opening command = "open -a Preview.app" - command = "(%s %s; sleep 20; rm -f %s)&" % ( - command, - quote(file), - quote(file), + command = "({} {}; sleep 20; rm -f {})&".format( + command, quote(file), quote(file) ) return command @@ -154,23 +146,13 @@ else: # unixoids - def which(executable): - path = os.environ.get("PATH") - if not path: - return None - for dirname in path.split(os.pathsep): - filename = os.path.join(dirname, executable) - if os.path.isfile(filename) and os.access(filename, os.X_OK): - return filename - return None - class UnixViewer(Viewer): format = "PNG" options = {"compress_level": 1} def get_command(self, file, **options): command = self.get_command_ex(file, **options)[0] - return "(%s %s; rm -f %s)&" % (command, quote(file), quote(file)) + return "({} {}; rm -f {})&".format(command, quote(file), quote(file)) def show_file(self, file, **options): """Display given file""" @@ -192,7 +174,7 @@ else: command = executable = "display" return command, executable - if which("display"): + if shutil.which("display"): register(DisplayViewer) class EogViewer(UnixViewer): @@ -200,7 +182,7 @@ else: command = executable = "eog" return command, executable - if which("eog"): + if shutil.which("eog"): register(EogViewer) class XVViewer(UnixViewer): @@ -212,7 +194,7 @@ else: command += " -name %s" % quote(title) return command, executable - if which("xv"): + if shutil.which("xv"): register(XVViewer) if __name__ == "__main__": diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index 9ba16fd85..50bafc972 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -26,7 +26,7 @@ import math import operator -class Stat(object): +class Stat: def __init__(self, image_or_list, mask=None): try: if mask: diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index bb825f353..ee707cffb 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -25,17 +25,11 @@ # See the README file for information on usage and redistribution. # -import sys +import tkinter from io import BytesIO from . import Image -if sys.version_info.major > 2: - import tkinter -else: - import Tkinter as tkinter - - # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -68,7 +62,7 @@ def _get_image_from_kw(kw): # PhotoImage -class PhotoImage(object): +class PhotoImage: """ A Tkinter-compatible photo image. This can be used everywhere Tkinter expects an image object. If the image is an RGBA @@ -209,7 +203,7 @@ class PhotoImage(object): # BitmapImage -class BitmapImage(object): +class BitmapImage: """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. @@ -299,7 +293,7 @@ def _show(image, title): super().__init__(master, image=self.image, bg="black", bd=0) if not tkinter._default_root: - raise IOError("tkinter not initialized") + raise OSError("tkinter not initialized") top = tkinter.Toplevel() if title: top.title(title) diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index c654569c5..927b1694b 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -20,7 +20,7 @@ from . import Image -class HDC(object): +class HDC: """ Wraps an HDC integer. The resulting object can be passed to the :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` @@ -34,7 +34,7 @@ class HDC(object): return self.dc -class HWND(object): +class HWND: """ Wraps an HWND integer. The resulting object can be passed to the :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` @@ -48,7 +48,7 @@ class HWND(object): return self.wnd -class Dib(object): +class Dib: """ A Windows bitmap with the given mode and size. The mode can be one of "1", "L", "P", or "RGB". @@ -186,7 +186,7 @@ class Dib(object): return self.image.tobytes() -class Window(object): +class Window: """Create a Window with the given title size.""" def __init__(self, title="PIL", width=None, height=None): diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index cb39aab83..86a7ee8cb 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -14,9 +14,6 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import print_function - import os import tempfile @@ -71,7 +68,7 @@ class IptcImageFile(ImageFile.ImageFile): # field size size = i8(s[3]) if size > 132: - raise IOError("illegal field length in IPTC/NAA file") + raise OSError("illegal field length in IPTC/NAA file") elif size == 128: size = 0 elif size > 128: @@ -122,7 +119,7 @@ class IptcImageFile(ImageFile.ImageFile): try: compression = COMPRESSION[self.getint((3, 120))] except KeyError: - raise IOError("Unknown IPTC image compression") + raise OSError("Unknown IPTC image compression") # tile if tag == (8, 10): @@ -211,7 +208,7 @@ def getiptcinfo(im): return None # no properties # create an IptcImagePlugin object without initializing it - class FakeImage(object): + class FakeImage: pass im = FakeImage() diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 42ac025b3..4286e1ae7 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -31,9 +31,6 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import print_function - import array import io import os @@ -44,7 +41,6 @@ import warnings from . import Image, ImageFile, TiffImagePlugin from ._binary import i8, i16be as i16, i32be as i32, o8 -from ._util import isStringType from .JpegPresets import presets # @@ -113,7 +109,10 @@ def APP(self, marker): while blocks[offset : offset + 4] == b"8BIM": offset += 4 # resource code - code = i16(blocks, offset) + try: + code = i16(blocks, offset) + except struct.error: + break offset += 2 # resource name (usually empty) name_len = i8(blocks[offset]) @@ -612,7 +611,7 @@ def _save(im, fp, filename): try: rawmode = RAWMODE[im.mode] except KeyError: - raise IOError("cannot write mode %s as JPEG" % im.mode) + raise OSError("cannot write mode %s as JPEG" % im.mode) info = im.encoderinfo @@ -636,7 +635,7 @@ def _save(im, fp, filename): else: if subsampling in presets: subsampling = presets[subsampling].get("subsampling", -1) - if isStringType(qtables) and qtables in presets: + if isinstance(qtables, str) and qtables in presets: qtables = presets[qtables].get("quantization") if subsampling == "4:4:4": @@ -657,7 +656,7 @@ def _save(im, fp, filename): def validate_qtables(qtables): if qtables is None: return qtables - if isStringType(qtables): + if isinstance(qtables, str): try: lines = [ int(num) diff --git a/src/PIL/JpegPresets.py b/src/PIL/JpegPresets.py index 387844f8e..012bf81b0 100644 --- a/src/PIL/JpegPresets.py +++ b/src/PIL/JpegPresets.py @@ -33,7 +33,10 @@ Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and 4:2:0. You can get the subsampling of a JPEG with the -`JpegImagePlugin.get_subsampling(im)` function. +`JpegImagePlugin.get_sampling(im)` function. + +In JPEG compressed data a JPEG marker is used instead of an EXIF tag. +(ref.: https://www.exiv2.org/tags.html) Quantization tables diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 5396b2014..8610988fc 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -46,7 +46,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: self.ole = olefile.OleFileIO(self.fp) - except IOError: + except OSError: raise SyntaxError("not an MIC file; invalid OLE file") # find ACI subfiles with Image members (maybe not the diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py index 5988c5be2..a358dfdce 100644 --- a/src/PIL/MpegImagePlugin.py +++ b/src/PIL/MpegImagePlugin.py @@ -21,7 +21,7 @@ from ._binary import i8 # Bitstream parser -class BitStream(object): +class BitStream: def __init__(self, fp): self.fp = fp self.bits = 0 diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index a63e76fe7..e97176d57 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -82,7 +82,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): self.offset = self.__mpoffsets[frame] self.fp.seek(self.offset + 2) # skip SOI marker - if i16(self.fp.read(2)) == 0xFFE1: # APP1 + segment = self.fp.read(2) + if not segment: + raise ValueError("No data found for frame") + if i16(segment) == 0xFFE1: # APP1 n = i16(self.fp.read(2)) - 2 self.info["exif"] = ImageFile._safe_read(self.fp, n) diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 21e71e78b..2b2937ecf 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -117,7 +117,7 @@ class MspDecoder(ImageFile.PyDecoder): "<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2) ) except struct.error: - raise IOError("Truncated MSP file in row map") + raise OSError("Truncated MSP file in row map") for x, rowlen in enumerate(rowmap): try: @@ -126,7 +126,7 @@ class MspDecoder(ImageFile.PyDecoder): continue row = self.fd.read(rowlen) if len(row) != rowlen: - raise IOError( + raise OSError( "Truncated MSP file, expected %d bytes on row %s", (rowlen, x) ) idx = 0 @@ -143,7 +143,7 @@ class MspDecoder(ImageFile.PyDecoder): idx += runcount except struct.error: - raise IOError("Corrupted MSP file in row %d" % x) + raise OSError("Corrupted MSP file in row %d" % x) self.set_as_raw(img.getvalue(), ("1", 0, 1)) @@ -160,7 +160,7 @@ Image.register_decoder("MSP", MspDecoder) def _save(im, fp, filename): if im.mode != "1": - raise IOError("cannot write mode %s as MSP" % im.mode) + raise OSError("cannot write mode %s as MSP" % im.mode) # create MSP header header = [0] * 16 diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index f37701ce9..90bcad036 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -18,13 +18,12 @@ import sys from . import EpsImagePlugin -from ._util import py3 ## # Simple Postscript graphics interface. -class PSDraw(object): +class PSDraw: """ Sets up printing to the given file. If **fp** is omitted, :py:attr:`sys.stdout` is assumed. @@ -36,7 +35,7 @@ class PSDraw(object): self.fp = fp def _fp_write(self, to_write): - if not py3 or self.fp == sys.stdout: + if self.fp == sys.stdout: self.fp.write(to_write) else: self.fp.write(bytes(to_write, "UTF-8")) @@ -72,7 +71,7 @@ class PSDraw(object): """ if font not in self.isofont: # reencode font - self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font, font)) + self._fp_write("/PSDraw-{} ISOLatin1Encoding /{} E\n".format(font, font)) self.isofont[font] = 1 # rough self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font)) @@ -133,12 +132,12 @@ class PSDraw(object): y = ymax dx = (xmax - x) / 2 + box[0] dy = (ymax - y) / 2 + box[1] - self._fp_write("gsave\n%f %f translate\n" % (dx, dy)) + self._fp_write("gsave\n{:f} {:f} translate\n".format(dx, dy)) if (x, y) != im.size: # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) sx = x / im.size[0] sy = y / im.size[1] - self._fp_write("%f %f scale\n" % (sx, sy)) + self._fp_write("{:f} {:f} scale\n".format(sx, sy)) EpsImagePlugin._save(im, self.fp, None, 0) self._fp_write("\ngrestore\n") diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index ab22d5f0c..73f1b4b27 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -19,7 +19,7 @@ from ._binary import o8 # File handler for Teragon-style palette files. -class PaletteFile(object): +class PaletteFile: rawmode = "RGB" diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 62f13f535..804ece34a 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -137,7 +137,7 @@ def _save(im, fp, filename): bpp = im.info["bpp"] im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval)) else: - raise IOError("cannot write mode %s as Palm" % im.mode) + raise OSError("cannot write mode %s as Palm" % im.mode) # we ignore the palette here im.mode = "P" @@ -153,7 +153,7 @@ def _save(im, fp, filename): else: - raise IOError("cannot write mode %s as Palm" % im.mode) + raise OSError("cannot write mode %s as Palm" % im.mode) # # make sure image data is available diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index 7b3075696..605cbbdf3 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -184,7 +184,7 @@ class PcfFontFile(FontFile.FontFile): nbitmaps = i32(fp.read(4)) if nbitmaps != len(metrics): - raise IOError("Wrong number of bitmaps") + raise OSError("Wrong number of bitmaps") offsets = [] for i in range(nbitmaps): diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index 6768a38f9..6cf10deb3 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -103,7 +103,7 @@ class PcxImageFile(ImageFile.ImageFile): rawmode = "RGB;L" else: - raise IOError("unknown PCX mode") + raise OSError("unknown PCX mode") self.mode = mode self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 0ec6bba14..3267ee491 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -7,24 +7,9 @@ import re import time import zlib -from ._util import py3 -try: - from UserDict import UserDict # Python 2.x -except ImportError: - UserDict = collections.UserDict # Python 3.x - - -if py3: # Python 3.x - - def make_bytes(s): - return s.encode("us-ascii") - - -else: # Python 2.x - - def make_bytes(s): # pragma: no cover - return s # pragma: no cover +def make_bytes(s): + return s.encode("us-ascii") # see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set @@ -34,57 +19,55 @@ def encode_text(s): PDFDocEncoding = { - 0x16: u"\u0017", - 0x18: u"\u02D8", - 0x19: u"\u02C7", - 0x1A: u"\u02C6", - 0x1B: u"\u02D9", - 0x1C: u"\u02DD", - 0x1D: u"\u02DB", - 0x1E: u"\u02DA", - 0x1F: u"\u02DC", - 0x80: u"\u2022", - 0x81: u"\u2020", - 0x82: u"\u2021", - 0x83: u"\u2026", - 0x84: u"\u2014", - 0x85: u"\u2013", - 0x86: u"\u0192", - 0x87: u"\u2044", - 0x88: u"\u2039", - 0x89: u"\u203A", - 0x8A: u"\u2212", - 0x8B: u"\u2030", - 0x8C: u"\u201E", - 0x8D: u"\u201C", - 0x8E: u"\u201D", - 0x8F: u"\u2018", - 0x90: u"\u2019", - 0x91: u"\u201A", - 0x92: u"\u2122", - 0x93: u"\uFB01", - 0x94: u"\uFB02", - 0x95: u"\u0141", - 0x96: u"\u0152", - 0x97: u"\u0160", - 0x98: u"\u0178", - 0x99: u"\u017D", - 0x9A: u"\u0131", - 0x9B: u"\u0142", - 0x9C: u"\u0153", - 0x9D: u"\u0161", - 0x9E: u"\u017E", - 0xA0: u"\u20AC", + 0x16: "\u0017", + 0x18: "\u02D8", + 0x19: "\u02C7", + 0x1A: "\u02C6", + 0x1B: "\u02D9", + 0x1C: "\u02DD", + 0x1D: "\u02DB", + 0x1E: "\u02DA", + 0x1F: "\u02DC", + 0x80: "\u2022", + 0x81: "\u2020", + 0x82: "\u2021", + 0x83: "\u2026", + 0x84: "\u2014", + 0x85: "\u2013", + 0x86: "\u0192", + 0x87: "\u2044", + 0x88: "\u2039", + 0x89: "\u203A", + 0x8A: "\u2212", + 0x8B: "\u2030", + 0x8C: "\u201E", + 0x8D: "\u201C", + 0x8E: "\u201D", + 0x8F: "\u2018", + 0x90: "\u2019", + 0x91: "\u201A", + 0x92: "\u2122", + 0x93: "\uFB01", + 0x94: "\uFB02", + 0x95: "\u0141", + 0x96: "\u0152", + 0x97: "\u0160", + 0x98: "\u0178", + 0x99: "\u017D", + 0x9A: "\u0131", + 0x9B: "\u0142", + 0x9C: "\u0153", + 0x9D: "\u0161", + 0x9E: "\u017E", + 0xA0: "\u20AC", } def decode_text(b): if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") - elif py3: # Python 3.x + else: return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) - else: # Python 2.x - return u"".join(PDFDocEncoding.get(ord(byte), byte) for byte in b) class PdfFormatError(RuntimeError): @@ -247,21 +230,15 @@ class PdfName: def from_pdf_stream(cls, data): return cls(PdfParser.interpret_name(data)) - allowed_chars = set(range(33, 127)) - set(ord(c) for c in "#%/()<>[]{}") + allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} def __bytes__(self): result = bytearray(b"/") for b in self.name: - if py3: # Python 3.x - if b in self.allowed_chars: - result.append(b) - else: - result.extend(make_bytes("#%02X" % b)) - else: # Python 2.x - if ord(b) in self.allowed_chars: - result.append(b) - else: - result.extend(b"#%02X" % ord(b)) + if b in self.allowed_chars: + result.append(b) + else: + result.extend(make_bytes("#%02X" % b)) return bytes(result) __str__ = __bytes__ @@ -274,13 +251,10 @@ class PdfArray(list): __str__ = __bytes__ -class PdfDict(UserDict): +class PdfDict(collections.UserDict): def __setattr__(self, key, value): if key == "data": - if hasattr(UserDict, "__setattr__"): - UserDict.__setattr__(self, key, value) - else: - self.__dict__[key] = value + collections.UserDict.__setattr__(self, key, value) else: self[key.encode("us-ascii")] = value @@ -324,23 +298,13 @@ class PdfDict(UserDict): out.extend(b"\n>>") return bytes(out) - if not py3: - __str__ = __bytes__ - class PdfBinary: def __init__(self, data): self.data = data - if py3: # Python 3.x - - def __bytes__(self): - return make_bytes("<%s>" % "".join("%02X" % b for b in self.data)) - - else: # Python 2.x - - def __str__(self): - return "<%s>" % "".join("%02X" % ord(b) for b in self.data) + def __bytes__(self): + return make_bytes("<%s>" % "".join("%02X" % b for b in self.data)) class PdfStream: @@ -382,9 +346,7 @@ def pdf_repr(x): return bytes(PdfDict(x)) elif isinstance(x, list): return bytes(PdfArray(x)) - elif (py3 and isinstance(x, str)) or ( - not py3 and isinstance(x, unicode) # noqa: F821 - ): + elif isinstance(x, str): return pdf_repr(encode_text(x)) elif isinstance(x, bytes): # XXX escape more chars? handle binary garbage @@ -471,7 +433,7 @@ class PdfParser: self.f.write(b"%PDF-1.4\n") def write_comment(self, s): - self.f.write(("%% %s\n" % (s,)).encode("utf-8")) + self.f.write(("% {}\n".format(s)).encode("utf-8")) def write_catalog(self): self.del_root() diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 62a1d1a1e..9f98cd3d7 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -38,7 +38,6 @@ import zlib 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 logger = logging.getLogger(__name__) @@ -98,7 +97,7 @@ def _crc32(data, seed=0): # Support classes. Suitable for PNG and related formats like MNG etc. -class ChunkStream(object): +class ChunkStream: def __init__(self, fp): self.fp = fp @@ -176,7 +175,7 @@ class ChunkStream(object): try: cid, pos, length = self.read() except struct.error: - raise IOError("truncated PNG file") + raise OSError("truncated PNG file") if cid == endchunk: break @@ -208,7 +207,7 @@ class iTXt(str): return self -class PngInfo(object): +class PngInfo: """ PNG chunk container (for use with save(pnginfo=)) @@ -445,9 +444,8 @@ class PngStream(ChunkStream): k = s v = b"" if k: - if py3: - k = k.decode("latin-1", "strict") - v = v.decode("latin-1", "replace") + k = k.decode("latin-1", "strict") + v = v.decode("latin-1", "replace") self.im_info[k] = self.im_text[k] = v self.check_text_memory(len(v)) @@ -482,9 +480,8 @@ class PngStream(ChunkStream): v = b"" if k: - if py3: - k = k.decode("latin-1", "strict") - v = v.decode("latin-1", "replace") + k = k.decode("latin-1", "strict") + v = v.decode("latin-1", "replace") self.im_info[k] = self.im_text[k] = v self.check_text_memory(len(v)) @@ -519,14 +516,13 @@ class PngStream(ChunkStream): return s else: return s - if py3: - try: - k = k.decode("latin-1", "strict") - lang = lang.decode("utf-8", "strict") - tk = tk.decode("utf-8", "strict") - v = v.decode("utf-8", "strict") - except UnicodeError: - return s + try: + k = k.decode("latin-1", "strict") + lang = lang.decode("utf-8", "strict") + tk = tk.decode("utf-8", "strict") + v = v.decode("utf-8", "strict") + except UnicodeError: + return s self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) self.check_text_memory(len(v)) @@ -741,7 +737,7 @@ def putchunk(fp, cid, *data): fp.write(o32(crc)) -class _idat(object): +class _idat: # wrap output from the encoder in IDAT chunks def __init__(self, fp, chunk): @@ -794,7 +790,7 @@ def _save(im, fp, filename, chunk=putchunk): try: rawmode, mode = _OUTMODES[mode] except KeyError: - raise IOError("cannot write mode %s as PNG" % mode) + raise OSError("cannot write mode %s as PNG" % mode) # # write minimal PNG file @@ -869,7 +865,7 @@ def _save(im, fp, filename, chunk=putchunk): if "transparency" in im.encoderinfo: # don't bother with transparency if it's an RGBA # and it's in the info dict. It's probably just stale. - raise IOError("cannot use transparency for this mode") + raise OSError("cannot use transparency for this mode") else: if im.mode == "P" and im.im.getpalettemode() == "RGBA": alpha = im.im.getpalette("RGBA", "A") @@ -917,7 +913,7 @@ def _save(im, fp, filename, chunk=putchunk): def getchunks(im, **params): """Return a list of PNG chunks representing this image.""" - class collector(object): + class collector: data = [] def write(self, data): diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 67c672485..35a77bafb 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -135,7 +135,7 @@ def _save(im, fp, filename): elif im.mode == "RGBA": rawmode, head = "RGB", b"P6" else: - raise IOError("cannot write mode %s as PPM" % im.mode) + raise OSError("cannot write mode %s as PPM" % im.mode) fp.write(head + ("\n%d %d\n" % im.size).encode("ascii")) if head == b"P6": fp.write(b"255\n") diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index cf1b4d94d..cceb85c5b 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -71,7 +71,7 @@ class PsdImageFile(ImageFile.ImageFile): mode, channels = MODES[(psd_mode, psd_bits)] if channels > psd_channels: - raise IOError("not enough channels") + raise OSError("not enough channels") self.mode = mode self._size = i32(s[18:]), i32(s[14:]) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 2ab06f93f..359a94919 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -40,7 +40,7 @@ ffi = FFI() ffi.cdef(defs) -class PyAccess(object): +class PyAccess: def __init__(self, img, readonly=False): vals = dict(img.im.unsafe_ptrs) self.readonly = readonly diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 8b2268104..ddd3de379 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -27,7 +27,6 @@ import struct from . import Image, ImageFile from ._binary import i8, i16be as i16, o8 -from ._util import py3 def _accept(prefix): @@ -160,7 +159,9 @@ def _save(im, fp, filename): # assert we've got the right number of bands. if len(im.getbands()) != z: raise ValueError( - "incorrect number of bands in SGI write: %s vs %s" % (z, len(im.getbands())) + "incorrect number of bands in SGI write: {} vs {}".format( + z, len(im.getbands()) + ) ) # Minimum Byte value @@ -169,8 +170,7 @@ def _save(im, fp, filename): pinmax = 255 # Image name (79 characters max, truncated below in write) imgName = os.path.splitext(os.path.basename(filename))[0] - if py3: - imgName = imgName.encode("ascii", "ignore") + imgName = imgName.encode("ascii", "ignore") # Standard representation of pixel in the file colormap = 0 fp.write(struct.pack(">h", magicNumber)) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index c7b6fa5f8..dd0620c14 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -32,9 +32,6 @@ # Details about the Spider image format: # https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html # - -from __future__ import print_function - import os import struct import sys @@ -274,7 +271,7 @@ def _save(im, fp, filename): hdr = makeSpiderHeader(im) if len(hdr) < 256: - raise IOError("Error creating Spider header") + raise OSError("Error creating Spider header") # write the SPIDER header fp.writelines(hdr) diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py index dc479b905..ede646453 100644 --- a/src/PIL/TarIO.py +++ b/src/PIL/TarIO.py @@ -37,12 +37,12 @@ class TarIO(ContainerIO.ContainerIO): s = self.fh.read(512) if len(s) != 512: - raise IOError("unexpected end of tar file") + raise OSError("unexpected end of tar file") name = s[:100].decode("utf-8") i = name.find("\0") if i == 0: - raise IOError("cannot find subfile") + raise OSError("cannot find subfile") if i > 0: name = name[:i] diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 40cadbb6b..fd71e545d 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -168,7 +168,7 @@ def _save(im, fp, filename): try: rawmode, bits, colormaptype, imagetype = SAVE[im.mode] except KeyError: - raise IOError("cannot write mode %s as TGA" % im.mode) + raise OSError("cannot write mode %s as TGA" % im.mode) if "rle" in im.encoderinfo: rle = im.encoderinfo["rle"] diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index c7c75102b..47b69a003 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -38,32 +38,20 @@ # # See the README file for information on usage and redistribution. # - -from __future__ import division, print_function - import distutils.version import io import itertools import os import struct -import sys import warnings +from collections.abc import MutableMapping from fractions import Fraction from numbers import Number, Rational from . import Image, ImageFile, ImagePalette, TiffTags from ._binary import i8, o8 -from ._util import py3 from .TiffTags import TYPES -try: - # Python 3 - from collections.abc import MutableMapping -except ImportError: - # Python 2.7 - from collections import MutableMapping - - DEBUG = False # Needs to be merged with the new logging approach. # Set these to true to force use of libtiff for reading or writing. @@ -363,10 +351,10 @@ class IFDRational(Rational): return delegate - """ a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul', - 'truediv', 'rtruediv', 'floordiv', - 'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg', - 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero', + """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', + 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', + 'mod','rmod', 'pow','rpow', 'pos', 'neg', + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', 'ceil', 'floor', 'round'] print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) """ @@ -375,8 +363,6 @@ class IFDRational(Rational): __radd__ = _delegate("__radd__") __sub__ = _delegate("__sub__") __rsub__ = _delegate("__rsub__") - __div__ = _delegate("__div__") - __rdiv__ = _delegate("__rdiv__") __mul__ = _delegate("__mul__") __rmul__ = _delegate("__rmul__") __truediv__ = _delegate("__truediv__") @@ -395,7 +381,7 @@ class IFDRational(Rational): __gt__ = _delegate("__gt__") __le__ = _delegate("__le__") __ge__ = _delegate("__ge__") - __nonzero__ = _delegate("__nonzero__") + __bool__ = _delegate("__bool__") __ceil__ = _delegate("__ceil__") __floor__ = _delegate("__floor__") __round__ = _delegate("__round__") @@ -524,18 +510,11 @@ class ImageFileDirectory_v2(MutableMapping): def __contains__(self, tag): return tag in self._tags_v2 or tag in self._tagdata - if not py3: - - def has_key(self, tag): - return tag in self - def __setitem__(self, tag, value): self._setitem(tag, value, self.legacy_api) def _setitem(self, tag, value, legacy_api): basetypes = (Number, bytes, str) - if not py3: - basetypes += (unicode,) # noqa: F821 info = TiffTags.lookup(tag) values = [value] if isinstance(value, basetypes) else value @@ -555,14 +534,10 @@ class ImageFileDirectory_v2(MutableMapping): elif all(isinstance(v, float) for v in values): self.tagtype[tag] = TiffTags.DOUBLE else: - if py3: - if all(isinstance(v, str) for v in values): - self.tagtype[tag] = TiffTags.ASCII - else: - # Never treat data as binary by default on Python 2. + if all(isinstance(v, str) for v in values): self.tagtype[tag] = TiffTags.ASCII - if self.tagtype[tag] == TiffTags.UNDEFINED and py3: + if self.tagtype[tag] == TiffTags.UNDEFINED: values = [ value.encode("ascii", "replace") if isinstance(value, str) else value ] @@ -682,8 +657,6 @@ class ImageFileDirectory_v2(MutableMapping): @_register_writer(2) def write_string(self, value): # remerge of https://github.com/python-pillow/Pillow/pull/1416 - if sys.version_info.major == 2: - value = value.decode("ascii", "replace") return b"" + value.encode("ascii", "replace") + b"\0" @_register_loader(5, 8) @@ -727,7 +700,7 @@ class ImageFileDirectory_v2(MutableMapping): def _ensure_read(self, fp, size): ret = fp.read(size) if len(ret) != size: - raise IOError( + raise OSError( "Corrupt EXIF data. " + "Expecting to read %d bytes but only got %d. " % (size, len(ret)) ) @@ -761,7 +734,7 @@ class ImageFileDirectory_v2(MutableMapping): (offset,) = self._unpack("L", data) if DEBUG: print( - "Tag Location: %s - Data Location: %s" % (here, offset), + "Tag Location: {} - Data Location: {}".format(here, offset), end=" ", ) fp.seek(offset) @@ -791,7 +764,7 @@ class ImageFileDirectory_v2(MutableMapping): print("- value:", self[tag]) (self.next,) = self._unpack("L", self._ensure_read(fp, 4)) - except IOError as msg: + except OSError as msg: warnings.warn(str(msg)) return @@ -810,7 +783,7 @@ class ImageFileDirectory_v2(MutableMapping): stripoffsets = len(entries) typ = self.tagtype.get(tag) if DEBUG: - print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + print("Tag {}, Type: {}, Value: {}".format(tag, typ, value)) values = value if isinstance(value, tuple) else (value,) data = self._write_dispatch[typ](self, *values) if DEBUG: @@ -1047,7 +1020,7 @@ class TiffImageFile(ImageFile.ImageFile): "Seeking to frame %s, on frame %s, __next %s, location: %s" % (frame, self.__frame, self.__next, self.fp.tell()) ) - # reset python3 buffered io handle in case fp + # reset buffered io handle in case fp # was passed to libtiff, invalidating the buffer self.fp.tell() self.fp.seek(self.__next) @@ -1075,7 +1048,7 @@ class TiffImageFile(ImageFile.ImageFile): def load(self): if self.use_load_libtiff: return self._load_libtiff() - return super(TiffImageFile, self).load() + return super().load() def load_end(self): if self._tile_orientation: @@ -1104,14 +1077,14 @@ class TiffImageFile(ImageFile.ImageFile): pixel = Image.Image.load(self) if self.tile is None: - raise IOError("cannot load this image") + raise OSError("cannot load this image") if not self.tile: return pixel self.load_prepare() if not len(self.tile) == 1: - raise IOError("Not exactly one tile") + raise OSError("Not exactly one tile") # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) @@ -1125,11 +1098,11 @@ class TiffImageFile(ImageFile.ImageFile): try: fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno()) # flush the file descriptor, prevents error on pypy 2.4+ - # should also eliminate the need for fp.tell for py3 + # should also eliminate the need for fp.tell # in _seek if hasattr(self.fp, "flush"): self.fp.flush() - except IOError: + except OSError: # io.BytesIO have a fileno, but returns an IOError if # it doesn't use a file descriptor. fp = False @@ -1143,7 +1116,7 @@ class TiffImageFile(ImageFile.ImageFile): try: decoder.setimage(self.im, extents) except ValueError: - raise IOError("Couldn't set the image") + raise OSError("Couldn't set the image") close_self_fp = self._exclusive_fp and not self._is_animated if hasattr(self.fp, "getvalue"): @@ -1186,7 +1159,7 @@ class TiffImageFile(ImageFile.ImageFile): self.fp = None # might be shared if err < 0: - raise IOError(err) + raise OSError(err) return Image.Image.load(self) @@ -1194,7 +1167,7 @@ class TiffImageFile(ImageFile.ImageFile): """Setup this image object based on current tags""" if 0xBC01 in self.tag_v2: - raise IOError("Windows Media Photo files not yet supported") + raise OSError("Windows Media Photo files not yet supported") # extract relevant tags self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] @@ -1436,7 +1409,7 @@ def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: - raise IOError("cannot write mode %s as TIFF" % im.mode) + raise OSError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory_v2(prefix=prefix) @@ -1595,13 +1568,10 @@ def _save(im, fp, filename): if tag in ifd.tagtype: types[tag] = ifd.tagtype[tag] - elif not ( - isinstance(value, (int, float, str, bytes)) - or (not py3 and isinstance(value, unicode)) # noqa: F821 - ): + elif not (isinstance(value, (int, float, str, bytes))): continue if tag not in atts and tag not in blocklist: - if isinstance(value, str if py3 else unicode): # noqa: F821 + if isinstance(value, str): atts[tag] = value.encode("ascii", "replace") + b"\0" elif isinstance(value, IFDRational): atts[tag] = float(value) @@ -1634,7 +1604,7 @@ def _save(im, fp, filename): if s: break if s < 0: - raise IOError("encoder error %d when writing image file" % s) + raise OSError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) @@ -1682,9 +1652,9 @@ class AppendingTiffWriter: self.name = fn self.close_fp = True try: - self.f = io.open(fn, "w+b" if new else "r+b") - except IOError: - self.f = io.open(fn, "w+b") + self.f = open(fn, "w+b" if new else "r+b") + except OSError: + self.f = open(fn, "w+b") self.beginning = self.f.tell() self.setup() @@ -1765,7 +1735,7 @@ class AppendingTiffWriter: # pad to 16 byte boundary padBytes = 16 - pos % 16 if 0 < padBytes < 16: - self.f.write(bytes(bytearray(padBytes))) + self.f.write(bytes(padBytes)) self.offsetOfNewPage = self.f.tell() def setEndian(self, endian): diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index c047f42b6..6cc9ff7f3 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -24,7 +24,7 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): - return super(TagInfo, cls).__new__(cls, value, name, type, length, enum or {}) + return super().__new__(cls, value, name, type, length, enum or {}) def cvt_enum(self, value): # Using get will call hash(value), which can be expensive diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index e2e1cd4f5..d5a5c8e67 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -1,4 +1,3 @@ -# encoding: utf-8 # # The Python Imaging Library. # $Id$ @@ -21,16 +20,11 @@ # https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml # and has been tested with a few sample files found using google. +import builtins + from . import Image from ._binary import i32le as i32 -try: - import builtins -except ImportError: - import __builtin__ - - builtins = __builtin__ - def open(filename): """ diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 18eda6d18..eda685508 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -105,7 +105,7 @@ class WebPImageFile(ImageFile.ImageFile): def seek(self, frame): if not _webp.HAVE_WEBPANIM: - return super(WebPImageFile, self).seek(frame) + return super().seek(frame) # Perform some simple checks first if frame >= self._n_frames: @@ -168,11 +168,11 @@ class WebPImageFile(ImageFile.ImageFile): self.fp = BytesIO(data) self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] - return super(WebPImageFile, self).load() + return super().load() def tell(self): if not _webp.HAVE_WEBPANIM: - return super(WebPImageFile, self).tell() + return super().tell() return self.__logical_frame @@ -233,7 +233,7 @@ def _save_all(im, fp, filename): or len(background) != 4 or not all(v >= 0 and v < 256 for v in background) ): - raise IOError( + raise OSError( "Background color is not an RGBA tuple clamped to (0-255): %s" % str(background) ) @@ -312,7 +312,7 @@ def _save_all(im, fp, filename): # Get the final output from the encoder data = enc.assemble(icc_profile, exif, xmp) if data is None: - raise IOError("cannot write file as WebP (encoder returned None)") + raise OSError("cannot write file as WebP (encoder returned None)") fp.write(data) @@ -346,7 +346,7 @@ def _save(im, fp, filename): xmp, ) if data is None: - raise IOError("cannot write file as WebP (encoder returned None)") + raise OSError("cannot write file as WebP (encoder returned None)") fp.write(data) diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 151c48ae2..7695a68e7 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -19,17 +19,11 @@ # http://wvware.sourceforge.net/caolan/index.html # http://wvware.sourceforge.net/caolan/ora-wmf.html -from __future__ import print_function - from . import Image, ImageFile from ._binary import i16le as word, i32le as dword, si16le as short, si32le as _long -from ._util import py3 _handler = None -if py3: - long = int - def register_handler(handler): """ @@ -44,7 +38,7 @@ def register_handler(handler): if hasattr(Image.core, "drawwmf"): # install default handler (windows only) - class WmfHandler(object): + class WmfHandler: def open(self, im): im.mode = "RGB" self.bbox = im.info["wmf_bbox"] @@ -154,7 +148,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): def _save(im, fp, filename): if _handler is None or not hasattr(_handler, "save"): - raise IOError("WMF save handler not installed") + raise OSError("WMF save handler not installed") _handler.save(im, fp, filename) diff --git a/src/PIL/XbmImagePlugin.py b/src/PIL/XbmImagePlugin.py index 4ca101701..ead9722c8 100644 --- a/src/PIL/XbmImagePlugin.py +++ b/src/PIL/XbmImagePlugin.py @@ -69,7 +69,7 @@ class XbmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode != "1": - raise IOError("cannot write mode %s as XBM" % im.mode) + raise OSError("cannot write mode %s as XBM" % im.mode) fp.write(("#define im_width %d\n" % im.size[0]).encode("ascii")) fp.write(("#define im_height %d\n" % im.size[1]).encode("ascii")) diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index fd02b40d9..e7f26488d 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -70,3 +70,7 @@ _plugins = [ "XpmImagePlugin", "XVThumbImagePlugin", ] + + +class UnidentifiedImageError(IOError): + pass diff --git a/src/PIL/_binary.py b/src/PIL/_binary.py index 53b1ca956..529b8c94b 100644 --- a/src/PIL/_binary.py +++ b/src/PIL/_binary.py @@ -13,24 +13,13 @@ from struct import pack, unpack_from -from ._util import py3 -if py3: - - def i8(c): - return c if c.__class__ is int else c[0] - - def o8(i): - return bytes((i & 255,)) +def i8(c): + return c if c.__class__ is int else c[0] -else: - - def i8(c): - return ord(c) - - def o8(i): - return chr(i & 255) +def o8(i): + return bytes((i & 255,)) # Input, le = little endian, be = big endian diff --git a/src/PIL/_tkinter_finder.py b/src/PIL/_tkinter_finder.py index d4f34196e..30493066a 100644 --- a/src/PIL/_tkinter_finder.py +++ b/src/PIL/_tkinter_finder.py @@ -1,11 +1,7 @@ """ Find compiled module linking to Tcl / Tk libraries """ import sys - -if sys.version_info.major > 2: - from tkinter import _tkinter as tk -else: - from Tkinter import tkinter as tk +from tkinter import _tkinter as tk if hasattr(sys, "pypy_find_executable"): # Tested with packages at https://bitbucket.org/pypy/pypy/downloads. diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 59964c7ef..755b4b272 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -1,33 +1,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) +if py36: + from pathlib import Path - if py36: - from pathlib import Path - - def isPath(f): - return isinstance(f, (bytes, str, Path)) - - else: - - def isPath(f): - return isinstance(f, (bytes, str)) + def isPath(f): + return isinstance(f, (bytes, str, Path)) else: - def isStringType(t): - return isinstance(t, basestring) # noqa: F821 - def isPath(f): - return isinstance(f, basestring) # noqa: F821 + return isinstance(f, (bytes, str)) # Checks if an object is a string, and that it points to a directory. @@ -35,7 +22,7 @@ def isDirectory(f): return isPath(f) and os.path.isdir(f) -class deferred_error(object): +class deferred_error: def __init__(self, ex): self.ex = ex diff --git a/src/PIL/features.py b/src/PIL/features.py index 7b007dc56..5822febab 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -1,5 +1,3 @@ -from __future__ import print_function, unicode_literals - import collections import os import sys diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index bb0fd33a3..59801f58e 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -225,11 +225,7 @@ TkImaging_Init(Tcl_Interp* interp) #include /* Must be linked with 'psapi' library */ -#if PY_VERSION_HEX >= 0x03000000 #define TKINTER_PKG "tkinter" -#else -#define TKINTER_PKG "Tkinter" -#endif FARPROC _dfunc(HMODULE lib_handle, const char *func_name) { @@ -354,7 +350,6 @@ int load_tkinter_funcs(void) */ /* From module __file__ attribute to char *string for dlopen. */ -#if PY_VERSION_HEX >= 0x03000000 char *fname2char(PyObject *fname) { PyObject* bytes; @@ -364,9 +359,6 @@ char *fname2char(PyObject *fname) } return PyBytes_AsString(bytes); } -#else -#define fname2char(s) (PyString_AsString(s)) -#endif #include diff --git a/src/_imaging.c b/src/_imaging.c index 81de38e2f..b0cbfc665 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -84,8 +84,6 @@ #include "Imaging.h" -#include "py3.h" - #define _USE_MATH_DEFINES #include @@ -237,45 +235,13 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie) int PyImaging_CheckBuffer(PyObject* buffer) { -#if PY_VERSION_HEX >= 0x03000000 return PyObject_CheckBuffer(buffer); -#else - return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer); -#endif } int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view) { /* must call check_buffer first! */ -#if PY_VERSION_HEX >= 0x03000000 return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); -#else - /* Use new buffer protocol if available - (mmap doesn't support this in 2.7, go figure) */ - if (PyObject_CheckBuffer(buffer)) { - int success = PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); - if (!success) { return success; } - PyErr_Clear(); - } - - /* Pretend we support the new protocol; PyBuffer_Release happily ignores - calling bf_releasebuffer on objects that don't support it */ - view->buf = NULL; - view->len = 0; - view->readonly = 1; - view->format = NULL; - view->ndim = 0; - view->shape = NULL; - view->strides = NULL; - view->suboffsets = NULL; - view->itemsize = 0; - view->internal = NULL; - - Py_INCREF(buffer); - view->obj = buffer; - - return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len); -#endif } /* -------------------------------------------------------------------- */ @@ -416,11 +382,11 @@ getlist(PyObject* arg, Py_ssize_t* length, const char* wrong_length, int type) // on this switch. And 3 fewer loops to copy/paste. switch (type) { case TYPE_UINT8: - itemp = PyInt_AsLong(op); + itemp = PyLong_AsLong(op); list[i] = CLIP8(itemp); break; case TYPE_INT32: - itemp = PyInt_AsLong(op); + itemp = PyLong_AsLong(op); memcpy(list + i * sizeof(INT32), &itemp, sizeof(itemp)); break; case TYPE_FLOAT32: @@ -499,7 +465,7 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) case IMAGING_TYPE_UINT8: switch (im->bands) { case 1: - return PyInt_FromLong(pixel.b[0]); + return PyLong_FromLong(pixel.b[0]); case 2: return Py_BuildValue("BB", pixel.b[0], pixel.b[1]); case 3: @@ -509,12 +475,12 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) } break; case IMAGING_TYPE_INT32: - return PyInt_FromLong(pixel.i); + return PyLong_FromLong(pixel.i); case IMAGING_TYPE_FLOAT32: return PyFloat_FromDouble(pixel.f); case IMAGING_TYPE_SPECIAL: if (strncmp(im->mode, "I;16", 4) == 0) - return PyInt_FromLong(pixel.h); + return PyLong_FromLong(pixel.h); break; } @@ -543,16 +509,8 @@ getink(PyObject* color, Imaging im, char* ink) if (im->type == IMAGING_TYPE_UINT8 || im->type == IMAGING_TYPE_INT32 || im->type == IMAGING_TYPE_SPECIAL) { -#if PY_VERSION_HEX >= 0x03000000 if (PyLong_Check(color)) { r = PyLong_AsLongLong(color); -#else - if (PyInt_Check(color) || PyLong_Check(color)) { - if (PyInt_Check(color)) - r = PyInt_AS_LONG(color); - else - r = PyLong_AsLongLong(color); -#endif rIsInt = 1; } if (r == -1 && PyErr_Occurred()) { @@ -1129,16 +1087,16 @@ _getxy(PyObject* xy, int* x, int *y) goto badarg; value = PyTuple_GET_ITEM(xy, 0); - if (PyInt_Check(value)) - *x = PyInt_AS_LONG(value); + if (PyLong_Check(value)) + *x = PyLong_AS_LONG(value); else if (PyFloat_Check(value)) *x = (int) PyFloat_AS_DOUBLE(value); else goto badval; value = PyTuple_GET_ITEM(xy, 1); - if (PyInt_Check(value)) - *y = PyInt_AS_LONG(value); + if (PyLong_Check(value)) + *y = PyLong_AS_LONG(value); else if (PyFloat_Check(value)) *y = (int) PyFloat_AS_DOUBLE(value); else @@ -1255,7 +1213,7 @@ _histogram(ImagingObject* self, PyObject* args) list = PyList_New(h->bands * 256); for (i = 0; i < h->bands * 256; i++) { PyObject* item; - item = PyInt_FromLong(h->histogram[i]); + item = PyLong_FromLong(h->histogram[i]); if (item == NULL) { Py_DECREF(list); list = NULL; @@ -1524,7 +1482,7 @@ _putdata(ImagingObject* self, PyObject* args) /* Clipped data */ for (i = x = y = 0; i < n; i++) { op = PySequence_Fast_GET_ITEM(seq, i); - image->image8[y][x] = (UINT8) CLIP8(PyInt_AsLong(op)); + image->image8[y][x] = (UINT8) CLIP8(PyLong_AsLong(op)); if (++x >= (int) image->xsize){ x = 0, y++; } @@ -1635,7 +1593,7 @@ _putpalette(ImagingObject* self, PyObject* args) char* rawmode; UINT8* palette; Py_ssize_t palettesize; - if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize)) + if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) return NULL; if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") && @@ -1698,7 +1656,7 @@ _putpalettealphas(ImagingObject* self, PyObject* args) int i; UINT8 *values; Py_ssize_t length; - if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &values, &length)) + if (!PyArg_ParseTuple(args, "y#", &values, &length)) return NULL; if (!self->image->palette) { @@ -2136,7 +2094,7 @@ _getprojection(ImagingObject* self, PyObject* args) ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); - result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH, + result = Py_BuildValue("y#y#", xprofile, (Py_ssize_t)self->image->xsize, yprofile, (Py_ssize_t)self->image->ysize); @@ -2414,7 +2372,7 @@ _font_new(PyObject* self_, PyObject* args) ImagingObject* imagep; unsigned char* glyphdata; Py_ssize_t glyphdata_length; - if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH, + if (!PyArg_ParseTuple(args, "O!y#", &Imaging_Type, &imagep, &glyphdata, &glyphdata_length)) return NULL; @@ -2648,7 +2606,7 @@ _draw_ink(ImagingDrawObject* self, PyObject* args) if (!getink(color, self->image->image, (char*) &ink)) return NULL; - return PyInt_FromLong((int) ink); + return PyLong_FromLong((int) ink); } static PyObject* @@ -3356,13 +3314,13 @@ _getattr_size(ImagingObject* self, void* closure) static PyObject* _getattr_bands(ImagingObject* self, void* closure) { - return PyInt_FromLong(self->image->bands); + return PyLong_FromLong(self->image->bands); } static PyObject* _getattr_id(ImagingObject* self, void* closure) { - return PyInt_FromSsize_t((Py_ssize_t) self->image); + return PyLong_FromSsize_t((Py_ssize_t) self->image); } static PyObject* @@ -3575,17 +3533,17 @@ _get_stats(PyObject* self, PyObject* args) if ( ! d) return NULL; PyDict_SetItemString(d, "new_count", - PyInt_FromLong(arena->stats_new_count)); + PyLong_FromLong(arena->stats_new_count)); PyDict_SetItemString(d, "allocated_blocks", - PyInt_FromLong(arena->stats_allocated_blocks)); + PyLong_FromLong(arena->stats_allocated_blocks)); PyDict_SetItemString(d, "reused_blocks", - PyInt_FromLong(arena->stats_reused_blocks)); + PyLong_FromLong(arena->stats_reused_blocks)); PyDict_SetItemString(d, "reallocated_blocks", - PyInt_FromLong(arena->stats_reallocated_blocks)); + PyLong_FromLong(arena->stats_reallocated_blocks)); PyDict_SetItemString(d, "freed_blocks", - PyInt_FromLong(arena->stats_freed_blocks)); + PyLong_FromLong(arena->stats_freed_blocks)); PyDict_SetItemString(d, "blocks_cached", - PyInt_FromLong(arena->blocks_cached)); + PyLong_FromLong(arena->blocks_cached)); return d; } @@ -3613,7 +3571,7 @@ _get_alignment(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, ":get_alignment")) return NULL; - return PyInt_FromLong(ImagingDefaultArena.alignment); + return PyLong_FromLong(ImagingDefaultArena.alignment); } static PyObject* @@ -3622,7 +3580,7 @@ _get_block_size(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, ":get_block_size")) return NULL; - return PyInt_FromLong(ImagingDefaultArena.block_size); + return PyLong_FromLong(ImagingDefaultArena.block_size); } static PyObject* @@ -3631,7 +3589,7 @@ _get_blocks_max(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, ":get_blocks_max")) return NULL; - return PyInt_FromLong(ImagingDefaultArena.blocks_max); + return PyLong_FromLong(ImagingDefaultArena.blocks_max); } static PyObject* @@ -3965,7 +3923,6 @@ setup_module(PyObject* m) { return 0; } -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imaging(void) { PyObject* m; @@ -3985,11 +3942,3 @@ PyInit__imaging(void) { return m; } -#else -PyMODINIT_FUNC -init_imaging(void) -{ - PyObject* m = Py_InitModule("_imaging", functions); - setup_module(m); -} -#endif diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 2c9f3aa68..0b22ab695 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -32,7 +32,6 @@ http://www.cazabon.com\n\ #include "lcms2.h" #include "Imaging.h" -#include "py3.h" #define PYCMSVERSION "1.0.0 pil" @@ -122,13 +121,8 @@ cms_profile_fromstring(PyObject* self, PyObject* args) char* pProfile; Py_ssize_t nProfile; -#if PY_VERSION_HEX >= 0x03000000 if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) return NULL; -#else - if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile)) - return NULL; -#endif hProfile = cmsOpenProfileFromMem(pProfile, nProfile); if (!hProfile) { @@ -172,11 +166,7 @@ cms_profile_tobytes(PyObject* self, PyObject* args) return NULL; } -#if PY_VERSION_HEX >= 0x03000000 ret = PyBytes_FromStringAndSize(pProfile, (Py_ssize_t)nProfile); -#else - ret = PyString_FromStringAndSize(pProfile, (Py_ssize_t)nProfile); -#endif free(pProfile); return ret; @@ -592,7 +582,7 @@ cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) /* printf("cmsIsIntentSupported(%p, %d, %d) => %d\n", self->profile, intent, direction, result); */ - return PyInt_FromLong(result != 0); + return PyLong_FromLong(result != 0); } #ifdef _WIN32 @@ -691,11 +681,7 @@ _profile_read_int_as_string(cmsUInt32Number nr) buf[3] = (char) (nr & 0xff); buf[4] = 0; -#if PY_VERSION_HEX >= 0x03000000 ret = PyUnicode_DecodeASCII(buf, 4, NULL); -#else - ret = PyString_FromStringAndSize(buf, 4); -#endif return ret; } @@ -898,7 +884,7 @@ _is_intent_supported(CmsProfileObject* self, int clut) || intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) continue; - id = PyInt_FromLong((long) intent); + id = PyLong_FromLong((long) intent); entry = Py_BuildValue("(OOO)", _check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True : Py_False, _check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True : Py_False, @@ -1010,7 +996,7 @@ cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) { - return PyInt_FromLong(cmsGetHeaderRenderingIntent(self->profile)); + return PyLong_FromLong(cmsGetHeaderRenderingIntent(self->profile)); } static PyObject* @@ -1098,7 +1084,7 @@ cms_profile_getattr_version(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_icc_version(CmsProfileObject* self, void* closure) { - return PyInt_FromLong((long) cmsGetEncodedICCversion(self->profile)); + return PyLong_FromLong((long) cmsGetEncodedICCversion(self->profile)); } static PyObject* @@ -1115,7 +1101,7 @@ static PyObject* cms_profile_getattr_header_flags(CmsProfileObject* self, void* closure) { cmsUInt32Number flags = cmsGetHeaderFlags(self->profile); - return PyInt_FromLong(flags); + return PyLong_FromLong(flags); } static PyObject* @@ -1611,7 +1597,6 @@ setup_module(PyObject* m) { return 0; } -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imagingcms(void) { PyObject* m; @@ -1633,12 +1618,3 @@ PyInit__imagingcms(void) { return m; } -#else -PyMODINIT_FUNC -init_imagingcms(void) -{ - PyObject *m = Py_InitModule("_imagingcms", pyCMSdll_methods); - setup_module(m); - PyDateTime_IMPORT; -} -#endif diff --git a/src/_imagingft.c b/src/_imagingft.c index 146df095b..62a4c283e 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -30,7 +30,6 @@ #include FT_SFNT_NAMES_H #define KEEP_PY_UNICODE -#include "py3.h" #if !defined(_MSC_VER) #include @@ -266,8 +265,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) return NULL; } - if (!PyArg_ParseTupleAndKeywords(args, kw, "etn|ns"PY_ARG_BYTES_LENGTH"n", - kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kw, "etn|nsy#n", kwlist, Py_FileSystemDefaultEncoding, &filename, &size, &index, &encoding, &font_bytes, &font_bytes_size, &layout_engine)) { @@ -328,34 +326,12 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) static int font_getchar(PyObject* string, int index, FT_ULong* char_out) { -#if (PY_VERSION_HEX < 0x03030000) || (defined(PYPY_VERSION_NUM)) - if (PyUnicode_Check(string)) { - Py_UNICODE* p = PyUnicode_AS_UNICODE(string); - int size = PyUnicode_GET_SIZE(string); - if (index >= size) - return 0; - *char_out = p[index]; - return 1; - } -#if PY_VERSION_HEX < 0x03000000 - if (PyString_Check(string)) { - unsigned char* p = (unsigned char*) PyString_AS_STRING(string); - int size = PyString_GET_SIZE(string); - if (index >= size) - return 0; - *char_out = (unsigned char) p[index]; - return 1; - } -#endif -#else if (PyUnicode_Check(string)) { if (index >= PyUnicode_GET_LENGTH(string)) return 0; *char_out = PyUnicode_READ_CHAR(string, index); return 1; } -#endif - return 0; } @@ -375,7 +351,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * goto failed; } -#if (PY_VERSION_HEX < 0x03030000) || (defined(PYPY_VERSION_NUM)) +#if (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM < 0x07020000)) if (PyUnicode_Check(string)) { Py_UNICODE *text = PyUnicode_AS_UNICODE(string); Py_ssize_t size = PyUnicode_GET_SIZE(string); @@ -395,25 +371,6 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * } } } -#if PY_VERSION_HEX < 0x03000000 - else if (PyString_Check(string)) { - char *text = PyString_AS_STRING(string); - int size = PyString_GET_SIZE(string); - if (! size) { - goto failed; - } - if (!(*p_raqm.set_text_utf8)(rq, text, size)) { - PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed"); - goto failed; - } - if (lang) { - if (!(*p_raqm.set_language)(rq, lang, start, size)) { - PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed"); - goto failed; - } - } - } -#endif #else if (PyUnicode_Check(string)) { Py_UCS4 *text = PyUnicode_AsUCS4Copy(string); @@ -423,7 +380,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * and raqm fails with empty strings */ goto failed; } - int set_text = (*p_raqm.set_text)(rq, (const uint32_t *)(text), size); + int set_text = (*p_raqm.set_text)(rq, text, size); PyMem_Free(text); if (!set_text) { PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed"); @@ -479,11 +436,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * Py_ssize_t size = 0; PyObject *bytes; -#if PY_VERSION_HEX >= 0x03000000 if (!PyUnicode_Check(item)) { -#else - if (!PyUnicode_Check(item) && !PyString_Check(item)) { -#endif PyErr_SetString(PyExc_TypeError, "expected a string"); goto failed; } @@ -495,12 +448,6 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * feature = PyBytes_AS_STRING(bytes); size = PyBytes_GET_SIZE(bytes); } -#if PY_VERSION_HEX < 0x03000000 - else { - feature = PyString_AsString(item); - size = PyString_GET_SIZE(item); - } -#endif if (!(*p_raqm.add_font_feature)(rq, feature, size)) { PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed"); goto failed; @@ -581,11 +528,7 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObje if (features != Py_None || dir != NULL || lang != NULL) { PyErr_SetString(PyExc_KeyError, "setting text direction, language or font features is not supported without libraqm"); } -#if PY_VERSION_HEX >= 0x03000000 if (!PyUnicode_Check(string)) { -#else - if (!PyUnicode_Check(string) && !PyString_Check(string)) { -#endif PyErr_SetString(PyExc_TypeError, "expected string"); return 0; } @@ -991,8 +934,7 @@ font_render(FontObject* self, PyObject* args) continue; if (master->namedstyle[j].strid == name.name_id) { - list_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, - name.string, name.string_len); + list_name = Py_BuildValue("y#", name.string, name.string_len); PyList_SetItem(list_names, j, list_name); break; } @@ -1026,11 +968,11 @@ font_render(FontObject* self, PyObject* args) list_axis = PyDict_New(); PyDict_SetItemString(list_axis, "minimum", - PyInt_FromLong(axis.minimum / 65536)); + PyLong_FromLong(axis.minimum / 65536)); PyDict_SetItemString(list_axis, "default", - PyInt_FromLong(axis.def / 65536)); + PyLong_FromLong(axis.def / 65536)); PyDict_SetItemString(list_axis, "maximum", - PyInt_FromLong(axis.maximum / 65536)); + PyLong_FromLong(axis.maximum / 65536)); for (j = 0; j < name_count; j++) { error = FT_Get_Sfnt_Name(self->face, j, &name); @@ -1038,8 +980,7 @@ font_render(FontObject* self, PyObject* args) return geterror(error); if (name.name_id == axis.strid) { - axis_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, - name.string, name.string_len); + axis_name = Py_BuildValue("y#", name.string, name.string_len); PyDict_SetItemString(list_axis, "name", axis_name); break; } @@ -1096,8 +1037,8 @@ font_render(FontObject* self, PyObject* args) item = PyList_GET_ITEM(axes, i); if (PyFloat_Check(item)) coord = PyFloat_AS_DOUBLE(item); - else if (PyInt_Check(item)) - coord = (float) PyInt_AS_LONG(item); + else if (PyLong_Check(item)) + coord = (float) PyLong_AS_LONG(item); else if (PyNumber_Check(item)) coord = PyFloat_AsDouble(item); else { @@ -1147,64 +1088,54 @@ static PyMethodDef font_methods[] = { static PyObject* font_getattr_family(FontObject* self, void* closure) { -#if PY_VERSION_HEX >= 0x03000000 if (self->face->family_name) return PyUnicode_FromString(self->face->family_name); -#else - if (self->face->family_name) - return PyString_FromString(self->face->family_name); -#endif Py_RETURN_NONE; } static PyObject* font_getattr_style(FontObject* self, void* closure) { -#if PY_VERSION_HEX >= 0x03000000 if (self->face->style_name) return PyUnicode_FromString(self->face->style_name); -#else - if (self->face->style_name) - return PyString_FromString(self->face->style_name); -#endif Py_RETURN_NONE; } static PyObject* font_getattr_ascent(FontObject* self, void* closure) { - return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); + return PyLong_FromLong(PIXEL(self->face->size->metrics.ascender)); } static PyObject* font_getattr_descent(FontObject* self, void* closure) { - return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); + return PyLong_FromLong(-PIXEL(self->face->size->metrics.descender)); } static PyObject* font_getattr_height(FontObject* self, void* closure) { - return PyInt_FromLong(PIXEL(self->face->size->metrics.height)); + return PyLong_FromLong(PIXEL(self->face->size->metrics.height)); } static PyObject* font_getattr_x_ppem(FontObject* self, void* closure) { - return PyInt_FromLong(self->face->size->metrics.x_ppem); + return PyLong_FromLong(self->face->size->metrics.x_ppem); } static PyObject* font_getattr_y_ppem(FontObject* self, void* closure) { - return PyInt_FromLong(self->face->size->metrics.y_ppem); + return PyLong_FromLong(self->face->size->metrics.y_ppem); } static PyObject* font_getattr_glyphs(FontObject* self, void* closure) { - return PyInt_FromLong(self->face->num_glyphs); + return PyLong_FromLong(self->face->num_glyphs); } static struct PyGetSetDef font_getsetters[] = { @@ -1272,11 +1203,7 @@ setup_module(PyObject* m) { FT_Library_Version(library, &major, &minor, &patch); -#if PY_VERSION_HEX >= 0x03000000 v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch); -#else - v = PyString_FromFormat("%d.%d.%d", major, minor, patch); -#endif PyDict_SetItemString(d, "freetype2_version", v); @@ -1287,7 +1214,6 @@ setup_module(PyObject* m) { return 0; } -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imagingft(void) { PyObject* m; @@ -1307,12 +1233,3 @@ PyInit__imagingft(void) { return m; } -#else -PyMODINIT_FUNC -init_imagingft(void) -{ - PyObject* m = Py_InitModule("_imagingft", _functions); - setup_module(m); -} -#endif - diff --git a/src/_imagingmath.c b/src/_imagingmath.c index ea9f103c6..bc66a581a 100644 --- a/src/_imagingmath.c +++ b/src/_imagingmath.c @@ -16,7 +16,6 @@ #include "Python.h" #include "Imaging.h" -#include "py3.h" #include "math.h" #include "float.h" @@ -215,7 +214,7 @@ static PyMethodDef _functions[] = { static void install(PyObject *d, char* name, void* value) { - PyObject *v = PyInt_FromSsize_t((Py_ssize_t) value); + PyObject *v = PyLong_FromSsize_t((Py_ssize_t) value); if (!v || PyDict_SetItemString(d, name, v)) PyErr_Clear(); Py_XDECREF(v); @@ -273,7 +272,6 @@ setup_module(PyObject* m) { return 0; } -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imagingmath(void) { PyObject* m; @@ -293,12 +291,3 @@ PyInit__imagingmath(void) { return m; } -#else -PyMODINIT_FUNC -init_imagingmath(void) -{ - PyObject* m = Py_InitModule("_imagingmath", _functions); - setup_module(m); -} -#endif - diff --git a/src/_imagingmorph.c b/src/_imagingmorph.c index fc8f246cc..050ae9f02 100644 --- a/src/_imagingmorph.c +++ b/src/_imagingmorph.c @@ -13,7 +13,6 @@ #include "Python.h" #include "Imaging.h" -#include "py3.h" #define LUT_SIZE (1<<9) @@ -273,7 +272,6 @@ static PyMethodDef functions[] = { {NULL, NULL, 0, NULL} }; -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imagingmorph(void) { PyObject* m; @@ -293,12 +291,3 @@ PyInit__imagingmorph(void) { return m; } -#else -PyMODINIT_FUNC -init_imagingmorph(void) -{ - PyObject* m = Py_InitModule("_imagingmorph", functions); - setup_module(m); -} -#endif - diff --git a/src/_imagingtk.c b/src/_imagingtk.c index d0295f317..bdf5e68d1 100644 --- a/src/_imagingtk.c +++ b/src/_imagingtk.c @@ -64,7 +64,6 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__imagingtk(void) { static PyModuleDef module_def = { @@ -78,12 +77,3 @@ PyInit__imagingtk(void) { m = PyModule_Create(&module_def); return (load_tkinter_funcs() == 0) ? m : NULL; } -#else -PyMODINIT_FUNC -init_imagingtk(void) -{ - Py_InitModule("_imagingtk", functions); - load_tkinter_funcs(); -} -#endif - diff --git a/src/_webp.c b/src/_webp.c index 66b6d3268..4581ef89d 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -1,7 +1,6 @@ #define PY_SSIZE_T_CLEAN #include #include "Imaging.h" -#include "py3.h" #include #include #include @@ -557,7 +556,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) Py_ssize_t xmp_size; size_t ret_size; - if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"iiifss#s#s#", + if (!PyArg_ParseTuple(args, "y#iiifss#s#s#", (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode, &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { return NULL; @@ -754,11 +753,7 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) config.output.u.YUVA.y_size); } -#if PY_VERSION_HEX >= 0x03000000 pymode = PyUnicode_FromString(mode); -#else - pymode = PyString_FromString(mode); -#endif ret = Py_BuildValue("SiiSSS", bytes, config.output.width, config.output.height, pymode, NULL == icc_profile ? Py_None : icc_profile, @@ -848,7 +843,6 @@ static int setup_module(PyObject* m) { return 0; } -#if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__webp(void) { PyObject* m; @@ -867,11 +861,3 @@ PyInit__webp(void) { return m; } -#else -PyMODINIT_FUNC -init_webp(void) -{ - PyObject* m = Py_InitModule("_webp", webpMethods); - setup_module(m); -} -#endif diff --git a/src/decode.c b/src/decode.c index 79133f48f..5ab6ca9d1 100644 --- a/src/decode.c +++ b/src/decode.c @@ -33,7 +33,6 @@ #include "Python.h" #include "Imaging.h" -#include "py3.h" #include "Gif.h" #include "Raw.h" @@ -122,7 +121,7 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) int status; ImagingSectionCookie cookie; - if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) + if (!PyArg_ParseTuple(args, "y#", &buffer, &bufsize)) return NULL; if (!decoder->pulls_fd) { diff --git a/src/display.c b/src/display.c index efabf1c86..4c2faf9e0 100644 --- a/src/display.c +++ b/src/display.c @@ -26,7 +26,6 @@ #include "Python.h" #include "Imaging.h" -#include "py3.h" /* -------------------------------------------------------------------- */ /* Windows DIB support */ @@ -187,13 +186,8 @@ _frombytes(ImagingDisplayObject* display, PyObject* args) char* ptr; int bytes; -#if PY_VERSION_HEX >= 0x03000000 if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) return NULL; -#else - if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes)) - return NULL; -#endif if (display->dib->ysize * display->dib->linesize != bytes) { PyErr_SetString(PyExc_ValueError, "wrong size"); @@ -209,13 +203,8 @@ _frombytes(ImagingDisplayObject* display, PyObject* args) static PyObject* _tobytes(ImagingDisplayObject* display, PyObject* args) { -#if PY_VERSION_HEX >= 0x03000000 if (!PyArg_ParseTuple(args, ":tobytes")) return NULL; -#else - if (!PyArg_ParseTuple(args, ":tostring")) - return NULL; -#endif return PyBytes_FromStringAndSize( display->dib->bits, display->dib->ysize * display->dib->linesize @@ -741,7 +730,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) int datasize; int width, height; int x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"(ii)(iiii):_load", &data, &datasize, + if (!PyArg_ParseTuple(args, "y#(ii)(iiii):_load", &data, &datasize, &width, &height, &x0, &x1, &y0, &y1)) return NULL; diff --git a/src/encode.c b/src/encode.c index 7dc1035c4..41ba124c4 100644 --- a/src/encode.c +++ b/src/encode.c @@ -26,7 +26,6 @@ #include "Python.h" #include "Imaging.h" -#include "py3.h" #include "Gif.h" #ifdef HAVE_UNISTD_H @@ -567,7 +566,7 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) Py_ssize_t compress_type = -1; char* dictionary = NULL; Py_ssize_t dictionary_size = 0; - if (!PyArg_ParseTuple(args, "ss|nnn"PY_ARG_BYTES_LENGTH, &mode, &rawmode, + if (!PyArg_ParseTuple(args, "ss|nnny#", &mode, &rawmode, &optimize, &compress_level, &compress_type, &dictionary, &dictionary_size)) @@ -693,7 +692,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) item = PyList_GetItem(tags, pos); // We already checked that tags is a 2-tuple list. key = PyTuple_GetItem(item, 0); - key_int = (int)PyInt_AsLong(key); + key_int = (int)PyLong_AsLong(key); value = PyTuple_GetItem(item, 1); status = 0; is_core_tag = 0; @@ -710,7 +709,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) if (!is_core_tag) { PyObject *tag_type = PyDict_GetItem(types, key); if (tag_type) { - int type_int = PyInt_AsLong(tag_type); + int type_int = PyLong_AsLong(tag_type); if (type_int >= TIFF_BYTE && type_int <= TIFF_DOUBLE) { type = (TIFFDataType)type_int; } @@ -721,7 +720,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) if (type == TIFF_NOTYPE) { // Autodetect type. Types should not be changed for backwards // compatibility. - if (PyInt_Check(value)) { + if (PyLong_Check(value)) { type = TIFF_LONG; } else if (PyFloat_Check(value)) { type = TIFF_DOUBLE; @@ -749,7 +748,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) if (type == TIFF_NOTYPE) { // Autodetect type based on first item. Types should not be // changed for backwards compatibility. - if (PyInt_Check(PyTuple_GetItem(value,0))) { + if (PyLong_Check(PyTuple_GetItem(value,0))) { type = TIFF_LONG; } else if (PyFloat_Check(PyTuple_GetItem(value,0))) { type = TIFF_FLOAT; @@ -775,7 +774,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(UINT8)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -786,7 +785,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(UINT16)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -797,7 +796,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(UINT32)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -808,7 +807,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(INT8)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -819,7 +818,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(INT16)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -830,7 +829,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) av = calloc(len, sizeof(INT32)); if (av) { for (i=0;istate, (ttag_t) key_int, len, av); free(av); @@ -862,19 +861,19 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) if (type == TIFF_SHORT) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (UINT16)PyInt_AsLong(value)); + (UINT16)PyLong_AsLong(value)); } else if (type == TIFF_LONG) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (UINT32)PyInt_AsLong(value)); + (UINT32)PyLong_AsLong(value)); } else if (type == TIFF_SSHORT) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (INT16)PyInt_AsLong(value)); + (INT16)PyLong_AsLong(value)); } else if (type == TIFF_SLONG) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (INT32)PyInt_AsLong(value)); + (INT32)PyLong_AsLong(value)); } else if (type == TIFF_FLOAT) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, @@ -886,11 +885,11 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } else if (type == TIFF_BYTE) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (UINT8)PyInt_AsLong(value)); + (UINT8)PyLong_AsLong(value)); } else if (type == TIFF_SBYTE) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, - (INT8)PyInt_AsLong(value)); + (INT8)PyLong_AsLong(value)); } else if (type == TIFF_ASCII) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, @@ -984,7 +983,7 @@ static unsigned int* get_qtables_arrays(PyObject* qtables, int* qtablesLen) { } table_data = PySequence_Fast(table, "expected a sequence"); for (j = 0; j < DCTSIZE2; j++) { - qarrays[i * DCTSIZE2 + j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); + qarrays[i * DCTSIZE2 + j] = PyLong_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); } Py_DECREF(table_data); } @@ -1024,7 +1023,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) char* rawExif = NULL; Py_ssize_t rawExifLen = 0; - if (!PyArg_ParseTuple(args, "ss|nnnnnnnnO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH, + if (!PyArg_ParseTuple(args, "ss|nnnnnnnnOy#y#", &mode, &rawmode, &quality, &progressive, &smooth, &optimize, &streamtype, &xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size, @@ -1109,8 +1108,8 @@ j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y) *x = *y = 0; if (tuple && PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2) { - *x = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 0)); - *y = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); + *x = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 0)); + *y = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 1)); if (*x < 0) *x = 0; diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c index eaa276af4..a239464d4 100644 --- a/src/libImaging/Pack.c +++ b/src/libImaging/Pack.c @@ -555,6 +555,9 @@ static struct { {"LA", "LA", 16, packLA}, {"LA", "LA;L", 16, packLAL}, + /* greyscale w. alpha premultiplied */ + {"La", "La", 16, packLA}, + /* palette */ {"P", "P;1", 1, pack1}, {"P", "P;2", 2, packP2}, diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index ab0c8dc60..adf2dd277 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1306,6 +1306,9 @@ static struct { /* greyscale w. alpha */ {"LA", "LA", 16, unpackLA}, {"LA", "LA;L", 16, unpackLAL}, + + /* greyscale w. alpha premultiplied */ + {"La", "La", 16, unpackLA}, /* palette */ {"P", "P;1", 1, unpackP1}, @@ -1384,7 +1387,6 @@ static struct { {"RGBX", "RGBX;16N", 64, unpackRGBA16B}, #endif - /* true colour w. alpha premultiplied */ {"RGBa", "RGBa", 32, copy4}, {"RGBa", "BGRa", 32, unpackBGRA}, diff --git a/src/libImaging/codec_fd.c b/src/libImaging/codec_fd.c index 7bd4dadf8..5cde31cdc 100644 --- a/src/libImaging/codec_fd.c +++ b/src/libImaging/codec_fd.c @@ -1,6 +1,5 @@ #include "Python.h" #include "Imaging.h" -#include "../py3.h" Py_ssize_t @@ -72,7 +71,7 @@ _imaging_tell_pyFd(PyObject *fd) Py_ssize_t location; result = PyObject_CallMethod(fd, "tell", NULL); - location = PyInt_AsSsize_t(result); + location = PyLong_AsSsize_t(result); Py_DECREF(result); return location; diff --git a/src/map.c b/src/map.c index 099bb4b3e..a8fb69c6e 100644 --- a/src/map.c +++ b/src/map.c @@ -22,8 +22,6 @@ #include "Imaging.h" -#include "py3.h" - /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); diff --git a/src/path.c b/src/path.c index 5f0541b0b..f69755d16 100644 --- a/src/path.c +++ b/src/path.c @@ -31,8 +31,6 @@ #include -#include "py3.h" - /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); @@ -170,8 +168,8 @@ PyPath_Flatten(PyObject* data, double **pxy) PyObject *op = PyList_GET_ITEM(data, i); if (PyFloat_Check(op)) xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyInt_Check(op)) - xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyLong_Check(op)) + xy[j++] = (float) PyLong_AS_LONG(op); else if (PyNumber_Check(op)) xy[j++] = PyFloat_AsDouble(op); else if (PyArg_ParseTuple(op, "dd", &x, &y)) { @@ -188,8 +186,8 @@ PyPath_Flatten(PyObject* data, double **pxy) PyObject *op = PyTuple_GET_ITEM(data, i); if (PyFloat_Check(op)) xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyInt_Check(op)) - xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyLong_Check(op)) + xy[j++] = (float) PyLong_AS_LONG(op); else if (PyNumber_Check(op)) xy[j++] = PyFloat_AsDouble(op); else if (PyArg_ParseTuple(op, "dd", &x, &y)) { @@ -217,8 +215,8 @@ PyPath_Flatten(PyObject* data, double **pxy) } if (PyFloat_Check(op)) xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyInt_Check(op)) - xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyLong_Check(op)) + xy[j++] = (float) PyLong_AS_LONG(op); else if (PyNumber_Check(op)) xy[j++] = PyFloat_AsDouble(op); else if (PyArg_ParseTuple(op, "dd", &x, &y)) { @@ -552,13 +550,8 @@ path_subscript(PyPathObject* self, PyObject* item) { int len = 4; Py_ssize_t start, stop, step, slicelength; -#if PY_VERSION_HEX >= 0x03020000 if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) return NULL; -#else - if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0) - return NULL; -#endif if (slicelength <= 0) { double *xy = alloc_array(0); diff --git a/src/py3.h b/src/py3.h deleted file mode 100644 index 310583845..000000000 --- a/src/py3.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Python3 definition file to consistently map the code to Python 2 or - Python 3. - - PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions - are mapped to PyLong. - - PyString, on the other hand, was split into PyBytes and PyUnicode. We map - both back onto PyString, so use PyBytes or PyUnicode where appropriate. The - only exception to this is _imagingft.c, where PyUnicode is left alone. -*/ - -#if PY_VERSION_HEX >= 0x03000000 -#define PY_ARG_BYTES_LENGTH "y#" - -/* Map PyInt -> PyLong */ -#define PyInt_AsLong PyLong_AsLong -#define PyInt_Check PyLong_Check -#define PyInt_FromLong PyLong_FromLong -#define PyInt_AS_LONG PyLong_AS_LONG -#define PyInt_FromSsize_t PyLong_FromSsize_t -#define PyInt_AsSsize_t PyLong_AsSsize_t - -#else /* PY_VERSION_HEX < 0x03000000 */ -#define PY_ARG_BYTES_LENGTH "s#" - -#if !defined(KEEP_PY_UNICODE) -/* Map PyUnicode -> PyString */ -#undef PyUnicode_AsString -#undef PyUnicode_AS_STRING -#undef PyUnicode_Check -#undef PyUnicode_FromStringAndSize -#undef PyUnicode_FromString -#undef PyUnicode_FromFormat -#undef PyUnicode_DecodeFSDefault - -#define PyUnicode_AsString PyString_AsString -#define PyUnicode_AS_STRING PyString_AS_STRING -#define PyUnicode_Check PyString_Check -#define PyUnicode_FromStringAndSize PyString_FromStringAndSize -#define PyUnicode_FromString PyString_FromString -#define PyUnicode_FromFormat PyString_FromFormat -#define PyUnicode_DecodeFSDefault PyString_FromString -#endif - -/* Map PyBytes -> PyString */ -#define PyBytesObject PyStringObject -#define PyBytes_AsString PyString_AsString -#define PyBytes_AS_STRING PyString_AS_STRING -#define PyBytes_Check PyString_Check -#define PyBytes_AsStringAndSize PyString_AsStringAndSize -#define PyBytes_FromStringAndSize PyString_FromStringAndSize -#define PyBytes_FromString PyString_FromString -#define _PyBytes_Resize _PyString_Resize - -#endif /* PY_VERSION_HEX < 0x03000000 */ diff --git a/tox.ini b/tox.ini index 3da68ba7b..07d75be64 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ [tox] envlist = lint - py{27,35,36,37,38,py3} + py{35,36,37,38,py3} minversion = 1.9 [testenv] @@ -24,7 +24,7 @@ deps = [testenv:lint] commands = - black --check --diff . + black --target-version py35 --check --diff . flake8 --statistics --count isort --check-only --diff check-manifest diff --git a/winbuild/appveyor_install_msys2_deps.sh b/winbuild/appveyor_install_msys2_deps.sh index 02b75e210..4cc01082d 100644 --- a/winbuild/appveyor_install_msys2_deps.sh +++ b/winbuild/appveyor_install_msys2_deps.sh @@ -2,15 +2,11 @@ mkdir /var/cache/pacman/pkg pacman -S --noconfirm mingw32/mingw-w64-i686-python3-pip \ - mingw32/mingw-w64-i686-python3-setuptools \ - mingw32/mingw-w64-i686-python3-pytest \ - mingw32/mingw-w64-i686-python3-pytest-cov \ - mingw32/mingw-w64-i686-python2-pip \ - mingw32/mingw-w64-i686-python2-setuptools \ - mingw32/mingw-w64-i686-python2-pytest \ - mingw32/mingw-w64-i686-python2-pytest-cov \ - mingw-w64-i686-libjpeg-turbo \ - mingw-w64-i686-libimagequant + mingw32/mingw-w64-i686-python3-setuptools \ + mingw32/mingw-w64-i686-python3-pytest \ + mingw32/mingw-w64-i686-python3-pytest-cov \ + mingw-w64-i686-libjpeg-turbo \ + mingw-w64-i686-libimagequant C:/msys64/mingw32/bin/python3 -m pip install --upgrade pip diff --git a/winbuild/appveyor_install_pypy2.cmd b/winbuild/appveyor_install_pypy2.cmd deleted file mode 100644 index fc56d0e56..000000000 --- a/winbuild/appveyor_install_pypy2.cmd +++ /dev/null @@ -1,3 +0,0 @@ -curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.1-win32.zip -7z x pypy2.zip -oc:\ -c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.1.1-win32\pypy.exe c:\vp\pypy2 diff --git a/winbuild/build.py b/winbuild/build.py index e66df2f03..e565226bd 100755 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -53,10 +53,10 @@ def run_script(params): print(stderr.decode()) print("-- stdout --") print(trace.decode()) - print("Done with %s: %s" % (version, status)) + print("Done with {}: {}".format(version, status)) return (version, status, trace, stderr) except Exception as msg: - print("Error with %s: %s" % (version, str(msg))) + print("Error with {}: {}".format(version, str(msg))) return (version, -1, "", str(msg)) @@ -98,17 +98,14 @@ def build_one(py_ver, compiler, bit): if "PYTHON" in os.environ: args["python_path"] = "%PYTHON%" else: - args["python_path"] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) + args["python_path"] = "{}{}\\Scripts".format(VIRT_BASE, py_ver) args["executable"] = "python.exe" if "EXECUTABLE" in os.environ: args["executable"] = "%EXECUTABLE%" args["py_ver"] = py_ver - if "27" in py_ver: - args["tcl_ver"] = "85" - else: - args["tcl_ver"] = "86" + args["tcl_ver"] = "86" if compiler["vc_version"] == "2015": args["imaging_libs"] = " build_ext --add-imaging-libs=msvcrt" @@ -160,7 +157,7 @@ def main(op): scripts.append( ( - "%s%s" % (py_version, X64_EXT), + "{}{}".format(py_version, X64_EXT), "\n".join( [ header(op), @@ -174,7 +171,7 @@ def main(op): results = map(run_script, scripts) for (version, status, trace, err) in results: - print("Compiled %s: %s" % (version, status and "ERR" or "OK")) + print("Compiled {}: {}".format(version, status and "ERR" or "OK")) def run_one(op): diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 0062ad0c9..5762de5e5 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -101,7 +101,7 @@ set CMAKE="cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build """ + "\n".join( - r"set %s=%%BUILD%%\%s" % (k.upper(), v["dir"]) + r"set {}=%BUILD%\{}".format(k.upper(), v["dir"]) for (k, v) in libs.items() if v["dir"] ) @@ -300,33 +300,6 @@ endlocal ) -def build_ghostscript(compiler, bit): - script = ( - r""" -rem Build gs -setlocal -""" - + vc_setup(compiler, bit) - + r""" -set MSVC_VERSION=""" - + {"2010": "90", "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 -nologo -f psi/msvc.mak -copy /Y /B bin\ C:\Python27\ -endlocal -""" - return script % compiler - - def add_compiler(compiler, bit): script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler, bit)) @@ -336,7 +309,6 @@ def add_compiler(compiler, bit): script.append(msbuild_freetype(compiler, bit)) script.append(build_lcms2(compiler)) script.append(nmake_openjpeg(compiler, bit)) - script.append(build_ghostscript(compiler, bit)) script.append(end_compiler()) diff --git a/winbuild/config.py b/winbuild/config.py index c76d9d5bf..b68fb6936 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -3,18 +3,17 @@ import os SF_MIRROR = "https://iweb.dl.sourceforge.net" pythons = { + "pypy3": {"compiler": 7.1, "vc": 2015}, # for AppVeyor - "27": {"compiler": 7, "vc": 2010}, - "pypy2": {"compiler": 7, "vc": 2010}, "35": {"compiler": 7.1, "vc": 2015}, "36": {"compiler": 7.1, "vc": 2015}, - "pypy3": {"compiler": 7.1, "vc": 2015}, "37": {"compiler": 7.1, "vc": 2015}, "38": {"compiler": 7.1, "vc": 2015}, # for GitHub Actions "3.5": {"compiler": 7.1, "vc": 2015}, "3.6": {"compiler": 7.1, "vc": 2015}, "3.7": {"compiler": 7.1, "vc": 2015}, + "3.8": {"compiler": 7.1, "vc": 2015}, } VIRT_BASE = "c:/vp/" @@ -55,11 +54,6 @@ libs = { "filename": "lcms2-2.8.zip", "dir": "lcms2-2.8", }, - "ghostscript": { - "url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501 - "filename": "ghostscript-9.27.tar.gz", - "dir": "ghostscript-9.27", - }, "tcl-8.5": { "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", "filename": "tcl8519-src.zip", @@ -72,15 +66,15 @@ libs = { "version": "8.5.19", }, "tcl-8.6": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip", - "filename": "tcl869-src.zip", + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tcl8610-src.zip", + "filename": "tcl8610-src.zip", "dir": "", }, "tk-8.6": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip", - "filename": "tk869-src.zip", + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tk8610-src.zip", + "filename": "tk8610-src.zip", "dir": "", - "version": "8.6.9", + "version": "8.6.10", }, "webp": { "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz", @@ -167,14 +161,14 @@ compilers = { def pyversion_from_env(): py = os.environ["PYTHON"] - py_version = "27" + py_version = "35" for k in pythons: if k in py: py_version = k break if "64" in py: - py_version = "%s%s" % (py_version, X64_EXT) + py_version = "{}{}".format(py_version, X64_EXT) return py_version diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index e24bb65f7..a853fc6f7 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -3,7 +3,7 @@ import os from fetch import fetch if __name__ == "__main__": - for version in ["2.7.15", "3.4.4"]: + for version in ["3.4.4"]: for platform in ["", ".amd64"]: for extension in ["", ".asc"]: fetch( diff --git a/winbuild/test.py b/winbuild/test.py index 559ecdec1..a05a20b18 100755 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -13,7 +13,7 @@ def test_one(params): try: print("Running: %s, %s" % params) command = [ - r"%s\%s%s\Scripts\python.exe" % (VIRT_BASE, python, architecture), + r"{}\{}{}\Scripts\python.exe".format(VIRT_BASE, python, architecture), "test-installed.py", "--processes=-0", "--process-timeout=30", @@ -22,10 +22,10 @@ def test_one(params): proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) (trace, stderr) = proc.communicate() status = proc.returncode - print("Done with %s, %s -- %s" % (python, architecture, status)) + print("Done with {}, {} -- {}".format(python, architecture, status)) return (python, architecture, status, trace) except Exception as msg: - print("Error with %s, %s: %s" % (python, architecture, msg)) + print("Error with {}, {}: {}".format(python, architecture, msg)) return (python, architecture, -1, str(msg)) @@ -39,7 +39,7 @@ if __name__ == "__main__": results = map(test_one, matrix) for (python, architecture, status, trace) in results: - print("%s%s: %s" % (python, architecture, status and "ERR" or "PASS")) + print("{}{}: {}".format(python, architecture, status and "ERR" or "PASS")) res = all(status for (python, architecture, status, trace) in results) sys.exit(res)