Merge branch 'master' into gitignore-review

This commit is contained in:
Andrew Murray 2019-11-30 07:26:22 +11:00 committed by GitHub
commit efcb5a9aef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
509 changed files with 18489 additions and 12493 deletions

View File

@ -13,13 +13,9 @@ environment:
TEST_OPTIONS: TEST_OPTIONS:
DEPLOY: YES DEPLOY: YES
matrix: matrix:
- PYTHON: C:/vp/pypy2 - PYTHON: C:/Python38
EXECUTABLE: bin/pypy.exe - PYTHON: C:/Python38-x64
PIP_DIR: bin
VENV: YES
- PYTHON: C:/Python27-x64
- PYTHON: C:/Python37 - PYTHON: C:/Python37
- PYTHON: C:/Python27
- PYTHON: C:/Python37-x64 - PYTHON: C:/Python37-x64
- PYTHON: C:/Python36 - PYTHON: C:/Python36
- PYTHON: C:/Python36-x64 - PYTHON: C:/Python36-x64
@ -30,6 +26,10 @@ environment:
PIP_DIR: bin PIP_DIR: bin
TEST_OPTIONS: --processes=0 TEST_OPTIONS: --processes=0
DEPLOY: NO DEPLOY: NO
- PYTHON: C:/vp/pypy3
EXECUTABLE: bin/pypy.exe
PIP_DIR: bin
VENV: YES
install: install:
@ -41,9 +41,9 @@ install:
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
- cd c:\pillow\winbuild\ - cd c:\pillow\winbuild\
- ps: | - ps: |
if ($env:PYTHON -eq "c:/vp/pypy2") if ($env:PYTHON -eq "c:/vp/pypy3")
{ {
c:\pillow\winbuild\appveyor_install_pypy.cmd c:\pillow\winbuild\appveyor_install_pypy3.cmd
} }
- ps: | - ps: |
if ($env:PYTHON -eq "c:/msys64/mingw32") if ($env:PYTHON -eq "c:/msys64/mingw32")
@ -52,10 +52,13 @@ install:
} }
else else
{ {
c:\python34\python.exe c:\pillow\winbuild\build_dep.py c:\python37\python.exe c:\pillow\winbuild\build_dep.py
c:\pillow\winbuild\build_deps.cmd c:\pillow\winbuild\build_deps.cmd
$host.SetShouldExit(0) $host.SetShouldExit(0)
} }
- curl -fsSL -o gs950.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs950/gs950w32.exe
- gs950.exe /S
- path %path%;C:\Program Files (x86)\gs\gs9.50\bin
build_script: build_script:
- ps: | - ps: |
@ -76,6 +79,7 @@ build_script:
test_script: test_script:
- cd c:\pillow - cd c:\pillow
- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov' - '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov'
- c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE%
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests' - '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-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?

View File

@ -0,0 +1,28 @@
parameters:
name: '' # defaults for any parameters that aren't specified
vmImage: ''
jobs:
- job: ${{ parameters.name }}
pool:
vmImage: ${{ parameters.vmImage }}
strategy:
matrix:
Python37:
python.version: '3.7'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'
- script: |
python -m pip install --upgrade tox
displayName: 'Install dependencies'
- script: |
tox -e lint
displayName: 'Lint'

View File

@ -0,0 +1,22 @@
parameters:
docker: '' # defaults for any parameters that aren't specified
dockerTag: 'master'
name: ''
vmImage: 'Ubuntu-16.04'
jobs:
- job: ${{ parameters.name }}
pool:
vmImage: ${{ parameters.vmImage }}
steps:
- script: |
docker pull pythonpillow/${{ parameters.docker }}:${{ parameters.dockerTag }}
displayName: 'Docker pull'
- script: |
# The Pillow user in the docker container is UID 1000
sudo chown -R 1000 $(Build.SourcesDirectory)
docker run -v $(Build.SourcesDirectory):/Pillow pythonpillow/${{ parameters.docker }}:${{ parameters.dockerTag }}
displayName: 'Docker build'

View File

@ -6,4 +6,6 @@ codecov:
# https://docs.codecov.io/v4.3.6/docs/comparing-commits # https://docs.codecov.io/v4.3.6/docs/comparing-commits
allow_coverage_offsets: true allow_coverage_offsets: true
token: 6dafc396-e7f5-4221-a38a-8b07a49fbdae
comment: off comment: off

View File

@ -9,14 +9,14 @@ 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 on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests. - Run the test suite. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
- Create a pull request to pull the changes from your branch to the Pillow master. - Create a pull request to pull the changes from your branch to the Pillow master.
### Guidelines ### Guidelines
- 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 PEP8. - Follow PEP 8.
- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor. - When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor.
## Reporting Issues ## Reporting Issues
@ -34,6 +34,4 @@ The best reproductions are self-contained scripts with minimal dependencies. If
## Security vulnerabilities ## Security vulnerabilities
To report sensitive vulnerability information, email security@python-pillow.org. Please see our [security policy](https://github.com/python-pillow/Pillow/blob/master/.github/SECURITY.md).
If your organisation/employer is a distributor of Pillow and would like advance notification of security-related bugs, please let us know your preferred contact method.

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
tidelift: "pypi/Pillow"

View File

@ -1,19 +0,0 @@
### What did you do?
### What did you expect to happen?
### What actually happened?
### What are your OS, Python and Pillow versions?
* OS:
* Python:
* Pillow:
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow.
```python
code goes here
```

59
.github/ISSUE_TEMPLATE/ISSUE_REPORT.md vendored Normal file
View File

@ -0,0 +1,59 @@
---
name: Issue report
about: Create a report to help us improve Pillow
---
<!--
Thank you for reporting an issue.
Follow these guidelines to ensure your issue is handled properly.
If you have a ...
1. General question: consider asking the question on Stack Overflow
with the python-imaging-library tag:
* https://stackoverflow.com/questions/tagged/python-imaging-library
Do not ask a question in both places.
If you think you have found a bug or have an unexplained exception
then file a bug report here.
2. Bug report: include a self-contained, copy-pastable example that
generates the issue if possible. Be concise with code posted.
Guidelines on how to provide a good bug report:
* https://stackoverflow.com/help/mcve
Bug reports which follow these guidelines are easier to diagnose,
and are often handled much more quickly.
3. Feature request: do a quick search of existing issues
to make sure this has not been asked before.
We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->
### What did you do?
### What did you expect to happen?
### What actually happened?
### What are your OS, Python and Pillow versions?
* OS:
* Python:
* Pillow:
<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->
```python
code goes here
```

5
.github/SECURITY.md vendored Normal file
View File

@ -0,0 +1,5 @@
# Security policy
To report sensitive vulnerability information, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
If your organisation/employer is a distributor of Pillow and would like advance notification of security-related bugs, please let us know your preferred contact method.

29
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Lint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8"]
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox
- name: Lint
run: tox -e lint

17
.github/workflows/macos-install.sh vendored Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype
PYTHONOPTIMIZE=0 pip install cffi
pip install coverage
pip install olefile
pip install -U pytest
pip install -U pytest-cov
pip install pyroma
pip install test-image-results
pip install numpy
# extra test images
pushd depends && ./install_extra_test_images.sh && popd

40
.github/workflows/test-docker.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Test Docker
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
docker: [
alpine,
arch,
ubuntu-16.04-xenial-amd64,
ubuntu-18.04-bionic-amd64,
debian-9-stretch-x86,
debian-10-buster-x86,
centos-6-amd64,
centos-7-amd64,
amazon-1-amd64,
amazon-2-amd64,
fedora-30-amd64,
fedora-31-amd64,
]
dockerTag: [master]
name: ${{ matrix.docker }}
steps:
- uses: actions/checkout@v1
- name: Docker pull
run: |
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
- name: Docker build
run: |
# The Pillow user in the docker container is UID 1000
sudo chown -R 1000 $GITHUB_WORKSPACE
docker run -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}

380
.github/workflows/test-windows.yml vendored Normal file
View File

