mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Merge remote-tracking branch 'upstream/master' into formats
This commit is contained in:
commit
a340dc5fd3
|
@ -30,7 +30,10 @@ pip install -U pytest-cov
|
||||||
pip install pyroma
|
pip install pyroma
|
||||||
pip install test-image-results
|
pip install test-image-results
|
||||||
pip install numpy
|
pip install numpy
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "3.9-dev" ]; then pip install setuptools==47.3.1 ; fi
|
|
||||||
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
if [ "$GHA_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi
|
||||||
|
|
||||||
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
||||||
# arm64, ppc64le, s390x CPUs:
|
# arm64, ppc64le, s390x CPUs:
|
||||||
# "ERROR: Could not find a version that satisfies the requirement pyqt5"
|
# "ERROR: Could not find a version that satisfies the requirement pyqt5"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
python -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
python -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
||||||
|
|
||||||
# Docs
|
# Docs
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then
|
if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then
|
||||||
|
|
3
.github/workflows/macos-install.sh
vendored
3
.github/workflows/macos-install.sh
vendored
|
@ -15,5 +15,8 @@ pip install test-image-results
|
||||||
echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg
|
echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg
|
||||||
pip install numpy
|
pip install numpy
|
||||||
|
|
||||||
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
if [ "$GHA_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi
|
||||||
|
|
||||||
# extra test images
|
# extra test images
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
81
.github/workflows/test-windows.yml
vendored
81
.github/workflows/test-windows.yml
vendored
|
@ -63,7 +63,12 @@ jobs:
|
||||||
- name: pip install wheel pytest pytest-cov
|
- name: pip install wheel pytest pytest-cov
|
||||||
run: python -m pip install wheel pytest pytest-cov
|
run: python -m pip install wheel pytest pytest-cov
|
||||||
|
|
||||||
- name: Prepare dependencies
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
- name: Upgrade setuptools
|
||||||
|
if: "contains(matrix.python-version, '3.9-dev')"
|
||||||
|
run: python -m pip install -U "setuptools>=49.3.2"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
7z x winbuild\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"
|
||||||
|
@ -72,41 +77,71 @@ jobs:
|
||||||
Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin"
|
Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin"
|
||||||
|
|
||||||
xcopy /s winbuild\depends\test_images\* Tests\images\
|
xcopy /s winbuild\depends\test_images\* Tests\images\
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
& python.exe winbuild\build_prepare.py -v --python=$env:pythonLocation
|
- name: Cache build
|
||||||
|
id: build-cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: winbuild\build
|
||||||
|
key:
|
||||||
|
${{ hashFiles('winbuild\build_prepare.py') }}-${{ hashFiles('.github\workflows\test-windows.yml') }}-${{ env.pythonLocation }}
|
||||||
|
|
||||||
|
- name: Prepare build
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
& python.exe winbuild\build_prepare.py -v --python=$env:pythonLocation --srcdir
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Build dependencies / libjpeg-turbo
|
- name: Build dependencies / libjpeg-turbo
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libjpeg.cmd"
|
run: "& winbuild\\build\\build_dep_libjpeg.cmd"
|
||||||
- name: Build dependencies / zlib
|
- name: Build dependencies / zlib
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_zlib.cmd"
|
run: "& winbuild\\build\\build_dep_zlib.cmd"
|
||||||
- name: Build dependencies / LibTiff
|
- name: Build dependencies / LibTiff
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libtiff.cmd"
|
run: "& winbuild\\build\\build_dep_libtiff.cmd"
|
||||||
- name: Build dependencies / WebP
|
- name: Build dependencies / WebP
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libwebp.cmd"
|
run: "& winbuild\\build\\build_dep_libwebp.cmd"
|
||||||
- name: Build dependencies / FreeType
|
- name: Build dependencies / FreeType
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_freetype.cmd"
|
run: "& winbuild\\build\\build_dep_freetype.cmd"
|
||||||
- name: Build dependencies / LCMS2
|
- name: Build dependencies / LCMS2
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_lcms2.cmd"
|
run: "& winbuild\\build\\build_dep_lcms2.cmd"
|
||||||
- name: Build dependencies / OpenJPEG
|
- name: Build dependencies / OpenJPEG
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
|
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
|
||||||
|
|
||||||
# GPL licensed; skip if building wheels
|
# GPL licensed
|
||||||
- name: Build dependencies / libimagequant
|
- name: Build dependencies / libimagequant
|
||||||
if: "github.event_name != 'push'"
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libimagequant.cmd"
|
run: "& winbuild\\build\\build_dep_libimagequant.cmd"
|
||||||
|
|
||||||
# Raqm dependencies
|
# Raqm dependencies
|
||||||
- name: Build dependencies / HarfBuzz
|
- name: Build dependencies / HarfBuzz
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
||||||
- name: Build dependencies / FriBidi
|
- name: Build dependencies / FriBidi
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_fribidi.cmd"
|
run: "& winbuild\\build\\build_dep_fribidi.cmd"
|
||||||
- name: Build dependencies / Raqm
|
- name: Build dependencies / Raqm
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libraqm.cmd"
|
run: "& winbuild\\build\\build_dep_libraqm.cmd"
|
||||||
|
|
||||||
|
# trim ~150MB x 9
|
||||||
|
- name: Optimize build cache
|
||||||
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
|
run: rmdir /S /Q winbuild\build\src
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
- name: Build Pillow
|
- name: Build Pillow
|
||||||
run: |
|
run: |
|
||||||
& winbuild\build\build_pillow.cmd install
|
$FLAGS=""
|
||||||
|
if ('${{ github.event_name }}' -eq 'push') { $FLAGS="--disable-imagequant" }
|
||||||
|
& winbuild\build\build_pillow.cmd $FLAGS install
|
||||||
& $env:pythonLocation\python.exe selftest.py --installed
|
& $env:pythonLocation\python.exe selftest.py --installed
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
|
@ -151,7 +186,7 @@ jobs:
|
||||||
if: "github.event_name == 'push'"
|
if: "github.event_name == 'push'"
|
||||||
run: |
|
run: |
|
||||||
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a
|
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a
|
||||||
winbuild\\build\\build_pillow.cmd bdist_wheel"
|
winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
@ -169,8 +204,10 @@ jobs:
|
||||||
mingw: ["MINGW32", "MINGW64"]
|
mingw: ["MINGW32", "MINGW64"]
|
||||||
include:
|
include:
|
||||||
- mingw: "MINGW32"
|
- mingw: "MINGW32"
|
||||||
|
name: "MSYS2 MinGW 32-bit"
|
||||||
package: "mingw-w64-i686"
|
package: "mingw-w64-i686"
|
||||||
- mingw: "MINGW64"
|
- mingw: "MINGW64"
|
||||||
|
name: "MSYS2 MinGW 64-bit"
|
||||||
package: "mingw-w64-x86_64"
|
package: "mingw-w64-x86_64"
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
|
@ -181,7 +218,7 @@ jobs:
|
||||||
CHERE_INVOKING: 1
|
CHERE_INVOKING: 1
|
||||||
|
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
name: MSYS2 ${{ matrix.mingw }}
|
name: ${{ matrix.name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -193,23 +230,23 @@ jobs:
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
pacman -S --noconfirm \
|
pacman -S --noconfirm \
|
||||||
|
${{ matrix.package }}-python3-cffi \
|
||||||
|
${{ matrix.package }}-python3-numpy \
|
||||||
|
${{ matrix.package }}-python3-olefile \
|
||||||
${{ matrix.package }}-python3-pip \
|
${{ matrix.package }}-python3-pip \
|
||||||
${{ matrix.package }}-python3-setuptools \
|
${{ matrix.package }}-python3-pyqt5 \
|
||||||
${{ matrix.package }}-python3-pytest \
|
${{ matrix.package }}-python3-pytest \
|
||||||
${{ matrix.package }}-python3-pytest-cov \
|
${{ matrix.package }}-python3-pytest-cov \
|
||||||
${{ matrix.package }}-python3-cffi \
|
${{ matrix.package }}-python3-setuptools \
|
||||||
${{ matrix.package }}-python3-olefile \
|
|
||||||
${{ matrix.package }}-python3-numpy \
|
|
||||||
${{ matrix.package }}-python3-pyqt5 \
|
|
||||||
${{ matrix.package }}-python3-numpy \
|
|
||||||
${{ matrix.package }}-freetype \
|
${{ matrix.package }}-freetype \
|
||||||
${{ matrix.package }}-lcms2 \
|
|
||||||
${{ matrix.package }}-libwebp \
|
|
||||||
${{ matrix.package }}-libjpeg-turbo \
|
|
||||||
${{ matrix.package }}-openjpeg2 \
|
|
||||||
${{ matrix.package }}-libimagequant \
|
|
||||||
${{ matrix.package }}-libraqm \
|
|
||||||
${{ matrix.package }}-ghostscript \
|
${{ matrix.package }}-ghostscript \
|
||||||
|
${{ matrix.package }}-lcms2 \
|
||||||
|
${{ matrix.package }}-libimagequant \
|
||||||
|
${{ matrix.package }}-libjpeg-turbo \
|
||||||
|
${{ matrix.package }}-libraqm \
|
||||||
|
${{ matrix.package }}-libtiff \
|
||||||
|
${{ matrix.package }}-libwebp \
|
||||||
|
${{ matrix.package }}-openjpeg2 \
|
||||||
subversion
|
subversion
|
||||||
|
|
||||||
python3 -m pip install pyroma
|
python3 -m pip install pyroma
|
||||||
|
@ -217,9 +254,7 @@ jobs:
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
||||||
- name: Build Pillow
|
- name: Build Pillow
|
||||||
run: |
|
run: CFLAGS="-coverage" python3 setup.py build_ext install
|
||||||
# libtiff is unable to open files
|
|
||||||
CFLAGS="-coverage" python3 setup.py build_ext --disable-tiff install
|
|
||||||
|
|
||||||
- name: Test Pillow
|
- name: Test Pillow
|
||||||
run: |
|
run: |
|
||||||
|
@ -231,4 +266,4 @@ jobs:
|
||||||
python3 -m pip install codecov
|
python3 -m pip install codecov
|
||||||
bash <(curl -s https://codecov.io/bash) -F GHA_Windows
|
bash <(curl -s https://codecov.io/bash) -F GHA_Windows
|
||||||
env:
|
env:
|
||||||
CODECOV_NAME: MSYS2 ${{ matrix.mingw }}
|
CODECOV_NAME: ${{ matrix.name }}
|
||||||
|
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
@ -62,11 +62,15 @@ jobs:
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
.ci/install.sh
|
.ci/install.sh
|
||||||
|
env:
|
||||||
|
GHA_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install macOS dependencies
|
- name: Install macOS dependencies
|
||||||
if: startsWith(matrix.os, 'macOS')
|
if: startsWith(matrix.os, 'macOS')
|
||||||
run: |
|
run: |
|
||||||
.github/workflows/macos-install.sh
|
.github/workflows/macos-install.sh
|
||||||
|
env:
|
||||||
|
GHA_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 6bedb5c58a7d8c25aa9509f8217bc24e9797e90d # frozen: 19.10b0
|
rev: e66be67b9b6811913470f70c28b4d50f94d05b22 # frozen: 20.8b1
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: ["--target-version", "py35"]
|
args: ["--target-version", "py36"]
|
||||||
# Only .py files, until https://github.com/psf/black/issues/402 resolved
|
# Only .py files, until https://github.com/psf/black/issues/402 resolved
|
||||||
files: \.py$
|
files: \.py$
|
||||||
types: []
|
types: []
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/timothycrosley/isort
|
||||||
rev: 7c29dd9d55161704cfc45998c6f5c2c43d39264b # frozen: 4.3.21
|
rev: 377d260ffa6f746693f97b46d95025afc4bd8275 # frozen: 5.4.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
|
|
||||||
- repo: https://github.com/asottile/yesqa
|
- repo: https://github.com/asottile/yesqa
|
||||||
rev: b13a51aa54142c59219c764e9f9362c049b439ed # frozen: v1.2.0
|
rev: 7a009f3ee493c796827ee334f9058b110a0e0db8 # frozen: v1.2.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: yesqa
|
- id: yesqa
|
||||||
|
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
rev: ffbd448645bad2e7ca13f96fca5830058d27ccd5 # frozen: v1.1.7
|
rev: f30f4974a08a6b2f6a1eeaf30a4d501cf909163a # frozen: v1.1.9
|
||||||
hooks:
|
hooks:
|
||||||
- id: remove-tabs
|
- id: remove-tabs
|
||||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
|
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
|
||||||
|
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
rev: 735cfe7e1c57a8e05f660ba75de72313005af54a # frozen: 3.8.2
|
rev: 05f6544aef321e2fee03a1277ce2eef8880fb927 # frozen: 3.8.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [flake8-2020, flake8-implicit-str-concat]
|
additional_dependencies: [flake8-2020, flake8-implicit-str-concat]
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: 0d7d077d6ed5624854f93ac601739c1804ebeb98 # frozen: v1.5.1
|
rev: eae6397e4c259ed3d057511f6dd5330b92867e62 # frozen: v1.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: python-check-blanket-noqa
|
- id: python-check-blanket-noqa
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: ebc15addedad713c86ef18ae9632c88e187dd0af # frozen: v3.1.0
|
rev: e1668fe86af3810fbca72b8653fe478e66a0afdc # frozen: v3.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
|
|
32
CHANGES.rst
32
CHANGES.rst
|
@ -5,13 +5,43 @@ Changelog (Pillow)
|
||||||
8.0.0 (unreleased)
|
8.0.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Raise proper TypeError in putpixel #4882
|
||||||
|
[nulano, hugovk]
|
||||||
|
|
||||||
|
- Added writing of subIFDs #4862
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fix IFDRational __eq__ bug #4888
|
||||||
|
[luphord, radarhere]
|
||||||
|
|
||||||
|
- Fixed duplicate variable name #4885
|
||||||
|
[liZe, radarhere]
|
||||||
|
|
||||||
|
- Added homebrew zlib include directory #4842
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Corrected inverted PDF CMYK colors #4866
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Do not try to close file pointer if file pointer is empty #4823
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- ImageOps.autocontrast: add mask parameter #4843
|
||||||
|
[navneeth, hugovk]
|
||||||
|
|
||||||
|
- Read EXIF data tEXt chunk into info as bytes instead of string #4828
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Remove long-deprecated Image.py functions #4798
|
- Remove long-deprecated Image.py functions #4798
|
||||||
[hugovk, nulano, radarhere]
|
[hugovk, nulano, radarhere]
|
||||||
|
|
||||||
|
- Replaced distutils with setuptools #4797, #4809, #4814, #4817, #4829, #4890
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
- Add MIME type to PsdImagePlugin #4788
|
- Add MIME type to PsdImagePlugin #4788
|
||||||
[samamorgan]
|
[samamorgan]
|
||||||
|
|
||||||
- Drop support for EOL Python 3.5 #4746
|
- Drop support for EOL Python 3.5 #4746, #4794
|
||||||
[hugovk, radarhere, nulano]
|
[hugovk, radarhere, nulano]
|
||||||
|
|
||||||
- Remove ImageCms.CmsProfile attributes deprecated since 3.2.0 #4768
|
- Remove ImageCms.CmsProfile attributes deprecated since 3.2.0 #4768
|
||||||
|
|
96
README.md
Normal file
96
README.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<p align="center">
|
||||||
|
<img width="248" height="250" src="https://raw.githubusercontent.com/python-pillow/pillow-logo/master/pillow-logo-248x250.png" alt="Pillow logo">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Pillow
|
||||||
|
|
||||||
|
## Python Imaging Library (Fork)
|
||||||
|
|
||||||
|
Pillow is the friendly PIL fork by [Alex Clark and
|
||||||
|
Contributors](https://github.com/python-pillow/Pillow/graphs/contributors).
|
||||||
|
PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
|
||||||
|
As of 2019, Pillow development is
|
||||||
|
[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>docs</th>
|
||||||
|
<td>
|
||||||
|
<a href="https://pillow.readthedocs.io/?badge=latest"><img
|
||||||
|
alt="Documentation Status"
|
||||||
|
src="https://readthedocs.org/projects/pillow/badge/?version=latest"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>tests</th>
|
||||||
|
<td>
|
||||||
|
<a href="https://travis-ci.org/python-pillow/Pillow"><img
|
||||||
|
alt="Travis CI build status (Linux)"
|
||||||
|
src="https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build"></a>
|
||||||
|
<a href="https://travis-ci.org/python-pillow/pillow-wheels"><img
|
||||||
|
alt="Travis CI build status (macOS)"
|
||||||
|
src="https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build"></a>
|
||||||
|
<a href="https://ci.appveyor.com/project/python-pillow/Pillow"><img
|
||||||
|
alt="AppVeyor CI build status (Windows)"
|
||||||
|
src="https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build"></a>
|
||||||
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint"><img
|
||||||
|
alt="GitHub Actions build status (Lint)"
|
||||||
|
src="https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg"></a>
|
||||||
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ATest"><img
|
||||||
|
alt="GitHub Actions build status (Test Linux and macOS)"
|
||||||
|
src="https://github.com/python-pillow/Pillow/workflows/Test/badge.svg"></a>
|
||||||
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22"><img
|
||||||
|
alt="GitHub Actions build status (Test Windows)"
|
||||||
|
src="https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg"></a>
|
||||||
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22"><img
|
||||||
|
alt="GitHub Actions build status (Test Docker)"
|
||||||
|
src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></a>
|
||||||
|
<a href="https://codecov.io/gh/python-pillow/Pillow"><img
|
||||||
|
alt="Code coverage"
|
||||||
|
src="https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>package</th>
|
||||||
|
<td>
|
||||||
|
<a href="https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow"><img
|
||||||
|
alt="Zenodo"
|
||||||
|
src="https://zenodo.org/badge/17549/python-pillow/Pillow.svg"></a>
|
||||||
|
<a href="https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=badge"><img
|
||||||
|
alt="Tidelift"
|
||||||
|
src="https://tidelift.com/badges/package/pypi/Pillow?style=flat"></a>
|
||||||
|
<a href="https://pypi.org/project/Pillow/"><img
|
||||||
|
alt="Newest PyPI version"
|
||||||
|
src="https://img.shields.io/pypi/v/pillow.svg"></a>
|
||||||
|
<a href="https://pypi.org/project/Pillow/"><img
|
||||||
|
alt="Number of PyPI downloads"
|
||||||
|
src="https://img.shields.io/pypi/dm/pillow.svg"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>social</th>
|
||||||
|
<td>
|
||||||
|
<a href="https://gitter.im/python-pillow/Pillow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img
|
||||||
|
alt="Join the chat at https://gitter.im/python-pillow/Pillow"
|
||||||
|
src="https://badges.gitter.im/python-pillow/Pillow.svg"></a>
|
||||||
|
<a href="https://twitter.com/PythonPillow"><img
|
||||||
|
alt="Follow on https://twitter.com/PythonPillow"
|
||||||
|
src="https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
- [Documentation](https://pillow.readthedocs.io/)
|
||||||
|
- [Installation](https://pillow.readthedocs.io/en/latest/installation.html)
|
||||||
|
- [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html)
|
||||||
|
- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md)
|
||||||
|
- [Issues](https://github.com/python-pillow/Pillow/issues)
|
||||||
|
- [Pull requests](https://github.com/python-pillow/Pillow/pulls)
|
||||||
|
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
|
||||||
|
- [Pre-fork](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork)
|
||||||
|
|
||||||
|
## Report a Vulnerability
|
||||||
|
|
||||||
|
To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security).
|
103
README.rst
103
README.rst
|
@ -1,103 +0,0 @@
|
||||||
Pillow
|
|
||||||
======
|
|
||||||
|
|
||||||
Python Imaging Library (Fork)
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is `supported by Tidelift <https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise>`_.
|
|
||||||
|
|
||||||
.. start-badges
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:stub-columns: 1
|
|
||||||
|
|
||||||
* - docs
|
|
||||||
- |docs|
|
|
||||||
* - tests
|
|
||||||
- |linux| |macos| |windows| |gha_lint| |gha| |gha_windows| |gha_docker| |coverage|
|
|
||||||
* - package
|
|
||||||
- |zenodo| |tidelift| |version| |downloads|
|
|
||||||
* - social
|
|
||||||
- |gitter| |twitter|
|
|
||||||
|
|
||||||
.. end-badges
|
|
||||||
|
|
||||||
More Information
|
|
||||||
----------------
|
|
||||||
|
|
||||||
- `Documentation <https://pillow.readthedocs.io/>`_
|
|
||||||
|
|
||||||
- `Installation <https://pillow.readthedocs.io/en/latest/installation.html>`_
|
|
||||||
- `Handbook <https://pillow.readthedocs.io/en/latest/handbook/index.html>`_
|
|
||||||
|
|
||||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
|
||||||
|
|
||||||
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
|
||||||
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
|
||||||
|
|
||||||
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
|
|
||||||
|
|
||||||
- `Pre-fork <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork>`_
|
|
||||||
|
|
||||||
Report a Vulnerability
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
To report a security vulnerability, please follow the procedure described in the `Tidelift security policy <https://tidelift.com/docs/security>`_.
|
|
||||||
|
|
||||||
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
|
||||||
:target: https://pillow.readthedocs.io/?badge=latest
|
|
||||||
:alt: Documentation Status
|
|
||||||
|
|
||||||
.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
|
|
||||||
:target: https://travis-ci.org/python-pillow/Pillow
|
|
||||||
:alt: Travis CI build status (Linux)
|
|
||||||
|
|
||||||
.. |macos| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build
|
|
||||||
:target: https://travis-ci.org/python-pillow/pillow-wheels
|
|
||||||
:alt: Travis CI build status (macOS)
|
|
||||||
|
|
||||||
.. |windows| image:: https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build
|
|
||||||
:target: https://ci.appveyor.com/project/python-pillow/Pillow
|
|
||||||
:alt: AppVeyor CI build status (Windows)
|
|
||||||
|
|
||||||
.. |gha_lint| image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg
|
|
||||||
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint
|
|
||||||
:alt: GitHub Actions build status (Lint)
|
|
||||||
|
|
||||||
.. |gha_docker| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg
|
|
||||||
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22
|
|
||||||
:alt: GitHub Actions build status (Test Docker)
|
|
||||||
|
|
||||||
.. |gha| image:: https://github.com/python-pillow/Pillow/workflows/Test/badge.svg
|
|
||||||
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ATest
|
|
||||||
:alt: GitHub Actions build status (Test Linux and macOS)
|
|
||||||
|
|
||||||
.. |gha_windows| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg
|
|
||||||
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22
|
|
||||||
:alt: GitHub Actions build status (Test Windows)
|
|
||||||
|
|
||||||
.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
|
|
||||||
:target: https://codecov.io/gh/python-pillow/Pillow
|
|
||||||
:alt: Code coverage
|
|
||||||
|
|
||||||
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
|
|
||||||
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
|
|
||||||
|
|
||||||
.. |tidelift| image:: https://tidelift.com/badges/package/pypi/Pillow?style=flat
|
|
||||||
:target: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=badge
|
|
||||||
|
|
||||||
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
|
|
||||||
:target: https://pypi.org/project/Pillow/
|
|
||||||
:alt: Latest PyPI version
|
|
||||||
|
|
||||||
.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg
|
|
||||||
:target: https://pypi.org/project/Pillow/
|
|
||||||
:alt: Number of PyPI downloads
|
|
||||||
|
|
||||||
.. |gitter| image:: https://badges.gitter.im/python-pillow/Pillow.svg
|
|
||||||
:target: https://gitter.im/python-pillow/Pillow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
|
||||||
:alt: Join the chat at https://gitter.im/python-pillow/Pillow
|
|
||||||
|
|
||||||
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
|
|
||||||
:target: https://twitter.com/PythonPillow
|
|
||||||
:alt: Follow on https://twitter.com/PythonPillow
|
|
|
@ -101,11 +101,7 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
cd pillow-wheels
|
cd pillow-wheels
|
||||||
./update-pillow-tag.sh [[release tag]]
|
./update-pillow-tag.sh [[release tag]]
|
||||||
```
|
```
|
||||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases).
|
||||||
```bash
|
|
||||||
wget -m -A 'Pillow-<VERSION>-*' \
|
|
||||||
http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com
|
|
||||||
```
|
|
||||||
|
|
||||||
## Publicize Release
|
## Publicize Release
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,17 @@ def timer(func, label, *args):
|
||||||
func(*args)
|
func(*args)
|
||||||
if time.time() - starttime > 10:
|
if time.time() - starttime > 10:
|
||||||
print(
|
print(
|
||||||
"%s: breaking at %s iterations, %.6f per iteration"
|
"{}: breaking at {} iterations, {:.6f} per iteration".format(
|
||||||
% (label, x + 1, (time.time() - starttime) / (x + 1.0))
|
label, x + 1, (time.time() - starttime) / (x + 1.0)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
if x == iterations - 1:
|
if x == iterations - 1:
|
||||||
endtime = time.time()
|
endtime = time.time()
|
||||||
print(
|
print(
|
||||||
"%s: %.4f s %.6f per iteration"
|
"{}: {:.4f} s {:.6f} per iteration".format(
|
||||||
% (label, endtime - starttime, (endtime - starttime) / (x + 1.0))
|
label, endtime - starttime, (endtime - starttime) / (x + 1.0)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32
|
from .helper import is_win32
|
||||||
|
@ -11,7 +12,7 @@ pytestmark = pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
|
||||||
|
|
||||||
|
|
||||||
def _get_mem_usage():
|
def _get_mem_usage():
|
||||||
from resource import getpagesize, getrusage, RUSAGE_SELF
|
from resource import RUSAGE_SELF, getpagesize, getrusage
|
||||||
|
|
||||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||||
return mem * getpagesize() / 1024 / 1024
|
return mem * getpagesize() / 1024 / 1024
|
||||||
|
@ -25,7 +26,7 @@ def _test_leak(min_iterations, max_iterations, fn, *args, **kwargs):
|
||||||
if i < min_iterations:
|
if i < min_iterations:
|
||||||
mem_limit = mem + 1
|
mem_limit = mem + 1
|
||||||
continue
|
continue
|
||||||
msg = "memory usage limit exceeded after %d iterations" % (i + 1)
|
msg = f"memory usage limit exceeded after {i + 1} iterations"
|
||||||
assert mem <= mem_limit, msg
|
assert mem <= mem_limit, msg
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32, skip_unless_feature
|
from .helper import is_win32, skip_unless_feature
|
||||||
|
@ -18,7 +19,7 @@ pytestmark = [
|
||||||
|
|
||||||
|
|
||||||
def test_leak_load():
|
def test_leak_load():
|
||||||
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
|
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit
|
||||||
|
|
||||||
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
||||||
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
||||||
|
@ -28,7 +29,7 @@ def test_leak_load():
|
||||||
|
|
||||||
|
|
||||||
def test_leak_save():
|
def test_leak_save():
|
||||||
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
|
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit
|
||||||
|
|
||||||
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
||||||
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -119,60 +119,59 @@ def test_qtables_leak():
|
||||||
|
|
||||||
def test_exif_leak():
|
def test_exif_leak():
|
||||||
"""
|
"""
|
||||||
pre patch:
|
pre patch:
|
||||||
|
|
||||||
MB
|
MB
|
||||||
177.1^ #
|
177.1^ #
|
||||||
| @@@#
|
| @@@#
|
||||||
| :@@@@@@#
|
| :@@@@@@#
|
||||||
| ::::@@@@@@#
|
| ::::@@@@@@#
|
||||||
| ::::::::@@@@@@#
|
| ::::::::@@@@@@#
|
||||||
| @@::::: ::::@@@@@@#
|
| @@::::: ::::@@@@@@#
|
||||||
| @@@@ ::::: ::::@@@@@@#
|
| @@@@ ::::: ::::@@@@@@#
|
||||||
| @@@@@@@ ::::: ::::@@@@@@#
|
| @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@::@@@@@@@ ::::: ::::@@@@@@#
|
| @@::@@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@@@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @@@@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@@@@@ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @@@@@@ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@@@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @@@@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @::@@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @::@@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| ::::@: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| ::::@: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| :@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| :@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| ::@@::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| ::@@::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@::: @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @@::: @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @::@ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @::@ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| :::@: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| :::@: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
| @@@:: @: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
| @@@:: @: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
|
||||||
0 +----------------------------------------------------------------------->Gi
|
0 +----------------------------------------------------------------------->Gi
|
||||||
0 11.37
|
0 11.37
|
||||||
|
|
||||||
|
|
||||||
post patch:
|
post patch:
|
||||||
|
|
||||||
MB
|
MB
|
||||||
21.06^ ::::::::::::::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
21.06^ ::::::::::::::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| ##::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| ##::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @@@@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @@@@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
|
||||||
0 +----------------------------------------------------------------------->Gi
|
0 +----------------------------------------------------------------------->Gi
|
||||||
0 11.33
|
0 11.33
|
||||||
|
"""
|
||||||
"""
|
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
exif = b"12345678" * 4096
|
exif = b"12345678" * 4096
|
||||||
|
|
||||||
|
@ -183,31 +182,30 @@ post patch:
|
||||||
|
|
||||||
def test_base_save():
|
def test_base_save():
|
||||||
"""
|
"""
|
||||||
base case:
|
base case:
|
||||||
MB
|
MB
|
||||||
20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@:::
|
20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@:::
|
||||||
| ##: : ::::::@::::::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| ##: : ::::::@::::::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@# : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@# : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@@ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@@ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| :@@@@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| :@@@@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
|
||||||
0 +----------------------------------------------------------------------->Gi
|
0 +----------------------------------------------------------------------->Gi
|
||||||
0 7.882
|
0 7.882"""
|
||||||
"""
|
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
|
|
||||||
for _ in range(iterations):
|
for _ in range(iterations):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
||||||
|
|
||||||
|
|
||||||
def test_libtiff_segfault():
|
def test_libtiff_segfault():
|
||||||
""" This test should not segfault. It will on Pillow <= 3.1.0 and
|
"""This test should not segfault. It will on Pillow <= 3.1.0 and
|
||||||
libtiff >= 4.0.0
|
libtiff >= 4.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
|
@ -42,8 +42,8 @@ def test_dos_total_memory():
|
||||||
info = PngImagePlugin.PngInfo()
|
info = PngImagePlugin.PngInfo()
|
||||||
|
|
||||||
for x in range(64):
|
for x in range(64):
|
||||||
info.add_text("t%s" % x, compressed_data, zip=True)
|
info.add_text(f"t{x}", compressed_data, zip=True)
|
||||||
info.add_itxt("i%s" % x, compressed_data, zip=True)
|
info.add_itxt(f"i{x}", compressed_data, zip=True)
|
||||||
|
|
||||||
b = BytesIO()
|
b = BytesIO()
|
||||||
im.save(b, "PNG", pnginfo=info)
|
im.save(b, "PNG", pnginfo=info)
|
||||||
|
|
|
@ -9,4 +9,4 @@ def pytest_report_header(config):
|
||||||
features.pilinfo(out=out, supported_formats=False)
|
features.pilinfo(out=out, supported_formats=False)
|
||||||
return out.getvalue()
|
return out.getvalue()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return "pytest_report_header failed: %s" % e
|
return f"pytest_report_header failed: {e}"
|
||||||
|
|
|
@ -6,7 +6,7 @@ if __name__ == "__main__":
|
||||||
# create font data chunk for embedding
|
# create font data chunk for embedding
|
||||||
font = "Tests/images/courB08"
|
font = "Tests/images/courB08"
|
||||||
print(" f._load_pilfont_data(")
|
print(" f._load_pilfont_data(")
|
||||||
print(" # %s" % os.path.basename(font))
|
print(f" # {os.path.basename(font)}")
|
||||||
print(" BytesIO(base64.decodestring(b'''")
|
print(" BytesIO(base64.decodestring(b'''")
|
||||||
with open(font + ".pil", "rb") as fp:
|
with open(font + ".pil", "rb") as fp:
|
||||||
print(base64.b64encode(fp.read()).decode())
|
print(base64.b64encode(fp.read()).decode())
|
||||||
|
|
|
@ -11,6 +11,7 @@ import tempfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMath, features
|
from PIL import Image, ImageMath, features
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -67,37 +68,31 @@ def convert_to_comparable(a, b):
|
||||||
|
|
||||||
def assert_deep_equal(a, b, msg=None):
|
def assert_deep_equal(a, b, msg=None):
|
||||||
try:
|
try:
|
||||||
assert len(a) == len(b), msg or "got length {}, expected {}".format(
|
assert len(a) == len(b), msg or f"got length {len(a)}, expected {len(b)}"
|
||||||
len(a), len(b)
|
|
||||||
)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
assert a == b, msg
|
assert a == b, msg
|
||||||
|
|
||||||
|
|
||||||
def assert_image(im, mode, size, msg=None):
|
def assert_image(im, mode, size, msg=None):
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
assert im.mode == mode, msg or "got mode {!r}, expected {!r}".format(
|
assert im.mode == mode, (
|
||||||
im.mode, mode
|
msg or f"got mode {repr(im.mode)}, expected {repr(mode)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if size is not None:
|
if size is not None:
|
||||||
assert im.size == size, msg or "got size {!r}, expected {!r}".format(
|
assert im.size == size, (
|
||||||
im.size, size
|
msg or f"got size {repr(im.size)}, expected {repr(size)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def assert_image_equal(a, b, msg=None):
|
def assert_image_equal(a, b, msg=None):
|
||||||
assert a.mode == b.mode, msg or "got mode {!r}, expected {!r}".format(
|
assert a.mode == b.mode, msg or f"got mode {repr(a.mode)}, expected {repr(b.mode)}"
|
||||||
a.mode, b.mode
|
assert a.size == b.size, msg or f"got size {repr(a.size)}, expected {repr(b.size)}"
|
||||||
)
|
|
||||||
assert a.size == b.size, msg or "got size {!r}, expected {!r}".format(
|
|
||||||
a.size, b.size
|
|
||||||
)
|
|
||||||
if a.tobytes() != b.tobytes():
|
if a.tobytes() != b.tobytes():
|
||||||
if HAS_UPLOADER:
|
if HAS_UPLOADER:
|
||||||
try:
|
try:
|
||||||
url = test_image_results.upload(a, b)
|
url = test_image_results.upload(a, b)
|
||||||
logger.error("Url for test images: %s" % url)
|
logger.error(f"Url for test images: {url}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -112,12 +107,8 @@ def assert_image_equal_tofile(a, filename, msg=None, mode=None):
|
||||||
|
|
||||||
|
|
||||||
def assert_image_similar(a, b, epsilon, msg=None):
|
def assert_image_similar(a, b, epsilon, msg=None):
|
||||||
assert a.mode == b.mode, msg or "got mode {!r}, expected {!r}".format(
|
assert a.mode == b.mode, msg or f"got mode {repr(a.mode)}, expected {repr(b.mode)}"
|
||||||
a.mode, b.mode
|
assert a.size == b.size, msg or f"got size {repr(a.size)}, expected {repr(b.size)}"
|
||||||
)
|
|
||||||
assert a.size == b.size, msg or "got size {!r}, expected {!r}".format(
|
|
||||||
a.size, b.size
|
|
||||||
)
|
|
||||||
|
|
||||||
a, b = convert_to_comparable(a, b)
|
a, b = convert_to_comparable(a, b)
|
||||||
|
|
||||||
|
@ -129,13 +120,14 @@ def assert_image_similar(a, b, epsilon, msg=None):
|
||||||
ave_diff = diff / (a.size[0] * a.size[1])
|
ave_diff = diff / (a.size[0] * a.size[1])
|
||||||
try:
|
try:
|
||||||
assert epsilon >= ave_diff, (
|
assert epsilon >= ave_diff, (
|
||||||
msg or ""
|
(msg or "")
|
||||||
) + " average pixel value difference %.4f > epsilon %.4f" % (ave_diff, epsilon)
|
+ f" average pixel value difference {ave_diff:.4f} > epsilon {epsilon:.4f}"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if HAS_UPLOADER:
|
if HAS_UPLOADER:
|
||||||
try:
|
try:
|
||||||
url = test_image_results.upload(a, b)
|
url = test_image_results.upload(a, b)
|
||||||
logger.error("Url for test images: %s" % url)
|
logger.error(f"Url for test images: {url}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
raise e
|
raise e
|
||||||
|
@ -166,7 +158,7 @@ def assert_tuple_approx_equal(actuals, targets, threshold, msg):
|
||||||
|
|
||||||
|
|
||||||
def skip_unless_feature(feature):
|
def skip_unless_feature(feature):
|
||||||
reason = "%s not available" % feature
|
reason = f"{feature} not available"
|
||||||
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,7 +176,7 @@ class PillowLeakTestCase:
|
||||||
:returns: memory usage in kilobytes
|
:returns: memory usage in kilobytes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from resource import getrusage, RUSAGE_SELF
|
from resource import RUSAGE_SELF, getrusage
|
||||||
|
|
||||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
|
@ -204,7 +196,7 @@ class PillowLeakTestCase:
|
||||||
for cycle in range(self.iterations):
|
for cycle in range(self.iterations):
|
||||||
core()
|
core()
|
||||||
mem = self._get_mem_usage() - start_mem
|
mem = self._get_mem_usage() - start_mem
|
||||||
msg = "memory usage limit exceeded in iteration %d" % cycle
|
msg = f"memory usage limit exceeded in iteration {cycle}"
|
||||||
assert mem < self.mem_limit, msg
|
assert mem < self.mem_limit, msg
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
Tests/images/empty_gps_ifd.jpg
Normal file
BIN
Tests/images/empty_gps_ifd.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
Tests/images/exif_text.png
Normal file
BIN
Tests/images/exif_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_similar
|
from .helper import assert_image_similar
|
||||||
|
@ -15,8 +16,8 @@ def get_files(d, ext=".bmp"):
|
||||||
|
|
||||||
|
|
||||||
def test_bad():
|
def test_bad():
|
||||||
""" These shouldn't crash/dos, but they shouldn't return anything
|
"""These shouldn't crash/dos, but they shouldn't return anything
|
||||||
either """
|
either"""
|
||||||
for f in get_files("b"):
|
for f in get_files("b"):
|
||||||
|
|
||||||
def open(f):
|
def open(f):
|
||||||
|
@ -31,8 +32,8 @@ def test_bad():
|
||||||
|
|
||||||
|
|
||||||
def test_questionable():
|
def test_questionable():
|
||||||
""" These shouldn't crash/dos, but it's not well defined that these
|
"""These shouldn't crash/dos, but it's not well defined that these
|
||||||
are in spec """
|
are in spec"""
|
||||||
supported = [
|
supported = [
|
||||||
"pal8os2v2.bmp",
|
"pal8os2v2.bmp",
|
||||||
"rgb24prof.bmp",
|
"rgb24prof.bmp",
|
||||||
|
@ -49,15 +50,15 @@ def test_questionable():
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
if os.path.basename(f) not in supported:
|
if os.path.basename(f) not in supported:
|
||||||
print("Please add %s to the partially supported bmp specs." % f)
|
print(f"Please add {f} to the partially supported bmp specs.")
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
if os.path.basename(f) in supported:
|
if os.path.basename(f) in supported:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def test_good():
|
def test_good():
|
||||||
""" These should all work. There's a set of target files in the
|
"""These should all work. There's a set of target files in the
|
||||||
html directory that we can compare against. """
|
html directory that we can compare against."""
|
||||||
|
|
||||||
# Target files, if they're not just replacing the extension
|
# Target files, if they're not just replacing the extension
|
||||||
file_map = {
|
file_map = {
|
||||||
|
@ -84,7 +85,7 @@ def test_good():
|
||||||
if name in file_map:
|
if name in file_map:
|
||||||
return os.path.join(base, "html", file_map[name])
|
return os.path.join(base, "html", file_map[name])
|
||||||
name = os.path.splitext(name)[0]
|
name = os.path.splitext(name)[0]
|
||||||
return os.path.join(base, "html", "%s.png" % name)
|
return os.path.join(base, "html", f"{name}.png")
|
||||||
|
|
||||||
for f in get_files("g"):
|
for f in get_files("g"):
|
||||||
try:
|
try:
|
||||||
|
@ -107,4 +108,4 @@ def test_good():
|
||||||
os.path.join(base, "g", "pal8rle.bmp"),
|
os.path.join(base, "g", "pal8rle.bmp"),
|
||||||
os.path.join(base, "g", "pal4rle.bmp"),
|
os.path.join(base, "g", "pal4rle.bmp"),
|
||||||
)
|
)
|
||||||
assert f in unsupported, "Unsupported Image {}: {}".format(f, msg)
|
assert f in unsupported, f"Unsupported Image {f}: {msg}"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
sample = Image.new("L", (7, 5))
|
sample = Image.new("L", (7, 5))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_pypy
|
from .helper import is_pypy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -2,6 +2,7 @@ import io
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import features
|
from PIL import features
|
||||||
|
|
||||||
from .helper import skip_unless_feature
|
from .helper import skip_unless_feature
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, PngImagePlugin
|
from PIL import Image, ImageSequence, PngImagePlugin
|
||||||
|
|
||||||
|
|
||||||
|
@ -311,7 +312,7 @@ def test_apng_sequence_errors():
|
||||||
]
|
]
|
||||||
for f in test_files:
|
for f in test_files:
|
||||||
with pytest.raises(SyntaxError):
|
with pytest.raises(SyntaxError):
|
||||||
with Image.open("Tests/images/apng/{0}".format(f)) as im:
|
with Image.open(f"Tests/images/apng/{f}") as im:
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
@ -358,7 +359,10 @@ def test_apng_save_split_fdat(tmp_path):
|
||||||
with Image.open("Tests/images/old-style-jpeg-compression.png") as im:
|
with Image.open("Tests/images/old-style-jpeg-compression.png") as im:
|
||||||
frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))]
|
frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))]
|
||||||
im.save(
|
im.save(
|
||||||
test_file, save_all=True, default_image=True, append_images=frames,
|
test_file,
|
||||||
|
save_all=True,
|
||||||
|
default_image=True,
|
||||||
|
append_images=frames,
|
||||||
)
|
)
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
exception = None
|
exception = None
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BmpImagePlugin, Image
|
from PIL import BmpImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BufrStubImagePlugin, Image
|
from PIL import BufrStubImagePlugin, Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import CurImagePlugin, Image
|
from PIL import CurImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/deerstalker.cur"
|
TEST_FILE = "Tests/images/deerstalker.cur"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import DcxImagePlugin, Image
|
from PIL import DcxImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import DdsImagePlugin, Image
|
from PIL import DdsImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import EpsImagePlugin, Image, features
|
from PIL import EpsImagePlugin, Image, features
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FitsStubImagePlugin, Image
|
from PIL import FitsStubImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.fits"
|
TEST_FILE = "Tests/images/hopper.fits"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image
|
from PIL import FliImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, is_pypy
|
from .helper import assert_image_equal, is_pypy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
FpxImagePlugin = pytest.importorskip(
|
FpxImagePlugin = pytest.importorskip(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GbrImagePlugin, Image
|
from PIL import GbrImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GdImageFile, UnidentifiedImageError
|
from PIL import GdImageFile, UnidentifiedImageError
|
||||||
|
|
||||||
TEST_GD_FILE = "Tests/images/hopper.gd"
|
TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL.GimpPaletteFile import GimpPaletteFile
|
from PIL.GimpPaletteFile import GimpPaletteFile
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GribStubImagePlugin, Image
|
from PIL import GribStubImagePlugin, Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Hdf5StubImagePlugin, Image
|
from PIL import Hdf5StubImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hdf5.h5"
|
TEST_FILE = "Tests/images/hdf5.h5"
|
||||||
|
|
|
@ -2,6 +2,7 @@ import io
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import IcnsImagePlugin, Image, features
|
from PIL import IcnsImagePlugin, Image, features
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar
|
from .helper import assert_image_equal, assert_image_similar
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import IcoImagePlugin, Image, ImageDraw
|
from PIL import IcoImagePlugin, Image, ImageDraw
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import filecmp
|
import filecmp
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImImagePlugin
|
from PIL import Image, ImImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -3,10 +3,12 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import (
|
from PIL import (
|
||||||
ExifTags,
|
ExifTags,
|
||||||
Image,
|
Image,
|
||||||
ImageFile,
|
ImageFile,
|
||||||
|
ImageOps,
|
||||||
JpegImagePlugin,
|
JpegImagePlugin,
|
||||||
UnidentifiedImageError,
|
UnidentifiedImageError,
|
||||||
features,
|
features,
|
||||||
|
@ -38,7 +40,7 @@ class TestFileJpeg:
|
||||||
return im
|
return im
|
||||||
|
|
||||||
def gen_random_image(self, size, mode="RGB"):
|
def gen_random_image(self, size, mode="RGB"):
|
||||||
""" Generates a very hard to compress file
|
"""Generates a very hard to compress file
|
||||||
:param size: tuple
|
:param size: tuple
|
||||||
:param mode: optional image mode
|
:param mode: optional image mode
|
||||||
|
|
||||||
|
@ -98,7 +100,8 @@ class TestFileJpeg:
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_image_path", [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"],
|
"test_image_path",
|
||||||
|
[TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"],
|
||||||
)
|
)
|
||||||
def test_dpi(self, test_image_path):
|
def test_dpi(self, test_image_path):
|
||||||
def test(xdpi, ydpi=None):
|
def test(xdpi, ydpi=None):
|
||||||
|
@ -223,23 +226,58 @@ class TestFileJpeg:
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_exif_gps(self):
|
def test_exif_gps(self, tmp_path):
|
||||||
# Arrange
|
expected_exif_gps = {
|
||||||
|
0: b"\x00\x00\x00\x01",
|
||||||
|
2: 4294967295,
|
||||||
|
5: b"\x01",
|
||||||
|
30: 65535,
|
||||||
|
29: "1999:99:99 99:99:99",
|
||||||
|
}
|
||||||
|
gps_index = 34853
|
||||||
|
|
||||||
|
# Reading
|
||||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
gps_index = 34853
|
|
||||||
expected_exif_gps = {
|
|
||||||
0: b"\x00\x00\x00\x01",
|
|
||||||
2: 4294967295,
|
|
||||||
5: b"\x01",
|
|
||||||
30: 65535,
|
|
||||||
29: "1999:99:99 99:99:99",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
assert exif[gps_index] == expected_exif_gps
|
||||||
|
|
||||||
# Assert
|
# Writing
|
||||||
assert exif[gps_index] == expected_exif_gps
|
f = str(tmp_path / "temp.jpg")
|
||||||
|
exif = Image.Exif()
|
||||||
|
exif[gps_index] = expected_exif_gps
|
||||||
|
hopper().save(f, exif=exif)
|
||||||
|
|
||||||
|
with Image.open(f) as reloaded:
|
||||||
|
exif = reloaded._getexif()
|
||||||
|
assert exif[gps_index] == expected_exif_gps
|
||||||
|
|
||||||
|
def test_empty_exif_gps(self):
|
||||||
|
with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
|
||||||
|
exif = im.getexif()
|
||||||
|
del exif[0x8769]
|
||||||
|
|
||||||
|
# Assert that it needs to be transposed
|
||||||
|
assert exif[0x0112] == Image.TRANSVERSE
|
||||||
|
|
||||||
|
# Assert that the GPS IFD is present and empty
|
||||||
|
assert exif[0x8825] == {}
|
||||||
|
|
||||||
|
transposed = ImageOps.exif_transpose(im)
|
||||||
|
exif = transposed.getexif()
|
||||||
|
assert exif[0x8825] == {}
|
||||||
|
|
||||||
|
# Assert that it was transposed
|
||||||
|
assert 0x0112 not in exif
|
||||||
|
|
||||||
|
def test_exif_equality(self):
|
||||||
|
# In 7.2.0, Exif rationals were changed to be read as
|
||||||
|
# TiffImagePlugin.IFDRational. This class had a bug in __eq__,
|
||||||
|
# breaking the self-equality of Exif data
|
||||||
|
exifs = []
|
||||||
|
for i in range(2):
|
||||||
|
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||||
|
exifs.append(im._getexif())
|
||||||
|
assert exifs[0] == exifs[1]
|
||||||
|
|
||||||
def test_exif_rollback(self):
|
def test_exif_rollback(self):
|
||||||
# rolling back exif support in 3.1 to pre-3.0 formatting.
|
# rolling back exif support in 3.1 to pre-3.0 formatting.
|
||||||
|
|
|
@ -2,6 +2,7 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
|
from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -7,6 +7,7 @@ from collections import namedtuple
|
||||||
from ctypes import c_float
|
from ctypes import c_float
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
|
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
@ -170,18 +171,18 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert (
|
assert (
|
||||||
c_float(val[0][0] / val[0][1]).value
|
c_float(val[0][0] / val[0][1]).value
|
||||||
== c_float(value[0][0] / value[0][1]).value
|
== c_float(value[0][0] / value[0][1]).value
|
||||||
), ("%s didn't roundtrip" % tag)
|
), f"{tag} didn't roundtrip"
|
||||||
else:
|
else:
|
||||||
assert c_float(val).value == c_float(value).value, (
|
assert (
|
||||||
"%s didn't roundtrip" % tag
|
c_float(val).value == c_float(value).value
|
||||||
)
|
), f"{tag} didn't roundtrip"
|
||||||
else:
|
else:
|
||||||
assert val == value, "%s didn't roundtrip" % tag
|
assert val == value, f"{tag} didn't roundtrip"
|
||||||
|
|
||||||
# https://github.com/python-pillow/Pillow/issues/1561
|
# https://github.com/python-pillow/Pillow/issues/1561
|
||||||
requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"]
|
requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"]
|
||||||
for field in requested_fields:
|
for field in requested_fields:
|
||||||
assert field in reloaded, "%s not in metadata" % field
|
assert field in reloaded, f"{field} not in metadata"
|
||||||
|
|
||||||
def test_additional_metadata(self, tmp_path):
|
def test_additional_metadata(self, tmp_path):
|
||||||
# these should not crash. Seriously dummy data, most of it doesn't make
|
# these should not crash. Seriously dummy data, most of it doesn't make
|
||||||
|
@ -401,8 +402,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert "temp.tif" == reread.tag[269][0]
|
assert "temp.tif" == reread.tag[269][0]
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
"""Are we generating the same interpretation
|
||||||
of the image as Imagemagick is? """
|
of the image as Imagemagick is?"""
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
@ -502,9 +503,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert len(reloaded.tag_v2[320]) == 768
|
assert len(reloaded.tag_v2[320]) == 768
|
||||||
|
|
||||||
def xtest_bw_compression_w_rgb(self, tmp_path):
|
def xtest_bw_compression_w_rgb(self, tmp_path):
|
||||||
""" This test passes, but when running all tests causes a failure due
|
"""This test passes, but when running all tests causes a failure due
|
||||||
to output on stderr from the error thrown by libtiff. We need to
|
to output on stderr from the error thrown by libtiff. We need to
|
||||||
capture that but not now"""
|
capture that but not now"""
|
||||||
|
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
out = str(tmp_path / "temp.tif")
|
out = str(tmp_path / "temp.tif")
|
||||||
|
@ -767,7 +768,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (100, 40)
|
assert im.size == (100, 40)
|
||||||
assert im.tile, [
|
assert im.tile, [
|
||||||
("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236),)
|
("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236))
|
||||||
]
|
]
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@ from .test_file_libtiff import LibTiffTestCase
|
||||||
|
|
||||||
class TestFileLibTiffSmall(LibTiffTestCase):
|
class TestFileLibTiffSmall(LibTiffTestCase):
|
||||||
|
|
||||||
""" The small lena image was failing on open in the libtiff
|
"""The small lena image was failing on open in the libtiff
|
||||||
decoder because the file pointer was set to the wrong place
|
decoder because the file pointer was set to the wrong place
|
||||||
by a spurious seek. It wasn't failing with the byteio method.
|
by a spurious seek. It wasn't failing with the byteio method.
|
||||||
|
|
||||||
It was fixed by forcing an lseek to the beginning of the
|
It was fixed by forcing an lseek to the beginning of the
|
||||||
file just before reading in libtiff. These tests remain
|
file just before reading in libtiff. These tests remain
|
||||||
to ensure that it stays fixed. """
|
to ensure that it stays fixed."""
|
||||||
|
|
||||||
def test_g4_hopper_file(self, tmp_path):
|
def test_g4_hopper_file(self, tmp_path):
|
||||||
"""Testing the open file load path"""
|
"""Testing the open file load path"""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, McIdasImagePlugin
|
from PIL import Image, McIdasImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImagePalette
|
from PIL import Image, ImagePalette
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_similar, is_pypy, skip_unless_feature
|
from .helper import assert_image_similar, is_pypy, skip_unless_feature
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, MspImagePlugin
|
from PIL import Image, MspImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
|
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PcxImagePlugin
|
from PIL import Image, ImageFile, PcxImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -5,6 +5,7 @@ import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PdfParser
|
from PIL import Image, PdfParser
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PixarImagePlugin
|
from PIL import Image, PixarImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -3,6 +3,7 @@ import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PngImagePlugin, features
|
from PIL import Image, ImageFile, PngImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
@ -606,6 +607,11 @@ class TestFilePng:
|
||||||
exif = im.copy().getexif()
|
exif = im.copy().getexif()
|
||||||
assert exif[274] == 1
|
assert exif[274] == 1
|
||||||
|
|
||||||
|
# With a tEXt chunk
|
||||||
|
with Image.open("Tests/images/exif_text.png") as im:
|
||||||
|
exif = im._getexif()
|
||||||
|
assert exif[274] == 1
|
||||||
|
|
||||||
# With XMP tags
|
# With XMP tags
|
||||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PsdImagePlugin
|
from PIL import Image, PsdImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, is_pypy
|
from .helper import assert_image_similar, hopper, is_pypy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, SgiImagePlugin
|
from PIL import Image, SgiImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -2,6 +2,7 @@ import tempfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, SunImagePlugin
|
from PIL import Image, SunImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
@ -45,7 +46,7 @@ def test_others():
|
||||||
with Image.open(path) as im:
|
with Image.open(path) as im:
|
||||||
im.load()
|
im.load()
|
||||||
assert isinstance(im, SunImagePlugin.SunImageFile)
|
assert isinstance(im, SunImagePlugin.SunImageFile)
|
||||||
target_path = "%s.png" % os.path.splitext(path)[0]
|
target_path = f"{os.path.splitext(path)[0]}.png"
|
||||||
# im.save(target_file)
|
# im.save(target_file)
|
||||||
with Image.open(target_path) as target:
|
with Image.open(target_path) as target:
|
||||||
assert_image_equal(im, target)
|
assert_image_equal(im, target)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TarIO, features
|
from PIL import Image, TarIO, features
|
||||||
|
|
||||||
from .helper import is_pypy
|
from .helper import is_pypy
|
||||||
|
|
|
@ -3,6 +3,7 @@ from glob import glob
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
@ -35,9 +36,7 @@ def test_sanity(tmp_path):
|
||||||
|
|
||||||
assert_image_equal(saved_im, original_im)
|
assert_image_equal(saved_im, original_im)
|
||||||
|
|
||||||
png_paths = glob(
|
png_paths = glob(os.path.join(_TGA_DIR_COMMON, f"*x*_{mode.lower()}.png"))
|
||||||
os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower()))
|
|
||||||
)
|
|
||||||
|
|
||||||
for png_path in png_paths:
|
for png_path in png_paths:
|
||||||
with Image.open(png_path) as reference_im:
|
with Image.open(png_path) as reference_im:
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||||
|
|
||||||
|
@ -224,8 +225,8 @@ class TestFileTiff:
|
||||||
assert im.getpixel((0, 1)) == 0
|
assert im.getpixel((0, 1)) == 0
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
"""Are we generating the same interpretation
|
||||||
of the image as Imagemagick is? """
|
of the image as Imagemagick is?"""
|
||||||
|
|
||||||
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
||||||
# to make the target --
|
# to make the target --
|
||||||
|
|
|
@ -2,6 +2,7 @@ import io
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin, TiffTags
|
from PIL import Image, TiffImagePlugin, TiffTags
|
||||||
from PIL.TiffImagePlugin import IFDRational
|
from PIL.TiffImagePlugin import IFDRational
|
||||||
|
|
||||||
|
@ -11,10 +12,10 @@ TAG_IDS = {info.name: info.value for info in TiffTags.TAGS_V2.values()}
|
||||||
|
|
||||||
|
|
||||||
def test_rt_metadata(tmp_path):
|
def test_rt_metadata(tmp_path):
|
||||||
""" Test writing arbitrary metadata into the tiff image directory
|
"""Test writing arbitrary metadata into the tiff image directory
|
||||||
Use case is ImageJ private tags, one numeric, one arbitrary
|
Use case is ImageJ private tags, one numeric, one arbitrary
|
||||||
data. https://github.com/python-pillow/Pillow/issues/291
|
data. https://github.com/python-pillow/Pillow/issues/291
|
||||||
"""
|
"""
|
||||||
|
|
||||||
img = hopper()
|
img = hopper()
|
||||||
|
|
||||||
|
@ -144,16 +145,16 @@ def test_write_metadata(tmp_path):
|
||||||
assert_deep_equal(
|
assert_deep_equal(
|
||||||
original[tag],
|
original[tag],
|
||||||
value,
|
value,
|
||||||
"{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
|
f"{tag} didn't roundtrip, {original[tag]}, {value}",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert original[tag] == value, "{} didn't roundtrip, {}, {}".format(
|
assert (
|
||||||
tag, original[tag], value
|
original[tag] == value
|
||||||
)
|
), f"{tag} didn't roundtrip, {original[tag]}, {value}"
|
||||||
|
|
||||||
for tag, value in original.items():
|
for tag, value in original.items():
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
assert value == reloaded[tag], "%s didn't roundtrip" % tag
|
assert value == reloaded[tag], f"{tag} didn't roundtrip"
|
||||||
|
|
||||||
|
|
||||||
def test_change_stripbytecounts_tag_type(tmp_path):
|
def test_change_stripbytecounts_tag_type(tmp_path):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import io
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, WebPImagePlugin, features
|
from PIL import Image, WebPImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, WmfImagePlugin
|
from PIL import Image, WmfImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, XpmImagePlugin
|
from PIL import Image, XpmImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, XVThumbImagePlugin
|
from PIL import Image, XVThumbImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BdfFontFile, FontFile
|
from PIL import BdfFontFile, FontFile
|
||||||
|
|
||||||
filename = "Tests/images/courB08.bdf"
|
filename = "Tests/images/courB08.bdf"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
||||||
|
|
|
@ -47,11 +47,11 @@ def save_font(request, tmp_path, encoding):
|
||||||
font.save(tempname)
|
font.save(tempname)
|
||||||
|
|
||||||
with Image.open(tempname.replace(".pil", ".pbm")) as loaded:
|
with Image.open(tempname.replace(".pil", ".pbm")) as loaded:
|
||||||
with Image.open("Tests/fonts/ter-x20b-%s.pbm" % encoding) as target:
|
with Image.open(f"Tests/fonts/ter-x20b-{encoding}.pbm") as target:
|
||||||
assert_image_equal(loaded, target)
|
assert_image_equal(loaded, target)
|
||||||
|
|
||||||
with open(tempname, "rb") as f_loaded:
|
with open(tempname, "rb") as f_loaded:
|
||||||
with open("Tests/fonts/ter-x20b-%s.pil" % encoding, "rb") as f_target:
|
with open(f"Tests/fonts/ter-x20b-{encoding}.pil", "rb") as f_target:
|
||||||
assert f_loaded.read() == f_target.read()
|
assert f_loaded.read() == f_target.read()
|
||||||
return tempname
|
return tempname
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,10 @@ def test_wedge():
|
||||||
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
|
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong",
|
im.getchannel(1),
|
||||||
|
comparable.getchannel(1),
|
||||||
|
1,
|
||||||
|
"Saturation conversion is wrong",
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
|
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
|
||||||
|
@ -113,7 +116,10 @@ def test_convert():
|
||||||
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
|
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong",
|
im.getchannel(1),
|
||||||
|
comparable.getchannel(1),
|
||||||
|
1,
|
||||||
|
"Saturation conversion is wrong",
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
|
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
|
||||||
|
@ -126,11 +132,20 @@ def test_hsv_to_rgb():
|
||||||
comparable = to_rgb_colorsys(comparable)
|
comparable = to_rgb_colorsys(comparable)
|
||||||
|
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
converted.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong",
|
converted.getchannel(0),
|
||||||
|
comparable.getchannel(0),
|
||||||
|
3,
|
||||||
|
"R conversion is wrong",
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
converted.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong",
|
converted.getchannel(1),
|
||||||
|
comparable.getchannel(1),
|
||||||
|
3,
|
||||||
|
"G conversion is wrong",
|
||||||
)
|
)
|
||||||
assert_image_similar(
|
assert_image_similar(
|
||||||
converted.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong",
|
converted.getchannel(2),
|
||||||
|
comparable.getchannel(2),
|
||||||
|
3,
|
||||||
|
"B conversion is wrong",
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,9 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import PIL
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import PIL
|
||||||
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
|
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
@ -726,7 +727,8 @@ class TestImage:
|
||||||
}
|
}
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_module", [PIL, Image],
|
"test_module",
|
||||||
|
[PIL, Image],
|
||||||
)
|
)
|
||||||
def test_pillow_version(self, test_module):
|
def test_pillow_version(self, test_module):
|
||||||
with pytest.warns(DeprecationWarning):
|
with pytest.warns(DeprecationWarning):
|
||||||
|
@ -754,7 +756,7 @@ class TestImage:
|
||||||
assert test_module.PILLOW_VERSION > "7.0.0"
|
assert test_module.PILLOW_VERSION > "7.0.0"
|
||||||
|
|
||||||
def test_overrun(self):
|
def test_overrun(self):
|
||||||
""" For overrun completeness, test as:
|
"""For overrun completeness, test as:
|
||||||
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
|
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
|
||||||
"""
|
"""
|
||||||
for file in [
|
for file in [
|
||||||
|
|
|
@ -4,9 +4,9 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
|
||||||
|
import pytest
|
||||||
from setuptools.command.build_ext import new_compiler
|
from setuptools.command.build_ext import new_compiler
|
||||||
|
|
||||||
import pytest
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_win32, on_ci
|
from .helper import assert_image_equal, hopper, is_win32, on_ci
|
||||||
|
@ -17,8 +17,9 @@ if os.environ.get("PYTHONOPTIMIZE") == "2":
|
||||||
cffi = None
|
cffi = None
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
from PIL import PyAccess
|
|
||||||
import cffi
|
import cffi
|
||||||
|
|
||||||
|
from PIL import PyAccess
|
||||||
except ImportError:
|
except ImportError:
|
||||||
cffi = None
|
cffi = None
|
||||||
|
|
||||||
|
@ -127,14 +128,13 @@ class TestImageGetPixel(AccessTest):
|
||||||
im.putpixel((0, 0), c)
|
im.putpixel((0, 0), c)
|
||||||
assert (
|
assert (
|
||||||
im.getpixel((0, 0)) == c
|
im.getpixel((0, 0)) == c
|
||||||
), "put/getpixel roundtrip failed for mode {}, color {}".format(mode, c)
|
), f"put/getpixel roundtrip failed for mode {mode}, color {c}"
|
||||||
|
|
||||||
# check putpixel negative index
|
# check putpixel negative index
|
||||||
im.putpixel((-1, -1), c)
|
im.putpixel((-1, -1), c)
|
||||||
assert im.getpixel((-1, -1)) == c, (
|
assert (
|
||||||
"put/getpixel roundtrip negative index failed for mode %s, color %s"
|
im.getpixel((-1, -1)) == c
|
||||||
% (mode, c)
|
), f"put/getpixel roundtrip negative index failed for mode {mode}, color {c}"
|
||||||
)
|
|
||||||
|
|
||||||
# Check 0
|
# Check 0
|
||||||
im = Image.new(mode, (0, 0), None)
|
im = Image.new(mode, (0, 0), None)
|
||||||
|
@ -152,11 +152,11 @@ class TestImageGetPixel(AccessTest):
|
||||||
im = Image.new(mode, (1, 1), c)
|
im = Image.new(mode, (1, 1), c)
|
||||||
assert (
|
assert (
|
||||||
im.getpixel((0, 0)) == c
|
im.getpixel((0, 0)) == c
|
||||||
), "initial color failed for mode {}, color {} ".format(mode, c)
|
), f"initial color failed for mode {mode}, color {c} "
|
||||||
# check initial color negative index
|
# check initial color negative index
|
||||||
assert (
|
assert (
|
||||||
im.getpixel((-1, -1)) == c
|
im.getpixel((-1, -1)) == c
|
||||||
), "initial color failed with negative index for mode %s, color %s " % (mode, c)
|
), f"initial color failed with negative index for mode {mode}, color {c} "
|
||||||
|
|
||||||
# Check 0
|
# Check 0
|
||||||
im = Image.new(mode, (0, 0), c)
|
im = Image.new(mode, (0, 0), c)
|
||||||
|
@ -327,6 +327,38 @@ class TestCffi(AccessTest):
|
||||||
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
|
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestImagePutPixelError(AccessTest):
|
||||||
|
IMAGE_MODES1 = ["L", "LA", "RGB", "RGBA"]
|
||||||
|
IMAGE_MODES2 = ["I", "I;16", "BGR;15"]
|
||||||
|
INVALID_TYPES1 = ["foo", 1.0, None]
|
||||||
|
INVALID_TYPES2 = [*INVALID_TYPES1, (10,)]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mode", IMAGE_MODES1)
|
||||||
|
def test_putpixel_type_error1(self, mode):
|
||||||
|
im = hopper(mode)
|
||||||
|
for v in self.INVALID_TYPES1:
|
||||||
|
with pytest.raises(TypeError, match="color must be int or tuple"):
|
||||||
|
im.putpixel((0, 0), v)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mode", IMAGE_MODES2)
|
||||||
|
def test_putpixel_type_error2(self, mode):
|
||||||
|
im = hopper(mode)
|
||||||
|
for v in self.INVALID_TYPES2:
|
||||||
|
with pytest.raises(TypeError, match="color must be int"):
|
||||||
|
im.putpixel((0, 0), v)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mode", IMAGE_MODES1 + IMAGE_MODES2)
|
||||||
|
def test_putpixel_overflow_error(self, mode):
|
||||||
|
im = hopper(mode)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
im.putpixel((0, 0), 2 ** 80)
|
||||||
|
|
||||||
|
def test_putpixel_unrecognized_mode(self):
|
||||||
|
im = hopper("BGR;15")
|
||||||
|
with pytest.raises(ValueError, match="unrecognized image mode"):
|
||||||
|
im.putpixel((0, 0), 0)
|
||||||
|
|
||||||
|
|
||||||
class TestEmbeddable:
|
class TestEmbeddable:
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
not is_win32() or on_ci(),
|
not is_win32() or on_ci(),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageQt
|
from PIL import Image, ImageQt
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
@ -22,6 +24,14 @@ def test_close():
|
||||||
im.getpixel((0, 0))
|
im.getpixel((0, 0))
|
||||||
|
|
||||||
|
|
||||||
|
def test_close_after_load(caplog):
|
||||||
|
im = Image.open("Tests/images/hopper.gif")
|
||||||
|
im.load()
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
im.close()
|
||||||
|
assert len(caplog.records) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_contextmanager():
|
def test_contextmanager():
|
||||||
fn = None
|
fn = None
|
||||||
with Image.open("Tests/images/hopper.gif") as im:
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
|
|
|
@ -24,9 +24,9 @@ def test_sanity():
|
||||||
|
|
||||||
|
|
||||||
def test_16bit_lut():
|
def test_16bit_lut():
|
||||||
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
"""Tests for 16 bit -> 8 bit lut for converting I->L images
|
||||||
see https://github.com/python-pillow/Pillow/issues/440
|
see https://github.com/python-pillow/Pillow/issues/440
|
||||||
"""
|
"""
|
||||||
im = hopper("I")
|
im = hopper("I")
|
||||||
im.point(list(range(256)) * 256, "L")
|
im.point(list(range(256)) * 256, "L")
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMath, ImageMode
|
from PIL import Image, ImageMath, ImageMode
|
||||||
|
|
||||||
from .helper import convert_to_comparable, skip_unless_feature
|
from .helper import convert_to_comparable, skip_unless_feature
|
||||||
|
@ -161,8 +162,8 @@ def compare_reduce_with_reference(im, factor, average_diff=0.4, max_diff=1):
|
||||||
|
|
||||||
|
|
||||||
def assert_compare_images(a, b, max_average_diff, max_diff=255):
|
def assert_compare_images(a, b, max_average_diff, max_diff=255):
|
||||||
assert a.mode == b.mode, "got mode %r, expected %r" % (a.mode, b.mode)
|
assert a.mode == b.mode, f"got mode {repr(a.mode)}, expected {repr(b.mode)}"
|
||||||
assert a.size == b.size, "got size %r, expected %r" % (a.size, b.size)
|
assert a.size == b.size, f"got size {repr(a.size)}, expected {repr(b.size)}"
|
||||||
|
|
||||||
a, b = convert_to_comparable(a, b)
|
a, b = convert_to_comparable(a, b)
|
||||||
|
|
||||||
|
@ -175,16 +176,15 @@ def assert_compare_images(a, b, max_average_diff, max_diff=255):
|
||||||
a.size[0] * a.size[1]
|
a.size[0] * a.size[1]
|
||||||
)
|
)
|
||||||
msg = (
|
msg = (
|
||||||
"average pixel value difference {:.4f} > expected {:.4f} "
|
f"average pixel value difference {average_diff:.4f} > "
|
||||||
"for '{}' band".format(average_diff, max_average_diff, band)
|
f"expected {max_average_diff:.4f} for '{band}' band"
|
||||||
)
|
)
|
||||||
assert max_average_diff >= average_diff, msg
|
assert max_average_diff >= average_diff, msg
|
||||||
|
|
||||||
last_diff = [i for i, num in enumerate(ch_hist) if num > 0][-1]
|
last_diff = [i for i, num in enumerate(ch_hist) if num > 0][-1]
|
||||||
assert (
|
assert max_diff >= last_diff, (
|
||||||
max_diff >= last_diff
|
f"max pixel value difference {last_diff} > expected {max_diff} "
|
||||||
), "max pixel value difference {} > expected {} for '{}' band".format(
|
f"for '{band}' band"
|
||||||
last_diff, max_diff, band
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
@ -81,15 +82,16 @@ class TestImagingCoreResampleAccuracy:
|
||||||
for y in range(case.size[1]):
|
for y in range(case.size[1]):
|
||||||
for x in range(case.size[0]):
|
for x in range(case.size[0]):
|
||||||
if c_px[x, y] != s_px[x, y]:
|
if c_px[x, y] != s_px[x, y]:
|
||||||
message = "\nHave: \n{}\n\nExpected: \n{}".format(
|
message = (
|
||||||
self.serialize_image(case), self.serialize_image(sample)
|
f"\nHave: \n{self.serialize_image(case)}\n"
|
||||||
|
f"\nExpected: \n{self.serialize_image(sample)}"
|
||||||
)
|
)
|
||||||
assert s_px[x, y] == c_px[x, y], message
|
assert s_px[x, y] == c_px[x, y], message
|
||||||
|
|
||||||
def serialize_image(self, image):
|
def serialize_image(self, image):
|
||||||
s_px = image.load()
|
s_px = image.load()
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
" ".join("{:02x}".format(s_px[x, y]) for x in range(image.size[0]))
|
" ".join(f"{s_px[x, y]:02x}" for x in range(image.size[0]))
|
||||||
for y in range(image.size[1])
|
for y in range(image.size[1])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -229,7 +231,7 @@ class TestCoreResampleConsistency:
|
||||||
for x in range(channel.size[0]):
|
for x in range(channel.size[0]):
|
||||||
for y in range(channel.size[1]):
|
for y in range(channel.size[1]):
|
||||||
if px[x, y] != color:
|
if px[x, y] != color:
|
||||||
message = "{} != {} for pixel {}".format(px[x, y], color, (x, y))
|
message = f"{px[x, y]} != {color} for pixel {(x, y)}"
|
||||||
assert px[x, y] == color, message
|
assert px[x, y] == color, message
|
||||||
|
|
||||||
def test_8u(self):
|
def test_8u(self):
|
||||||
|
@ -268,10 +270,9 @@ class TestCoreResampleAlphaCorrect:
|
||||||
px = i.load()
|
px = i.load()
|
||||||
for y in range(i.size[1]):
|
for y in range(i.size[1]):
|
||||||
used_colors = {px[x, y][0] for x in range(i.size[0])}
|
used_colors = {px[x, y][0] for x in range(i.size[0])}
|
||||||
assert 256 == len(
|
assert 256 == len(used_colors), (
|
||||||
used_colors
|
"All colors should be present in resized image. "
|
||||||
), "All colors should present in resized image. Only {} on {} line.".format(
|
f"Only {len(used_colors)} on {y} line."
|
||||||
len(used_colors), y
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||||
|
@ -307,8 +308,9 @@ class TestCoreResampleAlphaCorrect:
|
||||||
for y in range(i.size[1]):
|
for y in range(i.size[1]):
|
||||||
for x in range(i.size[0]):
|
for x in range(i.size[0]):
|
||||||
if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel:
|
if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel:
|
||||||
message = "pixel at ({}, {}) is differ:\n{}\n{}".format(
|
message = (
|
||||||
x, y, px[x, y], clean_pixel
|
f"pixel at ({x}, {y}) is different:\n"
|
||||||
|
f"{px[x, y]}\n{clean_pixel}"
|
||||||
)
|
)
|
||||||
assert px[x, y][:3] == clean_pixel, message
|
assert px[x, y][:3] == clean_pixel, message
|
||||||
|
|
||||||
|
@ -503,7 +505,7 @@ class TestCoreResampleBox:
|
||||||
]:
|
]:
|
||||||
res = im.resize(size, Image.LANCZOS, box)
|
res = im.resize(size, Image.LANCZOS, box)
|
||||||
assert res.size == size
|
assert res.size == size
|
||||||
assert_image_equal(res, im.crop(box), ">>> {} {}".format(size, box))
|
assert_image_equal(res, im.crop(box), f">>> {size} {box}")
|
||||||
|
|
||||||
def test_no_passthrough(self):
|
def test_no_passthrough(self):
|
||||||
# When resize is required
|
# When resize is required
|
||||||
|
@ -519,9 +521,7 @@ class TestCoreResampleBox:
|
||||||
assert res.size == size
|
assert res.size == size
|
||||||
with pytest.raises(AssertionError, match=r"difference \d"):
|
with pytest.raises(AssertionError, match=r"difference \d"):
|
||||||
# check that the difference at least that much
|
# check that the difference at least that much
|
||||||
assert_image_similar(
|
assert_image_similar(res, im.crop(box), 20, f">>> {size} {box}")
|
||||||
res, im.crop(box), 20, ">>> {} {}".format(size, box)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_skip_horizontal(self):
|
def test_skip_horizontal(self):
|
||||||
# Can skip resize for one dimension
|
# Can skip resize for one dimension
|
||||||
|
@ -541,7 +541,7 @@ class TestCoreResampleBox:
|
||||||
res,
|
res,
|
||||||
im.crop(box).resize(size, flt),
|
im.crop(box).resize(size, flt),
|
||||||
0.4,
|
0.4,
|
||||||
">>> {} {} {}".format(size, box, flt),
|
f">>> {size} {box} {flt}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_skip_vertical(self):
|
def test_skip_vertical(self):
|
||||||
|
@ -562,5 +562,5 @@ class TestCoreResampleBox:
|
||||||
res,
|
res,
|
||||||
im.crop(box).resize(size, flt),
|
im.crop(box).resize(size, flt),
|
||||||
0.4,
|
0.4,
|
||||||
">>> {} {} {}".format(size, box, flt),
|
f">>> {size} {box} {flt}",
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ Tests for resize functionality.
|
||||||
from itertools import permutations
|
from itertools import permutations
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageTransform
|
from PIL import Image, ImageTransform
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -4,6 +4,7 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMode, features
|
from PIL import Image, ImageMode, features
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
||||||
|
@ -436,7 +437,7 @@ def test_extended_information():
|
||||||
|
|
||||||
|
|
||||||
def test_profile_typesafety():
|
def test_profile_typesafety():
|
||||||
""" Profile init type safety
|
"""Profile init type safety
|
||||||
|
|
||||||
prepatch, these would segfault, postpatch they should emit a typeerror
|
prepatch, these would segfault, postpatch they should emit a typeerror
|
||||||
"""
|
"""
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user