Merge branch 'master' into gitignore-review
|
@ -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?
|
||||||
|
|
||||||
|
|
28
.azure-pipelines/jobs/lint.yml
Normal 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'
|
22
.azure-pipelines/jobs/test-docker.yml
Normal 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'
|
|
@ -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
|
||||||
|
|
8
.github/CONTRIBUTING.md
vendored
|
@ -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
|
@ -0,0 +1 @@
|
||||||
|
tidelift: "pypi/Pillow"
|
19
.github/ISSUE_TEMPLATE.md
vendored
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -73,6 +73,9 @@ pip-delete-this-directory.txt
|
||||||
\#*#
|
\#*#
|
||||||
.#*
|
.#*
|
||||||
|
|
||||||
|
#VS Code
|
||||||
|
.vscode
|
||||||
|
|
||||||
#Komodo
|
#Komodo
|
||||||
*.komodoproject
|
*.komodoproject
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
strictness: medium
|
|
||||||
test-warnings: yes
|
|
||||||
max-line-length: 80
|
|
59
.travis.yml
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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
|
421
CHANGES.rst
|
@ -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
|
||||||
|
|
10
MANIFEST.in
|
@ -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
|
||||||
|
|
36
Makefile
|
@ -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
|
||||||
|
|
107
README.rst
|
@ -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
|
||||||
|
|
14
RELEASING.md
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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::
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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, ".")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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')))
|
|
||||||
|
|
|
@ -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(
|
||||||
# Pass a new list at each iteration.
|
min_iterations,
|
||||||
lambda: im.point(range(256)))
|
max_iterations,
|
||||||
|
# Pass a new list at each iteration.
|
||||||
|
lambda: im.point(range(256)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -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')))
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
@ -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)
|
|
@ -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
|
||||||
|
|
||||||
|
|
BIN
Tests/fonts/AdobeVFPrototype.ttf
Normal file
BIN
Tests/fonts/ArefRuqaa-Regular.ttf
Normal file
BIN
Tests/fonts/KhmerOSBattambang-Regular.ttf
Executable 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."
|
||||||
|
|
BIN
Tests/fonts/NotoSansJP-Regular.otf
Normal file
BIN
Tests/fonts/NotoSansSymbols-Regular.ttf
Normal file
BIN
Tests/fonts/TINY5x3GX.ttf
Executable file
245
Tests/helper.py
|
@ -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:
|
try:
|
||||||
ok = self.currentResult.wasSuccessful()
|
os.remove(path)
|
||||||
except AttributeError: # for pytest
|
except OSError:
|
||||||
ok = True
|
pass # report?
|
||||||
|
|
||||||
if ok:
|
|
||||||
# only clean out tempfiles if test passed
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except OSError:
|
|
||||||
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,29 +199,17 @@ 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
|
self.skipTest(msg or "Known Bad Test")
|
||||||
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")
|
|
||||||
|
|
||||||
def tempfile(self, template):
|
def tempfile(self, template):
|
||||||
assert template[:5] in ("temp.", "temp_")
|
assert template[:5] in ("temp.", "temp_")
|
||||||
|
@ -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
|
try:
|
||||||
with open(os.devnull, 'wb') as f:
|
subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||||
try:
|
except OSError:
|
||||||
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
|
return False
|
||||||
except OSError:
|
|
||||||
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
After Width: | Height: | Size: 612 B |
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds
Normal file
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.png
Normal file
After Width: | Height: | Size: 106 B |
BIN
Tests/images/app13.jpg
Normal file
After Width: | Height: | Size: 563 B |
BIN
Tests/images/combined_larger_than_size.psd
Normal file
BIN
Tests/images/decompression_bomb.gif
Normal file
After Width: | Height: | Size: 44 B |
BIN
Tests/images/decompression_bomb.ico
Normal file
After Width: | Height: | Size: 58 B |
BIN
Tests/images/drawing_roundDown.emf
Normal file
BIN
Tests/images/exif.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
Tests/images/fli_overrun.bin
Normal file
BIN
Tests/images/fujifilm.mpo
Normal file
After Width: | Height: | Size: 9.5 MiB |
BIN
Tests/images/g4_orientation_1.tif
Executable file
BIN
Tests/images/g4_orientation_2.tif
Executable file
BIN
Tests/images/g4_orientation_3.tif
Executable file
BIN
Tests/images/g4_orientation_4.tif
Executable file
BIN
Tests/images/g4_orientation_5.tif
Executable file
BIN
Tests/images/g4_orientation_6.tif
Executable file
BIN
Tests/images/g4_orientation_7.tif
Executable file
BIN
Tests/images/g4_orientation_8.tif
Executable file
BIN
Tests/images/hopper.pnm
Normal file
BIN
Tests/images/hopper_draw.ico
Normal file
After Width: | Height: | Size: 846 B |
BIN
Tests/images/hopper_orientation_2.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_2.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_3.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_3.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
Tests/images/hopper_orientation_4.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_4.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_5.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_5.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
Tests/images/hopper_orientation_6.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_6.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_7.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_7.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_8.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_8.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_roundDown.bmp
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
Tests/images/hopper_roundDown_2.tif
Normal file
BIN
Tests/images/hopper_roundDown_3.tif
Normal file
BIN
Tests/images/hopper_roundDown_None.tif
Normal file
BIN
Tests/images/hopper_roundUp_2.tif
Normal file
BIN
Tests/images/hopper_roundUp_3.tif
Normal file
BIN
Tests/images/hopper_roundUp_None.tif
Normal file
BIN
Tests/images/hopper_unexpected.ico
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/i_trns.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/imagedraw_arc_width_non_whole_angle.png
Normal file
After Width: | Height: | Size: 439 B |
BIN
Tests/images/imagedraw_arc_width_pieslice.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
Tests/images/imagedraw_ellipse_width_large.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Tests/images/imagedraw_floodfill_not_negative.png
Normal file
After Width: | Height: | Size: 214 B |