@ -0,0 +1,380 @@
name: Test Windows
on: [push, pull_request]
jobs:
build:
runs-on: windows-2019
strategy:
fail-fast: false
matrix:
python-version: ["3.5", "3.6", "3.7", "3.8", "pypy3.6"]
architecture: ["x86", "x64"]
include:
- architecture: "x86"
platform-vcvars: "x86"
platform-msbuild: "Win32"
- architecture: "x64"
platform-vcvars: "x86_amd64"
platform-msbuild: "x64"
- python-version: "pypy3.6"
pypy-version: "pypy3.6-v7.2.0-win32"
pypy-url: "https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.2.0-win32.zip"
exclude:
- python-version: "pypy3.6"
architecture: "x64"
timeout-minutes: 30
name: Python ${{ matrix.python-version }} ${{ matrix.architecture }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v1
with:
repository: python-pillow/pillow-depends
ref: master
- name: Install PyPy
if: "contains(matrix.python-version, 'pypy')"
run: |
curl -fsSL -o pypy3.zip "${{ matrix.pypy-url }}"
7z x pypy3.zip "-o$env:RUNNER_WORKSPACE\"
mv "$env:RUNNER_WORKSPACE\${{ matrix.pypy-version }}" "$env:RUNNER_WORKSPACE\${{ matrix.python-version }}"
$env:PYTHON="$env:RUNNER_WORKSPACE\${{ matrix.python-version }}"
# set env: pythonLocation
Write-Host "`#`#[set-env name=pythonLocation;]$env:PYTHON" # old syntax https://github.com/actions/toolkit/issues/61
Write-Host "::set-env name=pythonLocation::$env:PYTHON" # new syntax https://github.com/actions/toolkit/blob/5bb77ec03fea98332e41f9347c8fbb1ce1e48f4a/docs/commands.md
New-Item -ItemType SymbolicLink -Path "$env:PYTHON\python.exe" -Target "$env:PYTHON\pypy3.exe"
curl -fsSL -o get-pip.py https://bootstrap.pypa.io/get-pip.py
$env:PATH = "$env:PYTHON\bin;$env:PATH"
& $env:PYTHON\python.exe get-pip.py
shell: pwsh
# sets env: pythonLocation
- name: Set up Python
if: "!contains(matrix.python-version, 'pypy')"
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}
- name: pip install wheel pytest pytest-cov codecov
run: |
"%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov
pip install codecov
shell: cmd
- name: Fetch dependencies
run: |
7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
Write-Host "`#`#[add-path]$env:RUNNER_WORKSPACE\nasm-2.14.02"
Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02"
..\pillow-depends\gs950w32.exe /S
Write-Host "`#`#[add-path]C:\Program Files (x86)\gs\gs9.50\bin"
Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin"
$env:PYTHON=$env:pythonLocation
xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\
xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\
xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\
cd $env:GITHUB_WORKSPACE/winbuild/
python.exe $env:GITHUB_WORKSPACE\winbuild\build_dep.py
env:
EXECUTABLE: bin\python.exe
shell: pwsh
- name: Build dependencies / libjpeg
if: false
run: |
REM FIXME uses /MT not /MD, see makefile.vc and win32.mak for more info
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\jpeg-9c
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
nmake -nologo -f makefile.vc setup-vc6
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib cjpeg.exe djpeg.exe
copy /Y /B j*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
copy /Y /B *.exe %INCLIB%
shell: cmd
- name: Build dependencies / libjpeg-turbo
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\libjpeg-turbo-2.0.3
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DENABLE_SHARED:BOOL=OFF -DWITH_JPEG8:BOOL=TRUE -DWITH_CRT_DLL:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile jpeg-static cjpeg-static djpeg-static
copy /Y /B j*.h %INCLIB%
copy /Y /B jpeg-static.lib %INCLIB%\libjpeg.lib
copy /Y /B cjpeg-static.exe %INCLIB%\cjpeg.exe
copy /Y /B djpeg-static.exe %INCLIB%\djpeg.exe
shell: cmd
- name: Build dependencies / zlib
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\zlib-1.2.11
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
nmake -nologo -f win32\Makefile.msc clean
nmake -nologo -f win32\Makefile.msc zlib.lib
copy /Y /B z*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
copy /Y /B zlib.lib %INCLIB%\z.lib
shell: cmd
- name: Build dependencies / LibTIFF
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\tiff-4.1.0
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy %GITHUB_WORKSPACE%\winbuild\tiff.opt nmake.opt
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc lib
copy /Y /B libtiff\tiff*.h %INCLIB%
copy /Y /B libtiff\*.dll %INCLIB%
copy /Y /B libtiff\*.lib %INCLIB%
shell: cmd
- name: Build dependencies / WebP
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\libwebp-1.0.3
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q output\release-static
nmake -nologo -f Makefile.vc CFG=release-static OBJDIR=output ARCH=${{ matrix.architecture }} all
mkdir %INCLIB%\webp
copy /Y /B src\webp\*.h %INCLIB%\webp
copy /Y /B output\release-static\${{ matrix.architecture }}\lib\* %INCLIB%
shell: cmd
- name: Build dependencies / FreeType
run: |
REM Toolkit v100 not available; missing VCTargetsPath; Clean fails
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\freetype-2.10.1
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q objs
set DefaultPlatformToolset=v142
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
powershell -Command "(gc builds\windows\vc2010\freetype.vcxproj) -replace 'MultiThreaded<', 'MultiThreadedDLL<' | Out-File -encoding ASCII builds\windows\vc2010\freetype.vcxproj"
%MSBUILD% builds\windows\vc2010\freetype.sln /t:Build /p:Configuration="Release Static" /p:Platform=${{ matrix.platform-msbuild }} /m
xcopy /Y /E /Q include %INCLIB%
copy /Y /B "objs\${{ matrix.platform-msbuild }}\Release Static\freetype.lib" %INCLIB%
shell: cmd
- name: Build dependencies / LCMS2
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\lcms2-2.8
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q Lib
rmdir /S /Q Projects\VC2015\Release
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
powershell %GITHUB_WORKSPACE%\winbuild\lcms2_patch.ps1
%MSBUILD% Projects\VC2015\lcms2.sln /t:Clean;lcms2_static /p:Configuration="Release" /p:Platform=${{ matrix.platform-msbuild }} /m
xcopy /Y /E /Q include %INCLIB%
copy /Y /B Lib\MS\*.lib %INCLIB%
shell: cmd
- name: Build dependencies / OpenJPEG
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\openjpeg-2.3.1msvcr10-x32
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile
mkdir %INCLIB%\openjpeg-2.3.1
copy /Y /B src\lib\openjp2\*.h %INCLIB%\openjpeg-2.3.1
copy /Y /B bin\*.lib %INCLIB%
shell: cmd
# GPL licensed; skip if building wheels
- name: Build dependencies / libimagequant
if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')"
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
rem ba653c8: Merge tag '2.12.5' into msvc
cd /D %BUILD%\libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
echo (gc CMakeLists.txt) -replace 'add_library', "add_compile_options(-openmp-)`r`nadd_library" ^| Out-File -encoding ASCII CMakeLists.txt > patch.ps1
echo (gc CMakeLists.txt) -replace ' SHARED', ' STATIC' ^| Out-File -encoding ASCII CMakeLists.txt >> patch.ps1
powershell .\patch.ps1
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile
copy /Y /B *.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
# for Raqm
- name: Build dependencies / HarfBuzz
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
set INCLUDE=%INCLUDE%;%INCLIB%
set LIB=%LIB%;%INCLIB%
cd /D %BUILD%\harfbuzz-2.6.1
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DHB_HAVE_FREETYPE:BOOL=ON -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile harfbuzz
copy /Y /B src\*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
# for Raqm
- name: Build dependencies / FriBidi
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\fribidi-1.0.7
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy /Y /B %GITHUB_WORKSPACE%\winbuild\fribidi.cmake CMakeLists.txt
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile fribidi
copy /Y /B lib\*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
- name: Build dependencies / Raqm
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
set INCLUDE=%INCLUDE%;%INCLIB%
set LIB=%LIB%;%INCLIB%
cd /D %BUILD%\libraqm-0.7.0
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy /Y /B %GITHUB_WORKSPACE%\winbuild\raqm.cmake CMakeLists.txt
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile libraqm
copy /Y /B src\*.h %INCLIB%
copy /Y /B libraqm.dll %INCLIB%
shell: cmd
- name: Build Pillow
run: |
set PYTHON=%pythonLocation%
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set MPLSRC=%GITHUB_WORKSPACE%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
cd /D %GITHUB_WORKSPACE%
set LIB=%INCLIB%;%PYTHON%\tcl
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
set MSSdk=1
set DISTUTILS_USE_SDK=1
set py_vcruntime_redist=true
%PYTHON%\python.exe setup.py build_ext install
rem Add libraqm.dll (copied to INCLIB) to PATH.
path %INCLIB%;%PATH%
%PYTHON%\python.exe selftest.py --installed
shell: cmd
# failing with PyPy3
- name: Enable heap verification
if: "!contains(matrix.python-version, 'pypy')"
run: |
c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\python.exe
shell: cmd
- name: Test Pillow
run: |
set PYTHON=%pythonLocation%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
rem Add libraqm.dll (copied to INCLIB) to PATH.
path %INCLIB%;%PATH%
cd /D %GITHUB_WORKSPACE%
%PYTHON%\python.exe -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests
shell: cmd
- name: Upload errors
uses: actions/upload-artifact@v1
if: failure()
with:
name: errors
path: Tests/errors
- name: Upload coverage
run: 'codecov --file "%GITHUB_WORKSPACE%\coverage.xml" --name "%pythonLocation%"'
shell: cmd
- name: Build wheel
id: wheel
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
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
set PYTHON=%pythonLocation%
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set MPLSRC=%GITHUB_WORKSPACE%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
cd /D %GITHUB_WORKSPACE%
set LIB=%INCLIB%;%PYTHON%\tcl
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
%PYTHON%\python.exe setup.py bdist_wheel
shell: cmd
- uses: actions/upload-artifact@v1
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
with:
name: ${{ steps.wheel.outputs.dist }}
path: dist

