Merge branch 'python-pillow:main' into test_lists_and_tuples

This commit is contained in:
Yay295 2023-04-13 00:20:44 -05:00 committed by GitHub
commit 886ed7a7d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
214 changed files with 2756 additions and 2488 deletions

View File

@ -13,7 +13,7 @@ environment:
- PYTHON: C:/Python311
ARCHITECTURE: x86
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
- PYTHON: C:/Python37-x64
- PYTHON: C:/Python38-x64
ARCHITECTURE: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
@ -21,15 +21,17 @@ environment:
install:
- '%PYTHON%\%EXECUTABLE% --version'
- curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/main.zip
- curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip
- 7z x pillow-depends.zip -oc:\
- 7z x pillow-test-images.zip -oc:\
- mv c:\pillow-depends-main c:\pillow-depends
- xcopy /S /Y c:\pillow-depends\test_images\* c:\pillow\tests\images
- xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images
- 7z x ..\pillow-depends\nasm-2.15.05-win64.zip -oc:\
- ..\pillow-depends\gs1000w32.exe /S
- path c:\nasm-2.15.05;C:\Program Files (x86)\gs\gs10.0.0\bin;%PATH%
- choco install ghostscript --version=10.0.0.20230317
- path c:\nasm-2.15.05;C:\Program Files\gs\gs10.00.0\bin;%PATH%
- cd c:\pillow\winbuild\
- ps: |
c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
c:\pillow\winbuild\build\build_dep_all.cmd
$host.SetShouldExit(0)
- path C:\pillow\winbuild\build\bin;%PATH%
@ -50,8 +52,8 @@ test_script:
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
after_test:
- python -m pip install codecov
- codecov --file coverage.xml --name %PYTHON% --flags AppVeyor
- curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
- .\codecov.exe --file coverage.xml --name %PYTHON% --flags AppVeyor
matrix:
fast_finish: true

View File

@ -1,7 +1,7 @@
#!/bin/bash
# gather the coverage data
python3 -m pip install codecov
python3 -m pip install coverage
if [[ $MATRIX_DOCKER ]]; then
python3 -m coverage xml --ignore-errors
else

View File

@ -37,11 +37,12 @@ python3 -m pip install -U pytest-timeout
python3 -m pip install pyroma
if [[ $(uname) != CYGWIN* ]]; then
python3 -m pip install numpy
# TODO Remove condition when NumPy supports 3.12
if ! [ "$GHA_PYTHON_VERSION" == "3.12-dev" ]; then python3 -m pip install numpy ; fi
# PyQt6 doesn't support PyPy3
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
sudo apt-get -qq install libegl1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
python3 -m pip install pyqt6
fi

View File

@ -3,10 +3,12 @@ name: CIFuzz
on:
push:
paths:
- ".github/workflows/cifuzz.yml"
- "**.c"
- "**.h"
pull_request:
paths:
- ".github/workflows/cifuzz.yml"
- "**.c"
- "**.h"
workflow_dispatch:
@ -14,7 +16,7 @@ on:
permissions:
contents: read
concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

55
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Docs
on:
push:
paths:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
FORCE_COLOR: 1
jobs:
build:
runs-on: ubuntu-latest
name: Docs
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
cache: pip
cache-dependency-path: ".ci/*.sh"
- name: Build system information
run: python3 .github/workflows/system-info.py
- name: Install Linux dependencies
run: |
.ci/install.sh
env:
GHA_PYTHON_VERSION: "3.x"
- name: Build
run: |
.ci/build.sh
- name: Docs
run: |
make doccheck

View File

@ -13,7 +13,8 @@ python3 -m pip install -U pytest-cov
python3 -m pip install -U pytest-timeout
python3 -m pip install pyroma
python3 -m pip install numpy
# TODO Remove condition when NumPy supports 3.12
if ! [ "$GHA_PYTHON_VERSION" == "3.12-dev" ]; then python3 -m pip install numpy ; fi
# extra test images
pushd depends && ./install_extra_test_images.sh && popd

View File

@ -20,7 +20,7 @@ jobs:
steps:
- name: "Check issues"
uses: actions/stale@v7
uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
only-labels: "Awaiting OP Action"

View File

@ -1,6 +1,15 @@
name: Test Cygwin
on: [push, pull_request, workflow_dispatch]
on:
push:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
@ -58,8 +67,7 @@ jobs:
python3${{ matrix.python-minor-version }}-numpy
python3${{ matrix.python-minor-version }}-sip
python3${{ matrix.python-minor-version }}-tkinter
qt5-devel-tools
subversion
wget
xorg-server-extra
zlib-devel

View File

@ -1,6 +1,15 @@
name: Test Docker
on: [push, pull_request, workflow_dispatch]
on:
push:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
@ -24,11 +33,11 @@ jobs:
# Then run the remainder
alpine,
amazon-2-amd64,
amazon-2023-amd64,
arch,
centos-7-amd64,
centos-stream-8-amd64,
centos-stream-9-amd64,
debian-10-buster-x86,
debian-11-bullseye-x86,
fedora-36-amd64,
fedora-37-amd64,
@ -87,6 +96,7 @@ jobs:
with:
flags: GHA_Docker
name: ${{ matrix.docker }}
gcov: true
success:
permissions:

View File

@ -1,6 +1,15 @@
name: Test MinGW
on: [push, pull_request, workflow_dispatch]
on:
push:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
@ -59,8 +68,7 @@ jobs:
${{ matrix.package }}-python3-numpy \
${{ matrix.package }}-python3-olefile \
${{ matrix.package }}-python3-pip \
${{ matrix.package }}-python3-setuptools \
subversion
${{ matrix.package }}-python3-setuptools
if [ ${{ matrix.package }} == "mingw-w64-x86_64" ]; then
pacman -S --noconfirm \

View File

@ -5,10 +5,12 @@ name: Test Valgrind
on:
push:
paths:
- ".github/workflows/test-valgrind.yml"
- "**.c"
- "**.h"
pull_request:
paths:
- ".github/workflows/test-valgrind.yml"
- "**.c"
- "**.h"
workflow_dispatch:
@ -16,7 +18,7 @@ on:
permissions:
contents: read
concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

View File

