Merge branch 'master' into gitignore-review
|
@ -32,7 +32,7 @@ install:
|
||||||
c:\pillow\winbuild\build\build_dep_all.cmd
|
c:\pillow\winbuild\build\build_dep_all.cmd
|
||||||
$host.SetShouldExit(0)
|
$host.SetShouldExit(0)
|
||||||
- path C:\pillow\winbuild\build\bin;%PATH%
|
- path C:\pillow\winbuild\build\bin;%PATH%
|
||||||
- '%PYTHON%\%EXECUTABLE% -m pip install -U "setuptools>=49.3.2"'
|
- '%PYTHON%\%EXECUTABLE% -m pip install -U setuptools'
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: |
|
- ps: |
|
||||||
|
@ -45,6 +45,7 @@ test_script:
|
||||||
- cd c:\pillow
|
- cd c:\pillow
|
||||||
- '%PYTHON%\%EXECUTABLE% -m pip install pytest pytest-cov'
|
- '%PYTHON%\%EXECUTABLE% -m pip install pytest pytest-cov'
|
||||||
- c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE%
|
- c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE%
|
||||||
|
- '%PYTHON%\%EXECUTABLE% -c "from PIL import Image"'
|
||||||
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests'
|
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests'
|
||||||
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,3 @@ if [[ $MATRIX_DOCKER ]]; then
|
||||||
else
|
else
|
||||||
coverage xml
|
coverage xml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $TRAVIS ]]; then
|
|
||||||
codecov --flags TravisCI
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "3.9" ]; then
|
|
||||||
# Coverage and quality reports on just the latest diff.
|
|
||||||
depends/diffcover-install.sh
|
|
||||||
depends/diffcover-run.sh
|
|
||||||
fi
|
|
||||||
|
|
|
@ -21,35 +21,30 @@ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
|
||||||
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
||||||
cmake imagemagick libharfbuzz-dev libfribidi-dev
|
cmake imagemagick libharfbuzz-dev libfribidi-dev
|
||||||
|
|
||||||
if [[ $TRAVIS_CPU_ARCH == "s390x" ]]; then sudo chown $USER ~/.cache/pip/wheels ; fi
|
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
||||||
python3 -m pip install coverage
|
python3 -m pip install coverage
|
||||||
python3 -m pip install olefile
|
python3 -m pip install olefile
|
||||||
python3 -m pip install -U pytest
|
python3 -m pip install -U pytest
|
||||||
python3 -m pip install -U pytest-cov
|
python3 -m pip install -U pytest-cov
|
||||||
|
python3 -m pip install -U pytest-timeout
|
||||||
python3 -m pip install pyroma
|
python3 -m pip install pyroma
|
||||||
python3 -m pip install test-image-results
|
python3 -m pip install test-image-results
|
||||||
# TODO Remove condition when numpy supports 3.10
|
# TODO Remove condition when numpy supports 3.10
|
||||||
if ! [ "$GHA_PYTHON_VERSION" == "3.10-dev" ]; then python3 -m pip install numpy ; fi
|
if ! [ "$GHA_PYTHON_VERSION" == "3.10-dev" ]; then python3 -m pip install numpy ; fi
|
||||||
|
|
||||||
# TODO Remove when 3.8 / 3.9 / PyPy3 includes setuptools 49.3.2+:
|
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
|
||||||
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
|
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
|
||||||
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
|
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "pypy3.6-7.3.1" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
|
|
||||||
|
|
||||||
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
# PyQt5 doesn't support PyPy3
|
||||||
|
# Wheel doesn't yet support 3.10
|
||||||
|
if [[ $GHA_PYTHON_VERSION == 3.* && $GHA_PYTHON_VERSION != "3.10-dev" ]]; 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"
|
||||||
if [[ $TRAVIS_CPU_ARCH == "amd64" ]]; then
|
|
||||||
sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools
|
sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools
|
||||||
python3 -m pip install pyqt5
|
python3 -m pip install pyqt5
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# docs only on Python 3.9
|
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "3.9" ]; then python3 -m pip install -r requirements.txt ; fi
|
|
||||||
|
|
||||||
# webp
|
# webp
|
||||||
pushd depends && ./install_webp.sh && popd
|
pushd depends && ./install_webp.sh && popd
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
python -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
python3 -c "from PIL import Image"
|
||||||
|
|
||||||
# Docs
|
python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "3.9" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then
|
|
||||||
make doccheck
|
|
||||||
fi
|
|
||||||
|
|
20
.clang-format
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# A clang-format style that approximates Python's PEP 7
|
||||||
|
# Useful for IDE integration
|
||||||
|
BasedOnStyle: Google
|
||||||
|
AlwaysBreakAfterReturnType: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
ColumnLimit: 88
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
IndentWidth: 4
|
||||||
|
Language: Cpp
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: false
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpacesInParentheses: false
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
5
.github/CONTRIBUTING.md
vendored
|
@ -9,7 +9,7 @@ Please send a pull request to the master branch. Please include [documentation](
|
||||||
- Fork the Pillow repository.
|
- Fork the Pillow repository.
|
||||||
- Create a branch from master.
|
- Create a branch from master.
|
||||||
- Develop bug fixes, features, tests, etc.
|
- Develop bug fixes, features, tests, etc.
|
||||||
- Run the test suite. You can enable [Travis CI](https://travis-ci.com/account/repositories) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
|
- Run the test suite. You can enable GitHub Actions (https://github.com/MY-USERNAME/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
|
||||||
- Create a pull request to pull the changes from your branch to the Pillow master.
|
- Create a pull request to pull the changes from your branch to the Pillow master.
|
||||||
|
|
||||||
### Guidelines
|
### Guidelines
|
||||||
|
@ -17,7 +17,8 @@ Please send a pull request to the master branch. Please include [documentation](
|
||||||
- Separate code commits from reformatting commits.
|
- Separate code commits from reformatting commits.
|
||||||
- Provide tests for any newly added code.
|
- Provide tests for any newly added code.
|
||||||
- Follow PEP 8.
|
- Follow PEP 8.
|
||||||
- When committing only documentation changes please include `[ci skip]` in the commit message to avoid running tests on Travis CI and AppVeyor.
|
- When committing only documentation changes please include `[ci skip]` in the commit message to avoid running tests on AppVeyor.
|
||||||
|
- Include [release notes](https://github.com/python-pillow/Pillow/tree/master/docs/releasenotes) as needed or appropriate with your bug fixes, feature additions and tests.
|
||||||
|
|
||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
||||||
|
|
1
.github/mergify.yml
vendored
|
@ -7,7 +7,6 @@ pull_request_rules:
|
||||||
- status-success=Test Successful
|
- status-success=Test Successful
|
||||||
- status-success=Docker Test Successful
|
- status-success=Docker Test Successful
|
||||||
- status-success=Windows Test Successful
|
- status-success=Windows Test Successful
|
||||||
- status-success=Travis CI - Pull Request
|
|
||||||
- status-success=continuous-integration/appveyor/pr
|
- status-success=continuous-integration/appveyor/pr
|
||||||
actions:
|
actions:
|
||||||
merge:
|
merge:
|
||||||
|
|
26
.github/release-drafter.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name-template: "$NEXT_MINOR_VERSION"
|
||||||
|
tag-template: "$NEXT_MINOR_VERSION"
|
||||||
|
change-template: '- $TITLE #$NUMBER [@$AUTHOR]'
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- title: "Dependencies"
|
||||||
|
label: "Dependency"
|
||||||
|
- title: "Deprecations"
|
||||||
|
label: "Deprecation"
|
||||||
|
- title: "Documentation"
|
||||||
|
label: "Documentation"
|
||||||
|
- title: "Removals"
|
||||||
|
label: "Removal"
|
||||||
|
- title: "Testing"
|
||||||
|
label: "Testing"
|
||||||
|
|
||||||
|
exclude-labels:
|
||||||
|
- "changelog: skip"
|
||||||
|
|
||||||
|
template: |
|
||||||
|
|
||||||
|
https://pillow.readthedocs.io/en/stable/releasenotes/$NEXT_MINOR_VERSION.html
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
$CHANGES
|
47
.github/workflows/cifuzz.yml
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
name: CIFuzz
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "**.c"
|
||||||
|
- "**.h"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**.c"
|
||||||
|
- "**.h"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'pillow'
|
||||||
|
language: python
|
||||||
|
dry-run: false
|
||||||
|
- name: Run Fuzzers
|
||||||
|
id: run
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'pillow'
|
||||||
|
fuzz-seconds: 600
|
||||||
|
language: python
|
||||||
|
dry-run: false
|
||||||
|
- name: Upload New Crash
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
||||||
|
- name: Upload Legacy Crash
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: steps.run.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: crash
|
||||||
|
path: ./out/crash*
|
||||||
|
- name: Fail on legacy crash
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
[ ! -e out/crash-* ]
|
||||||
|
echo No legacy crash detected
|
6
.github/workflows/lint.yml
vendored
|
@ -34,12 +34,12 @@ jobs:
|
||||||
python-version: 3.8
|
python-version: 3.8
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install -U pip
|
python3 -m pip install -U pip
|
||||||
python -m pip install -U tox
|
python3 -m pip install -U tox
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: tox -e lint
|
run: tox -e lint
|
||||||
|
|
3
.github/workflows/macos-install.sh
vendored
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas
|
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas libraqm
|
||||||
|
|
||||||
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
||||||
python3 -m pip install coverage
|
python3 -m pip install coverage
|
||||||
python3 -m pip install olefile
|
python3 -m pip install olefile
|
||||||
python3 -m pip install -U pytest
|
python3 -m pip install -U pytest
|
||||||
python3 -m pip install -U pytest-cov
|
python3 -m pip install -U pytest-cov
|
||||||
|
python3 -m pip install -U pytest-timeout
|
||||||
python3 -m pip install pyroma
|
python3 -m pip install pyroma
|
||||||
python3 -m pip install test-image-results
|
python3 -m pip install test-image-results
|
||||||
|
|
||||||
|
|
17
.github/workflows/release-drafter.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: Release drafter
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# branches to consider in the event; optional, defaults to all
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_release_draft:
|
||||||
|
if: github.repository == 'python-pillow/Pillow'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# Drafts your next release notes as pull requests are merged into "master"
|
||||||
|
- uses: release-drafter/release-drafter@v5
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
29
.github/workflows/test-docker.yml
vendored
|
@ -10,20 +10,30 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
docker: [
|
docker: [
|
||||||
|
# Run slower jobs first to give them a headstart and reduce waiting time
|
||||||
|
ubuntu-20.04-focal-arm64v8,
|
||||||
|
ubuntu-20.04-focal-ppc64le,
|
||||||
|
ubuntu-20.04-focal-s390x,
|
||||||
|
# Then run the remainder
|
||||||
alpine,
|
alpine,
|
||||||
|
amazon-2-amd64,
|
||||||
arch,
|
arch,
|
||||||
ubuntu-18.04-bionic-amd64,
|
|
||||||
ubuntu-20.04-focal-amd64,
|
|
||||||
debian-10-buster-x86,
|
|
||||||
centos-6-amd64,
|
|
||||||
centos-7-amd64,
|
centos-7-amd64,
|
||||||
centos-8-amd64,
|
centos-8-amd64,
|
||||||
amazon-1-amd64,
|
debian-10-buster-x86,
|
||||||
amazon-2-amd64,
|
|
||||||
fedora-32-amd64,
|
fedora-32-amd64,
|
||||||
fedora-33-amd64,
|
fedora-33-amd64,
|
||||||
|
ubuntu-18.04-bionic-amd64,
|
||||||
|
ubuntu-20.04-focal-amd64,
|
||||||
]
|
]
|
||||||
dockerTag: [master]
|
dockerTag: [master]
|
||||||
|
include:
|
||||||
|
- docker: "ubuntu-20.04-focal-arm64v8"
|
||||||
|
qemu-arch: "aarch64"
|
||||||
|
- docker: "ubuntu-20.04-focal-ppc64le"
|
||||||
|
qemu-arch: "ppc64le"
|
||||||
|
- docker: "ubuntu-20.04-focal-s390x"
|
||||||
|
qemu-arch: "s390x"
|
||||||
|
|
||||||
name: ${{ matrix.docker }}
|
name: ${{ matrix.docker }}
|
||||||
|
|
||||||
|
@ -31,7 +41,12 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
if: "matrix.qemu-arch"
|
||||||
|
run: |
|
||||||
|
docker run --rm --privileged aptman/qus -s -- -p ${{ matrix.qemu-arch }}
|
||||||
|
|
||||||
- name: Docker pull
|
- name: Docker pull
|
||||||
run: |
|
run: |
|
||||||
|
|
52
.github/workflows/test-valgrind.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
name: Test Valgrind
|
||||||
|
|
||||||
|
# like the docker tests, but running valgrind only on *.c/*.h changes.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "**.c"
|
||||||
|
- "**.h"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**.c"
|
||||||
|
- "**.h"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
docker: [
|
||||||
|
ubuntu-20.04-focal-amd64-valgrind,
|
||||||
|
]
|
||||||
|
dockerTag: [master]
|
||||||
|
|
||||||
|
name: ${{ matrix.docker }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build system information
|
||||||
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
|
- name: Docker pull
|
||||||
|
run: |
|
||||||
|
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||||
|
|
||||||
|
- name: Build and Run Valgrind
|
||||||
|
run: |
|
||||||
|
# The Pillow user in the docker container is UID 1000
|
||||||
|
sudo chown -R 1000 $GITHUB_WORKSPACE
|
||||||
|
docker run --name pillow_container -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||||
|
sudo chown -R runner $GITHUB_WORKSPACE
|
||||||
|
|
||||||
|
success:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Valgrind Test Successful
|
||||||
|
steps:
|
||||||
|
- name: Success
|
||||||
|
run: echo Valgrind Test Successful
|
43
.github/workflows/test-windows.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev", "pypy3"]
|
python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10-dev"]
|
||||||
architecture: ["x86", "x64"]
|
architecture: ["x86", "x64"]
|
||||||
include:
|
include:
|
||||||
- architecture: "x86"
|
- architecture: "x86"
|
||||||
|
@ -19,7 +19,9 @@ jobs:
|
||||||
platform-msbuild: "x64"
|
platform-msbuild: "x64"
|
||||||
exclude:
|
exclude:
|
||||||
# PyPy does not support 64-bit on Windows
|
# PyPy does not support 64-bit on Windows
|
||||||
- python-version: "pypy3"
|
- python-version: "pypy-3.6"
|
||||||
|
architecture: "x64"
|
||||||
|
- python-version: "pypy-3.7"
|
||||||
architecture: "x64"
|
architecture: "x64"
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
|
@ -55,8 +57,8 @@ jobs:
|
||||||
- name: Print build system information
|
- name: Print build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: python -m pip install wheel pytest pytest-cov
|
- name: python -m pip install wheel pytest pytest-cov pytest-timeout
|
||||||
run: python -m pip install wheel pytest pytest-cov
|
run: python -m pip install wheel pytest pytest-cov pytest-timeout
|
||||||
|
|
||||||
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
|
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
|
||||||
- name: Upgrade setuptools
|
- name: Upgrade setuptools
|
||||||
|
@ -64,6 +66,7 @@ jobs:
|
||||||
run: python -m pip install -U "setuptools>=49.3.2"
|
run: python -m pip install -U "setuptools>=49.3.2"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
id: install
|
||||||
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\"
|
||||||
echo "$env:RUNNER_WORKSPACE\nasm-2.14.02" >> $env:GITHUB_PATH
|
echo "$env:RUNNER_WORKSPACE\nasm-2.14.02" >> $env:GITHUB_PATH
|
||||||
|
@ -72,6 +75,9 @@ jobs:
|
||||||
echo "C:\Program Files (x86)\gs\gs9.53.3\bin" >> $env:GITHUB_PATH
|
echo "C:\Program Files (x86)\gs\gs9.53.3\bin" >> $env:GITHUB_PATH
|
||||||
|
|
||||||
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
|
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
|
||||||
|
|
||||||
|
# make cache key depend on VS version
|
||||||
|
& "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" | find """catalog_buildVersion""" | ForEach-Object { $a = $_.split(" ")[1]; echo "::set-output name=vs::$a" }
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
|
@ -80,7 +86,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
path: winbuild\build
|
path: winbuild\build
|
||||||
key:
|
key:
|
||||||
${{ hashFiles('winbuild\build_prepare.py') }}-${{ hashFiles('.github\workflows\test-windows.yml') }}-${{ env.pythonLocation }}
|
${{ hashFiles('winbuild\build_prepare.py') }}-${{ hashFiles('.github\workflows\test-windows.yml') }}-${{ env.pythonLocation }}-${{ steps.install.outputs.vs }}
|
||||||
|
|
||||||
- name: Prepare build
|
- name: Prepare build
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
|
@ -91,25 +97,32 @@ jobs:
|
||||||
- name: Build dependencies / libjpeg-turbo
|
- name: Build dependencies / libjpeg-turbo
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
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'
|
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'
|
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'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libwebp.cmd"
|
run: "& winbuild\\build\\build_dep_libwebp.cmd"
|
||||||
# for FreeType CBDT font support
|
|
||||||
|
# for FreeType CBDT/SBIX font support
|
||||||
- name: Build dependencies / libpng
|
- name: Build dependencies / libpng
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_libpng.cmd"
|
run: "& winbuild\\build\\build_dep_libpng.cmd"
|
||||||
|
|
||||||
- name: Build dependencies / FreeType
|
- name: Build dependencies / FreeType
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
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'
|
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'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
|
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
|
||||||
|
@ -123,12 +136,11 @@ jobs:
|
||||||
- name: Build dependencies / HarfBuzz
|
- name: Build dependencies / HarfBuzz
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
||||||
|
|
||||||
|
# Raqm dependencies
|
||||||
- name: Build dependencies / FriBidi
|
- name: Build dependencies / FriBidi
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
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
|
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
|
||||||
run: "& winbuild\\build\\build_dep_libraqm.cmd"
|
|
||||||
|
|
||||||
# trim ~150MB x 9
|
# trim ~150MB x 9
|
||||||
- name: Optimize build cache
|
- name: Optimize build cache
|
||||||
|
@ -159,7 +171,7 @@ jobs:
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
mkdir -p Tests/errors
|
mkdir -p Tests/errors
|
||||||
shell: pwsh
|
shell: bash
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
@ -182,16 +194,14 @@ jobs:
|
||||||
|
|
||||||
- name: Build wheel
|
- name: Build wheel
|
||||||
id: wheel
|
id: wheel
|
||||||
# Skip wheels on 3.10 due to https://github.com/pypa/wheel/issues/354
|
if: "github.event_name == 'push'"
|
||||||
if: "github.event_name == 'push' && !contains(matrix.python-version, '3.10')"
|
|
||||||
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 --disable-imagequant 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
|
||||||
# Skip wheels on 3.10 due to https://github.com/pypa/wheel/issues/354
|
if: "github.event_name == 'push'"
|
||||||
if: "github.event_name == 'push' && !contains(matrix.python-version, '3.10')"
|
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.wheel.outputs.dist }}
|
name: ${{ steps.wheel.outputs.dist }}
|
||||||
path: dist\*.whl
|
path: dist\*.whl
|
||||||
|
@ -236,8 +246,6 @@ jobs:
|
||||||
${{ matrix.package }}-python3-olefile \
|
${{ matrix.package }}-python3-olefile \
|
||||||
${{ matrix.package }}-python3-pip \
|
${{ matrix.package }}-python3-pip \
|
||||||
${{ matrix.package }}-python3-pyqt5 \
|
${{ matrix.package }}-python3-pyqt5 \
|
||||||
${{ matrix.package }}-python3-pytest \
|
|
||||||
${{ matrix.package }}-python3-pytest-cov \
|
|
||||||
${{ matrix.package }}-python3-setuptools \
|
${{ matrix.package }}-python3-setuptools \
|
||||||
${{ matrix.package }}-freetype \
|
${{ matrix.package }}-freetype \
|
||||||
${{ matrix.package }}-ghostscript \
|
${{ matrix.package }}-ghostscript \
|
||||||
|
@ -250,7 +258,7 @@ jobs:
|
||||||
${{ matrix.package }}-openjpeg2 \
|
${{ matrix.package }}-openjpeg2 \
|
||||||
subversion
|
subversion
|
||||||
|
|
||||||
python3 -m pip install pyroma
|
python3 -m pip install pyroma pytest pytest-cov
|
||||||
|
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
||||||
|
@ -260,6 +268,7 @@ jobs:
|
||||||
- name: Test Pillow
|
- name: Test Pillow
|
||||||
run: |
|
run: |
|
||||||
python3 selftest.py --installed
|
python3 selftest.py --installed
|
||||||
|
python3 -c "from PIL import Image"
|
||||||
python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
|
python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
|
|
18
.github/workflows/test.yml
vendored
|
@ -13,7 +13,8 @@ jobs:
|
||||||
"macOS-latest",
|
"macOS-latest",
|
||||||
]
|
]
|
||||||
python-version: [
|
python-version: [
|
||||||
"pypy3",
|
"pypy-3.7",
|
||||||
|
"pypy-3.6",
|
||||||
"3.10-dev",
|
"3.10-dev",
|
||||||
"3.9",
|
"3.9",
|
||||||
"3.8",
|
"3.8",
|
||||||
|
@ -22,9 +23,9 @@ jobs:
|
||||||
]
|
]
|
||||||
include:
|
include:
|
||||||
- python-version: "3.6"
|
- python-version: "3.6"
|
||||||
env: PYTHONOPTIMIZE=1
|
PYTHONOPTIMIZE: 1
|
||||||
- python-version: "3.7"
|
- python-version: "3.7"
|
||||||
env: PYTHONOPTIMIZE=2
|
PYTHONOPTIMIZE: 2
|
||||||
# Include new variables for Codecov
|
# Include new variables for Codecov
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
codecov-flag: GHA_Ubuntu
|
codecov-flag: GHA_Ubuntu
|
||||||
|
@ -57,7 +58,7 @@ jobs:
|
||||||
${{ matrix.os }}-${{ matrix.python-version }}-
|
${{ matrix.os }}-${{ matrix.python-version }}-
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: Install Linux dependencies
|
- name: Install Linux dependencies
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
|
@ -79,13 +80,18 @@ jobs:
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
|
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||||
|
xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh
|
||||||
|
else
|
||||||
.ci/test.sh
|
.ci/test.sh
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
PYTHONOPTIMIZE: ${{ matrix.PYTHONOPTIMIZE }}
|
||||||
|
|
||||||
- name: Prepare to upload errors
|
- name: Prepare to upload errors
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
mkdir -p Tests/errors
|
mkdir -p Tests/errors
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
@ -97,7 +103,7 @@ jobs:
|
||||||
- name: Docs
|
- name: Docs
|
||||||
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9
|
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install sphinx-removed-in sphinx-rtd-theme
|
python3 -m pip install sphinx-issues sphinx-removed-in sphinx-rtd-theme
|
||||||
make doccheck
|
make doccheck
|
||||||
|
|
||||||
- name: After success
|
- name: After success
|
||||||
|
|
3
.gitignore
vendored
|
@ -88,3 +88,6 @@ pip-delete-this-directory.txt
|
||||||
|
|
||||||
# JetBrains
|
# JetBrains
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# pyinstaller
|
||||||
|
*.spec
|
||||||
|
|
|
@ -8,7 +8,7 @@ repos:
|
||||||
files: \.py$
|
files: \.py$
|
||||||
types: []
|
types: []
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/PyCQA/isort
|
||||||
rev: 377d260ffa6f746693f97b46d95025afc4bd8275 # frozen: 5.4.2
|
rev: 377d260ffa6f746693f97b46d95025afc4bd8275 # frozen: 5.4.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
|
|
67
.travis.yml
|
@ -1,67 +0,0 @@
|
||||||
dist: xenial
|
|
||||||
language: python
|
|
||||||
cache:
|
|
||||||
pip: true
|
|
||||||
directories:
|
|
||||||
- $HOME/.cache/pre-commit
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
irc: "chat.freenode.net#pil"
|
|
||||||
|
|
||||||
# Run fast lint first to get fast feedback.
|
|
||||||
# Run slower CPUs next, to give them a headstart and reduce waiting time.
|
|
||||||
# Then run the remainder.
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- python: "3.6"
|
|
||||||
name: "Lint"
|
|
||||||
env: LINT="true"
|
|
||||||
|
|
||||||
- python: "3.6"
|
|
||||||
arch: arm64
|
|
||||||
- python: "3.7"
|
|
||||||
arch: ppc64le
|
|
||||||
- python: "3.8"
|
|
||||||
arch: s390x
|
|
||||||
|
|
||||||
- python: "pypy3.6-7.3.1"
|
|
||||||
name: "PyPy3 Xenial"
|
|
||||||
- python: "3.9"
|
|
||||||
name: "3.9 Xenial"
|
|
||||||
services: xvfb
|
|
||||||
- python: "3.8"
|
|
||||||
name: "3.8 Xenial"
|
|
||||||
services: xvfb
|
|
||||||
- python: '3.7'
|
|
||||||
name: "3.7 Xenial PYTHONOPTIMIZE=2"
|
|
||||||
env: PYTHONOPTIMIZE=2
|
|
||||||
services: xvfb
|
|
||||||
- python: '3.6'
|
|
||||||
name: "3.6 Xenial PYTHONOPTIMIZE=1"
|
|
||||||
env: PYTHONOPTIMIZE=1
|
|
||||||
services: xvfb
|
|
||||||
|
|
||||||
install:
|
|
||||||
- |
|
|
||||||
if [ "$LINT" == "true" ]; then
|
|
||||||
python3 -m pip install tox
|
|
||||||
else
|
|
||||||
.ci/install.sh;
|
|
||||||
fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- |
|
|
||||||
if [ "$LINT" == "true" ]; then
|
|
||||||
tox -e lint
|
|
||||||
else
|
|
||||||
.ci/build.sh
|
|
||||||
.ci/test.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- |
|
|
||||||
if [ "$LINT" == "" ]; then
|
|
||||||
.ci/after_success.sh
|
|
||||||
fi
|
|
168
CHANGES.rst
|
@ -2,9 +2,171 @@
|
||||||
Changelog (Pillow)
|
Changelog (Pillow)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
8.1.0 (unreleased)
|
8.2.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Fixed linear_gradient and radial_gradient I and F modes #5274
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add support for reading TIFFs with PlanarConfiguration=2 #5364
|
||||||
|
[kkopachev, wiredfool, nulano]
|
||||||
|
|
||||||
|
- Deprecated categories #5351
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Do not premultiply alpha when resizing with Image.NEAREST resampling #5304
|
||||||
|
[nulano]
|
||||||
|
|
||||||
|
- Dynamically link FriBiDi instead of Raqm #5062
|
||||||
|
[nulano]
|
||||||
|
|
||||||
|
- Allow fewer PNG palette entries than the bit depth maximum when saving #5330
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Use duration from info dictionary when saving WebP #5338
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Stop flattening EXIF IFD into getexif() #4947
|
||||||
|
[radarhere, kkopachev]
|
||||||
|
|
||||||
|
- Replaced tiff_deflate with tiff_adobe_deflate compression when saving TIFF images #5343
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Save ICC profile from TIFF encoderinfo #5321
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Moved RGB fix inside ImageQt class #5268
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Allow alpha_composite destination to be negative #5313
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Ensure file is closed if it is opened by ImageQt.ImageQt #5260
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added ImageDraw rounded_rectangle method #5208
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added IPythonViewer #5289
|
||||||
|
[radarhere, Kipkurui-mutai]
|
||||||
|
|
||||||
|
- Only draw each rectangle outline pixel once #5183
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Use mmap instead of built-in Win32 mapper #5224
|
||||||
|
[radarhere, cgohlke]
|
||||||
|
|
||||||
|
- Handle PCX images with an odd stride #5214
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Only read different sizes for "Large Thumbnail" MPO frames #5168
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added PyQt6 support #5258
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Changed Image.open formats parameter to be case-insensitive #5250
|
||||||
|
[Piolie, radarhere]
|
||||||
|
|
||||||
|
- Deprecate Tk/Tcl 8.4, to be removed in Pillow 10 (2023-01-02) #5216
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added tk version to pilinfo #5226
|
||||||
|
[radarhere, nulano]
|
||||||
|
|
||||||
|
- Support for ignoring tests when running valgrind #5150
|
||||||
|
[wiredfool, radarhere, hugovk]
|
||||||
|
|
||||||
|
- OSS-Fuzz support #5189
|
||||||
|
[wiredfool, radarhere]
|
||||||
|
|
||||||
|
8.1.2 (2021-03-06)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Fix Memory DOS in BLP (CVE-2021-27921), ICNS (CVE-2021-27922) and ICO (CVE-2021-27923) Image Plugins
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
8.1.1 (2021-03-01)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Use more specific regex chars to prevent ReDoS. CVE-2021-25292
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Fix OOB Read in TiffDecode.c, and check the tile validity before reading. CVE-2021-25291
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Fix negative size read in TiffDecode.c. CVE-2021-25290
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Fix OOB read in SgiRleDecode.c. CVE-2021-25293
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Incorrect error code checking in TiffDecode.c. CVE-2021-25289
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- PyModule_AddObject fix for Python 3.10 #5194
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
8.1.0 (2021-01-02)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Fix TIFF OOB Write error. CVE-2020-35654 #5175
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Fix for Read Overflow in PCX Decoding. CVE-2020-35653 #5174
|
||||||
|
[wiredfool, radarhere]
|
||||||
|
|
||||||
|
- Fix for SGI Decode buffer overrun. CVE-2020-35655 #5173
|
||||||
|
[wiredfool, radarhere]
|
||||||
|
|
||||||
|
- Fix OOB Read when saving GIF of xsize=1 #5149
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Makefile updates #5159
|
||||||
|
[wiredfool, radarhere]
|
||||||
|
|
||||||
|
- Add support for PySide6 #5161
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Use disposal settings from previous frame in APNG #5126
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added exception explaining that _repr_png_ saves to PNG #5139
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Use previous disposal method in GIF load_end #5125
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Allow putpalette to accept 1024 integers to include alpha values #5089
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fix OOB Read when writing TIFF with custom Metadata #5148
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Added append_images support for ICO #4568
|
||||||
|
[ziplantil, radarhere]
|
||||||
|
|
||||||
|
- Block TIFFTAG_SUBIFD #5120
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fixed dereferencing potential null pointers #5108, #5111
|
||||||
|
[cgohlke, radarhere]
|
||||||
|
|
||||||
|
- Deprecate FreeType 2.7 #5098
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- Moved warning to end of execution #4965
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Removed unused fromstring and tostring C methods #5026
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- init() if one of the formats is unrecognised #5037
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Moved string_dimension CVE image to pillow-depends #4993
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Support raw rgba8888 for DDS #4760
|
- Support raw rgba8888 for DDS #4760
|
||||||
[qiankanglai]
|
[qiankanglai]
|
||||||
|
|
||||||
|
@ -4039,8 +4201,8 @@ Changelog (Pillow)
|
||||||
1.0 (07/30/2010)
|
1.0 (07/30/2010)
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- Remove support for ``import Image``, etc. from the standard namespace. ``from PIL import Image`` etc. now required.
|
- Remove support for ``import Image``. ``from PIL import Image`` now required.
|
||||||
- Forked PIL based on `Hanno Schlichting's re-packaging <https://dist.plone.org/thirdparty/PIL-1.1.7.tar.gz>`_
|
- Forked PIL based on `Chris McDonough and Hanno Schlichting's setuptools compatible re-packaging <https://dist.plone.org/thirdparty/PIL-1.1.7.tar.gz>`_
|
||||||
[aclark4life]
|
[aclark4life]
|
||||||
|
|
||||||
Pre-fork
|
Pre-fork
|
||||||
|
|
2
LICENSE
|
@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
|
||||||
|
|
||||||
Pillow is the friendly PIL fork. It is
|
Pillow is the friendly PIL fork. It is
|
||||||
|
|
||||||
Copyright © 2010-2020 by Alex Clark and contributors
|
Copyright © 2010-2021 by Alex Clark and contributors
|
||||||
|
|
||||||
Like PIL, Pillow is licensed under the open source HPND License:
|
Like PIL, Pillow is licensed under the open source HPND License:
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ graft docs
|
||||||
|
|
||||||
# build/src control detritus
|
# build/src control detritus
|
||||||
exclude .appveyor.yml
|
exclude .appveyor.yml
|
||||||
|
exclude .clang-format
|
||||||
exclude .coveragerc
|
exclude .coveragerc
|
||||||
exclude .editorconfig
|
exclude .editorconfig
|
||||||
exclude .readthedocs.yml
|
exclude .readthedocs.yml
|
||||||
|
|
31
Makefile
|
@ -1,4 +1,4 @@
|
||||||
.DEFAULT_GOAL := release-test
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
@ -7,13 +7,6 @@ clean:
|
||||||
rm -r build || true
|
rm -r build || true
|
||||||
find . -name __pycache__ | xargs rm -r || true
|
find . -name __pycache__ | xargs rm -r || true
|
||||||
|
|
||||||
BRANCHES=`git branch -a | grep -v HEAD | grep -v master | grep remote`
|
|
||||||
.PHONY: co
|
|
||||||
co:
|
|
||||||
-for i in $(BRANCHES) ; do \
|
|
||||||
git checkout -t $$i ; \
|
|
||||||
done
|
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
pytest -qq
|
pytest -qq
|
||||||
|
@ -33,7 +26,7 @@ doccheck:
|
||||||
|
|
||||||
.PHONY: docserve
|
.PHONY: docserve
|
||||||
docserve:
|
docserve:
|
||||||
cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
|
cd docs/_build/html && python3 -m http.server 2> /dev/null&
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
|
@ -47,7 +40,9 @@ help:
|
||||||
@echo " install make and install"
|
@echo " install make and install"
|
||||||
@echo " install-coverage make and install with C coverage"
|
@echo " install-coverage make and install with C coverage"
|
||||||
@echo " install-req install documentation and test dependencies"
|
@echo " install-req install documentation and test dependencies"
|
||||||
@echo " install-venv install in virtualenv"
|
@echo " install-venv (deprecated) install in virtualenv"
|
||||||
|
@echo " lint run the lint checks"
|
||||||
|
@echo " lint-fix run black and isort to (mostly) fix lint issues."
|
||||||
@echo " release-test run code and package tests before release"
|
@echo " release-test run code and package tests before release"
|
||||||
@echo " test run tests on installed pillow"
|
@echo " test run tests on installed pillow"
|
||||||
@echo " upload build and upload sdists to PyPI"
|
@echo " upload build and upload sdists to PyPI"
|
||||||
|
@ -81,6 +76,7 @@ install-req:
|
||||||
|
|
||||||
.PHONY: install-venv
|
.PHONY: install-venv
|
||||||
install-venv:
|
install-venv:
|
||||||
|
echo "'install-venv' is deprecated and will be removed in a future Pillow release"
|
||||||
virtualenv .
|
virtualenv .
|
||||||
bin/pip install -r requirements.txt
|
bin/pip install -r requirements.txt
|
||||||
|
|
||||||
|
@ -96,7 +92,7 @@ release-test:
|
||||||
python3 -m pytest -qq
|
python3 -m pytest -qq
|
||||||
check-manifest
|
check-manifest
|
||||||
pyroma .
|
pyroma .
|
||||||
viewdoc
|
$(MAKE) readme
|
||||||
|
|
||||||
.PHONY: sdist
|
.PHONY: sdist
|
||||||
sdist:
|
sdist:
|
||||||
|
@ -108,4 +104,15 @@ test:
|
||||||
|
|
||||||
.PHONY: readme
|
.PHONY: readme
|
||||||
readme:
|
readme:
|
||||||
viewdoc
|
python3 setup.py --long-description | markdown2 > .long-description.html && open .long-description.html
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
tox --help > /dev/null || python3 -m pip install tox
|
||||||
|
tox -e lint
|
||||||
|
|
||||||
|
.PHONY: lint-fix
|
||||||
|
lint-fix:
|
||||||
|
black --target-version py36 .
|
||||||
|
isort .
|
||||||
|
|
16
README.md
|
@ -24,15 +24,6 @@ As of 2019, Pillow development is
|
||||||
<tr>
|
<tr>
|
||||||
<th>tests</th>
|
<th>tests</th>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://travis-ci.com/github/python-pillow/Pillow"><img
|
|
||||||
alt="Travis CI build status (Linux)"
|
|
||||||
src="https://img.shields.io/travis/com/python-pillow/Pillow/master.svg?label=Linux%20build"></a>
|
|
||||||
<a href="https://travis-ci.com/github/python-pillow/pillow-wheels"><img
|
|
||||||
alt="Travis CI build status (macOS)"
|
|
||||||
src="https://img.shields.io/travis/com/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
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint"><img
|
||||||
alt="GitHub Actions build status (Lint)"
|
alt="GitHub Actions build status (Lint)"
|
||||||
src="https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg"></a>
|
src="https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg"></a>
|
||||||
|
@ -45,6 +36,12 @@ As of 2019, Pillow development is
|
||||||
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22"><img
|
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22"><img
|
||||||
alt="GitHub Actions build status (Test Docker)"
|
alt="GitHub Actions build status (Test Docker)"
|
||||||
src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></a>
|
src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></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://travis-ci.com/github/python-pillow/pillow-wheels"><img
|
||||||
|
alt="Travis CI build status (macOS)"
|
||||||
|
src="https://img.shields.io/travis/com/python-pillow/pillow-wheels/master.svg?label=macOS%20build"></a>
|
||||||
<a href="https://codecov.io/gh/python-pillow/Pillow"><img
|
<a href="https://codecov.io/gh/python-pillow/Pillow"><img
|
||||||
alt="Code coverage"
|
alt="Code coverage"
|
||||||
src="https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg"></a>
|
src="https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg"></a>
|
||||||
|
@ -96,6 +93,7 @@ The core image library is designed for fast access to data stored in a few basic
|
||||||
- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md)
|
- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md)
|
||||||
- [Issues](https://github.com/python-pillow/Pillow/issues)
|
- [Issues](https://github.com/python-pillow/Pillow/issues)
|
||||||
- [Pull requests](https://github.com/python-pillow/Pillow/pulls)
|
- [Pull requests](https://github.com/python-pillow/Pillow/pulls)
|
||||||
|
- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html)
|
||||||
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
|
- [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)
|
- [Pre-fork](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork)
|
||||||
|
|
||||||
|
|
41
RELEASING.md
|
@ -1,13 +1,16 @@
|
||||||
# Release Checklist
|
# Release Checklist
|
||||||
|
|
||||||
|
See https://pillow.readthedocs.io/en/stable/releasenotes/versioning.html for
|
||||||
|
information about how the version numbers line up with releases.
|
||||||
|
|
||||||
## Main Release
|
## Main Release
|
||||||
|
|
||||||
Released quarterly on January 2nd, April 1st, July 1st and October 15th.
|
Released quarterly on January 2nd, April 1st, July 1st and October 15th.
|
||||||
|
|
||||||
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154
|
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154
|
||||||
* [ ] Develop and prepare release in `master` branch.
|
* [ ] Develop and prepare release in `master` branch.
|
||||||
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions), [Travis CI](https://travis-ci.com/github/python-pillow/Pillow) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `master` branch.
|
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `master` branch.
|
||||||
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI.
|
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI and GitHub Actions.
|
||||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
||||||
* [ ] Update `CHANGES.rst`.
|
* [ ] Update `CHANGES.rst`.
|
||||||
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
|
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
|
||||||
|
@ -18,13 +21,18 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th.
|
||||||
git push --all
|
git push --all
|
||||||
git push --tags
|
git push --tags
|
||||||
```
|
```
|
||||||
* [ ] Create source distributions e.g.:
|
* [ ] Create and check source distribution:
|
||||||
```bash
|
```bash
|
||||||
make sdist
|
make sdist
|
||||||
|
twine check dist/*
|
||||||
```
|
```
|
||||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
||||||
* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.0*`
|
* [ ] Check and upload all binaries and source distributions e.g.:
|
||||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
```bash
|
||||||
|
twine check dist/*
|
||||||
|
twine upload dist/Pillow-5.2.0*
|
||||||
|
```
|
||||||
|
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
|
||||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py`
|
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py`
|
||||||
|
|
||||||
## Point Release
|
## Point Release
|
||||||
|
@ -41,7 +49,7 @@ Released as needed for security, installation or critical bug fixes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions), [Travis CI](https://travis-ci.com/github/python-pillow/Pillow) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`.
|
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`.
|
||||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
||||||
* [ ] Run pre-release check via `make release-test`.
|
* [ ] Run pre-release check via `make release-test`.
|
||||||
* [ ] Create tag for release e.g.:
|
* [ ] Create tag for release e.g.:
|
||||||
|
@ -50,13 +58,18 @@ Released as needed for security, installation or critical bug fixes.
|
||||||
git push
|
git push
|
||||||
git push --tags
|
git push --tags
|
||||||
```
|
```
|
||||||
* [ ] Create source distributions e.g.:
|
* [ ] Create and check source distribution:
|
||||||
```bash
|
```bash
|
||||||
make sdist
|
make sdist
|
||||||
|
twine check dist/*
|
||||||
```
|
```
|
||||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
||||||
* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.1*`
|
* [ ] Check and upload all binaries and source distributions e.g.:
|
||||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
```bash
|
||||||
|
twine check dist/*
|
||||||
|
twine upload dist/Pillow-5.2.1*
|
||||||
|
```
|
||||||
|
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
|
||||||
|
|
||||||
## Embargoed Release
|
## Embargoed Release
|
||||||
|
|
||||||
|
@ -75,18 +88,19 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
git push origin 2.5.x
|
git push origin 2.5.x
|
||||||
git push origin --tags
|
git push origin --tags
|
||||||
```
|
```
|
||||||
* [ ] Create source distributions e.g.:
|
* [ ] Create and check source distribution:
|
||||||
```bash
|
```bash
|
||||||
make sdist
|
make sdist
|
||||||
|
twine check dist/*
|
||||||
```
|
```
|
||||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
||||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
|
||||||
|
|
||||||
## Binary Distributions
|
## Binary Distributions
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
* [ ] Contact `@cgohlke` for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174.
|
* [ ] Contact `@cgohlke` for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174.
|
||||||
* [ ] Download and extract tarball from `@cgohlke` and `twine upload *`.
|
* [ ] Download and extract tarball from `@cgohlke` and copy into `dist/`
|
||||||
|
|
||||||
### Mac and Linux
|
### Mac and Linux
|
||||||
* [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels):
|
* [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels):
|
||||||
|
@ -95,7 +109,8 @@ 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 wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases).
|
* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases)
|
||||||
|
and copy into `dist/`
|
||||||
|
|
||||||
## Publicize Release
|
## Publicize Release
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from PIL import PyAccess
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
||||||
# Not running this test by default. No DOS against Travis CI.
|
# Not running this test by default. No DOS against CI.
|
||||||
|
|
||||||
|
|
||||||
def iterate_get(size, access):
|
def iterate_get(size, access):
|
||||||
|
|
|
@ -5,4 +5,5 @@ from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))
|
with Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00")):
|
||||||
|
pass
|
||||||
|
|
|
@ -5,4 +5,7 @@ from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
Image.open(BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
with Image.open(
|
||||||
|
BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang")
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
|
@ -10,3 +13,19 @@ def pytest_report_header(config):
|
||||||
return out.getvalue()
|
return out.getvalue()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"pytest_report_header failed: {e}"
|
return f"pytest_report_header failed: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
# We're marking some tests to ignore valgrind errors and XFAIL them.
|
||||||
|
# Ensure that the mark is defined
|
||||||
|
# even in cases where pytest-valgrind isn't installed
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("error")
|
||||||
|
try:
|
||||||
|
getattr(pytest.mark, "valgrind_known_error")
|
||||||
|
except Exception:
|
||||||
|
config.addinivalue_line(
|
||||||
|
"markers",
|
||||||
|
"valgrind_known_error: Tests that have known issues with valgrind",
|
||||||
|
)
|
||||||
|
|
40
Tests/fonts/DejaVuSans/LICENSE.txt
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
DejaVuSans-24-{1,2,4,8}-stripped.ttf are based on DejaVuSans.ttf converted using FontForge to add bitmap strikes and keep only the ASCII range.
|
||||||
|
|
||||||
|
DejaVu Fonts — License
|
||||||
|
Fonts are © Bitstream (see below). DejaVu changes are in public domain. Explanation of copyright is on Gnome page on Bitstream Vera fonts. Glyphs imported from Arev fonts are © Tavmjung Bah (see below)
|
||||||
|
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
|
||||||
|
|
||||||
|
Arev Fonts Copyright
|
||||||
|
Original text
|
||||||
|
|
||||||
|
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.
|
|
@ -15,8 +15,11 @@ FreeMono.ttf is licensed under GPLv3, with the GPL font exception.
|
||||||
|
|
||||||
OpenSansCondensed-LightItalic.tt, from https://fonts.google.com/specimen/Open+Sans, under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
OpenSansCondensed-LightItalic.tt, from https://fonts.google.com/specimen/Open+Sans, under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
DejaVuSans-24-{1,2,4,8}-stripped.ttf are based on DejaVuSans.ttf converted using FontForge to add bitmap strikes and keep only the ASCII range.
|
chromacheck-sbix.woff, from https://github.com/RoelN/ChromaCheck, under The MIT License (MIT), Copyright (c) 2018 Roel Nieskens, https://pixelambacht.nl Copyright (c) 2018 Google LLC
|
||||||
|
|
||||||
|
KhmerOSBattambang-Regular.ttf is licensed under LGPL-2.1 or later.
|
||||||
|
|
||||||
|
FreeMono.ttf is licensed under GPLv3.
|
||||||
|
|
||||||
10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base
|
10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base
|
||||||
|
|
||||||
|
|
BIN
Tests/fonts/chromacheck-sbix.woff
Normal file
|
@ -270,7 +270,7 @@ def on_github_actions():
|
||||||
|
|
||||||
|
|
||||||
def on_ci():
|
def on_ci():
|
||||||
# GitHub Actions, Travis and AppVeyor have "CI"
|
# GitHub Actions and AppVeyor have "CI"
|
||||||
return "CI" in os.environ
|
return "CI" in os.environ
|
||||||
|
|
||||||
|
|
||||||
|
@ -278,6 +278,12 @@ def is_big_endian():
|
||||||
return sys.byteorder == "big"
|
return sys.byteorder == "big"
|
||||||
|
|
||||||
|
|
||||||
|
def is_ppc64le():
|
||||||
|
import platform
|
||||||
|
|
||||||
|
return platform.machine() == "ppc64le"
|
||||||
|
|
||||||
|
|
||||||
def is_win32():
|
def is_win32():
|
||||||
return sys.platform.startswith("win32")
|
return sys.platform.startswith("win32")
|
||||||
|
|
||||||
|
|
BIN
Tests/images/apng/dispose_op_previous_frame.png
Normal file
After Width: | Height: | Size: 582 B |
BIN
Tests/images/chromacheck-sbix.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/chromacheck-sbix_mask.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/crash-0c7e0e8e11ce787078f00b5b0ca409a167f070e0.tif
Normal file
BIN
Tests/images/crash-0e16d3bfb83be87356d026d66919deaefca44dac.tif
Normal file
BIN
Tests/images/crash-1152ec2d1a1a71395b6f2ce6721c38924d025bf3.tif
Normal file
BIN
Tests/images/crash-1185209cf7655b5aed8ae5e77784dfdd18ab59e9.tif
Normal file
BIN
Tests/images/crash-2020-10-test.tif
Normal file
BIN
Tests/images/crash-338516dbd2f0e83caddb8ce256c22db3bd6dc40f.tif
Normal file
BIN
Tests/images/crash-465703f71a0f0094873a3e0e82c9f798161171b8.sgi
Normal file
BIN
Tests/images/crash-4f085cc12ece8cde18758d42608bed6a2a2cfb1c.tif
Normal file
BIN
Tests/images/crash-63b1dffefc8c075ddc606c0a2f5fdc15ece78863.tif
Normal file
BIN
Tests/images/crash-64834657ee604b8797bf99eac6a194c124a9a8ba.sgi
Normal file
BIN
Tests/images/crash-6b7f2244da6d0ae297ee0754a424213444e92778.sgi
Normal file
BIN
Tests/images/crash-754d9c7ec485ffb76a90eeaab191ef69a2a3a3cd.sgi
Normal file
BIN
Tests/images/crash-86214e58da443d2b80820cff9677a38a33dcbbca.tif
Normal file
BIN
Tests/images/crash-abcf1c97b8fe42a6c68f1fb0b978530c98d57ced.sgi
Normal file
BIN
Tests/images/crash-b82e64d4f3f76d7465b6af535283029eda211259.sgi
Normal file
BIN
Tests/images/crash-c1b2595b8b0b92cc5f38b6635e98e3a119ade807.sgi
Normal file
BIN
Tests/images/crash-db8bfa78b19721225425530c5946217720d7df4e.sgi
Normal file
BIN
Tests/images/crash-f46f5b2f43c370fe65706c11449f567ecc345e74.tif
Normal file
BIN
Tests/images/dispose_none_load_end.gif
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
Tests/images/dispose_none_load_end_second.gif
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Tests/images/ignore_frame_size.mpo
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
Tests/images/imagedraw_rectangle_translucent_outline.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
Tests/images/imagedraw_rounded_rectangle.png
Normal file
After Width: | Height: | Size: 934 B |
BIN
Tests/images/imagedraw_rounded_rectangle_both.png
Normal file
After Width: | Height: | Size: 530 B |
BIN
Tests/images/imagedraw_rounded_rectangle_x.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
Tests/images/imagedraw_rounded_rectangle_y.png
Normal file
After Width: | Height: | Size: 528 B |
BIN
Tests/images/odd_stride.pcx
Normal file
BIN
Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns
Normal file
BIN
Tests/images/ossfuzz-4836216264589312.pcx
Normal file
BIN
Tests/images/ossfuzz-5730089102868480.sgi
Normal file
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
BIN
Tests/images/tiff_strip_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_lzw.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_lzw.tiff
Normal file
48
Tests/oss-fuzz/build.sh
Executable file
|
@ -0,0 +1,48 @@
|
||||||
|
#!/bin/bash -eu
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
python3 setup.py build --build-base=/tmp/build install
|
||||||
|
|
||||||
|
# Build fuzzers in $OUT.
|
||||||
|
for fuzzer in $(find $SRC -name 'fuzz_*.py'); do
|
||||||
|
fuzzer_basename=$(basename -s .py $fuzzer)
|
||||||
|
fuzzer_package=${fuzzer_basename}.pkg
|
||||||
|
pyinstaller \
|
||||||
|
--add-binary /usr/local/lib/libjpeg.so.9:. \
|
||||||
|
--add-binary /usr/local/lib/libfreetype.so.6:. \
|
||||||
|
--add-binary /usr/local/lib/liblcms2.so.2:. \
|
||||||
|
--add-binary /usr/local/lib/libopenjp2.so.7:. \
|
||||||
|
--add-binary /usr/local/lib/libpng16.so.16:. \
|
||||||
|
--add-binary /usr/local/lib/libtiff.so.5:. \
|
||||||
|
--add-binary /usr/local/lib/libwebp.so.7:. \
|
||||||
|
--add-binary /usr/local/lib/libwebpdemux.so.2:. \
|
||||||
|
--add-binary /usr/local/lib/libwebpmux.so.3:. \
|
||||||
|
--add-binary /usr/local/lib/libxcb.so.1:. \
|
||||||
|
--distpath $OUT --onefile --name $fuzzer_package $fuzzer
|
||||||
|
|
||||||
|
# Create execution wrapper.
|
||||||
|
echo "#!/bin/sh
|
||||||
|
# LLVMFuzzerTestOneInput for fuzzer detection.
|
||||||
|
this_dir=\$(dirname \"\$0\")
|
||||||
|
LD_PRELOAD=\$this_dir/sanitizer_with_fuzzer.so \
|
||||||
|
ASAN_OPTIONS=\$ASAN_OPTIONS:symbolize=1:external_symbolizer_path=\$this_dir/llvm-symbolizer:detect_leaks=0 \
|
||||||
|
\$this_dir/$fuzzer_package \$@" > $OUT/$fuzzer_basename
|
||||||
|
chmod u+x $OUT/$fuzzer_basename
|
||||||
|
done
|
||||||
|
|
||||||
|
find Tests/images Tests/icc -print | zip -q $OUT/fuzz_pillow_seed_corpus.zip -@
|
||||||
|
find Tests/fonts -print | zip -q $OUT/fuzz_font_seed_corpus.zip -@
|
33
Tests/oss-fuzz/build_dictionaries.sh
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/bash -eu
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Generate image dictionaries here for each of the fuzzers and put them in the
|
||||||
|
# $OUT directory, named for the fuzzer
|
||||||
|
|
||||||
|
git clone --depth 1 https://github.com/google/fuzzing
|
||||||
|
cat fuzzing/dictionaries/bmp.dict \
|
||||||
|
fuzzing/dictionaries/dds.dict \
|
||||||
|
fuzzing/dictionaries/gif.dict \
|
||||||
|
fuzzing/dictionaries/icns.dict \
|
||||||
|
fuzzing/dictionaries/jpeg.dict \
|
||||||
|
fuzzing/dictionaries/jpeg2000.dict \
|
||||||
|
fuzzing/dictionaries/pbm.dict \
|
||||||
|
fuzzing/dictionaries/png.dict \
|
||||||
|
fuzzing/dictionaries/psd.dict \
|
||||||
|
fuzzing/dictionaries/tiff.dict \
|
||||||
|
fuzzing/dictionaries/webp.dict \
|
||||||
|
> $OUT/fuzz_pillow.dict
|
40
Tests/oss-fuzz/fuzz_font.py
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import atheris_no_libfuzzer as atheris
|
||||||
|
import fuzzers
|
||||||
|
|
||||||
|
|
||||||
|
def TestOneInput(data):
|
||||||
|
try:
|
||||||
|
fuzzers.fuzz_font(data)
|
||||||
|
except Exception:
|
||||||
|
# We're catching all exceptions because Pillow's exceptions are
|
||||||
|
# directly inheriting from Exception.
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fuzzers.enable_decompressionbomb_error()
|
||||||
|
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||||
|
atheris.Fuzz()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
40
Tests/oss-fuzz/fuzz_pillow.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import atheris_no_libfuzzer as atheris
|
||||||
|
import fuzzers
|
||||||
|
|
||||||
|
|
||||||
|
def TestOneInput(data):
|
||||||
|
try:
|
||||||
|
fuzzers.fuzz_image(data)
|
||||||
|
except Exception:
|
||||||
|
# We're catching all exceptions because Pillow's exceptions are
|
||||||
|
# directly inheriting from Exception.
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fuzzers.enable_decompressionbomb_error()
|
||||||
|
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||||
|
atheris.Fuzz()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
36
Tests/oss-fuzz/fuzzers.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import io
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFile, ImageFilter, ImageFont
|
||||||
|
|
||||||
|
|
||||||
|
def enable_decompressionbomb_error():
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
warnings.simplefilter("error", Image.DecompressionBombWarning)
|
||||||
|
|
||||||
|
|
||||||
|
def fuzz_image(data):
|
||||||
|
# This will fail on some images in the corpus, as we have many
|
||||||
|
# invalid images in the test suite.
|
||||||
|
with Image.open(io.BytesIO(data)) as im:
|
||||||
|
im.rotate(45)
|
||||||
|
im.filter(ImageFilter.DETAIL)
|
||||||
|
im.save(io.BytesIO(), "BMP")
|
||||||
|
|
||||||
|
|
||||||
|
def fuzz_font(data):
|
||||||
|
wrapper = io.BytesIO(data)
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype(wrapper)
|
||||||
|
except OSError:
|
||||||
|
# Catch pcf/pilfonts/random garbage here. They return
|
||||||
|
# different font objects.
|
||||||
|
return
|
||||||
|
|
||||||
|
font.getsize_multiline("ABC\nAaaa")
|
||||||
|
font.getmask("test text")
|
||||||
|
with Image.new(mode="RGBA", size=(200, 200)) as im:
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2)
|
||||||
|
draw.text((10, 10), "Test Text", font=font, fill="#000")
|
53
Tests/oss-fuzz/test_fuzzers.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import fuzzers
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
if sys.platform.startswith("win32"):
|
||||||
|
pytest.skip("Fuzzer is linux only", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
subprocess.check_output("find Tests/images -type f", shell=True).split(b"\n"),
|
||||||
|
)
|
||||||
|
def test_fuzz_images(path):
|
||||||
|
fuzzers.enable_decompressionbomb_error()
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
fuzzers.fuzz_image(f.read())
|
||||||
|
assert True
|
||||||
|
except (
|
||||||
|
OSError,
|
||||||
|
SyntaxError,
|
||||||
|
MemoryError,
|
||||||
|
ValueError,
|
||||||
|
NotImplementedError,
|
||||||
|
OverflowError,
|
||||||
|
):
|
||||||
|
# Known exceptions that are through from Pillow
|
||||||
|
assert True
|
||||||
|
except (
|
||||||
|
Image.DecompressionBombError,
|
||||||
|
Image.DecompressionBombWarning,
|
||||||
|
Image.UnidentifiedImageError,
|
||||||
|
):
|
||||||
|
# Known Image.* exceptions
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path", subprocess.check_output("find Tests/fonts -type f", shell=True).split(b"\n")
|
||||||
|
)
|
||||||
|
def test_fuzz_fonts(path):
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
try:
|
||||||
|
fuzzers.fuzz_font(f.read())
|
||||||
|
except (Image.DecompressionBombError, Image.DecompressionBombWarning):
|
||||||
|
pass
|
||||||
|
assert True
|
|
@ -20,7 +20,7 @@ def test_bad():
|
||||||
either"""
|
either"""
|
||||||
for f in get_files("b"):
|
for f in get_files("b"):
|
||||||
|
|
||||||
def open(f):
|
with pytest.warns(None) as record:
|
||||||
try:
|
try:
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
@ -28,7 +28,7 @@ def test_bad():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
# Assert that there is no unclosed file warning
|
||||||
pytest.warns(None, open, f)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_questionable():
|
def test_questionable():
|
||||||
|
|
|
@ -11,7 +11,7 @@ ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS
|
||||||
|
|
||||||
class TestDecompressionBomb:
|
class TestDecompressionBomb:
|
||||||
@classmethod
|
@classmethod
|
||||||
def teardown_class(self):
|
def teardown_class(cls):
|
||||||
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
|
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
|
||||||
|
|
||||||
def test_no_warning_small_file(self):
|
def test_no_warning_small_file(self):
|
||||||
|
@ -54,15 +54,18 @@ class TestDecompressionBomb:
|
||||||
|
|
||||||
def test_exception_ico(self):
|
def test_exception_ico(self):
|
||||||
with pytest.raises(Image.DecompressionBombError):
|
with pytest.raises(Image.DecompressionBombError):
|
||||||
Image.open("Tests/images/decompression_bomb.ico")
|
with Image.open("Tests/images/decompression_bomb.ico"):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_exception_gif(self):
|
def test_exception_gif(self):
|
||||||
with pytest.raises(Image.DecompressionBombError):
|
with pytest.raises(Image.DecompressionBombError):
|
||||||
Image.open("Tests/images/decompression_bomb.gif")
|
with Image.open("Tests/images/decompression_bomb.gif"):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_exception_bmp(self):
|
def test_exception_bmp(self):
|
||||||
with pytest.raises(Image.DecompressionBombError):
|
with pytest.raises(Image.DecompressionBombError):
|
||||||
Image.open("Tests/images/bmp/b/reallybig.bmp")
|
with Image.open("Tests/images/bmp/b/reallybig.bmp"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestDecompressionCrop:
|
class TestDecompressionCrop:
|
||||||
|
|
|
@ -105,6 +105,31 @@ def test_apng_dispose_region():
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
|
def test_apng_dispose_op_previous_frame():
|
||||||
|
# Test that the dispose settings being used are from the previous frame
|
||||||
|
#
|
||||||
|
# Image created with:
|
||||||
|
# red = Image.new("RGBA", (128, 64), (255, 0, 0, 255))
|
||||||
|
# green = red.copy()
|
||||||
|
# green.paste(Image.new("RGBA", (64, 32), (0, 255, 0, 255)))
|
||||||
|
# blue = red.copy()
|
||||||
|
# blue.paste(Image.new("RGBA", (64, 32), (0, 255, 0, 255)), (64, 32))
|
||||||
|
#
|
||||||
|
# red.save(
|
||||||
|
# "Tests/images/apng/dispose_op_previous_frame.png",
|
||||||
|
# save_all=True,
|
||||||
|
# append_images=[green, blue],
|
||||||
|
# disposal=[
|
||||||
|
# PngImagePlugin.APNG_DISPOSE_OP_NONE,
|
||||||
|
# PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS,
|
||||||
|
# PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS
|
||||||
|
# ],
|
||||||
|
# )
|
||||||
|
with Image.open("Tests/images/apng/dispose_op_previous_frame.png") as im:
|
||||||
|
im.seek(im.n_frames - 1)
|
||||||
|
assert im.getpixel((0, 0)) == (255, 0, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
def test_apng_dispose_op_background_p_mode():
|
def test_apng_dispose_op_background_p_mode():
|
||||||
with Image.open("Tests/images/apng/dispose_op_background_p_mode.png") as im:
|
with Image.open("Tests/images/apng/dispose_op_background_p_mode.png") as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal_tofile
|
||||||
|
|
||||||
|
|
||||||
def test_load_blp2_raw():
|
def test_load_blp2_raw():
|
||||||
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
||||||
with Image.open("Tests/images/blp/blp2_raw.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/blp/blp2_raw.png")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_blp2_dxt1():
|
def test_load_blp2_dxt1():
|
||||||
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
||||||
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1.png")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_blp2_dxt1a():
|
def test_load_blp2_dxt1a():
|
||||||
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
|
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
|
||||||
with Image.open("Tests/images/blp/blp2_dxt1a.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1a.png")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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, assert_image_equal_tofile, hopper
|
||||||
|
|
||||||
|
|
||||||
def test_sanity(tmp_path):
|
def test_sanity(tmp_path):
|
||||||
|
@ -111,8 +111,7 @@ def test_load_dib():
|
||||||
assert im.format == "DIB"
|
assert im.format == "DIB"
|
||||||
assert im.get_format_mimetype() == "image/bmp"
|
assert im.get_format_mimetype() == "image/bmp"
|
||||||
|
|
||||||
with Image.open("Tests/images/clipboard_target.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/clipboard_target.png")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
||||||
|
|
||||||
def test_save_dib(tmp_path):
|
def test_save_dib(tmp_path):
|
||||||
|
@ -136,5 +135,4 @@ def test_rgba_bitfields():
|
||||||
b, g, r = im.split()[1:]
|
b, g, r = im.split()[1:]
|
||||||
im = Image.merge("RGB", (r, g, b))
|
im = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target:
|
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
|
@ -31,20 +31,20 @@ def test_unclosed_file():
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file():
|
def test_closed_file():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
im.close()
|
im.close()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_context_manager():
|
def test_context_manager():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_file():
|
def test_invalid_file():
|
||||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import DdsImagePlugin, Image
|
from PIL import DdsImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal, assert_image_equal_tofile
|
||||||
|
|
||||||
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
||||||
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
||||||
|
@ -41,14 +41,12 @@ def test_sanity_dxt5():
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (256, 256)
|
assert im.size == (256, 256)
|
||||||
|
|
||||||
with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target:
|
assert_image_equal_tofile(im, TEST_FILE_DXT5.replace(".dds", ".png"))
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_dxt3():
|
def test_sanity_dxt3():
|
||||||
"""Check DXT3 images can be opened"""
|
"""Check DXT3 images can be opened"""
|
||||||
|
|
||||||
with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target:
|
|
||||||
with Image.open(TEST_FILE_DXT3) as im:
|
with Image.open(TEST_FILE_DXT3) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
@ -56,7 +54,7 @@ def test_sanity_dxt3():
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (256, 256)
|
assert im.size == (256, 256)
|
||||||
|
|
||||||
assert_image_equal(target, im)
|
assert_image_equal_tofile(im, TEST_FILE_DXT3.replace(".dds", ".png"))
|
||||||
|
|
||||||
|
|
||||||
def test_dx10_bc7():
|
def test_dx10_bc7():
|
||||||
|
@ -69,8 +67,7 @@ def test_dx10_bc7():
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (256, 256)
|
assert im.size == (256, 256)
|
||||||
|
|
||||||
with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target:
|
assert_image_equal_tofile(im, TEST_FILE_DX10_BC7.replace(".dds", ".png"))
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_dx10_bc7_unorm_srgb():
|
def test_dx10_bc7_unorm_srgb():
|
||||||
|
@ -84,10 +81,9 @@ def test_dx10_bc7_unorm_srgb():
|
||||||
assert im.size == (16, 16)
|
assert im.size == (16, 16)
|
||||||
assert im.info["gamma"] == 1 / 2.2
|
assert im.info["gamma"] == 1 / 2.2
|
||||||
|
|
||||||
with Image.open(
|
assert_image_equal_tofile(
|
||||||
TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
im, TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
||||||
) as target:
|
)
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_dx10_r8g8b8a8():
|
def test_dx10_r8g8b8a8():
|
||||||
|
@ -100,8 +96,7 @@ def test_dx10_r8g8b8a8():
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (256, 256)
|
assert im.size == (256, 256)
|
||||||
|
|
||||||
with Image.open(TEST_FILE_DX10_R8G8B8A8.replace(".dds", ".png")) as target:
|
assert_image_equal_tofile(im, TEST_FILE_DX10_R8G8B8A8.replace(".dds", ".png"))
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_dx10_r8g8b8a8_unorm_srgb():
|
def test_dx10_r8g8b8a8_unorm_srgb():
|
||||||
|
@ -115,15 +110,15 @@ def test_dx10_r8g8b8a8_unorm_srgb():
|
||||||
assert im.size == (16, 16)
|
assert im.size == (16, 16)
|
||||||
assert im.info["gamma"] == 1 / 2.2
|
assert im.info["gamma"] == 1 / 2.2
|
||||||
|
|
||||||
with Image.open(
|
assert_image_equal_tofile(
|
||||||
TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB.replace(".dds", ".png")
|
im, TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB.replace(".dds", ".png")
|
||||||
) as target:
|
)
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_unimplemented_dxgi_format():
|
def test_unimplemented_dxgi_format():
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
Image.open("Tests/images/unimplemented_dxgi_format.dds")
|
with Image.open("Tests/images/unimplemented_dxgi_format.dds"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_uncompressed_rgb():
|
def test_uncompressed_rgb():
|
||||||
|
@ -136,8 +131,9 @@ def test_uncompressed_rgb():
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (800, 600)
|
assert im.size == (800, 600)
|
||||||
|
|
||||||
with Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")) as target:
|
assert_image_equal_tofile(
|
||||||
assert_image_equal(target, im)
|
im, TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test__validate_true():
|
def test__validate_true():
|
||||||
|
@ -170,7 +166,8 @@ def test_short_header():
|
||||||
img_file = f.read()
|
img_file = f.read()
|
||||||
|
|
||||||
def short_header():
|
def short_header():
|
||||||
Image.open(BytesIO(img_file[:119]))
|
with Image.open(BytesIO(img_file[:119])):
|
||||||
|
pass # pragma: no cover
|
||||||
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
short_header()
|
short_header()
|
||||||
|
@ -192,4 +189,5 @@ def test_short_file():
|
||||||
|
|
||||||
def test_unimplemented_pixel_format():
|
def test_unimplemented_pixel_format():
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
Image.open("Tests/images/unimplemented_pixel_format.dds")
|
with Image.open("Tests/images/unimplemented_pixel_format.dds"):
|
||||||
|
pass
|
||||||
|
|
|
@ -4,7 +4,12 @@ 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,
|
||||||
|
assert_image_similar_tofile,
|
||||||
|
hopper,
|
||||||
|
skip_unless_feature,
|
||||||
|
)
|
||||||
|
|
||||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||||
|
|
||||||
|
@ -59,6 +64,7 @@ def test_invalid_file():
|
||||||
EpsImagePlugin.EpsImageFile(invalid_file)
|
EpsImagePlugin.EpsImageFile(invalid_file)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_cmyk():
|
def test_cmyk():
|
||||||
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||||
|
@ -71,8 +77,9 @@ def test_cmyk():
|
||||||
assert cmyk_image.mode == "RGB"
|
assert cmyk_image.mode == "RGB"
|
||||||
|
|
||||||
if features.check("jpg"):
|
if features.check("jpg"):
|
||||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as target:
|
assert_image_similar_tofile(
|
||||||
assert_image_similar(cmyk_image, target, 10)
|
cmyk_image, "Tests/images/pil_sample_rgb.jpg", 10
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
|
|
|
@ -2,7 +2,7 @@ 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_tofile, is_pypy
|
||||||
|
|
||||||
# created as an export of a palette image from Gimp2.6
|
# created as an export of a palette image from Gimp2.6
|
||||||
# save as...-> hopper.fli, default options.
|
# save as...-> hopper.fli, default options.
|
||||||
|
@ -38,20 +38,20 @@ def test_unclosed_file():
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file():
|
def test_closed_file():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
im.load()
|
im.load()
|
||||||
im.close()
|
im.close()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_context_manager():
|
def test_context_manager():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
with Image.open(static_test_file) as im:
|
with Image.open(static_test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_tell():
|
def test_tell():
|
||||||
|
@ -122,5 +122,4 @@ def test_seek():
|
||||||
with Image.open(animated_test_file) as im:
|
with Image.open(animated_test_file) as im:
|
||||||
im.seek(50)
|
im.seek(50)
|
||||||
|
|
||||||
with Image.open("Tests/images/a_fli.png") as expected:
|
assert_image_equal_tofile(im, "Tests/images/a_fli.png")
|
||||||
assert_image_equal(im, expected)
|
|
||||||
|
|
|
@ -21,4 +21,5 @@ def test_invalid_file():
|
||||||
|
|
||||||
def test_fpx_invalid_number_of_bands():
|
def test_fpx_invalid_number_of_bands():
|
||||||
with pytest.raises(OSError, match="Invalid number of bands"):
|
with pytest.raises(OSError, match="Invalid number of bands"):
|
||||||
Image.open("Tests/images/input_bw_five_bands.fpx")
|
with Image.open("Tests/images/input_bw_five_bands.fpx"):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar
|
from .helper import assert_image_equal_tofile, assert_image_similar
|
||||||
|
|
||||||
|
|
||||||
def test_load_raw():
|
def test_load_raw():
|
||||||
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
|
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
|
||||||
with Image.open("Tests/images/ftex_uncompressed.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/ftex_uncompressed.png")
|
||||||
assert_image_equal(im, target)
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_dxt1():
|
def test_load_dxt1():
|
||||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import GbrImagePlugin, Image
|
from PIL import GbrImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal_tofile
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_file():
|
def test_invalid_file():
|
||||||
|
@ -14,13 +14,11 @@ def test_invalid_file():
|
||||||
|
|
||||||
def test_gbr_file():
|
def test_gbr_file():
|
||||||
with Image.open("Tests/images/gbr.gbr") as im:
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
with Image.open("Tests/images/gbr.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/gbr.png")
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_load_operations():
|
def test_multiple_load_operations():
|
||||||
with Image.open("Tests/images/gbr.gbr") as im:
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
im.load()
|
im.load()
|
||||||
im.load()
|
im.load()
|
||||||
with Image.open("Tests/images/gbr.png") as target:
|
assert_image_equal_tofile(im, "Tests/images/gbr.png")
|
||||||
assert_image_equal(target, im)
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
assert_image_equal_tofile,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
hopper,
|
hopper,
|
||||||
is_pypy,
|
is_pypy,
|
||||||
|
@ -38,20 +39,20 @@ def test_unclosed_file():
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file():
|
def test_closed_file():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
im = Image.open(TEST_GIF)
|
im = Image.open(TEST_GIF)
|
||||||
im.load()
|
im.load()
|
||||||
im.close()
|
im.close()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_context_manager():
|
def test_context_manager():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_file():
|
def test_invalid_file():
|
||||||
|
@ -74,10 +75,10 @@ def test_optimize():
|
||||||
im.save(test_file, "GIF", optimize=optimize)
|
im.save(test_file, "GIF", optimize=optimize)
|
||||||
return len(test_file.getvalue())
|
return len(test_file.getvalue())
|
||||||
|
|
||||||
assert test_grayscale(0) == 800
|
assert test_grayscale(0) == 799
|
||||||
assert test_grayscale(1) == 44
|
assert test_grayscale(1) == 43
|
||||||
assert test_bilevel(0) == 800
|
assert test_bilevel(0) == 799
|
||||||
assert test_bilevel(1) == 800
|
assert test_bilevel(1) == 799
|
||||||
|
|
||||||
|
|
||||||
def test_optimize_correctness():
|
def test_optimize_correctness():
|
||||||
|
@ -307,6 +308,19 @@ def test_dispose_none():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_dispose_none_load_end():
|
||||||
|
# Test image created with:
|
||||||
|
#
|
||||||
|
# im = Image.open("transparent.gif")
|
||||||
|
# im_rotated = im.rotate(180)
|
||||||
|
# im.save("dispose_none_load_end.gif",
|
||||||
|
# save_all=True, append_images=[im_rotated], disposal=[1,2])
|
||||||
|
with Image.open("Tests/images/dispose_none_load_end.gif") as img:
|
||||||
|
img.seek(1)
|
||||||
|
|
||||||
|
assert_image_equal_tofile(img, "Tests/images/dispose_none_load_end_second.gif")
|
||||||
|
|
||||||
|
|
||||||
def test_dispose_background():
|
def test_dispose_background():
|
||||||
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
||||||
try:
|
try:
|
||||||
|
@ -615,8 +629,7 @@ def test_comment_over_255(tmp_path):
|
||||||
|
|
||||||
def test_zero_comment_subblocks():
|
def test_zero_comment_subblocks():
|
||||||
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
||||||
with Image.open(TEST_GIF) as expected:
|
assert_image_equal_tofile(im, TEST_GIF)
|
||||||
assert_image_equal(im, expected)
|
|
||||||
|
|
||||||
|
|
||||||
def test_version(tmp_path):
|
def test_version(tmp_path):
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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_tofile
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
TEST_FILE = "Tests/images/pillow.icns"
|
TEST_FILE = "Tests/images/pillow.icns"
|
||||||
|
@ -19,7 +19,9 @@ def test_sanity():
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
# Assert that there is no unclosed file warning
|
||||||
pytest.warns(None, im.load)
|
with pytest.warns(None) as record:
|
||||||
|
im.load()
|
||||||
|
assert not record
|
||||||
|
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (1024, 1024)
|
assert im.size == (1024, 1024)
|
||||||
|
@ -47,8 +49,7 @@ def test_save_append_images(tmp_path):
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.save(temp_file, append_images=[provided_im])
|
im.save(temp_file, append_images=[provided_im])
|
||||||
|
|
||||||
with Image.open(temp_file) as reread:
|
assert_image_similar_tofile(im, temp_file, 1)
|
||||||
assert_image_similar(reread, im, 1)
|
|
||||||
|
|
||||||
with Image.open(temp_file) as reread:
|
with Image.open(temp_file) as reread:
|
||||||
reread.size = (16, 16, 2)
|
reread.size = (16, 16, 2)
|
||||||
|
@ -139,3 +140,11 @@ def test_not_an_icns_file():
|
||||||
with io.BytesIO(b"invalid\n") as fp:
|
with io.BytesIO(b"invalid\n") as fp:
|
||||||
with pytest.raises(SyntaxError):
|
with pytest.raises(SyntaxError):
|
||||||
IcnsImagePlugin.IcnsFile(fp)
|
IcnsImagePlugin.IcnsFile(fp)
|
||||||
|
|
||||||
|
|
||||||
|
def test_icns_decompression_bomb():
|
||||||
|
with Image.open(
|
||||||
|
"Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns"
|
||||||
|
) as im:
|
||||||
|
with pytest.raises(Image.DecompressionBombError):
|
||||||
|
im.load()
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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, assert_image_equal_tofile, hopper
|
||||||
|
|
||||||
TEST_ICO_FILE = "Tests/images/hopper.ico"
|
TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||||
|
|
||||||
|
@ -86,6 +86,20 @@ def test_only_save_relevant_sizes(tmp_path):
|
||||||
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}
|
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_append_images(tmp_path):
|
||||||
|
# append_images should be used for scaled down versions of the image
|
||||||
|
im = hopper("RGBA")
|
||||||
|
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0))
|
||||||
|
outfile = str(tmp_path / "temp_saved_multi_icon.ico")
|
||||||
|
im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])
|
||||||
|
|
||||||
|
with Image.open(outfile) as reread:
|
||||||
|
assert_image_equal(reread, hopper("RGBA"))
|
||||||
|
|
||||||
|
reread.size = (32, 32)
|
||||||
|
assert_image_equal(reread, provided_im)
|
||||||
|
|
||||||
|
|
||||||
def test_unexpected_size():
|
def test_unexpected_size():
|
||||||
# This image has been manually hexedited to state that it is 16x32
|
# This image has been manually hexedited to state that it is 16x32
|
||||||
# while the image within is still 16x16
|
# while the image within is still 16x16
|
||||||
|
@ -106,5 +120,4 @@ def test_draw_reloaded(tmp_path):
|
||||||
|
|
||||||
with Image.open(outfile) as im:
|
with Image.open(outfile) as im:
|
||||||
im.save("Tests/images/hopper_draw.ico")
|
im.save("Tests/images/hopper_draw.ico")
|
||||||
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
assert_image_equal_tofile(im, "Tests/images/hopper_draw.ico")
|
||||||
assert_image_equal(im, reloaded)
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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_tofile, hopper, is_pypy
|
||||||
|
|
||||||
# sample im
|
# sample im
|
||||||
TEST_IM = "Tests/images/hopper.im"
|
TEST_IM = "Tests/images/hopper.im"
|
||||||
|
@ -35,20 +35,20 @@ def test_unclosed_file():
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file():
|
def test_closed_file():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
im = Image.open(TEST_IM)
|
im = Image.open(TEST_IM)
|
||||||
im.load()
|
im.load()
|
||||||
im.close()
|
im.close()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_context_manager():
|
def test_context_manager():
|
||||||
def open():
|
with pytest.warns(None) as record:
|
||||||
with Image.open(TEST_IM) as im:
|
with Image.open(TEST_IM) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
pytest.warns(None, open)
|
assert not record
|
||||||
|
|
||||||
|
|
||||||
def test_tell():
|
def test_tell():
|
||||||
|
@ -86,8 +86,7 @@ def test_roundtrip(tmp_path):
|
||||||
out = str(tmp_path / "temp.im")
|
out = str(tmp_path / "temp.im")
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(out)
|
im.save(out)
|
||||||
with Image.open(out) as reread:
|
assert_image_equal_tofile(im, out)
|
||||||
assert_image_equal(reread, im)
|
|
||||||
|
|
||||||
for mode in ["RGB", "P", "PA"]:
|
for mode in ["RGB", "P", "PA"]:
|
||||||
roundtrip(mode)
|
roundtrip(mode)
|
||||||
|
|