76
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,76 @@
name: Test
on: [push, pull_request]
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [
"ubuntu-latest",
"macOS-latest",
]
python-version: [
"pypy3",
"3.8",
"3.7",
"3.6",
"3.5",
]
include:
- python-version: "3.5"
env: PYTHONOPTIMIZE=2
- python-version: "3.6"
env: PYTHONOPTIMIZE=1
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install Linux dependencies
if: startsWith(matrix.os, 'ubuntu')
run: |
.travis/install.sh
- name: Install macOS dependencies
if: startsWith(matrix.os, 'macOS')
run: |
.github/workflows/macos-install.sh
- name: Build
run: |
.travis/build.sh
- name: Test
run: |
.travis/test.sh
- name: Upload errors
uses: actions/upload-artifact@v1
if: failure()
with:
name: errors
path: Tests/errors
- name: Docs
if: matrix.python-version == 3.8
run: |
pip install sphinx-rtd-theme
make doccheck
- name: After success
if: success()
run: |
.travis/after_success.sh
env:
MATRIX_OS: ${{ matrix.os }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

3
.gitignore vendored
View File

@ -73,6 +73,9 @@ pip-delete-this-directory.txt
\#*# \#*#
.#* .#*
#VS Code
.vscode
#Komodo #Komodo
*.komodoproject *.komodoproject

View File

@ -1,3 +0,0 @@
strictness: medium
test-warnings: yes
max-line-length: 80

View File

@ -6,8 +6,8 @@ notifications:
irc: "chat.freenode.net#pil" irc: "chat.freenode.net#pil"
# Run fast lint first to get fast feedback. # Run fast lint first to get fast feedback.
# Run slow PyPy* next, to give them a headstart and reduce waiting time. # Run slow PyPy next, to give it a headstart and reduce waiting time.
# Run latest 3.x and 2.x next, to get quick compatibility results. # Run latest 3.x next, to get quick compatibility results.
# Then run the remainder, with fastest Docker jobs last. # Then run the remainder, with fastest Docker jobs last.
matrix: matrix:
@ -16,50 +16,34 @@ matrix:
- python: "3.6" - python: "3.6"
name: "Lint" name: "Lint"
env: LINT="true" env: LINT="true"
- python: "pypy2.7-6.0" - python: "pypy3"
name: "PyPy2 Xenial"
dist: xenial
- python: "pypy3.5-6.0"
name: "PyPy3 Xenial" name: "PyPy3 Xenial"
dist: xenial - python: "3.8"
name: "3.8 Xenial"
services: xvfb
- python: '3.7' - python: '3.7'
name: "3.7 Xenial" name: "3.7 Xenial"
- python: '2.7'
name: "2.7 Xenial"
- python: '2.7'
name: "2.7 Trusty"
dist: trusty
- python: "2.7_with_system_site_packages" # For PyQt4
name: "2.7_with_system_site_packages Xenial"
services: xvfb services: xvfb
- python: "2.7_with_system_site_packages" # For PyQt4
name: "2.7_with_system_site_packages Trusty"
dist: trusty
- python: '3.6' - python: '3.6'
name: "3.6 Xenial" name: "3.6 Xenial PYTHONOPTIMIZE=1"
- python: '3.6'
name: "3.6 Trusty PYTHONOPTIMIZE=1"
dist: trusty
env: PYTHONOPTIMIZE=1 env: PYTHONOPTIMIZE=1
services: xvfb
- python: '3.5' - python: '3.5'
name: "3.5 Xenial" name: "3.5 Xenial PYTHONOPTIMIZE=2"
- python: '3.5'
name: "3.5 Trusty PYTHONOPTIMIZE=2"
dist: trusty
env: PYTHONOPTIMIZE=2 env: PYTHONOPTIMIZE=2
- python: "3.8-dev" services: xvfb
name: "3.8-dev Xenial"
- env: DOCKER="alpine" DOCKER_TAG="master" - env: DOCKER="alpine" DOCKER_TAG="master"
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5 - env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="master" - env: DOCKER="ubuntu-16.04-xenial-amd64" DOCKER_TAG="master"
- env: DOCKER="ubuntu-xenial-amd64" DOCKER_TAG="master" - env: DOCKER="ubuntu-18.04-bionic-amd64" DOCKER_TAG="master"
- env: DOCKER="debian-stretch-x86" DOCKER_TAG="master" - env: DOCKER="debian-9-stretch-x86" DOCKER_TAG="master"
- env: DOCKER="debian-10-buster-x86" DOCKER_TAG="master"
- env: DOCKER="centos-6-amd64" DOCKER_TAG="master" - env: DOCKER="centos-6-amd64" DOCKER_TAG="master"
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master" - env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master" - env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master" - env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-28-amd64" DOCKER_TAG="master" - env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-29-amd64" DOCKER_TAG="master" - env: DOCKER="fedora-31-amd64" DOCKER_TAG="master"
services: services:
- docker - docker
@ -75,20 +59,13 @@ install:
.travis/install.sh; .travis/install.sh;
fi fi
before_script:
# Qt needs a display for some of the tests, and it's only run on the system site packages install
- |
if [ "$TRAVIS_JOB_NAME" == "2.7_with_system_site_packages Trusty" ]; then
export DISPLAY=:99.0
sh -e /etc/init.d/xvfb start
fi
script: script:
- | - |
if [ "$LINT" == "true" ]; then if [ "$LINT" == "true" ]; then
tox -e lint tox -e lint
elif [ "$DOCKER" == "" ]; then elif [ "$DOCKER" == "" ]; then
.travis/script.sh .travis/build.sh
.travis/test.sh
elif [ "$DOCKER" ]; then elif [ "$DOCKER" ]; then
# the Pillow user in the docker container is UID 1000 # the Pillow user in the docker container is UID 1000
sudo chown -R 1000 $TRAVIS_BUILD_DIR sudo chown -R 1000 $TRAVIS_BUILD_DIR

View File

@ -1,7 +1,12 @@
#!/bin/bash #!/bin/bash
# gather the coverage data # gather the coverage data
sudo apt-get -qq install lcov if [[ "$MATRIX_OS" == "macOS-latest" ]]; then
brew install lcov
else
sudo apt-get -qq install lcov
fi
lcov --capture --directory . -b . --output-file coverage.info lcov --capture --directory . -b . --output-file coverage.info
# filter to remove system headers # filter to remove system headers
lcov --remove coverage.info '/usr/*' -o coverage.filtered.info lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
@ -15,9 +20,8 @@ pip install coveralls-merge
coveralls-merge coverage.c.json coveralls-merge coverage.c.json
codecov codecov
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ] && [ "$DOCKER" == "" ]; then
# Coverage and quality reports on just the latest diff. # Coverage and quality reports on just the latest diff.
# (Installation is very slow on Py3, so just do it for Py2.)
depends/diffcover-install.sh depends/diffcover-install.sh
depends/diffcover-run.sh depends/diffcover-run.sh
fi fi

10
.travis/build.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
coverage erase
if [ $(uname) == "Darwin" ]; then
export CPPFLAGS="-I/usr/local/miniconda/include";
fi
make clean
make install-coverage

View File

@ -4,11 +4,10 @@ set -e
sudo apt-get update sudo apt-get update
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\
python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick\ ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
libharfbuzz-dev libfribidi-dev cmake imagemagick libharfbuzz-dev libfribidi-dev
PYTHONOPTIMIZE=0 pip install cffi PYTHONOPTIMIZE=0 pip install cffi
pip install check-manifest
pip install coverage pip install coverage
pip install olefile pip install olefile
pip install -U pytest pip install -U pytest
@ -16,22 +15,22 @@ pip install -U pytest-cov
pip install pyroma pip install pyroma
pip install test-image-results pip install test-image-results
pip install numpy pip install numpy
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
sudo apt-get -qq install pyqt5-dev-tools
pip install pyqt5
fi
# docs only on Python 2.7 # docs only on Python 3.7
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then pip install -r requirements.txt ; fi
# clean checkout for manifest
mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest
# webp # webp
pushd depends && ./install_webp.sh && popd pushd depends && ./install_webp.sh && popd
# openjpeg
pushd depends && ./install_openjpeg.sh && popd
# libimagequant # libimagequant
pushd depends && ./install_imagequant.sh && popd pushd depends && ./install_imagequant.sh && popd
# raqm
pushd depends && ./install_raqm.sh && popd
# extra test images # extra test images
pushd depends && ./install_extra_test_images.sh && popd pushd depends && ./install_extra_test_images.sh && popd

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -e
coverage erase
make clean
make install-coverage
python selftest.py
python -m pytest -vx --cov PIL --cov-report term Tests
pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
# Docs
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make doccheck; fi

8
.travis/test.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
python -m pytest -v -x --cov PIL --cov-report term Tests
# Docs
if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then make doccheck; fi

View File