@ -1,6 +1,15 @@
name: Test Windows
on: [push, pull_request, workflow_dispatch]
on:
push:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
@ -15,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev"]
architecture: ["x86", "x64"]
include:
# PyPy 7.3.4+ only ships 64-bit binaries for Windows
@ -38,6 +47,12 @@ jobs:
repository: python-pillow/pillow-depends
path: winbuild\depends
- name: Checkout extra test images
uses: actions/checkout@v3
with:
repository: python-pillow/test-images
path: Tests\test-images
# sets env: pythonLocation
- name: Set up Python
uses: actions/setup-python@v4
@ -59,10 +74,11 @@ jobs:
7z x winbuild\depends\nasm-2.15.05-win64.zip "-o$env:RUNNER_WORKSPACE\"
echo "$env:RUNNER_WORKSPACE\nasm-2.15.05" >> $env:GITHUB_PATH
winbuild\depends\gs1000w32.exe /S
echo "C:\Program Files (x86)\gs\gs10.0.0\bin" >> $env:GITHUB_PATH
choco install ghostscript --version=10.0.0.20230317
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
# Install extra test images
xcopy /S /Y Tests\test-images\* Tests\images
# make cache key depend on VS version
& "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" `
@ -81,7 +97,7 @@ jobs:
- name: Prepare build
if: steps.build-cache.outputs.cache-hit != 'true'
run: |
& python.exe winbuild\build_prepare.py -v --python=$env:pythonLocation --srcdir
& python.exe winbuild\build_prepare.py -v --python $env:pythonLocation
shell: pwsh
- name: Build dependencies / libjpeg-turbo

View File

@ -1,6 +1,15 @@
name: Test
on: [push, pull_request, workflow_dispatch]
on:
push:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
pull_request:
paths-ignore:
- ".github/workflows/docs.yml"
- "docs/**"
workflow_dispatch:
permissions:
contents: read
@ -22,14 +31,14 @@ jobs:
python-version: [
"pypy3.9",
"pypy3.8",
"3.12-dev",
"3.11",
"3.10",
"3.9",
"3.8",
"3.7",
]
include:
- python-version: "3.7"
- python-version: "3.9"
PYTHONOPTIMIZE: 1
REVERSE: "--reverse"
- python-version: "3.8"
@ -95,11 +104,6 @@ jobs:
name: errors
path: Tests/errors
- name: Docs
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.11
run: |
make doccheck
- name: After success
run: |
.ci/after_success.sh
@ -107,9 +111,9 @@ jobs:
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: ${{ matrix.os == 'macos-latest' && 'GHA_macOS' || 'GHA_Ubuntu' }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
gcov: true
success:
permissions:

2
.gitignore vendored
View File

@ -79,7 +79,7 @@ docs/_build/
# JetBrains
.idea
# Extra test images installed from pillow-depends/test_images
# Extra test images installed from python-pillow/test-images
Tests/images/README.md
Tests/images/crash_1.tif
Tests/images/crash_2.tif

View File

@ -1,9 +1,9 @@
repos:
- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.3.0
hooks:
- id: black
args: [--target-version=py37]
args: [--target-version=py38]
# Only .py files, until https://github.com/psf/black/issues/402 resolved
files: \.py$
types: []
@ -14,7 +14,7 @@ repos:
- id: isort
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
rev: 1.7.5
hooks:
- id: bandit
args: [--severity-level=high]
@ -26,10 +26,10 @@ repos:
- id: yesqa
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.4.2
rev: v1.5.1
hooks:
- id: remove-tabs
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
@ -57,7 +57,7 @@ repos:
- id: sphinx-lint
- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 0.6.1
rev: 1.0.0
hooks:
- id: tox-ini-fmt

View File

@ -1,5 +1,10 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python:
install:
- method: pip

View File

@ -2,9 +2,99 @@
Changelog (Pillow)
==================
9.5.0 (unreleased)
10.0.0 (unreleased)
-------------------
- Fixed type handling for include and lib directories #7069
[adisbladis, radarhere]
- Remove deprecations for Pillow 10.0.0 #7059, #7080
[hugovk, radarhere]
- Drop support for soon-EOL Python 3.7 #7058
[hugovk, radarhere]
9.5.0 (2023-04-01)
------------------
- Added ImageSourceData to TAGS_V2 #7053
[radarhere]
- Clear PPM half token after use #7052
[radarhere]
- Removed absolute path to ldconfig #7044
[radarhere]
- Support custom comments and PLT markers when saving JPEG2000 images #6903
[joshware, radarhere, hugovk]
- Load before getting size in __array_interface__ #7034
[radarhere]
- Support creating BGR;15, BGR;16 and BGR;24 images, but drop support for BGR;32 #7010
[radarhere]
- Consider transparency when applying APNG blend mask #7018
[radarhere]
- Round duration when saving animated WebP images #6996
[radarhere]
- Added reading of JPEG2000 comments #6909
[radarhere]
- Decrement reference count #7003
[radarhere, nulano]
- Allow libtiff_support_custom_tags to be missing #7020
[radarhere]
- Improved I;16N support #6834
[radarhere]
- Added QOI reading #6852
[radarhere, hugovk]
- Added saving RGBA images as PDFs #6925
[radarhere]
- Do not raise an error if os.environ does not contain PATH #6935
[radarhere, hugovk]
- Close OleFileIO instance when closing or exiting FPX or MIC #7005
[radarhere]
- Added __int__ to IFDRational for Python >= 3.11 #6998
[radarhere]
- Added memoryview support to Dib.frombytes() #6988
[radarhere, nulano]
- Close file pointer copy in the libtiff encoder if still open #6986
[fcarron, radarhere]
- Raise an error if ImageDraw co-ordinates are incorrectly ordered #6978
[radarhere]
- Added "corners" argument to ImageDraw rounded_rectangle() #6954
[radarhere]
- Added memoryview support to frombytes() #6974
[radarhere]
- Allow comments in FITS images #6973
[radarhere]
- Support saving PDF with different X and Y resolutions #6961
[jvanderneutstulen, radarhere, hugovk]
- Fixed writing int as UNDEFINED tag #6950
[radarhere]
- Raise an error if EXIF data is too long when saving JPEG #6939
[radarhere]
- Handle more than one directory returned by pkg-config #6896
[sebastic, radarhere]

View File

@ -13,8 +13,8 @@ By obtaining, using, and/or copying this software and/or its associated
documentation, you agree that you have read, understood, and will comply
with the following terms and conditions:
Permission to use, copy, modify, and distribute this software and its
associated documentation for any purpose and without fee is hereby granted,
Permission to use, copy, modify and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appears in all copies, and that
both that copyright notice and this permission notice appear in supporting
documentation, and that the name of Secret Labs AB or the author not be

View File

@ -16,10 +16,16 @@ coverage:
python3 -m coverage report
.PHONY: doc
doc:
.PHONY: html
doc html:
python3 -c "import PIL" > /dev/null 2>&1 || python3 -m pip install .
$(MAKE) -C docs html
.PHONY: htmlview
htmlview:
python3 -c "import PIL" > /dev/null 2>&1 || python3 -m pip install .
$(MAKE) -C docs htmlview
.PHONY: doccheck
doccheck:
$(MAKE) doc
@ -38,7 +44,8 @@ help:
@echo " coverage run coverage test (in progress)"
@echo " doc make HTML docs"
@echo " docserve run an HTTP server on the docs directory"
@echo " html to make standalone HTML files"
@echo " html make HTML docs"
@echo " htmlview open the index page built by the html target in your browser"
@echo " inplace make inplace extension"
@echo " install make and install"
@echo " install-coverage make and install with C coverage"
@ -71,6 +78,7 @@ debug:
.PHONY: release-test
release-test:
python3 Tests/check_release_notes.py
python3 -m pip install -e .[tests]
python3 selftest.py
python3 -m pytest Tests
@ -116,5 +124,5 @@ lint:
lint-fix:
python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
python3 -c "import isort" > /dev/null 2>&1 || python3 -m pip install isort
python3 -m black --target-version py37 .
python3 -m black --target-version py38 .
python3 -m isort .

View File

@ -11,14 +11,13 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th.
* [ ] Develop and prepare release in `main` branch.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `main` branch.
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI and GitHub Actions.
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] In compliance with [PEP 440](https://peps.python.org/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] Update `CHANGES.rst`.
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
* [ ] Create branch and tag for release e.g.:
```bash
git branch 5.2.x
git tag 5.2.0
git push --all
git push --tags
```
* [ ] Create and check source distribution:
@ -32,8 +31,11 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th.
python3 -m twine upload dist/Pillow-5.2.0*
```
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py`
* [ ] In compliance with [PEP 440](https://peps.python.org/pep-0440/),
increment and append `.dev0` to version identifier in `src/PIL/_version.py` and then:
```bash
git push --all
```
## Point Release
Released as needed for security, installation or critical bug fixes.
@ -45,16 +47,12 @@ Released as needed for security, installation or critical bug fixes.
git checkout -t remotes/origin/5.2.x
```
* [ ] Cherry pick individual commits from `main` branch to release branch e.g. `5.2.x`, then `git push`.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`.
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] In compliance with [PEP 440](https://peps.python.org/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] Run pre-release check via `make release-test`.
* [ ] Create tag for release e.g.:
```bash
git tag 5.2.1
git push
git push --tags
```
* [ ] Create and check source distribution:
@ -67,7 +65,10 @@ Released as needed for security, installation or critical bug fixes.
python3 -m twine check --strict dist/*
python3 -m twine upload dist/Pillow-5.2.1*
```
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) and then:
```bash
git push
```
## Embargoed Release
@ -83,7 +84,6 @@ Released as needed privately to individual vendors for critical security-related
```bash
git checkout 2.5.x
git tag 2.5.3
git push origin 2.5.x
git push origin --tags
```
* [ ] Create and check source distribution:
@ -91,15 +91,14 @@ Released as needed privately to individual vendors for critical security-related
make sdist
```
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions)
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) and then:
```bash
git push origin 2.5.x
```
## Binary Distributions
### Windows
* [ ] Download the artifacts from the [GitHub Actions "Test Windows" workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml)
and copy into `dist/`
### Mac and Linux
### macOS and Linux
* [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels):
```bash
git clone https://github.com/python-pillow/pillow-wheels
@ -107,7 +106,18 @@ Released as needed privately to individual vendors for critical security-related
./update-pillow-tag.sh [[release tag]]
```
* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases)
and copy into `dist/`
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli) from the main repo:
```bash
gh release download --dir dist --pattern "*.whl" --repo python-pillow/pillow-wheels
```
### Windows
* [ ] Download the artifacts from the [GitHub Actions "Test Windows" workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml)
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
```bash
gh run download --dir dist
# select dist-x.y.z
```
## Publicize Release

View File

@ -0,0 +1,6 @@
import sys
from pathlib import Path
for rst in Path("docs/releasenotes").glob("[1-9]*.rst"):
if "TODO" in open(rst).read():
sys.exit(f"Error: remove TODO from {rst}")

View File

@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
HAS_UPLOADER = False
if os.environ.get("SHOW_ERRORS", None):
if os.environ.get("SHOW_ERRORS"):
# local img.show for errors.
HAS_UPLOADER = True
@ -271,7 +271,7 @@ def netpbm_available():
def magick_command():
if sys.platform == "win32":
magickhome = os.environ.get("MAGICK_HOME", "")
magickhome = os.environ.get("MAGICK_HOME")
if magickhome:
imagemagick = [os.path.join(magickhome, "convert.exe")]
graphicsmagick = [os.path.join(magickhome, "gm.exe"), "convert"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

BIN
Tests/images/comment.jp2 Normal file

Binary file not shown.

BIN
Tests/images/hopper.qoi Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

BIN
Tests/images/pil123rgba.qoi Normal file

Binary file not shown.

View File

@ -177,13 +177,14 @@ class TestEnvVars:
Image._apply_env_variables({"PILLOW_BLOCK_SIZE": "2m"})
assert Image.core.get_block_size() == 2 * 1024 * 1024
def test_warnings(self):
pytest.warns(
UserWarning, Image._apply_env_variables, {"PILLOW_ALIGNMENT": "15"}
)
pytest.warns(
UserWarning, Image._apply_env_variables, {"PILLOW_BLOCK_SIZE": "1024"}
)
pytest.warns(
UserWarning, Image._apply_env_variables, {"PILLOW_BLOCKS_MAX": "wat"}
)
@pytest.mark.parametrize(
"var",
(
{"PILLOW_ALIGNMENT": "15"},
{"PILLOW_BLOCK_SIZE": "1024"},
{"PILLOW_BLOCKS_MAX": "wat"},
),
)
def test_warnings(self, var):
with pytest.warns(UserWarning):
Image._apply_env_variables(var)

View File

@ -36,12 +36,10 @@ class TestDecompressionBomb:
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
assert Image.MAX_IMAGE_PIXELS == 128 * 128 - 1
def open():
with pytest.warns(Image.DecompressionBombWarning):
with Image.open(TEST_FILE):
pass
pytest.warns(Image.DecompressionBombWarning, open)
def test_exception(self):
# Set limit to trigger exception on the test file
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
@ -87,7 +85,8 @@ class TestDecompressionCrop:
# same decompression bomb warnings on them.
with hopper() as src:
box = (0, 0, src.width * 2, src.height * 2)
pytest.warns(Image.DecompressionBombWarning, src.crop, box)
with pytest.warns(Image.DecompressionBombWarning):
src.crop(box)
def test_crop_decompression_checks(self):
im = Image.new("RGB", (100, 100))
@ -95,7 +94,8 @@ class TestDecompressionCrop:
for value in ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990)):
assert im.crop(value).size == (9, 9)
pytest.warns(Image.DecompressionBombWarning, im.crop, (-160, -160, 99, 99))
with pytest.warns(Image.DecompressionBombWarning):
im.crop((-160, -160, 99, 99))
with pytest.raises(Image.DecompressionBombError):
im.crop((-99909, -99990, 99999, 99999))

View File

@ -6,11 +6,6 @@ from PIL import _deprecate
@pytest.mark.parametrize(
"version, expected",
[
(
10,
"Old thing is deprecated and will be removed in Pillow 10 "
r"\(2023-07-01\)\. Use new thing instead\.",
),
(
11,
"Old thing is deprecated and will be removed in Pillow 11 "
@ -29,7 +24,7 @@ def test_version(version, expected):
def test_unknown_version():
expected = r"Unknown removal version, update PIL\._deprecate\?"
expected = r"Unknown removal version: 12345. Update PIL\._deprecate\?"
with pytest.raises(ValueError, match=expected):
_deprecate.deprecate("Old thing", 12345, "new thing")
@ -57,18 +52,18 @@ def test_old_version(deprecated, plural, expected):
def test_plural():
expected = (
r"Old things are deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Old things are deprecated and will be removed in Pillow 11 \(2024-10-15\)\. "
r"Use new thing instead\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old things", 10, "new thing", plural=True)
_deprecate.deprecate("Old things", 11, "new thing", plural=True)
def test_replacement_and_action():
expected = "Use only one of 'replacement' and 'action'"
with pytest.raises(ValueError, match=expected):
_deprecate.deprecate(
"Old thing", 10, replacement="new thing", action="Upgrade to new thing"
"Old thing", 11, replacement="new thing", action="Upgrade to new thing"
)
@ -81,16 +76,16 @@ def test_replacement_and_action():
)
def test_action(action):
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Old thing is deprecated and will be removed in Pillow 11 \(2024-10-15\)\. "
r"Upgrade to new thing\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10, action=action)
_deprecate.deprecate("Old thing", 11, action=action)
def test_no_replacement_or_action():
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)"
r"Old thing is deprecated and will be removed in Pillow 11 \(2024-10-15\)"
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10)
_deprecate.deprecate("Old thing", 11)

View File

@ -1,18 +0,0 @@
import warnings
with warnings.catch_warnings(record=True) as w:
# Arrange: cause all warnings to always be triggered
warnings.simplefilter("always")
# Act: trigger a warning with Qt5
from PIL import ImageQt
def test_deprecated():
# Assert
if ImageQt.qt_version in ("5", "side2"):
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "deprecated" in str(w[0].message)
else:
assert len(w) == 0

View File

@ -163,6 +163,12 @@ def test_apng_blend():
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
def test_apng_blend_transparency():
with Image.open("Tests/images/blend_transparency.png") as im:
im.seek(1)
assert im.getpixel((0, 0)) == (255, 0, 0)
def test_apng_chunk_order():
with Image.open("Tests/images/apng/fctl_actl.png") as im:
im.seek(im.n_frames - 1)
@ -263,13 +269,11 @@ def test_apng_chunk_errors():
with Image.open("Tests/images/apng/chunk_no_actl.png") as im:
assert not im.is_animated
def open():
with pytest.warns(UserWarning):
with Image.open("Tests/images/apng/chunk_multi_actl.png") as im:
im.load()
assert not im.is_animated
pytest.warns(UserWarning, open)
with Image.open("Tests/images/apng/chunk_actl_after_idat.png") as im:
assert not im.is_animated
@ -287,21 +291,17 @@ def test_apng_chunk_errors():
def test_apng_syntax_errors():
def open_frames_zero():
with pytest.warns(UserWarning):
with Image.open("Tests/images/apng/syntax_num_frames_zero.png") as im:
assert not im.is_animated
with pytest.raises(OSError):
im.load()
pytest.warns(UserWarning, open_frames_zero)
def open_frames_zero_default():
with pytest.warns(UserWarning):
with Image.open("Tests/images/apng/syntax_num_frames_zero_default.png") as im:
assert not im.is_animated
im.load()
pytest.warns(UserWarning, open_frames_zero_default)
# we can handle this case gracefully
exception = None
with Image.open("Tests/images/apng/syntax_num_frames_low.png") as im:
@ -316,13 +316,11 @@ def test_apng_syntax_errors():
im.seek(im.n_frames - 1)
im.load()
def open():
with pytest.warns(UserWarning):
with Image.open("Tests/images/apng/syntax_num_frames_invalid.png") as im:
assert not im.is_animated
im.load()
pytest.warns(UserWarning, open)
@pytest.mark.parametrize(
"test_file",
@ -657,13 +655,3 @@ def test_different_modes_in_later_frames(mode, tmp_path):
im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))])
with Image.open(test_file) as reloaded:
assert reloaded.mode == mode
def test_constants_deprecation():
for enum, prefix in {
PngImagePlugin.Disposal: "APNG_DISPOSE_",
PngImagePlugin.Blend: "APNG_BLEND_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(PngImagePlugin, prefix + name) == enum[name]

View File

@ -1,6 +1,6 @@
import pytest
from PIL import BlpImagePlugin, Image
from PIL import Image
from .helper import (
assert_image_equal,
@ -72,14 +72,3 @@ def test_crashes(test_file):
with Image.open(f) as im:
with pytest.raises(OSError):
im.load()
def test_constants_deprecation():
for enum, prefix in {
BlpImagePlugin.Format: "BLP_FORMAT_",
BlpImagePlugin.Encoding: "BLP_ENCODING_",
BlpImagePlugin.AlphaEncoding: "BLP_ALPHA_ENCODING_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(BlpImagePlugin, prefix + name) == enum[name]

View File

@ -56,6 +56,7 @@ def test_handler(tmp_path):
def load(self, im):
self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1))
def save(self, im, fp, filename):

View File

@ -28,7 +28,8 @@ def test_unclosed_file():
im = Image.open(TEST_FILE)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

View File

@ -28,34 +28,65 @@ FILE2_COMPARE_SCALE2 = "Tests/images/non_zero_bb_scale2.png"
# EPS test files with binary preview
FILE3 = "Tests/images/binary_preview_map.eps"
# Three unsigned 32bit little-endian values:
# 0xC6D3D0C5 magic number
# byte position of start of postscript section (12)
# byte length of postscript section (0)
# this byte length isn't valid, but we don't read it
simple_binary_header = b"\xc5\xd0\xd3\xc6\x0c\x00\x00\x00\x00\x00\x00\x00"
# taken from page 8 of the specification
# https://web.archive.org/web/20220120164601/https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/5002.EPSF_Spec.pdf
simple_eps_file = (
b"%!PS-Adobe-3.0 EPSF-3.0",
b"%%BoundingBox: 5 5 105 105",
b"10 setlinewidth",
b"10 10 moveto",
b"0 90 rlineto 90 0 rlineto 0 -90 rlineto closepath",
b"stroke",
)
simple_eps_file_with_comments = (
simple_eps_file[:1]
+ (
b"%%Comment1: Some Value",
b"%%SecondComment: Another Value",
)
+ simple_eps_file[1:]
)
simple_eps_file_without_version = simple_eps_file[1:]
simple_eps_file_without_boundingbox = simple_eps_file[:1] + simple_eps_file[2:]
simple_eps_file_with_invalid_boundingbox = (
simple_eps_file[:1] + (b"%%BoundingBox: a b c d",) + simple_eps_file[2:]
)
simple_eps_file_with_invalid_boundingbox_valid_imagedata = (
simple_eps_file_with_invalid_boundingbox + (b"%ImageData: 100 100 8 3",)
)
simple_eps_file_with_long_ascii_comment = (
simple_eps_file[:2] + (b"%%Comment: " + b"X" * 300,) + simple_eps_file[2:]
)
simple_eps_file_with_long_binary_data = (
simple_eps_file[:2]
+ (
b"%%BeginBinary: 300",
b"\0" * 300,
b"%%EndBinary",
)
+ simple_eps_file[2:]
)
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
def test_sanity():
# Regular scale
with Image.open(FILE1) as image1:
image1.load()
assert image1.mode == "RGB"
assert image1.size == (460, 352)
assert image1.format == "EPS"
with Image.open(FILE2) as image2:
image2.load()
assert image2.mode == "RGB"
assert image2.size == (360, 252)
assert image2.format == "EPS"
# Double scale
with Image.open(FILE1) as image1_scale2:
image1_scale2.load(scale=2)
assert image1_scale2.mode == "RGB"
assert image1_scale2.size == (920, 704)
assert image1_scale2.format == "EPS"
with Image.open(FILE2) as image2_scale2:
image2_scale2.load(scale=2)
assert image2_scale2.mode == "RGB"
assert image2_scale2.size == (720, 504)
assert image2_scale2.format == "EPS"
@pytest.mark.parametrize(
("filename", "size"), ((FILE1, (460, 352)), (FILE2, (360, 252)))
)
@pytest.mark.parametrize("scale", (1, 2))
def test_sanity(filename, size, scale):
expected_size = tuple(s * scale for s in size)
with Image.open(filename) as image:
image.load(scale=scale)
assert image.mode == "RGB"
assert image.size == expected_size
assert image.format == "EPS"
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
@ -69,11 +100,72 @@ def test_load():
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
EpsImagePlugin.EpsImageFile(invalid_file)
def test_binary_header_only():
data = io.BytesIO(simple_binary_header)
with pytest.raises(SyntaxError, match='EPS header missing "%!PS-Adobe" comment'):
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_missing_version_comment(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
with pytest.raises(SyntaxError):
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_missing_boundingbox_comment(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_boundingbox))
with pytest.raises(SyntaxError, match='EPS header missing "%%BoundingBox" comment'):
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_invalid_boundingbox_comment(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox))
with pytest.raises(OSError, match="cannot determine EPS bounding box"):
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix):
data = io.BytesIO(
prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox_valid_imagedata)
)
with Image.open(data) as img:
assert img.mode == "RGB"
assert img.size == (100, 100)
assert img.format == "EPS"
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_ascii_comment_too_long(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
with pytest.raises(SyntaxError, match="not an EPS file"):
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_long_binary_data(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
EpsImagePlugin.EpsImageFile(data)
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_load_long_binary_data(prefix):
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
with Image.open(data) as img:
img.load()
assert img.mode == "RGB"
assert img.size == (100, 100)
assert img.format == "EPS"
@mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
)
@ -100,7 +192,7 @@ def test_showpage():
with Image.open("Tests/images/reqd_showpage.png") as target:
# should not crash/hang
plot_image.load()
# fonts could be slightly different
# fonts could be slightly different
assert_image_similar(plot_image, target, 6)
@ -111,7 +203,7 @@ def test_transparency():
assert plot_image.mode == "RGBA"
with Image.open("Tests/images/reqd_showpage_transparency.png") as target:
# fonts could be slightly different
# fonts could be slightly different
assert_image_similar(plot_image, target, 6)
@ -206,7 +298,6 @@ def test_resize(filename):
@pytest.mark.parametrize("filename", (FILE1, FILE2))
def test_thumbnail(filename):
# Issue #619
# Arrange
with Image.open(filename) as im:
new_size = (100, 100)
im.thumbnail(new_size)
@ -220,7 +311,7 @@ def test_read_binary_preview():
pass
def test_readline(tmp_path):
def test_readline_psfile(tmp_path):
# check all the freaking line endings possible from the spec
# test_string = u'something\r\nelse\n\rbaz\rbif\n'
line_endings = ["\r\n", "\n", "\n\r", "\r"]
@ -237,7 +328,8 @@ def test_readline(tmp_path):
def _test_readline_io_psfile(test_string, ending):
f = io.BytesIO(test_string.encode("latin-1"))
t = EpsImagePlugin.PSFile(f)
with pytest.warns(DeprecationWarning):
t = EpsImagePlugin.PSFile(f)
_test_readline(t, ending)
def _test_readline_file_psfile(test_string, ending):
@ -246,7 +338,8 @@ def test_readline(tmp_path):
w.write(test_string.encode("latin-1"))
with open(f, "rb") as r:
t = EpsImagePlugin.PSFile(r)
with pytest.warns(DeprecationWarning):
t = EpsImagePlugin.PSFile(r)
_test_readline(t, ending)
for ending in line_endings:
@ -255,6 +348,25 @@ def test_readline(tmp_path):
_test_readline_file_psfile(s, ending)
def test_psfile_deprecation():
with pytest.warns(DeprecationWarning):
EpsImagePlugin.PSFile(None)
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
@pytest.mark.parametrize(
"line_ending",
(b"\r\n", b"\n", b"\n\r", b"\r"),
)
def test_readline(prefix, line_ending):
simple_file = prefix + line_ending.join(simple_eps_file_with_comments)
data = io.BytesIO(simple_file)
test_file = EpsImagePlugin.EpsImageFile(data)
assert test_file.info["Comment1"] == "Some Value"
assert test_file.info["SecondComment"] == "Another Value"
assert test_file.size == (100, 100)
@pytest.mark.parametrize(
"filename",
(

View File

@ -2,7 +2,7 @@ from io import BytesIO
import pytest
from PIL import FitsImagePlugin, FitsStubImagePlugin, Image
from PIL import FitsImagePlugin, Image
from .helper import assert_image_equal, hopper
@ -44,36 +44,7 @@ def test_naxis_zero():
pass
def test_stub_deprecated():
class Handler:
opened = False
loaded = False
def open(self, im):
self.opened = True
def load(self, im):
self.loaded = True
return Image.new("RGB", (1, 1))
handler = Handler()
with pytest.warns(DeprecationWarning):
FitsStubImagePlugin.register_handler(handler)
with Image.open(TEST_FILE) as im:
assert im.format == "FITS"
assert im.size == (128, 128)
assert im.mode == "L"
assert handler.opened
assert not handler.loaded
im.load()
assert handler.loaded
FitsStubImagePlugin._handler = None
Image.register_open(
FitsImagePlugin.FitsImageFile.format,
FitsImagePlugin.FitsImageFile,
FitsImagePlugin._accept,
)
def test_comment():
image_data = b"SIMPLE = T / comment string"
with pytest.raises(OSError):
FitsImagePlugin.FitsImageFile(BytesIO(image_data))

View File

@ -36,7 +36,8 @@ def test_unclosed_file():
im = Image.open(static_test_file)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

View File

@ -18,6 +18,16 @@ def test_sanity():
assert_image_equal_tofile(im, "Tests/images/input_bw_one_band.png")
def test_close():
with Image.open("Tests/images/input_bw_one_band.fpx") as im:
pass
assert im.ole.fp.closed
im = Image.open("Tests/images/input_bw_one_band.fpx")
im.close()
assert im.ole.fp.closed
def test_invalid_file():
# Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg"

View File

@ -21,12 +21,3 @@ def test_invalid_file():
with pytest.raises(SyntaxError):
FtexImagePlugin.FtexImageFile(invalid_file)
def test_constants_deprecation():
for enum, prefix in {
FtexImagePlugin.Format: "FORMAT_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(FtexImagePlugin, prefix + name) == enum[name]

View File

@ -36,7 +36,8 @@ def test_unclosed_file():
im = Image.open(TEST_GIF)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():
@ -1087,7 +1088,8 @@ def test_rgb_transparency(tmp_path):
im = Image.new("RGB", (1, 1))
im.info["transparency"] = b""
ims = [Image.new("RGB", (1, 1))]
pytest.warns(UserWarning, im.save, out, save_all=True, append_images=ims)
with pytest.warns(UserWarning):
im.save(out, save_all=True, append_images=ims)
with Image.open(out) as reloaded:
assert "transparency" not in reloaded.info

View File

@ -56,6 +56,7 @@ def test_handler(tmp_path):
def load(self, im):
self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1))
def save(self, im, fp, filename):

View File

@ -57,6 +57,7 @@ def test_handler(tmp_path):
def load(self, im):
self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1))
def save(self, im, fp, filename):

View File

@ -212,12 +212,10 @@ def test_save_append_images(tmp_path):
def test_unexpected_size():
# This image has been manually hexedited to state that it is 16x32
# while the image within is still 16x16
def open():
with pytest.warns(UserWarning):
with Image.open("Tests/images/hopper_unexpected.ico") as im:
assert im.size == (16, 16)
pytest.warns(UserWarning, open)
def test_draw_reloaded(tmp_path):
with Image.open(TEST_ICO_FILE) as im:

View File

@ -32,7 +32,8 @@ def test_unclosed_file():
im = Image.open(TEST_IM)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

View File

@ -270,7 +270,10 @@ class TestFileJpeg:
# https://github.com/python-pillow/Pillow/issues/148
f = str(tmp_path / "temp.jpg")
im = hopper()
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
im.save(f, "JPEG", quality=90, exif=b"1" * 65533)
with pytest.raises(ValueError):
im.save(f, "JPEG", quality=90, exif=b"1" * 65534)
def test_exif_typeerror(self):
with Image.open("Tests/images/exif_typeerror.jpg") as im:
@ -445,7 +448,7 @@ class TestFileJpeg:
ims = im.get_child_images()
assert len(ims) == 1
assert_image_equal_tofile(ims[0], "Tests/images/flower_thumbnail.png")
assert_image_similar_tofile(ims[0], "Tests/images/flower_thumbnail.png", 2.1)
def test_mp(self):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
@ -633,12 +636,6 @@ class TestFileJpeg:
assert max(im2.quantization[0]) <= 255
assert max(im2.quantization[1]) <= 255
def test_convert_dict_qtables_deprecation(self):
with pytest.warns(DeprecationWarning):
qtable = {0: [1, 2, 3, 4]}
qtable2 = JpegImagePlugin.convert_dict_qtables(qtable)
assert qtable == qtable2
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self):
with Image.open(TEST_FILE) as img:

View File

@ -4,13 +4,21 @@ from io import BytesIO
import pytest
from PIL import Image, ImageFile, Jpeg2KImagePlugin, UnidentifiedImageError, features
from PIL import (
Image,
ImageFile,
Jpeg2KImagePlugin,
UnidentifiedImageError,
_binary,
features,
)
from .helper import (
assert_image_equal,
assert_image_similar,
assert_image_similar_tofile,
skip_unless_feature,
skip_unless_feature_version,
)
EXTRA_DIR = "Tests/images/jpeg2000"
@ -353,6 +361,35 @@ def test_subsampling_decode(name):
assert_image_similar(im, expected, epsilon)
def test_comment():
with Image.open("Tests/images/comment.jp2") as im:
assert im.info["comment"] == b"Created by OpenJPEG version 2.5.0"
# Test an image that is truncated partway through a codestream
with open("Tests/images/comment.jp2", "rb") as fp:
b = BytesIO(fp.read(130))
with Image.open(b) as im:
pass
def test_save_comment():
for comment in ("Created by Pillow", b"Created by Pillow"):
out = BytesIO()
test_card.save(out, "JPEG2000", comment=comment)
with Image.open(out) as im:
assert im.info["comment"] == b"Created by Pillow"
out = BytesIO()
long_comment = b" " * 65531
test_card.save(out, "JPEG2000", comment=long_comment)
with Image.open(out) as im:
assert im.info["comment"] == long_comment
with pytest.raises(ValueError):
test_card.save(out, "JPEG2000", comment=long_comment + b" ")
@pytest.mark.parametrize(
"test_file",
[
@ -370,3 +407,29 @@ def test_crashes(test_file):
im.load()
except OSError:
pass
@skip_unless_feature_version("jpg_2000", "2.4.0")
def test_plt_marker():
# Search the start of the codesteam for PLT
out = BytesIO()
test_card.save(out, "JPEG2000", no_jp2=True, plt=True)
out.seek(0)
while True:
marker = out.read(2)
if not marker:
assert False, "End of stream without PLT"
jp2_boxid = _binary.i16be(marker)
if jp2_boxid == 0xFF4F:
# SOC has no length
continue
elif jp2_boxid == 0xFF58:
# PLT
return
elif jp2_boxid == 0xFF93:
assert False, "SOD without finding PLT first"
hdr = out.read(2)
length = _binary.i16be(hdr)
out.seek(length - 2, os.SEEK_CUR)

View File

@ -668,6 +668,16 @@ class TestFileLibTiff(LibTiffTestCase):
assert reloaded.tag_v2[530] == (1, 1)
assert reloaded.tag_v2[532] == (0, 255, 128, 255, 128, 255)
def test_exif_ifd(self, tmp_path):
outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/tiff_adobe_deflate.tif") as im:
assert im.tag_v2[34665] == 125456
im.save(outfile)
with Image.open(outfile) as reloaded:
if Image.core.libtiff_support_custom_tags:
assert reloaded.tag_v2[34665] == 125456
def test_crashing_metadata(self, tmp_path):
# issue 1597
with Image.open("Tests/images/rdf.tif") as im:
@ -984,6 +994,36 @@ class TestFileLibTiff(LibTiffTestCase):
) as im:
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
@pytest.mark.parametrize(
"file_name, mode, size, tile",
[
(
"tiff_wrong_bits_per_sample.tiff",
"RGBA",
(52, 53),
[("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_2.tiff",
"RGB",
(16, 16),
[("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_3.tiff",
"RGBA",
(512, 256),
[("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))],
),
],
)
def test_wrong_bits_per_sample(self, file_name, mode, size, tile):
with Image.open("Tests/images/" + file_name) as im:
assert im.mode == mode
assert im.size == size
assert im.tile == tile
im.load()
def test_no_rows_per_strip(self):
# This image does not have a RowsPerStrip TIFF tag
infile = "Tests/images/no_rows_per_strip.tif"
@ -1065,3 +1105,27 @@ class TestFileLibTiff(LibTiffTestCase):
out = str(tmp_path / "temp.tif")
with pytest.raises(SystemError):
im.save(out, compression=compression)
def test_save_many_compressed(self, tmp_path):
im = hopper()
out = str(tmp_path / "temp.tif")
for _ in range(10000):
im.save(out, compression="jpeg")
@pytest.mark.parametrize(
"path, sizes",
(
("Tests/images/hopper.tif", ()),
("Tests/images/child_ifd.tiff", (16, 8)),
("Tests/images/child_ifd_jpeg.tiff", (20,)),
),
)
def test_get_child_images(self, path, sizes):
with Image.open(path) as im:
ims = im.get_child_images()
assert len(ims) == len(sizes)
for i, im in enumerate(ims):
w = sizes[i]
expected = Image.new("RGB", (w, w), "#f00")
assert_image_similar(im, expected, 1)

View File

@ -51,6 +51,16 @@ def test_seek():
assert im.tell() == 0
def test_close():
with Image.open(TEST_FILE) as im:
pass
assert im.ole.fp.closed
im = Image.open(TEST_FILE)
im.close()
assert im.ole.fp.closed
def test_invalid_file():
# Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg"

View File

@ -42,7 +42,8 @@ def test_unclosed_file():
im = Image.open(test_files[0])
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

View File

@ -8,7 +8,7 @@ import pytest
from PIL import Image, PdfParser, features
from .helper import hopper, mark_if_feature_version
from .helper import hopper, mark_if_feature_version, skip_unless_feature
def helper_save_as_pdf(tmp_path, mode, **kwargs):
@ -42,6 +42,11 @@ def test_save(tmp_path, mode):
helper_save_as_pdf(tmp_path, mode)
@skip_unless_feature("jpg_2000")
def test_save_rgba(tmp_path):
helper_save_as_pdf(tmp_path, "RGBA")
def test_monochrome(tmp_path):
# Arrange
mode = "1"
@ -80,6 +85,34 @@ def test_resolution(tmp_path):
assert size == (61.44, 61.44)
@pytest.mark.parametrize(
"params",
(
{"dpi": (75, 150)},
{"dpi": (75, 150), "resolution": 200},
),
)
def test_dpi(params, tmp_path):
im = hopper()
outfile = str(tmp_path / "temp.pdf")
im.save(outfile, **params)
with open(outfile, "rb") as fp:
contents = fp.read()
size = tuple(
float(d)
for d in contents.split(b"stream\nq ")[1].split(b" 0 0 cm")[0].split(b" 0 0 ")
)
assert size == (122.88, 61.44)
size = tuple(
float(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
)
assert size == (122.88, 61.44)
@mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
)

View File

@ -256,6 +256,16 @@ def test_truncated_file(tmp_path):
im.load()
def test_not_enough_image_data(tmp_path):
path = str(tmp_path / "temp.ppm")
with open(path, "wb") as f:
f.write(b"P2 1 2 255 255")
with Image.open(path) as im:
with pytest.raises(ValueError):
im.load()
@pytest.mark.parametrize("maxval", (b"0", b"65536"))
def test_invalid_maxval(maxval, tmp_path):
path = str(tmp_path / "temp.ppm")

View File

@ -27,7 +27,8 @@ def test_unclosed_file():
im = Image.open(test_file)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

28
Tests/test_file_qoi.py Normal file
View File

@ -0,0 +1,28 @@
import pytest
from PIL import Image, QoiImagePlugin
from .helper import assert_image_equal_tofile, assert_image_similar_tofile
def test_sanity():
with Image.open("Tests/images/hopper.qoi") as im:
assert im.mode == "RGB"
assert im.size == (128, 128)
assert im.format == "QOI"
assert_image_equal_tofile(im, "Tests/images/hopper.png")
with Image.open("Tests/images/pil123rgba.qoi") as im:
assert im.mode == "RGBA"
assert im.size == (162, 150)
assert im.format == "QOI"
assert_image_similar_tofile(im, "Tests/images/pil123rgba.png", 0.03)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
QoiImagePlugin.QoiImageFile(invalid_file)

View File

@ -25,7 +25,8 @@ def test_unclosed_file():
im = Image.open(TEST_FILE)
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file():

View File

@ -29,11 +29,9 @@ def test_sanity(codec, test_path, format):
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
def test_unclosed_file():
def open():
with pytest.warns(ResourceWarning):
TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
pytest.warns(ResourceWarning, open)
def test_close():
with warnings.catch_warnings():

View File

@ -163,7 +163,9 @@ def test_save_id_section(tmp_path):
# Save with custom id section greater than 255 characters
id_section = b"Test content" * 25
pytest.warns(UserWarning, lambda: im.save(out, id_section=id_section))
with pytest.warns(UserWarning):
im.save(out, id_section=id_section)
with Image.open(out) as test_im:
assert test_im.info["id_section"] == id_section[:255]

View File

@ -61,7 +61,8 @@ class TestFileTiff:
im = Image.open("Tests/images/multipage.tiff")
im.load()
pytest.warns(ResourceWarning, open)
with pytest.warns(ResourceWarning):
open()
def test_closed_file(self):
with warnings.catch_warnings():
@ -83,24 +84,6 @@ class TestFileTiff:
with Image.open("Tests/images/multipage.tiff") as im:
im.load()
@pytest.mark.parametrize(
"path, sizes",
(
("Tests/images/hopper.tif", ()),
("Tests/images/child_ifd.tiff", (16, 8)),
("Tests/images/child_ifd_jpeg.tiff", (20,)),
),
)
def test_get_child_images(self, path, sizes):
with Image.open(path) as im:
ims = im.get_child_images()
assert len(ims) == len(sizes)
for i, im in enumerate(ims):
w = sizes[i]
expected = Image.new("RGB", (w, w), "#f00")
assert_image_similar(im, expected, 1)
def test_mac_tiff(self):
# Read RGBa images from macOS [@PIL136]
@ -117,36 +100,6 @@ class TestFileTiff:
with Image.open("Tests/images/hopper_bigtiff.tif") as im:
assert_image_equal_tofile(im, "Tests/images/hopper.tif")
@pytest.mark.parametrize(
"file_name,mode,size,tile",
[
(
"tiff_wrong_bits_per_sample.tiff",
"RGBA",
(52, 53),
[("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_2.tiff",
"RGB",
(16, 16),
[("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_3.tiff",
"RGBA",
(512, 256),
[("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))],
),
],
)
def test_wrong_bits_per_sample(self, file_name, mode, size, tile):
with Image.open("Tests/images/" + file_name) as im:
assert im.mode == mode
assert im.size == size
assert im.tile == tile
im.load()
def test_set_legacy_api(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2()
with pytest.raises(Exception) as e:
@ -231,7 +184,8 @@ class TestFileTiff:
def test_bad_exif(self):
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
# Should not raise struct.error.
pytest.warns(UserWarning, i._getexif)
with pytest.warns(UserWarning):
i._getexif()
def test_save_rgba(self, tmp_path):
im = hopper("RGBA")

View File

@ -216,6 +216,22 @@ def test_writing_other_types_to_bytes(value, tmp_path):
assert reloaded.tag_v2[700] == b"\x01"
def test_writing_other_types_to_undefined(tmp_path):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
tag = TiffTags.TAGS_V2[33723]
assert tag.type == TiffTags.UNDEFINED
info[33723] = 1
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info)
with Image.open(out) as reloaded:
assert reloaded.tag_v2[33723] == b"1"
def test_undefined_zero(tmp_path):
# Check that the tag has not been changed since this test was created
tag = TiffTags.TAGS_V2[45059]
@ -236,7 +252,8 @@ def test_empty_metadata():
head = f.read(8)
info = TiffImagePlugin.ImageFileDirectory(head)
# Should not raise struct.error.
pytest.warns(UserWarning, info.load, f)
with pytest.warns(UserWarning):
info.load(f)
def test_iccprofile(tmp_path):
@ -402,11 +419,12 @@ def test_too_many_entries():
ifd = TiffImagePlugin.ImageFileDirectory_v2()
# 277: ("SamplesPerPixel", SHORT, 1),
ifd._tagdata[277] = struct.pack("hh", 4, 4)
ifd._tagdata[277] = struct.pack("<hh", 4, 4)
ifd.tagtype[277] = TiffTags.SHORT
# Should not raise ValueError.
pytest.warns(UserWarning, lambda: ifd[277])
with pytest.warns(UserWarning):
assert ifd[277] == 4
def test_tag_group_data():

View File

@ -29,7 +29,10 @@ class TestUnsupportedWebp:
WebPImagePlugin.SUPPORTED = False
file_path = "Tests/images/hopper.webp"
pytest.warns(UserWarning, lambda: pytest.raises(OSError, Image.open, file_path))
with pytest.warns(UserWarning):
with pytest.raises(OSError):
with Image.open(file_path):
pass
if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = True

View File

@ -134,6 +134,18 @@ def test_timestamp_and_duration(tmp_path):
ts += durations[frame]
def test_float_duration(tmp_path):
temp_file = str(tmp_path / "temp.webp")
with Image.open("Tests/images/iss634.apng") as im:
assert im.info["duration"] == 70.0
im.save(temp_file, save_all=True)
with Image.open(temp_file) as reloaded:
reloaded.load()
assert reloaded.info["duration"] == 70
def test_seeking(tmp_path):
"""
Create an animated WebP file, and then try seeking through frames in reverse-order,

View File

@ -82,9 +82,6 @@ def test_textsize(request, tmp_path):
assert dy == 20
assert dx in (0, 10)
assert font.getlength(chr(i)) == dx
with pytest.warns(DeprecationWarning) as log:
assert font.getsize(chr(i)) == (dx, dy)
assert len(log) == 1
for i in range(len(message)):
msg = message[: i + 1]
assert font.getlength(msg) == len(msg) * 10

View File

@ -48,6 +48,9 @@ class TestImage:
"RGBX",
"RGBA",
"RGBa",
"BGR;15",
"BGR;16",
"BGR;24",
"CMYK",
"YCbCr",
"LAB",
@ -57,9 +60,7 @@ class TestImage:
def test_image_modes_success(self, mode):
Image.new(mode, (1, 1))
@pytest.mark.parametrize(
"mode", ("", "bad", "very very long", "BGR;15", "BGR;16", "BGR;24", "BGR;32")
)
@pytest.mark.parametrize("mode", ("", "bad", "very very long"))
def test_image_modes_fail(self, mode):
with pytest.raises(ValueError) as e:
Image.new(mode, (1, 1))
@ -928,25 +929,7 @@ class TestImage:
im.apply_transparency()
assert im.palette.colors[(27, 35, 6, 214)] == 24
def test_categories_deprecation(self):
with pytest.warns(DeprecationWarning):
assert hopper().category == 0
with pytest.warns(DeprecationWarning):
assert Image.NORMAL == 0
with pytest.warns(DeprecationWarning):
assert Image.SEQUENCE == 1
with pytest.warns(DeprecationWarning):
assert Image.CONTAINER == 2
def test_constants(self):
with pytest.warns(DeprecationWarning):
assert Image.LINEAR == Image.Resampling.BILINEAR
with pytest.warns(DeprecationWarning):
assert Image.CUBIC == Image.Resampling.BICUBIC
with pytest.warns(DeprecationWarning):
assert Image.ANTIALIAS == Image.Resampling.LANCZOS
for enum in (
Image.Transpose,
Image.Transform,

View File

@ -132,22 +132,26 @@ class TestImageGetPixel(AccessTest):
return 1
return tuple(range(1, bands + 1))
def check(self, mode, c=None):
if not c:
c = self.color(mode)
def check(self, mode, expected_color=None):
if not expected_color:
expected_color = self.color(mode)
# check putpixel
im = Image.new(mode, (1, 1), None)
im.putpixel((0, 0), c)
assert (
im.getpixel((0, 0)) == c
), f"put/getpixel roundtrip failed for mode {mode}, color {c}"
im.putpixel((0, 0), expected_color)
actual_color = im.getpixel((0, 0))
assert actual_color == expected_color, (
f"put/getpixel roundtrip failed for mode {mode}, "
f"expected {expected_color} got {actual_color}"
)
# check putpixel negative index
im.putpixel((-1, -1), c)
assert (
im.getpixel((-1, -1)) == c
), f"put/getpixel roundtrip negative index failed for mode {mode}, color {c}"
im.putpixel((-1, -1), expected_color)
actual_color = im.getpixel((-1, -1))
assert actual_color == expected_color, (
f"put/getpixel roundtrip negative index failed for mode {mode}, "
f"expected {expected_color} got {actual_color}"
)
# Check 0
im = Image.new(mode, (0, 0), None)
@ -155,27 +159,32 @@ class TestImageGetPixel(AccessTest):
error = ValueError if self._need_cffi_access else IndexError
with pytest.raises(error):
im.putpixel((0, 0), c)
im.putpixel((0, 0), expected_color)
with pytest.raises(error):
im.getpixel((0, 0))
# Check 0 negative index
with pytest.raises(error):
im.putpixel((-1, -1), c)
im.putpixel((-1, -1), expected_color)
with pytest.raises(error):
im.getpixel((-1, -1))
# check initial color
im = Image.new(mode, (1, 1), c)
assert (
im.getpixel((0, 0)) == c
), f"initial color failed for mode {mode}, color {c} "
im = Image.new(mode, (1, 1), expected_color)
actual_color = im.getpixel((0, 0))
assert actual_color == expected_color, (
f"initial color failed for mode {mode}, "
f"expected {expected_color} got {actual_color}"
)
# check initial color negative index
assert (
im.getpixel((-1, -1)) == c
), f"initial color failed with negative index for mode {mode}, color {c} "
actual_color = im.getpixel((-1, -1))
assert actual_color == expected_color, (
f"initial color failed with negative index for mode {mode}, "
f"expected {expected_color} got {actual_color}"
)
# Check 0
im = Image.new(mode, (0, 0), c)
im = Image.new(mode, (0, 0), expected_color)
with pytest.raises(error):
im.getpixel((0, 0))
# Check 0 negative index
@ -205,13 +214,13 @@ class TestImageGetPixel(AccessTest):
self.check(mode)
@pytest.mark.parametrize("mode", ("I;16", "I;16B"))
def test_signedness(self, mode):
@pytest.mark.parametrize(
"expected_color", (2**15 - 1, 2**15, 2**15 + 1, 2**16 - 1)
)
def test_signedness(self, mode, expected_color):
# see https://github.com/python-pillow/Pillow/issues/452
# pixelaccess is using signed int* instead of uint*
self.check(mode, 2**15 - 1)
self.check(mode, 2**15)
self.check(mode, 2**15 + 1)
self.check(mode, 2**16 - 1)
self.check(mode, expected_color)
@pytest.mark.parametrize("mode", ("P", "PA"))
@pytest.mark.parametrize("color", ((255, 0, 0), (255, 0, 0, 255)))
@ -266,15 +275,10 @@ class TestCffi(AccessTest):
# self._test_get_access(hopper('PA')) # PA -- how do I make a PA image?
self._test_get_access(hopper("F"))
im = Image.new("I;16", (10, 10), 40000)
self._test_get_access(im)
im = Image.new("I;16L", (10, 10), 40000)
self._test_get_access(im)
im = Image.new("I;16B", (10, 10), 40000)
self._test_get_access(im)
for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
im = Image.new(mode, (10, 10), 40000)
self._test_get_access(im)
im = Image.new("I", (10, 10), 40000)
self._test_get_access(im)
# These don't actually appear to be modes that I can actually make,
# as unpack sets them directly into the I mode.
# im = Image.new('I;32L', (10, 10), -2**10)
@ -313,15 +317,10 @@ class TestCffi(AccessTest):
# self._test_set_access(i, (128, 128)) #PA -- undone how to make
self._test_set_access(hopper("F"), 1024.0)
im = Image.new("I;16", (10, 10), 40000)
self._test_set_access(im, 45000)
im = Image.new("I;16L", (10, 10), 40000)
self._test_set_access(im, 45000)
im = Image.new("I;16B", (10, 10), 40000)
self._test_set_access(im, 45000)
for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
im = Image.new(mode, (10, 10), 40000)
self._test_set_access(im, 45000)
im = Image.new("I", (10, 10), 40000)
self._test_set_access(im, 45000)
# im = Image.new('I;32L', (10, 10), -(2**10))
# self._test_set_access(im, -(2**13)+1)
# im = Image.new('I;32B', (10, 10), 2**10)
@ -354,8 +353,8 @@ class TestCffi(AccessTest):
class TestImagePutPixelError(AccessTest):
IMAGE_MODES1 = ["L", "LA", "RGB", "RGBA"]
IMAGE_MODES2 = ["I", "I;16", "BGR;15"]
IMAGE_MODES1 = ["LA", "RGB", "RGBA", "BGR;15"]
IMAGE_MODES2 = ["L", "I", "I;16"]
INVALID_TYPES = ["foo", 1.0, None]
@pytest.mark.parametrize("mode", IMAGE_MODES1)
@ -370,6 +369,11 @@ class TestImagePutPixelError(AccessTest):
(
("L", (0, 2), "color must be int or single-element tuple"),
("LA", (0, 3), "color must be int, or tuple of one or two elements"),
(
"BGR;15",
(0, 2),
"color must be int, or tuple of one or three elements",
),
(
"RGB",
(0, 2, 5),
@ -398,11 +402,6 @@ class TestImagePutPixelError(AccessTest):
with pytest.raises(OverflowError):
im.putpixel((0, 0), 2**80)
def test_putpixel_unrecognized_mode(self):
im = hopper("BGR;15")
with pytest.raises(ValueError, match="unrecognized image mode"):
im.putpixel((0, 0), 0)
class TestEmbeddable:
@pytest.mark.xfail(reason="failing test")

View File

@ -254,17 +254,6 @@ def test_p2pa_palette():
assert im_pa.getpalette() == im.getpalette()
@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode):
im = Image.new(mode, (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (0, 128, 128)
im = Image.new("LAB", (1, 1), (255, 0, 0))
converted_im = im.convert(mode)
assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255)
def test_matrix_illegal_conversion():
# Arrange
im = hopper("CMYK")

View File

@ -1,10 +1,17 @@
import pytest
from PIL import Image
from .helper import assert_image_equal, hopper
def test_sanity():
@pytest.mark.parametrize("data_type", ("bytes", "memoryview"))
def test_sanity(data_type):
im1 = hopper()
im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes())
data = im1.tobytes()
if data_type == "memoryview":
data = memoryview(data)
im2 = Image.frombytes(im1.mode, im1.size, data)
assert_image_equal(im1, im2)

View File

@ -1,7 +1,5 @@
import pytest
from PIL import Image
from .helper import assert_image_equal, hopper
@ -62,8 +60,3 @@ def test_f_mode():
im = hopper("F")
with pytest.raises(ValueError):
im.point(None)
def test_coerce_e_deprecation():
with pytest.warns(DeprecationWarning):
assert Image.coerce_e(2).data == 2

View File

@ -617,11 +617,12 @@ def test_auxiliary_channels_isolated():
assert_image_equal(test_image.convert(dst_format[2]), reference_image)
def test_constants_deprecation():
for enum, prefix in {
ImageCms.Intent: "INTENT_",
ImageCms.Direction: "DIRECTION_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(ImageCms, prefix + name) == enum[name]
@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode):
im = Image.new(mode, (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (0, 128, 128)
im = Image.new("LAB", (1, 1), (255, 0, 0))
converted_im = im.convert(mode)
assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255)

View File

@ -374,7 +374,13 @@ def ellipse_various_sizes_helper(filled):
for w in ellipse_sizes:
y = 1
for h in ellipse_sizes:
border = [x, y, x + w - 1, y + h - 1]
x1 = x + w
if w:
x1 -= 1
y1 = y + h
if h:
y1 -= 1
border = [x, y, x1, y1]
if filled:
draw.ellipse(border, fill="white")
else:
@ -763,6 +769,36 @@ def test_rounded_rectangle(xy):
assert_image_equal_tofile(im, "Tests/images/imagedraw_rounded_rectangle.png")
@pytest.mark.parametrize("top_left", (True, False))
@pytest.mark.parametrize("top_right", (True, False))
@pytest.mark.parametrize("bottom_right", (True, False))
@pytest.mark.parametrize("bottom_left", (True, False))
def test_rounded_rectangle_corners(top_left, top_right, bottom_right, bottom_left):
corners = (top_left, top_right, bottom_right, bottom_left)
# Arrange
im = Image.new("RGB", (200, 200))
draw = ImageDraw.Draw(im)
# Act
draw.rounded_rectangle(
(10, 20, 190, 180), 30, fill="red", outline="green", width=5, corners=corners
)
# Assert
suffix = "".join(
(
("y" if top_left else "n"),
("y" if top_right else "n"),
("y" if bottom_right else "n"),
("y" if bottom_left else "n"),
)
)
assert_image_equal_tofile(
im, "Tests/images/imagedraw_rounded_rectangle_corners_" + suffix + ".png"
)
@pytest.mark.parametrize(
"xy, radius, type",
[
@ -934,9 +970,6 @@ def test_square():
img, draw = create_base_image_draw((10, 10))
draw.rectangle((2, 2, 7, 7), BLACK)
assert_image_equal_tofile(img, expected, "square as normal rectangle failed")
img, draw = create_base_image_draw((10, 10))
draw.rectangle((7, 7, 2, 2), BLACK)
assert_image_equal_tofile(img, expected, "square as inverted rectangle failed")
def test_triangle_right():
@ -1223,21 +1256,6 @@ def test_textbbox_stroke():
assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 50)
def test_textsize_deprecation():
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello\nWorld")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.multiline_textsize("Hello\nWorld")
assert len(log) == 1
@skip_unless_feature("freetype2")
def test_stroke():
for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items():
@ -1502,3 +1520,21 @@ def test_polygon2():
draw.polygon([(18, 30), (19, 31), (18, 30), (85, 30), (60, 72)], "red")
expected = "Tests/images/imagedraw_outline_polygon_RGB.png"
assert_image_similar_tofile(im, expected, 1)
@pytest.mark.parametrize("xy", ((1, 1, 0, 1), (1, 1, 1, 0)))
def test_incorrectly_ordered_coordinates(xy):
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
with pytest.raises(ValueError):
draw.arc(xy, 10, 260)
with pytest.raises(ValueError):
draw.chord(xy, 10, 260)
with pytest.raises(ValueError):
draw.ellipse(xy)
with pytest.raises(ValueError):
draw.pieslice(xy, 10, 260)
with pytest.raises(ValueError):
draw.rectangle(xy)
with pytest.raises(ValueError):
draw.rounded_rectangle(xy)

View File

@ -2,7 +2,7 @@ import os.path
import pytest
from PIL import Image, ImageDraw, ImageDraw2
from PIL import Image, ImageDraw, ImageDraw2, features
from .helper import (
assert_image_equal,
@ -173,19 +173,18 @@ def test_text():
@skip_unless_feature("freetype2")
def test_textsize():
def test_textbbox():
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw2.Draw(im)
font = ImageDraw2.Font("white", FONT_PATH)
# Act
with pytest.warns(DeprecationWarning) as log:
size = draw.textsize("ImageDraw2", font)
assert len(log) == 1
bbox = draw.textbbox((0, 0), "ImageDraw2", font)
# Assert
assert size[1] == 12
right = 72 if features.check_feature("raqm") else 70
assert bbox == (0, 2, right, 12)
@skip_unless_feature("freetype2")

View File

@ -251,27 +251,6 @@ def test_draw_align(font):
draw.text((100, 40), line, (0, 0, 0), font=font, align="left")
def test_multiline_size(font):
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
with pytest.warns(DeprecationWarning) as log:
# Test that textsize() correctly connects to multiline_textsize()
assert draw.textsize(TEST_TEXT, font=font) == draw.multiline_textsize(
TEST_TEXT, font=font
)
# Test that multiline_textsize corresponds to ImageFont.textsize()
# for single line text
assert font.getsize("A") == draw.multiline_textsize("A", font=font)
# Test that textsize() can pass on additional arguments
# to multiline_textsize()
draw.textsize(TEST_TEXT, font=font, spacing=4)
draw.textsize(TEST_TEXT, font, 4)
assert len(log) == 6
def test_multiline_bbox(font):
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
@ -298,12 +277,6 @@ def test_multiline_width(font):
draw.textbbox((0, 0), "longest line", font=font)[2]
== draw.multiline_textbbox((0, 0), "longest line\nline", font=font)[2]
)
with pytest.warns(DeprecationWarning) as log:
assert (
draw.textsize("longest line", font=font)[0]
== draw.multiline_textsize("longest line\nline", font=font)[0]
)
assert len(log) == 2
def test_multiline_spacing(font):
@ -326,32 +299,27 @@ def test_rotated_transposed_font(font, orientation):
# Original font
draw.font = font
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert box_size_a == font.getsize(word)
assert len(log) == 2
bbox_a = draw.textbbox((10, 10), word)
# Rotated font
draw.font = transposed_font
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert box_size_b == transposed_font.getsize(word)
assert len(log) == 2
bbox_b = draw.textbbox((20, 20), word)
# Check (w,h) of box a is (h,w) of box b
assert box_size_a[0] == box_size_b[1]
assert box_size_a[1] == box_size_b[0]
# Check (w, h) of box a is (h, w) of box b
assert (
bbox_a[2] - bbox_a[0],
bbox_a[3] - bbox_a[1],
) == (
bbox_b[3] - bbox_b[1],
bbox_b[2] - bbox_b[0],
)
# Check bbox b is (20, 20, 20 + h, 20 + w)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[3] - bbox_a[1]
assert bbox_b[3] == 20 + bbox_a[2] - bbox_a[0]
# Check top left co-ordinates are correct
assert bbox_b[:2] == (20, 20)
# text length is undefined for vertical text
pytest.raises(ValueError, draw.textlength, word)
with pytest.raises(ValueError):
draw.textlength(word)
@pytest.mark.parametrize(
@ -372,28 +340,25 @@ def test_unrotated_transposed_font(font, orientation):
# Original font
draw.font = font
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert len(log) == 1
bbox_a = draw.textbbox((10, 10), word)
length_a = draw.textlength(word)
# Rotated font
draw.font = transposed_font
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert len(log) == 1
bbox_b = draw.textbbox((20, 20), word)
length_b = draw.textlength(word)
# Check boxes a and b are same size
assert box_size_a == box_size_b
assert (
bbox_a[2] - bbox_a[0],
bbox_a[3] - bbox_a[1],
) == (
bbox_b[2] - bbox_b[0],
bbox_b[3] - bbox_b[1],
)
# Check bbox b is (20, 20, 20 + w, 20 + h)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[2] - bbox_a[0]
assert bbox_b[3] == 20 + bbox_a[3] - bbox_a[1]
# Check top left co-ordinates are correct
assert bbox_b[:2] == (20, 20)
assert length_a == length_b
@ -446,19 +411,6 @@ def test_free_type_font_get_metrics(font):
assert (ascent, descent) == (16, 4)
def test_free_type_font_get_offset(font):
# Arrange
text = "offset this"
# Act
with pytest.warns(DeprecationWarning) as log:
offset = font.getoffset(text)
# Assert
assert len(log) == 1
assert offset == (0, 3)
def test_free_type_font_get_mask(font):
# Arrange
text = "mask this"
@ -617,19 +569,6 @@ def test_imagefont_getters(font):
assert font.getlength("M") == 12
assert font.getlength("y") == 12
assert font.getlength("a") == 12
with pytest.warns(DeprecationWarning) as log:
assert font.getsize("A") == (12, 16)
assert font.getsize("AB") == (24, 16)
assert font.getsize("M") == (12, 16)
assert font.getsize("y") == (12, 20)
assert font.getsize("a") == (12, 16)
assert font.getsize_multiline("A") == (12, 16)
assert font.getsize_multiline("AB") == (24, 16)
assert font.getsize_multiline("a") == (12, 16)
assert font.getsize_multiline("ABC\n") == (36, 36)
assert font.getsize_multiline("ABC\nA") == (36, 36)
assert font.getsize_multiline("ABC\nAaaa") == (48, 36)
assert len(log) == 11
@pytest.mark.parametrize("stroke_width", (0, 2))
@ -640,16 +579,6 @@ def test_getsize_stroke(font, stroke_width):
12 + stroke_width,
16 + stroke_width,
)
with pytest.warns(DeprecationWarning) as log:
assert font.getsize("A", stroke_width=stroke_width) == (
12 + stroke_width * 2,
16 + stroke_width * 2,
)
assert font.getsize_multiline("ABC\nAaaa", stroke_width=stroke_width) == (
48 + stroke_width * 2,
36 + stroke_width * 4,
)
assert len(log) == 2
def test_complex_font_settings():
@ -780,11 +709,8 @@ def test_textbbox_non_freetypefont():
im = Image.new("RGB", (200, 200))
d = ImageDraw.Draw(im)
default_font = ImageFont.load_default()
with pytest.warns(DeprecationWarning) as log:
width, height = d.textsize("test", font=default_font)
assert len(log) == 1
assert d.textlength("test", font=default_font) == width
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, width, height)
assert d.textlength("test", font=default_font) == 24
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)
@pytest.mark.parametrize(
@ -872,25 +798,23 @@ def test_anchor_invalid(font):
d.font = font
for anchor in ["", "l", "a", "lax", "sa", "xa", "lx"]:
pytest.raises(ValueError, lambda: font.getmask2("hello", anchor=anchor))
pytest.raises(ValueError, lambda: font.getbbox("hello", anchor=anchor))
pytest.raises(ValueError, lambda: d.text((0, 0), "hello", anchor=anchor))
pytest.raises(ValueError, lambda: d.textbbox((0, 0), "hello", anchor=anchor))
pytest.raises(
ValueError, lambda: d.multiline_text((0, 0), "foo\nbar", anchor=anchor)
)
pytest.raises(
ValueError,
lambda: d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor),
)
with pytest.raises(ValueError):
font.getmask2("hello", anchor=anchor)
with pytest.raises(ValueError):
font.getbbox("hello", anchor=anchor)
with pytest.raises(ValueError):
d.text((0, 0), "hello", anchor=anchor)
with pytest.raises(ValueError):
d.textbbox((0, 0), "hello", anchor=anchor)
with pytest.raises(ValueError):
d.multiline_text((0, 0), "foo\nbar", anchor=anchor)
with pytest.raises(ValueError):
d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor)
for anchor in ["lt", "lb"]:
pytest.raises(
ValueError, lambda: d.multiline_text((0, 0), "foo\nbar", anchor=anchor)
)
pytest.raises(
ValueError,
lambda: d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor),
)
with pytest.raises(ValueError):
d.multiline_text((0, 0), "foo\nbar", anchor=anchor)
with pytest.raises(ValueError):
d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor)
@pytest.mark.parametrize("bpp", (1, 2, 4, 8))
@ -1084,14 +1008,6 @@ def test_woff2(layout_engine):
assert_image_similar_tofile(im, "Tests/images/test_woff2.png", 5)
def test_fill_deprecation(font):
with pytest.warns(DeprecationWarning):
font.getmask2("Hello world", fill=Image.core.fill)
with pytest.warns(DeprecationWarning):
with pytest.raises(TypeError):
font.getmask2("Hello world", fill=None)
def test_render_mono_size():
# issue 4177
@ -1131,12 +1047,3 @@ def test_raqm_missing_warning(monkeypatch):
"Raqm layout was requested, but Raqm is not available. "
"Falling back to basic layout."
)
def test_constants_deprecation():
for enum, prefix in {
ImageFont.Layout: "LAYOUT_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(ImageFont, prefix + name) == enum[name]

View File

@ -360,37 +360,20 @@ def test_anchor_invalid_ttb():
d.font = font
for anchor in ["", "l", "a", "lax", "xa", "la", "ls", "ld", "lx"]:
pytest.raises(
ValueError, lambda: font.getmask2("hello", anchor=anchor, direction="ttb")
)
pytest.raises(
ValueError, lambda: font.getbbox("hello", anchor=anchor, direction="ttb")
)
pytest.raises(
ValueError, lambda: d.text((0, 0), "hello", anchor=anchor, direction="ttb")
)
pytest.raises(
ValueError,
lambda: d.textbbox((0, 0), "hello", anchor=anchor, direction="ttb"),
)
pytest.raises(
ValueError,
lambda: d.multiline_text(
(0, 0), "foo\nbar", anchor=anchor, direction="ttb"
),
)
pytest.raises(
ValueError,
lambda: d.multiline_textbbox(
(0, 0), "foo\nbar", anchor=anchor, direction="ttb"
),
)
with pytest.raises(ValueError):
font.getmask2("hello", anchor=anchor, direction="ttb")
with pytest.raises(ValueError):
font.getbbox("hello", anchor=anchor, direction="ttb")
with pytest.raises(ValueError):
d.text((0, 0), "hello", anchor=anchor, direction="ttb")
with pytest.raises(ValueError):
d.textbbox((0, 0), "hello", anchor=anchor, direction="ttb")
with pytest.raises(ValueError):
d.multiline_text((0, 0), "foo\nbar", anchor=anchor, direction="ttb")
with pytest.raises(ValueError):
d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor, direction="ttb")
# ttb multiline text does not support anchors at all
pytest.raises(
ValueError,
lambda: d.multiline_text((0, 0), "foo\nbar", anchor="mm", direction="ttb"),
)
pytest.raises(
ValueError,
lambda: d.multiline_textbbox((0, 0), "foo\nbar", anchor="mm", direction="ttb"),
)
with pytest.raises(ValueError):
d.multiline_text((0, 0), "foo\nbar", anchor="mm", direction="ttb")
with pytest.raises(ValueError):
d.multiline_textbbox((0, 0), "foo\nbar", anchor="mm", direction="ttb")

View File

@ -9,10 +9,6 @@ def test_sanity():
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
assert len(palette.colors) == 256
with pytest.warns(DeprecationWarning):
with pytest.raises(ValueError):
ImagePalette.ImagePalette("RGB", list(range(256)) * 3, 10)
def test_reload():
with Image.open("Tests/images/hopper.gif") as im:

View File

@ -2,13 +2,10 @@ import warnings
import pytest
from PIL import ImageQt
from .helper import assert_image_similar, hopper
with warnings.catch_warnings() as w:
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
pytestmark = pytest.mark.skipif(
not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
)
@ -26,10 +23,6 @@ def test_rgb():
from PyQt6.QtGui import qRgb
elif ImageQt.qt_version == "side6":
from PySide6.QtGui import qRgb
elif ImageQt.qt_version == "5":
from PyQt5.QtGui import qRgb
elif ImageQt.qt_version == "side2":
from PySide2.QtGui import qRgb
assert qRgb(0, 0, 0) == qRgba(0, 0, 0, 255)

View File

@ -55,8 +55,8 @@ def test_show_without_viewers():
viewers = ImageShow._viewers
ImageShow._viewers = []
im = hopper()
assert not ImageShow.show(im)
with hopper() as im:
assert not ImageShow.show(im)
ImageShow._viewers = viewers
@ -89,20 +89,3 @@ def test_ipythonviewer():
im = hopper()
assert test_viewer.show(im) == 1
@pytest.mark.skipif(
not on_ci() or is_win32(),
reason="Only run on CIs; hangs on Windows CIs",
)
@pytest.mark.parametrize("viewer", ImageShow._viewers)
def test_file_deprecated(tmp_path, viewer):
f = str(tmp_path / "temp.jpg")
hopper().save(f)
with pytest.warns(DeprecationWarning):
try:
viewer.show_file(file=f)
except NotImplementedError:
pass
with pytest.raises(TypeError):
viewer.show_file()

View File

@ -89,13 +89,6 @@ def test_photoimage_blank(mode):
assert_image_equal(reloaded.convert(mode), im)
def test_box_deprecation():
im = hopper()
im_tk = ImageTk.PhotoImage(im)
with pytest.warns(DeprecationWarning):
im_tk.paste(im, (0, 0, 128, 128))
def test_bitmapimage():
im = hopper("1")

View File

@ -100,8 +100,11 @@ class TestImageWinDib:
# Act
# Make one the same as the using tobytes()/frombytes()
test_buffer = dib1.tobytes()
dib2.frombytes(test_buffer)
for datatype in ("bytes", "memoryview"):
if datatype == "memoryview":
test_buffer = memoryview(test_buffer)
dib2.frombytes(test_buffer)
# Assert
# Confirm they're the same
assert dib1.tobytes() == dib2.tobytes()
# Assert
# Confirm they're the same
assert dib1.tobytes() == dib2.tobytes()

View File

@ -207,6 +207,9 @@ class TestLibPack:
0x01000083,
)
def test_I16(self):
self.assert_pack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605)
def test_F_float(self):
self.assert_pack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34)
@ -761,10 +764,12 @@ class TestLibUnpack:
self.assert_unpack("I;16", "I;16N", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16B", "I;16N", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16L", "I;16N", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605)
else:
self.assert_unpack("I;16", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16B", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16L", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16N", "I;16N", 2, 0x0102, 0x0304, 0x0506)
def test_CMYK16(self):
self.assert_unpack("CMYK", "CMYK;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16))

View File

@ -88,10 +88,7 @@ def test_tobytes():
def test_convert():
im = original.copy()
verify(im.convert("I;16"))
verify(im.convert("I;16").convert("L"))
verify(im.convert("I;16").convert("I"))
verify(im.convert("I;16B"))
verify(im.convert("I;16B").convert("L"))
verify(im.convert("I;16B").convert("I"))
for mode in ("I;16", "I;16B", "I;16N"):
verify(im.convert(mode))
verify(im.convert(mode).convert("L"))
verify(im.convert(mode).convert("I"))

View File

@ -4,7 +4,7 @@ import pytest
from PIL import Image
from .helper import assert_deep_equal, assert_image, hopper
from .helper import assert_deep_equal, assert_image, hopper, skip_unless_feature
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
@ -219,6 +219,13 @@ def test_zero_size():
assert im.size == (0, 0)
@skip_unless_feature("libtiff")
def test_load_first():
with Image.open("Tests/images/g4_orientation_5.tif") as im:
a = numpy.array(im)
assert a.shape == (88, 590)
def test_bool():
# https://github.com/python-pillow/Pillow/issues/2044
a = numpy.zeros((10, 2), dtype=bool)

View File

@ -1,10 +1,6 @@
import warnings
import pytest
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
from PIL import ImageQt
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
@ -19,14 +15,6 @@ if ImageQt.qt_is_installed:
from PySide6.QtCore import QPoint
from PySide6.QtGui import QImage, QPainter, QRegion
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "5":
from PyQt5.QtCore import QPoint
from PyQt5.QtGui import QImage, QPainter, QRegion
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "side2":
from PySide2.QtCore import QPoint
from PySide2.QtGui import QImage, QPainter, QRegion
from PySide2.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
class Example(QWidget):
def __init__(self):
@ -48,7 +36,7 @@ if ImageQt.qt_is_installed:
def roundtrip(expected):
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb
assert_image_similar(result, expected.convert("RGB"), 0.3)
assert_image_similar(result, expected.convert("RGB"), 1)
@pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed")

View File

@ -1,10 +1,6 @@
import warnings
import pytest
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
from PIL import ImageQt
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
@ -32,7 +28,7 @@ def test_sanity(mode, tmp_path):
assert_image_equal(rt, src)
if mode == "1":
# BW appears to not save correctly on QT5
# BW appears to not save correctly on Qt
# kicks out errors on console:
# libpng warning: Invalid color type/bit depth combination
# in IHDR

View File

@ -8,5 +8,5 @@ if [ ! -f $archive.tar.gz ]; then
wget -O $archive.tar.gz $url
fi
rm -r $archive
rmdir $archive
tar -xvzf $archive.tar.gz

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