@ -2,9 +2,410 @@
Changelog (Pillow) Changelog (Pillow)
================== ==================
6.0.0 (unreleased) 7.0.0 (unreleased)
------------------ ------------------
- Drop support for EOL Python 2.7 #4109
[hugovk, radarhere, jdufresne]
- Added UnidentifiedImageError #4182
[radarhere, hugovk]
- Remove deprecated __version__ from plugins #4197
[hugovk, radarhere]
- Fixed freeing unallocated pointer when resizing with height too large #4116
[radarhere]
- Copy info in Image.transform #4128
[radarhere]
- Corrected DdsImagePlugin setting info gamma #4171
[radarhere]
- Depends: Update libtiff to 4.1.0 #4195, Tk Tcl to 8.6.10 #4229
[radarhere]
- Improve handling of file resources #3577
[jdufresne]
- Removed CI testing of Fedora 29 #4165
[hugovk]
- Added pypy3 to tox envlist #4137
[jdufresne]
- Drop support for EOL PyQt4 and PySide #4108
[hugovk, radarhere]
- Removed deprecated setting of TIFF image sizes #4114
[radarhere]
- Removed deprecated PILLOW_VERSION #4107
[hugovk]
- Changed default frombuffer raw decoder args #1730
[radarhere]
6.2.1 (2019-10-21)
------------------
- This is the last Pillow release to support Python 2.7 #3642
- Add support for Python 3.8 #4141
[hugovk]
6.2.0 (2019-10-01)
------------------
- Catch buffer overruns #4104
[radarhere]
- Initialize rows_per_strip when RowsPerStrip tag is missing #4034
[cgohlke, radarhere]
- Raise error if TIFF dimension is a string #4103
[radarhere]
- Added decompression bomb checks #4102
[radarhere]
- Fix ImageGrab.grab DPI scaling on Windows 10 version 1607+ #4000
[nulano, radarhere]
- Corrected negative seeks #4101
[radarhere]
- Added argument to capture all screens on Windows #3950
[nulano, radarhere]
- Updated warning to specify when Image.frombuffer defaults will change #4086
[radarhere]
- Changed WindowsViewer format to PNG #4080
[radarhere]
- Use TIFF orientation #4063
[radarhere]
- Raise the same error if a truncated image is loaded a second time #3965
[radarhere]
- Lazily use ImageFileDirectory_v1 values from Exif #4031
[radarhere]
- Improved HSV conversion #4004
[radarhere]
- Added text stroking #3978
[radarhere, hugovk]
- No more deprecated bdist_wininst .exe installers #4029
[hugovk]
- Do not allow floodfill to extend into negative coordinates #4017
[radarhere]
- Fixed arc drawing bug for a non-whole number of degrees #4014
[radarhere]
- Fix bug when merging identical images to GIF with a list of durations #4003
[djy0, radarhere]
- Fix bug in TIFF loading of BufferedReader #3998
[chadawagner]
- Added fallback for finding ld on MinGW Cygwin #4019
[radarhere]
- Remove indirect dependencies from requirements.txt #3976
[hugovk]
- Depends: Update libwebp to 1.0.3 #3983, libimagequant to 2.12.5 #3993, freetype to 2.10.1 #3991
[radarhere]
- Change overflow check to use PY_SSIZE_T_MAX #3964
[radarhere]
- Report reason for pytest skips #3942
[hugovk]
6.1.0 (2019-07-01)
------------------
- Deprecate Image.__del__ #3929
[jdufresne]
- Tiff: Add support for JPEG quality #3886
[olt]
- Respect the PKG_CONFIG environment variable when building #3928
[chewi]
- Use explicit memcpy() to avoid unaligned memory accesses #3225
[DerDakon]
- Improve encoding of TIFF tags #3861
[olt]
- Update Py_UNICODE to Py_UCS4 #3780
[nulano]
- Consider I;16 pixel size when drawing #3899
[radarhere]
- Add TIFFTAG_SAMPLEFORMAT to blocklist #3926
[cgohlke, radarhere]
- Create GIF deltas from background colour of GIF frames if disposal mode is 2 #3708
[sircinnamon, radarhere]
- Added ImageSequence all_frames #3778
[radarhere]
- Use unsigned int to store TIFF IFD offsets #3923
[cgohlke]
- Include CPPFLAGS when searching for libraries #3819
[jefferyto]
- Updated TIFF tile descriptors to match current decoding functionality #3795
[dmnisson]
- Added an ``image.entropy()`` method (second revision) #3608
[fish2000]
- Pass the correct types to PyArg_ParseTuple #3880
[QuLogic]
- Fixed crash when loading non-font bytes #3912
[radarhere]
- Fix SPARC memory alignment issues in Pack/Unpack functions #3858
[kulikjak]
- Added CMYK;16B and CMYK;16N unpackers #3913
[radarhere]
- Fixed bugs in calculating text size #3864
[radarhere]
- Add __main__.py to output basic format and support information #3870
[jdufresne]
- Added variation font support #3802
[radarhere]
- Do not down-convert if image is LA when showing with PNG format #3869
[radarhere]
- Improve handling of PSD frames #3759
[radarhere]
- Improved ICO and ICNS loading #3897
[radarhere]
- Changed Preview application path so that it is no longer static #3896
[radarhere]
- Corrected ttb text positioning #3856
[radarhere]
- Handle unexpected ICO image sizes #3836
[radarhere]
- Fixed bits value for RGB;16N unpackers #3837
[kkopachev]
- Travis CI: Add Fedora 30, remove Fedora 28 #3821
[hugovk]
- Added reading of CMYK;16L TIFF images #3817
[radarhere]
- Fixed dimensions of 1-bit PDFs #3827
[radarhere]
- Fixed opening mmap image through Path on Windows #3825
[radarhere]
- Fixed ImageDraw arc gaps #3824
[radarhere]
- Expand GIF to include frames with extents outside the image size #3822
[radarhere]
- Fixed ImageTk getimage #3814
[radarhere]
- Fixed bug in decoding large images #3791
[radarhere]
- Fixed reading APP13 marker without Photoshop data #3771
[radarhere]
- Added option to include layered windows in ImageGrab.grab on Windows #3808
[radarhere]
- Detect libimagequant when installed by pacman on MingW #3812
[radarhere]
- Fixed raqm layout bug #3787
[radarhere]
- Fixed loading font with non-Unicode path on Windows #3785
[radarhere]
- Travis CI: Upgrade PyPy from 6.0.0 to 7.1.1 #3783
[hugovk, johnthagen]
- Depends: Updated openjpeg to 2.3.1 #3794, raqm to 0.7.0 #3877, libimagequant to 2.12.3 #3889
[radarhere]
- Fix numpy bool bug #3790
[radarhere]
6.0.0 (2019-04-01)
------------------
- Python 2.7 support will be removed in Pillow 7.0.0 #3682
[hugovk]
- Add EXIF class #3625
[radarhere]
- Add ImageOps exif_transpose method #3687
[radarhere]
- Added warnings to deprecated CMSProfile attributes #3615
[hugovk]
- Documented reading TIFF multiframe images #3720
[akuchling]
- Improved speed of opening an MPO file #3658
[Glandos]
- Update palette in quantize #3721
[radarhere]
- Improvements to TIFF is_animated and n_frames #3714
[radarhere]
- Fixed incompatible pointer type warnings #3754
[radarhere]
- Improvements to PA and LA conversion and palette operations #3728
[radarhere]
- Consistent DPI rounding #3709
[radarhere]
- Change size of MPO image to match frame #3588
[radarhere]
- Read Photoshop resolution data #3701
[radarhere]
- Ensure image is mutable before saving #3724
[radarhere]
- Correct remap_palette documentation #3740
[radarhere]
- Promote P images to PA in putalpha #3726
[radarhere]
- Allow RGB and RGBA values for new P images #3719
[radarhere]
- Fixed TIFF bug when seeking backwards and then forwards #3713
[radarhere]
- Cache EXIF information #3498
[Glandos]
- Added transparency for all PNG greyscale modes #3744
[radarhere]
- Fix deprecation warnings in Python 3.8 #3749
[radarhere]
- Fixed GIF bug when rewinding to a non-zero frame #3716
[radarhere]
- Only close original fp in __del__ and __exit__ if original fp is exclusive #3683
[radarhere]
- Fix BytesWarning in Tests/test_numpy.py #3725
[jdufresne]
- Add missing MIME types and extensions #3520
[pirate486743186]
- Add I;16 PNG save #3566
[radarhere]
- Add support for BMP RGBA bitfield compression #3705
[radarhere]
- Added ability to set language for text rendering #3693
[iwsfutcmd]
- Only close exclusive fp on Image __exit__ #3698
[radarhere]
- Changed EPS subprocess stdout from devnull to None #3635
[radarhere]
- Add reading old-JPEG compressed TIFFs #3489
[kkopachev]
- Add EXIF support for PNG #3674
[radarhere]
- Add option to set dither param on quantize #3699
[glasnt]
- Add reading of DDS uncompressed RGB data #3673
[radarhere]
- Correct length of Tiff BYTE tags #3672
[radarhere]
- Add DIB saving and loading through Image open #3691
[radarhere]
- Removed deprecated VERSION #3624
[hugovk]
- Fix 'BytesWarning: Comparison between bytes and string' in PdfDict #3580
[jdufresne]
- Do not resize in Image.thumbnail if already the destination size #3632
[radarhere]
- Replace .seek() magic numbers with io.SEEK_* constants #3572
[jdufresne]
- Make ContainerIO.isatty() return a bool, not int #3568
[jdufresne]
- Add support to all transpose operations for I;16 modes #3563, #3741
[radarhere]
- Deprecate support for PyQt4 and PySide #3655
[hugovk, radarhere]
- Add TIFF compression codecs: LZMA, Zstd, WebP #3555
[cgohlke]
- Fixed pickling of iTXt class with protocol > 1 #3537
[radarhere]
- _util.isPath returns True for pathlib.Path objects #3616
[wbadart]
- Remove unnecessary unittest.main() boilerplate from test files #3631 - Remove unnecessary unittest.main() boilerplate from test files #3631
[jdufresne] [jdufresne]
@ -407,7 +808,7 @@ Changelog (Pillow)
- Enable background colour parameter on rotate #3057 - Enable background colour parameter on rotate #3057
[storesource] [storesource]
- Remove unnecessary `#if 1` directive #3072 - Remove unnecessary ``#if 1`` directive #3072
[jdufresne] [jdufresne]
- Remove unused Python class, Path #3070 - Remove unused Python class, Path #3070
@ -944,7 +1345,7 @@ Changelog (Pillow)
- Add decompression bomb check to Image.crop #2410 - Add decompression bomb check to Image.crop #2410
[wiredfool] [wiredfool]
- ImageFile: Ensure that the `err_code` variable is initialized in case of exception. #2363 - ImageFile: Ensure that the ``err_code`` variable is initialized in case of exception. #2363
[alexkiro] [alexkiro]
- Tiff: Support append_images for saving multipage TIFFs #2406 - Tiff: Support append_images for saving multipage TIFFs #2406
@ -1181,7 +1582,7 @@ Changelog (Pillow)
- Removed PIL 1.0 era TK readme that concerns Windows 95/NT #2360 - Removed PIL 1.0 era TK readme that concerns Windows 95/NT #2360
[wiredfool] [wiredfool]
- Prevent `nose -v` printing docstrings #2369 - Prevent ``nose -v`` printing docstrings #2369
[hugovk] [hugovk]
- Replaced absolute PIL imports with relative imports #2349 - Replaced absolute PIL imports with relative imports #2349
@ -1270,7 +1671,7 @@ Changelog (Pillow)
- Test: Faster assert_image_similar #2279 - Test: Faster assert_image_similar #2279
[homm] [homm]
- Removed depreciated internal "stretch" method #2276 - Removed deprecated internal "stretch" method #2276
[homm] [homm]
- Removed the handles_eof flag in decode.c #2223 - Removed the handles_eof flag in decode.c #2223
@ -1626,7 +2027,7 @@ Changelog (Pillow)
- Changed depends/install_*.sh urls to point to github pillow-depends repo #1983 - Changed depends/install_*.sh urls to point to github pillow-depends repo #1983
[wiredfool] [wiredfool]
- Allow ICC profile from `encoderinfo` while saving PNGs #1909 - Allow ICC profile from ``encoderinfo`` while saving PNGs #1909
[homm] [homm]
- Fix integer overflow on ILP32 systems (32-bit Linux). #1975 - Fix integer overflow on ILP32 systems (32-bit Linux). #1975
@ -2069,7 +2470,7 @@ Changelog (Pillow)
- Added PDF multipage saving #1445 - Added PDF multipage saving #1445
[radarhere] [radarhere]
- Removed deprecated code, Image.tostring, Image.fromstring, Image.offset, ImageDraw.setink, ImageDraw.setfill, ImageFileIO, ImageFont.FreeTypeFont and ImageFont.truetype `file` kwarg, ImagePalette private _make functions, ImageWin.fromstring and ImageWin.tostring #1343 - Removed deprecated code, Image.tostring, Image.fromstring, Image.offset, ImageDraw.setink, ImageDraw.setfill, ImageFileIO, ImageFont.FreeTypeFont and ImageFont.truetype ``file`` kwarg, ImagePalette private _make functions, ImageWin.fromstring and ImageWin.tostring #1343
[radarhere] [radarhere]
- Load more broken images #1428 - Load more broken images #1428
@ -2561,7 +2962,7 @@ Changelog (Pillow)
- Doc cleanup - Doc cleanup
[wiredfool] [wiredfool]
- Fix `ImageStat` docs #796 - Fix ``ImageStat`` docs #796
[akx] [akx]
- Added docs for ExifTags #794 - Added docs for ExifTags #794
@ -2998,7 +3399,7 @@ Changelog (Pillow)
- Add RGBA support to ImageColor #309 - Add RGBA support to ImageColor #309
[yoavweiss] [yoavweiss]
- Test for `str`, not `"utf-8"` #306 (fixes #304) - Test for ``str``, not ``"utf-8"`` #306 (fixes #304)
[mjpieters] [mjpieters]
- Fix missing import os in _util.py #303 - Fix missing import os in _util.py #303
@ -3104,7 +3505,7 @@ Changelog (Pillow)
- Partial work to add a wrapper for WebPGetFeatures to correctly support #220 (fixes #204) - Partial work to add a wrapper for WebPGetFeatures to correctly support #220 (fixes #204)
- Significant performance improvement of `alpha_composite` function #156 - Significant performance improvement of ``alpha_composite`` function #156
[homm] [homm]
- Support explicitly disabling features via --disable-* options #240 - Support explicitly disabling features via --disable-* options #240

View File

@ -1,4 +1,3 @@
include *.c include *.c
include *.h include *.h
include *.in include *.in
@ -9,23 +8,22 @@ include *.sh
include *.txt include *.txt
include LICENSE include LICENSE
include Makefile include Makefile
include tox.ini
graft Tests graft Tests
graft src graft src
graft depends graft depends
graft winbuild graft winbuild
graft docs graft docs
prune docs/_static
# build/src control detritus # build/src control detritus
exclude .appveyor.yml exclude .appveyor.yml
exclude .coveragerc exclude .coveragerc
exclude .codecov.yml exclude .codecov.yml
exclude .editorconfig exclude .editorconfig
exclude .landscape.yaml
exclude .readthedocs.yml exclude .readthedocs.yml
exclude .travis exclude azure-pipelines.yml
exclude .travis/*
exclude tox.ini
global-exclude .git* global-exclude .git*
global-exclude *.pyc global-exclude *.pyc
global-exclude *.so global-exclude *.so
prune .azure-pipelines
prune .travis

View File

@ -3,7 +3,7 @@
.DEFAULT_GOAL := release-test .DEFAULT_GOAL := release-test
clean: clean:
python setup.py clean python3 setup.py clean
rm src/PIL/*.so || true rm src/PIL/*.so || true
rm -r build || true rm -r build || true
find . -name __pycache__ | xargs rm -r || true find . -name __pycache__ | xargs rm -r || true
@ -15,8 +15,8 @@ co:
done done
coverage: coverage:
python selftest.py python3 selftest.py
python setup.py test python3 setup.py test
rm -r htmlcov || true rm -r htmlcov || true
coverage report coverage report
@ -30,7 +30,7 @@ doccheck:
$(MAKE) -C docs linkcheck || true $(MAKE) -C docs linkcheck || true
docserve: docserve:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
help: help:
@echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of" @echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of"
@ -50,22 +50,22 @@ help:
@echo " upload-test build and upload sdists to test.pythonpackages.com" @echo " upload-test build and upload sdists to test.pythonpackages.com"
inplace: clean inplace: clean
python setup.py develop build_ext --inplace python3 setup.py develop build_ext --inplace
install: install:
python setup.py install python3 setup.py install
python selftest.py python3 selftest.py
install-coverage: install-coverage:
CFLAGS="-coverage" python setup.py build_ext install CFLAGS="-coverage" python3 setup.py build_ext install
python selftest.py python3 selftest.py
debug: debug:
# make a debug version if we don't have a -dbg python. Leaves in symbols # make a debug version if we don't have a -dbg python. Leaves in symbols
# for our stuff, kills optimization, and redirects to dev null so we # for our stuff, kills optimization, and redirects to dev null so we
# see any build failures. # see any build failures.
make clean > /dev/null make clean > /dev/null
CFLAGS='-g -O0' python setup.py build_ext install > /dev/null CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
install-req: install-req:
pip install -r requirements.txt pip install -r requirements.txt
@ -76,17 +76,17 @@ install-venv:
release-test: release-test:
$(MAKE) install-req $(MAKE) install-req
python setup.py develop python3 setup.py develop
python selftest.py python3 selftest.py
python -m pytest Tests python3 -m pytest Tests
python setup.py install python3 setup.py install
python -m pytest -qq python3 -m pytest -qq
check-manifest check-manifest
pyroma . pyroma .
viewdoc viewdoc
sdist: sdist:
python setup.py sdist --format=gztar python3 setup.py sdist --format=gztar
test: test:
pytest -qq pytest -qq
@ -97,10 +97,10 @@ upload-test:
# username: # username:
# password: # password:
# repository = http://test.pythonpackages.com # repository = http://test.pythonpackages.com
python setup.py sdist --format=gztar upload -r test python3 setup.py sdist --format=gztar upload -r test
upload: upload:
python setup.py sdist --format=gztar upload python3 setup.py sdist --format=gztar upload
readme: readme:
viewdoc viewdoc

View File

@ -4,7 +4,7 @@ Pillow
Python Imaging Library (Fork) Python Imaging Library (Fork)
----------------------------- -----------------------------
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is `supported by Tidelift <https://tidelift.com/subscription/pkg/pypi-pillow>`_.
.. start-badges .. start-badges
@ -14,58 +14,14 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
* - docs * - docs
- |docs| - |docs|
* - tests * - tests
- |linux| |macos| |windows| |coverage| - |linux| |macos| |windows| |gha_lint| |gha| |gha_windows| |gha_docker| |coverage|
* - package * - package
- |zenodo| |tidelift| |version| |downloads| - |zenodo| |tidelift| |version| |downloads|
* - social * - social
- |gitter| |twitter| - |gitter| |twitter|
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
:target: https://pillow.readthedocs.io/?badge=latest
:alt: Documentation Status
.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status (Linux)
.. |macos| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build
:target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (macOS)
.. |windows| image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
.. |coverage| image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/python-pillow/Pillow?branch=master
:alt: Code coverage
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
.. |tidelift| image:: https://tidelift.com/badges/github/python-pillow/Pillow?style=flat
:target: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=referral&utm_campaign=readme
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
:target: https://pypi.org/project/Pillow/
:alt: Latest PyPI version
.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg
:target: https://pypi.org/project/Pillow/
:alt: Number of PyPI downloads
.. |gitter| image:: https://badges.gitter.im/python-pillow/Pillow.svg
:target: https://gitter.im/python-pillow/Pillow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
:alt: Join the chat at https://gitter.im/python-pillow/Pillow
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
:target: https://twitter.com/PythonPillow
:alt: Follow on https://twitter.com/PythonPillow
.. end-badges .. end-badges
More Information More Information
---------------- ----------------
@ -82,3 +38,62 @@ More Information
- `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>`_
Report a Vulnerability
----------------------
To report a security vulnerability, please follow the procedure described in the `Tidelift security policy <https://tidelift.com/docs/security>`_.
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
:target: https://pillow.readthedocs.io/?badge=latest
:alt: Documentation Status
.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status (Linux)
.. |macos| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build
:target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (macOS)
.. |windows| image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
.. |gha_lint| image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg
:alt: GitHub Actions build status (Lint)
.. |gha_docker| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg
:alt: GitHub Actions build status (Test Docker)
.. |gha| image:: https://github.com/python-pillow/Pillow/workflows/Test/badge.svg
:alt: GitHub Actions build status (Test Linux and macOS)
.. |gha_windows| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg
:alt: GitHub Actions build status (Test Windows)
.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
:target: https://codecov.io/gh/python-pillow/Pillow
:alt: Code coverage
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
.. |tidelift| image:: https://tidelift.com/badges/package/pypi/Pillow?style=flat
:target: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=referral&utm_campaign=readme
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
:target: https://pypi.org/project/Pillow/
:alt: Latest PyPI version
.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg
:target: https://pypi.org/project/Pillow/
:alt: Number of PyPI downloads
.. |gitter| image:: https://badges.gitter.im/python-pillow/Pillow.svg
:target: https://gitter.im/python-pillow/Pillow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
:alt: Join the chat at https://gitter.im/python-pillow/Pillow
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
:target: https://twitter.com/PythonPillow
:alt: Follow on https://twitter.com/PythonPillow

View File

@ -51,6 +51,7 @@ Released as needed for security, installation or critical bug fixes.
make sdist make sdist
``` ```
* [ ] 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*`
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) * [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
## Embargoed Release ## Embargoed Release
@ -88,18 +89,11 @@ Released as needed privately to individual vendors for critical security-related
```bash ```bash
git clone https://github.com/python-pillow/pillow-wheels git clone https://github.com/python-pillow/pillow-wheels
cd pillow-wheels cd pillow-wheels
git submodule init ./update-pillow-tag.sh [[release tag]]
git submodule update Pillow
cd Pillow
git fetch --all
git checkout [[release tag]]
cd ..
git commit -m "Pillow -> 5.2.0" Pillow
git push
``` ```
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/). * [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
```bash ```bash
wget -m -A 'Pillow-<VERSION>*' \ wget -m -A 'Pillow-<VERSION>-*' \
http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com
``` ```
@ -109,4 +103,4 @@ Released as needed privately to individual vendors for critical security-related
## Documentation ## Documentation
* [ ] Make sure the default version for Read the Docs is the latest tagged release e.g. `d2d43879` (5.4.0) * [ ] Make sure the [default version for Read the Docs](https://pillow.readthedocs.io/en/stable/) is up-to-date with the release changes

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
from PIL import Image
import sys import sys
from PIL import Image
if sys.maxsize < 2**32: if sys.maxsize < 2 ** 32:
im = Image.new('L', (999999, 999999), 0) im = Image.new("L", (999999, 999999), 0)

View File

@ -4,7 +4,7 @@ Pillow Tests
Test scripts are named ``test_xxx.py`` and use the ``unittest`` module. A base class and helper functions can be found in ``helper.py``. Test scripts are named ``test_xxx.py`` and use the ``unittest`` module. A base class and helper functions can be found in ``helper.py``.
Dependencies Dependencies
----------- ------------
Install:: Install::

View File

@ -1,10 +1,11 @@
from .helper import unittest, PillowTestCase, hopper import time
import unittest
# Not running this test by default. No DOS against Travis CI.
from PIL import PyAccess from PIL import PyAccess
import time from .helper import PillowTestCase, hopper
# Not running this test by default. No DOS against Travis CI.
def iterate_get(size, access): def iterate_get(size, access):
@ -26,18 +27,21 @@ def timer(func, label, *args):
starttime = time.time() starttime = time.time()
for x in range(iterations): for x in range(iterations):
func(*args) func(*args)
if time.time()-starttime > 10: if time.time() - starttime > 10:
print("%s: breaking at %s iterations, %.6f per iteration" % ( print(
label, x+1, (time.time()-starttime)/(x+1.0))) "%s: breaking at %s iterations, %.6f per iteration"
% (label, x + 1, (time.time() - starttime) / (x + 1.0))
)
break break
if x == iterations-1: if x == iterations - 1:
endtime = time.time() endtime = time.time()
print("%s: %.4f s %.6f per iteration" % ( print(
label, endtime-starttime, (endtime-starttime)/(x+1.0))) "%s: %.4f s %.6f per iteration"
% (label, endtime - starttime, (endtime - starttime) / (x + 1.0))
)
class BenchCffiAccess(PillowTestCase): class BenchCffiAccess(PillowTestCase):
def test_direct(self): def test_direct(self):
im = hopper() im = hopper()
im.load() im.load()
@ -48,11 +52,11 @@ class BenchCffiAccess(PillowTestCase):
self.assertEqual(caccess[(0, 0)], access[(0, 0)]) self.assertEqual(caccess[(0, 0)], access[(0, 0)])
print("Size: %sx%s" % im.size) print("Size: %sx%s" % im.size)
timer(iterate_get, 'PyAccess - get', im.size, access) timer(iterate_get, "PyAccess - get", im.size, access)
timer(iterate_set, 'PyAccess - set', im.size, access) timer(iterate_set, "PyAccess - set", im.size, access)
timer(iterate_get, 'C-api - get', im.size, caccess) timer(iterate_get, "C-api - get", im.size, caccess)
timer(iterate_set, 'C-api - set', im.size, caccess) timer(iterate_set, "C-api - set", im.size, caccess)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,7 +1,8 @@
from . import helper import sys
import timeit import timeit
import sys from . import helper
sys.path.insert(0, ".") sys.path.insert(0, ".")

View File

@ -1,6 +1,9 @@
from .helper import unittest, PillowTestCase import unittest
from PIL import Image from PIL import Image
from .helper import PillowTestCase
TEST_FILE = "Tests/images/fli_overflow.fli" TEST_FILE = "Tests/images/fli_overflow.fli"
@ -12,5 +15,5 @@ class TestFliOverflow(PillowTestCase):
im.load() im.load()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,12 +1,8 @@
# Tests potential DOS of IcnsImagePlugin with 0 length block. # Tests potential DOS of IcnsImagePlugin with 0 length block.
# Run from anywhere that PIL is importable. # Run from anywhere that PIL is importable.
from PIL import Image
from PIL._util import py3
from io import BytesIO from io import BytesIO
if py3: from PIL import Image
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00',
'latin-1'))) Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))
else:
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00')))

View File

@ -1,19 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
import unittest
from __future__ import division
from .helper import unittest, PillowTestCase
import sys
from PIL import Image from PIL import Image
from .helper import PillowTestCase, is_win32
min_iterations = 100 min_iterations = 100
max_iterations = 10000 max_iterations = 10000
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") @unittest.skipIf(is_win32(), "requires Unix or macOS")
class TestImagingLeaks(PillowTestCase): class TestImagingLeaks(PillowTestCase):
def _get_mem_usage(self): def _get_mem_usage(self):
from resource import getpagesize, getrusage, RUSAGE_SELF from resource import getpagesize, getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss mem = getrusage(RUSAGE_SELF).ru_maxrss
return mem * getpagesize() / 1024 / 1024 return mem * getpagesize() / 1024 / 1024
@ -25,20 +25,22 @@ class TestImagingLeaks(PillowTestCase):
if i < min_iterations: if i < min_iterations:
mem_limit = mem + 1 mem_limit = mem + 1
continue continue
msg = 'memory usage limit exceeded after %d iterations' % (i + 1) msg = "memory usage limit exceeded after %d iterations" % (i + 1)
self.assertLessEqual(mem, mem_limit, msg) self.assertLessEqual(mem, mem_limit, msg)
def test_leak_putdata(self): def test_leak_putdata(self):
im = Image.new('RGB', (25, 25)) im = Image.new("RGB", (25, 25))
self._test_leak(min_iterations, max_iterations, self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata())
im.putdata, im.getdata())
def test_leak_getlist(self): def test_leak_getlist(self):
im = Image.new('P', (25, 25)) im = Image.new("P", (25, 25))
self._test_leak(min_iterations, max_iterations, self._test_leak(
min_iterations,
max_iterations,
# Pass a new list at each iteration. # Pass a new list at each iteration.
lambda: im.point(range(256))) lambda: im.point(range(256)),
)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,15 +1,8 @@
# Tests potential DOS of Jpeg2kImagePlugin with 0 length block. # Tests potential DOS of Jpeg2kImagePlugin with 0 length block.
# Run from anywhere that PIL is importable. # Run from anywhere that PIL is importable.
from PIL import Image
from PIL._util import py3
from io import BytesIO from io import BytesIO
if py3: from PIL import Image
Image.open(BytesIO(bytes(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang',
'latin-1')))
else: Image.open(BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
Image.open(BytesIO(bytes(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang')))

View File

@ -1,24 +1,27 @@
from .helper import unittest, PillowTestCase import unittest
import sys
from PIL import Image
from io import BytesIO from io import BytesIO
from PIL import Image
from .helper import PillowTestCase, is_win32
# Limits for testing the leak # Limits for testing the leak
mem_limit = 1024*1048576 mem_limit = 1024 * 1048576
stack_size = 8*1048576 stack_size = 8 * 1048576
iterations = int((mem_limit/stack_size)*2) iterations = int((mem_limit / stack_size) * 2)
codecs = dir(Image.core) codecs = dir(Image.core)
test_file = "Tests/images/rgb_trns_ycbc.jp2" test_file = "Tests/images/rgb_trns_ycbc.jp2"
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") @unittest.skipIf(is_win32(), "requires Unix or macOS")
class TestJpegLeaks(PillowTestCase): class TestJpegLeaks(PillowTestCase):
def setUp(self): def setUp(self):
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs:
self.skipTest('JPEG 2000 support not available') self.skipTest("JPEG 2000 support not available")
def test_leak_load(self): def test_leak_load(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations): for _ in range(iterations):
@ -27,6 +30,7 @@ class TestJpegLeaks(PillowTestCase):
def test_leak_save(self): def test_leak_save(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations): for _ in range(iterations):
@ -38,5 +42,5 @@ class TestJpegLeaks(PillowTestCase):
test_output.read() test_output.read()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,15 +1,18 @@
import unittest
from PIL import Image from PIL import Image
from .helper import unittest, PillowTestCase
from .helper import PillowTestCase
class TestJ2kEncodeOverflow(PillowTestCase): class TestJ2kEncodeOverflow(PillowTestCase):
def test_j2k_overflow(self): def test_j2k_overflow(self):
im = Image.new('RGBA', (1024, 131584)) im = Image.new("RGBA", (1024, 131584))
target = self.tempfile('temp.jpc') target = self.tempfile("temp.jpc")
with self.assertRaises(IOError): with self.assertRaises(IOError):
im.save(target) im.save(target)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,6 +1,7 @@
from .helper import unittest, PillowTestCase, hopper import unittest
from io import BytesIO from io import BytesIO
import sys
from .helper import PillowTestCase, hopper, is_win32
iterations = 5000 iterations = 5000
@ -14,7 +15,7 @@ valgrind --tool=massif python test-installed.py -s -v Tests/check_jpeg_leaks.py
""" """
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") @unittest.skipIf(is_win32(), "requires Unix or macOS")
class TestJpegLeaks(PillowTestCase): class TestJpegLeaks(PillowTestCase):
""" """
@ -74,9 +75,11 @@ post-patch:
""" """
def test_qtables_leak(self): def test_qtables_leak(self):
im = hopper('RGB') im = hopper("RGB")
standard_l_qtable = [int(s) for s in """ standard_l_qtable = [
int(s)
for s in """
16 11 10 16 24 40 51 61 16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55 12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56 14 13 16 24 40 57 69 56
@ -85,9 +88,14 @@ post-patch:
24 35 55 64 81 104 113 92 24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101 49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99 72 92 95 98 112 100 103 99
""".split(None)] """.split(
None
)
]
standard_chrominance_qtable = [int(s) for s in """ standard_chrominance_qtable = [
int(s)
for s in """
17 18 24 47 99 99 99 99 17 18 24 47 99 99 99 99
18 21 26 66 99 99 99 99 18 21 26 66 99 99 99 99
24 26 56 99 99 99 99 99 24 26 56 99 99 99 99 99
@ -96,10 +104,12 @@ post-patch:
99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
""".split(None)] """.split(
None
)
]
qtables = [standard_l_qtable, qtables = [standard_l_qtable, standard_chrominance_qtable]
standard_chrominance_qtable]
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
@ -161,8 +171,8 @@ post patch:
0 11.33 0 11.33
""" """
im = hopper('RGB') im = hopper("RGB")
exif = b'12345678'*4096 exif = b"12345678" * 4096
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
@ -195,12 +205,12 @@ base case:
0 +----------------------------------------------------------------------->Gi 0 +----------------------------------------------------------------------->Gi
0 7.882 0 7.882
""" """
im = hopper('RGB') im = hopper("RGB")
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
im.save(test_output, "JPEG") im.save(test_output, "JPEG")
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,6 +1,9 @@
import sys import sys
import unittest
from .helper import unittest, PillowTestCase from PIL import Image
from .helper import PillowTestCase
# This test is not run automatically. # This test is not run automatically.
# #
@ -11,17 +14,21 @@ from .helper import unittest, PillowTestCase
# Raspberry Pis). It does succeed on a 3gb Ubuntu 12.04x64 VM on Python # Raspberry Pis). It does succeed on a 3gb Ubuntu 12.04x64 VM on Python
# 2.7 and 3.2. # 2.7 and 3.2.
from PIL import Image
try:
import numpy
except ImportError:
numpy = None
YDIM = 32769 YDIM = 32769
XDIM = 48000 XDIM = 48000
@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") @unittest.skipIf(sys.maxsize <= 2 ** 32, "requires 64-bit system")
class LargeMemoryTest(PillowTestCase): class LargeMemoryTest(PillowTestCase):
def _write_png(self, xdim, ydim): def _write_png(self, xdim, ydim):
f = self.tempfile('temp.png') f = self.tempfile("temp.png")
im = Image.new('L', (xdim, ydim), 0) im = Image.new("L", (xdim, ydim), 0)
im.save(f) im.save(f)
def test_large(self): def test_large(self):
@ -32,6 +39,11 @@ class LargeMemoryTest(PillowTestCase):
"""failed prepatch""" """failed prepatch"""
self._write_png(XDIM, XDIM) self._write_png(XDIM, XDIM)
@unittest.skipIf(numpy is None, "Numpy is not installed")
def test_size_greater_than_int(self):
arr = numpy.ndarray(shape=(16394, 16394))
Image.fromarray(arr)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,6 +1,9 @@
import sys import sys
import unittest
from .helper import unittest, PillowTestCase from PIL import Image
from .helper import PillowTestCase
# This test is not run automatically. # This test is not run automatically.
# #
@ -10,7 +13,7 @@ from .helper import unittest, PillowTestCase
# on any 32-bit machine, as well as any smallish things (like # on any 32-bit machine, as well as any smallish things (like
# Raspberry Pis). # Raspberry Pis).
from PIL import Image
try: try:
import numpy as np import numpy as np
except ImportError: except ImportError:
@ -20,14 +23,13 @@ YDIM = 32769
XDIM = 48000 XDIM = 48000
@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") @unittest.skipIf(sys.maxsize <= 2 ** 32, "requires 64-bit system")
class LargeMemoryNumpyTest(PillowTestCase): class LargeMemoryNumpyTest(PillowTestCase):
def _write_png(self, xdim, ydim): def _write_png(self, xdim, ydim):
dtype = np.uint8 dtype = np.uint8
a = np.zeros((xdim, ydim), dtype=dtype) a = np.zeros((xdim, ydim), dtype=dtype)
f = self.tempfile('temp.png') f = self.tempfile("temp.png")
im = Image.fromarray(a, 'L') im = Image.fromarray(a, "L")
im.save(f) im.save(f)
def test_large(self): def test_large(self):
@ -39,5 +41,5 @@ class LargeMemoryNumpyTest(PillowTestCase):
self._write_png(XDIM, XDIM) self._write_png(XDIM, XDIM)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,6 +1,9 @@
from .helper import unittest, PillowTestCase import unittest
from PIL import Image from PIL import Image
from .helper import PillowTestCase
TEST_FILE = "Tests/images/libtiff_segfault.tif" TEST_FILE = "Tests/images/libtiff_segfault.tif"
@ -15,5 +18,5 @@ class TestLibtiffSegfault(PillowTestCase):
im.load() im.load()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,7 +1,10 @@
from .helper import unittest, PillowTestCase import unittest
from PIL import Image, PngImagePlugin, ImageFile
from io import BytesIO
import zlib import zlib
from io import BytesIO
from PIL import Image, ImageFile, PngImagePlugin
from .helper import PillowTestCase
TEST_FILE = "Tests/images/png_decompression_dos.png" TEST_FILE = "Tests/images/png_decompression_dos.png"
@ -17,10 +20,10 @@ class TestPngDos(PillowTestCase):
ImageFile.LOAD_TRUNCATED_IMAGES = False ImageFile.LOAD_TRUNCATED_IMAGES = False
for s in im.text.values(): for s in im.text.values():
self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M")
for s in im.info.values(): for s in im.info.values():
self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M")
def test_dos_text(self): def test_dos_text(self):
@ -32,20 +35,20 @@ class TestPngDos(PillowTestCase):
return return
for s in im.text.values(): for s in im.text.values():
self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M")
def test_dos_total_memory(self): def test_dos_total_memory(self):
im = Image.new('L', (1, 1)) im = Image.new("L", (1, 1))
compressed_data = zlib.compress(b'a'*1024*1023) compressed_data = zlib.compress(b"a" * 1024 * 1023)
info = PngImagePlugin.PngInfo() info = PngImagePlugin.PngInfo()
for x in range(64): for x in range(64):
info.add_text('t%s' % x, compressed_data, zip=True) info.add_text("t%s" % x, compressed_data, zip=True)
info.add_itxt('i%s' % x, compressed_data, zip=True) info.add_itxt("i%s" % x, compressed_data, zip=True)
b = BytesIO() b = BytesIO()
im.save(b, 'PNG', pnginfo=info) im.save(b, "PNG", pnginfo=info)
b.seek(0) b.seek(0)
try: try:
@ -57,9 +60,10 @@ class TestPngDos(PillowTestCase):
total_len = 0 total_len = 0
for txt in im2.text.values(): for txt in im2.text.values():
total_len += len(txt) total_len += len(txt)
self.assertLess(total_len, 64*1024*1024, self.assertLess(
"Total text chunks greater than 64M") total_len, 64 * 1024 * 1024, "Total text chunks greater than 64M"
)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

11
Tests/conftest.py Normal file
View File

@ -0,0 +1,11 @@
def pytest_report_header(config):
import io
try:
from PIL import features
with io.StringIO() as out:
features.pilinfo(out=out, supported_formats=False)
return out.getvalue()
except Exception as e:
return "pytest_report_header failed: %s" % str(e)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import print_function
import base64 import base64
import os import os

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,13 +1,13 @@
NotoNastaliqUrdu-Regular.ttf: NotoNastaliqUrdu-Regular.ttf and NotoSansSymbols-Regular.ttf, from https://github.com/googlei18n/noto-fonts
NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/
AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype
TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny
ArefRuqaa-Regular.ttf, from https://github.com/google/fonts/tree/master/ofl/arefruqaa
(from https://github.com/googlei18n/noto-fonts) All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to.
All Noto fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to.
10x20-ISO8859-1.pcf 10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base
(from https://packages.ubuntu.com/xenial/xfonts-base)
"Public domain font. Share and enjoy." "Public domain font. Share and enjoy."

Binary file not shown.

Binary file not shown.

BIN
Tests/fonts/TINY5x3GX.ttf Executable file

Binary file not shown.

View File

@ -1,33 +1,51 @@
""" """
Helper functions. Helper functions.
""" """
from __future__ import print_function
import sys
import tempfile
import os
import unittest
from PIL import Image, ImageMath
from PIL._util import py3
import logging import logging
import os
import subprocess
import sys
import tempfile
import unittest
from io import BytesIO
from PIL import Image, ImageMath
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
HAS_UPLOADER = False HAS_UPLOADER = False
if os.environ.get('SHOW_ERRORS', None): if os.environ.get("SHOW_ERRORS", None):
# local img.show for errors. # local img.show for errors.
HAS_UPLOADER = True HAS_UPLOADER = True
class test_image_results: class test_image_results:
@classmethod @staticmethod
def upload(self, a, b): def upload(a, b):
a.show() a.show()
b.show() b.show()
elif "GITHUB_ACTIONS" in os.environ:
HAS_UPLOADER = True
class test_image_results:
@staticmethod
def upload(a, b):
dir_errors = os.path.join(os.path.dirname(__file__), "errors")
os.makedirs(dir_errors, exist_ok=True)
tmpdir = tempfile.mkdtemp(dir=dir_errors)
a.save(os.path.join(tmpdir, "a.png"))
b.save(os.path.join(tmpdir, "b.png"))
return tmpdir
else: else:
try: try:
import test_image_results import test_image_results
HAS_UPLOADER = True HAS_UPLOADER = True
except ImportError: except ImportError:
pass pass
@ -35,72 +53,60 @@ else:
def convert_to_comparable(a, b): def convert_to_comparable(a, b):
new_a, new_b = a, b new_a, new_b = a, b
if a.mode == 'P': if a.mode == "P":
new_a = Image.new('L', a.size) new_a = Image.new("L", a.size)
new_b = Image.new('L', b.size) new_b = Image.new("L", b.size)
new_a.putdata(a.getdata()) new_a.putdata(a.getdata())
new_b.putdata(b.getdata()) new_b.putdata(b.getdata())
elif a.mode == 'I;16': elif a.mode == "I;16":
new_a = a.convert('I') new_a = a.convert("I")
new_b = b.convert('I') new_b = b.convert("I")
return new_a, new_b return new_a, new_b
class PillowTestCase(unittest.TestCase): class PillowTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
# holds last result object passed to run method:
self.currentResult = None
def run(self, result=None):
self.currentResult = result # remember result for use later
unittest.TestCase.run(self, result) # call superclass run method
def delete_tempfile(self, path): def delete_tempfile(self, path):
try:
ok = self.currentResult.wasSuccessful()
except AttributeError: # for pytest
ok = True
if ok:
# only clean out tempfiles if test passed
try: try:
os.remove(path) os.remove(path)
except OSError: except OSError:
pass # report? pass # report?
else:
print("=== orphaned temp file: %s" % path)
def assert_deep_equal(self, a, b, msg=None): def assert_deep_equal(self, a, b, msg=None):
try: try:
self.assertEqual( self.assertEqual(
len(a), len(b), len(a),
msg or "got length %s, expected %s" % (len(a), len(b))) len(b),
msg or "got length {}, expected {}".format(len(a), len(b)),
)
self.assertTrue( self.assertTrue(
all(x == y for x, y in zip(a, b)), all(x == y for x, y in zip(a, b)),
msg or "got %s, expected %s" % (a, b)) msg or "got {}, expected {}".format(a, b),
)
except Exception: except Exception:
self.assertEqual(a, b, msg) self.assertEqual(a, b, msg)
def assert_image(self, im, mode, size, msg=None): def assert_image(self, im, mode, size, msg=None):
if mode is not None: if mode is not None:
self.assertEqual( self.assertEqual(
im.mode, mode, im.mode,
msg or "got mode %r, expected %r" % (im.mode, mode)) mode,
msg or "got mode {!r}, expected {!r}".format(im.mode, mode),
)
if size is not None: if size is not None:
self.assertEqual( self.assertEqual(
im.size, size, im.size,
msg or "got size %r, expected %r" % (im.size, size)) size,
msg or "got size {!r}, expected {!r}".format(im.size, size),
)
def assert_image_equal(self, a, b, msg=None): def assert_image_equal(self, a, b, msg=None):
self.assertEqual( self.assertEqual(
a.mode, b.mode, a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
msg or "got mode %r, expected %r" % (a.mode, b.mode)) )
self.assertEqual( self.assertEqual(
a.size, b.size, a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
msg or "got size %r, expected %r" % (a.size, b.size)) )
if a.tobytes() != b.tobytes(): if a.tobytes() != b.tobytes():
if HAS_UPLOADER: if HAS_UPLOADER:
try: try:
@ -120,26 +126,28 @@ class PillowTestCase(unittest.TestCase):
def assert_image_similar(self, a, b, epsilon, msg=None): def assert_image_similar(self, a, b, epsilon, msg=None):
epsilon = float(epsilon) epsilon = float(epsilon)
self.assertEqual( self.assertEqual(
a.mode, b.mode, a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
msg or "got mode %r, expected %r" % (a.mode, b.mode)) )
self.assertEqual( self.assertEqual(
a.size, b.size, a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
msg or "got size %r, expected %r" % (a.size, b.size)) )
a, b = convert_to_comparable(a, b) a, b = convert_to_comparable(a, b)
diff = 0 diff = 0
for ach, bch in zip(a.split(), b.split()): for ach, bch in zip(a.split(), b.split()):
chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert('L') chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert("L")
diff += sum(i * num for i, num in enumerate(chdiff.histogram())) diff += sum(i * num for i, num in enumerate(chdiff.histogram()))
ave_diff = float(diff)/(a.size[0]*a.size[1]) ave_diff = float(diff) / (a.size[0] * a.size[1])
try: try:
self.assertGreaterEqual( self.assertGreaterEqual(
epsilon, ave_diff, epsilon,
(msg or '') + ave_diff,
" average pixel value difference %.4f > epsilon %.4f" % ( (msg or "")
ave_diff, epsilon)) + " average pixel value difference %.4f > epsilon %.4f"
% (ave_diff, epsilon),
)
except Exception as e: except Exception as e:
if HAS_UPLOADER: if HAS_UPLOADER:
try: try:
@ -149,8 +157,7 @@ class PillowTestCase(unittest.TestCase):
pass pass
raise e raise e
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None):
mode=None):
with Image.open(filename) as img: with Image.open(filename) as img:
if mode: if mode:
img = img.convert(mode) img = img.convert(mode)
@ -168,9 +175,9 @@ class PillowTestCase(unittest.TestCase):
# Verify some things. # Verify some things.
if warn_class is None: if warn_class is None:
self.assertEqual(len(w), 0, self.assertEqual(
"Expected no warnings, got %s" % len(w), 0, "Expected no warnings, got %s" % [v.category for v in w]
[v.category for v in w]) )
else: else:
self.assertGreaterEqual(len(w), 1) self.assertGreaterEqual(len(w), 1)
found = False found = False
@ -192,28 +199,16 @@ class PillowTestCase(unittest.TestCase):
value = True value = True
for i, target in enumerate(targets): for i, target in enumerate(targets):
value *= (target - threshold <= actuals[i] <= target + threshold) value *= target - threshold <= actuals[i] <= target + threshold
self.assertTrue(value, self.assertTrue(value, msg + ": " + repr(actuals) + " != " + repr(targets))
msg + ': ' + repr(actuals) + ' != ' + repr(targets))
def skipKnownBadTest(self, msg=None, platform=None, def skipKnownBadTest(self, msg=None):
travis=None, interpreter=None): # Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment.
# Skip if platform/travis matches, and if os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
# PILLOW_RUN_KNOWN_BAD is not true in the environment. print(os.environ.get("PILLOW_RUN_KNOWN_BAD", False))
if os.environ.get('PILLOW_RUN_KNOWN_BAD', False):
print(os.environ.get('PILLOW_RUN_KNOWN_BAD', False))
return return
skip = True
if platform is not None:
skip = sys.platform.startswith(platform)
if travis is not None:
skip = skip and (travis == bool(os.environ.get('TRAVIS', False)))
if interpreter is not None:
skip = skip and (interpreter == 'pypy' and
hasattr(sys, 'pypy_version_info'))
if skip:
self.skipTest(msg or "Known Bad Test") self.skipTest(msg or "Known Bad Test")
def tempfile(self, template): def tempfile(self, template):
@ -226,15 +221,15 @@ class PillowTestCase(unittest.TestCase):
def open_withImagemagick(self, f): def open_withImagemagick(self, f):
if not imagemagick_available(): if not imagemagick_available():
raise IOError() raise OSError()
outfile = self.tempfile("temp.png") outfile = self.tempfile("temp.png")
if command_succeeds([IMCONVERT, f, outfile]): if command_succeeds([IMCONVERT, f, outfile]):
return Image.open(outfile) return Image.open(outfile)
raise IOError() raise OSError()
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
class PillowLeakTestCase(PillowTestCase): class PillowLeakTestCase(PillowTestCase):
# requires unix/macOS # requires unix/macOS
iterations = 100 # count iterations = 100 # count
@ -249,8 +244,9 @@ class PillowLeakTestCase(PillowTestCase):
""" """
from resource import getrusage, RUSAGE_SELF from resource import getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == 'darwin': if sys.platform == "darwin":
# man 2 getrusage: # man 2 getrusage:
# ru_maxrss # ru_maxrss
# This is the maximum resident set size utilized (in bytes). # This is the maximum resident set size utilized (in bytes).
@ -266,26 +262,19 @@ class PillowLeakTestCase(PillowTestCase):
start_mem = self._get_mem_usage() start_mem = self._get_mem_usage()
for cycle in range(self.iterations): for cycle in range(self.iterations):
core() core()
mem = (self._get_mem_usage() - start_mem) mem = self._get_mem_usage() - start_mem
msg = 'memory usage limit exceeded in iteration %d' % cycle msg = "memory usage limit exceeded in iteration %d" % cycle
self.assertLess(mem, self.mem_limit, msg) self.assertLess(mem, self.mem_limit, msg)
# helpers # helpers
if not py3:
# Remove DeprecationWarning in Python 3
PillowTestCase.assertRaisesRegex = PillowTestCase.assertRaisesRegexp
PillowTestCase.assertRegex = PillowTestCase.assertRegexpMatches
def fromstring(data): def fromstring(data):
from io import BytesIO
return Image.open(BytesIO(data)) return Image.open(BytesIO(data))
def tostring(im, string_format, **options): def tostring(im, string_format, **options):
from io import BytesIO
out = BytesIO() out = BytesIO()
im.save(out, string_format, **options) im.save(out, string_format, **options)
return out.getvalue() return out.getvalue()
@ -317,53 +306,73 @@ def command_succeeds(cmd):
Runs the command, which must be a list of strings. Returns True if the Runs the command, which must be a list of strings. Returns True if the
command succeeds, or False if an OSError was raised by subprocess.Popen. command succeeds, or False if an OSError was raised by subprocess.Popen.
""" """
import subprocess
with open(os.devnull, 'wb') as f:
try: try:
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT) subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
except OSError: except OSError:
return False return False
return True return True
def djpeg_available(): def djpeg_available():
return command_succeeds(['djpeg', '-version']) return command_succeeds(["djpeg", "-version"])
def cjpeg_available(): def cjpeg_available():
return command_succeeds(['cjpeg', '-version']) return command_succeeds(["cjpeg", "-version"])
def netpbm_available(): def netpbm_available():
return (command_succeeds(["ppmquant", "--version"]) and return command_succeeds(["ppmquant", "--version"]) and command_succeeds(
command_succeeds(["ppmtogif", "--version"])) ["ppmtogif", "--version"]
)
def imagemagick_available(): def imagemagick_available():
return IMCONVERT and command_succeeds([IMCONVERT, '-version']) return IMCONVERT and command_succeeds([IMCONVERT, "-version"])
def on_appveyor(): def on_appveyor():
return 'APPVEYOR' in os.environ return "APPVEYOR" in os.environ
if sys.platform == 'win32': def on_github_actions():
IMCONVERT = os.environ.get('MAGICK_HOME', '') return "GITHUB_ACTIONS" in os.environ
def on_ci():
# Travis and AppVeyor have "CI"
# Azure Pipelines has "TF_BUILD"
# GitHub Actions has "GITHUB_ACTIONS"
return (
"CI" in os.environ or "TF_BUILD" in os.environ or "GITHUB_ACTIONS" in os.environ
)
def is_win32():
return sys.platform.startswith("win32")
def is_pypy():
return hasattr(sys, "pypy_translation_info")
if sys.platform == "win32":
IMCONVERT = os.environ.get("MAGICK_HOME", "")
if IMCONVERT: if IMCONVERT:
IMCONVERT = os.path.join(IMCONVERT, 'convert.exe') IMCONVERT = os.path.join(IMCONVERT, "convert.exe")
else: else:
IMCONVERT = 'convert' IMCONVERT = "convert"
def distro(): def distro():
if os.path.exists('/etc/os-release'): if os.path.exists("/etc/os-release"):
with open('/etc/os-release', 'r') as f: with open("/etc/os-release", "r") as f:
for line in f: for line in f:
if 'ID=' in line: if "ID=" in line:
return line.strip().split('=')[1] return line.strip().split("=")[1]
class cached_property(object): class cached_property:
def __init__(self, func): def __init__(self, func):
self.func = func self.func = func

BIN
Tests/images/1_trns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

BIN
Tests/images/app13.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

BIN
Tests/images/exif.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

BIN
Tests/images/fujifilm.mpo Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 MiB

BIN
Tests/images/g4_orientation_1.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_2.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_3.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_4.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_5.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_6.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_7.tif Executable file

Binary file not shown.

BIN
Tests/images/g4_orientation_8.tif Executable file

Binary file not shown.

BIN
Tests/images/hopper.pnm Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
Tests/images/i_trns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Some files were not shown because too many files have changed in this diff Show More