Merge branch 'main' into jxl-support2
							
								
								
									
										100
									
								
								.appveyor.yml
									
									
									
									
									
								
							
							
						
						|  | @ -1,100 +0,0 @@ | |||
| skip_commits: | ||||
|   files: | ||||
|     - ".github/**/*" | ||||
|     - ".gitmodules" | ||||
|     - "docs/**/*" | ||||
|     - "wheels/**/*" | ||||
| 
 | ||||
| version: '{build}' | ||||
| clone_folder: c:\pillow | ||||
| init: | ||||
| - ECHO %PYTHON% | ||||
| #- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) | ||||
| # Uncomment previous line to get RDP access during the build. | ||||
| 
 | ||||
| environment: | ||||
|   COVERAGE_CORE: sysmon | ||||
|   EXECUTABLE: python.exe | ||||
|   TEST_OPTIONS: | ||||
|   DEPLOY: YES | ||||
|   matrix: | ||||
|   - PYTHON: C:/Python312 | ||||
|     ARCHITECTURE: x86 | ||||
|     APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 | ||||
|   - PYTHON: C:/Python39-x64 | ||||
|     ARCHITECTURE: AMD64 | ||||
|     APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 | ||||
| 
 | ||||
| 
 | ||||
| install: | ||||
| - '%PYTHON%\%EXECUTABLE% --version' | ||||
| - '%PYTHON%\%EXECUTABLE% -m pip install --upgrade pip' | ||||
| - curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip | ||||
| - 7z x pillow-test-images.zip -oc:\ | ||||
| - xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images | ||||
| - curl -fsSL -o nasm-win64.zip https://raw.githubusercontent.com/python-pillow/pillow-depends/main/nasm-2.16.03-win64.zip | ||||
| - 7z x nasm-win64.zip -oc:\ | ||||
| - choco install ghostscript --version=10.3.1 | ||||
| - path c:\nasm-2.16.03;C:\Program Files\gs\gs10.03.1\bin;%PATH% | ||||
| - cd c:\pillow\winbuild\ | ||||
| - ps: | | ||||
|         c:\python39\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% | ||||
| 
 | ||||
| build_script: | ||||
| - cd c:\pillow | ||||
| - winbuild\build\build_env.cmd | ||||
| - '%PYTHON%\%EXECUTABLE% -m pip install -v -C raqm=vendor -C fribidi=vendor .' | ||||
| - '%PYTHON%\%EXECUTABLE% selftest.py --installed' | ||||
| 
 | ||||
| test_script: | ||||
| - cd c:\pillow | ||||
| - '%PYTHON%\%EXECUTABLE% -m pip install pytest pytest-cov pytest-timeout defusedxml ipython numpy olefile pyroma' | ||||
| - c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE% | ||||
| - '%PYTHON%\%EXECUTABLE% -c "from PIL import Image"' | ||||
| - '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests' | ||||
| #- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest? | ||||
| 
 | ||||
| after_test: | ||||
| - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe | ||||
| - .\codecov.exe --file coverage.xml --name %PYTHON% --flags AppVeyor | ||||
| 
 | ||||
| matrix: | ||||
|   fast_finish: true | ||||
| 
 | ||||
| cache: | ||||
| - '%LOCALAPPDATA%\pip\Cache' | ||||
| 
 | ||||
| artifacts: | ||||
| - path: pillow\*.egg | ||||
|   name: egg | ||||
| - path: pillow\*.whl | ||||
|   name: wheel | ||||
| 
 | ||||
| before_deploy: | ||||
|   - cd c:\pillow | ||||
|   - '%PYTHON%\%EXECUTABLE% -m pip wheel -v -C raqm=vendor -C fribidi=vendor .' | ||||
|   - ps: Get-ChildItem .\*.whl | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } | ||||
| 
 | ||||
| deploy: | ||||
|   provider: S3 | ||||
|   region: us-west-2 | ||||
|   access_key_id: AKIAIRAXC62ZNTVQJMOQ | ||||
|   secret_access_key: | ||||
|     secure: Hwb6klTqtBeMgxAjRoDltiiqpuH8xbwD4UooDzBSiCWXjuFj1lyl4kHgHwTCCGqi | ||||
|   bucket: pillow-nightly | ||||
|   folder: win/$(APPVEYOR_BUILD_NUMBER)/ | ||||
|   artifact: /.*egg|wheel/ | ||||
|   on: | ||||
|     APPVEYOR_REPO_NAME: python-pillow/Pillow | ||||
|     branch: main | ||||
|     deploy: YES | ||||
| 
 | ||||
| 
 | ||||
| # Uncomment the following lines to get RDP access after the build/test and block for | ||||
| # up to the timeout limit (~1hr) | ||||
| # | ||||
| #on_finish: | ||||
| #- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) | ||||
|  | @ -2,8 +2,4 @@ | |||
| 
 | ||||
| # gather the coverage data | ||||
| python3 -m pip install coverage | ||||
| if [[ $MATRIX_DOCKER ]]; then | ||||
|   python3 -m coverage xml --ignore-errors | ||||
| else | ||||
|   python3 -m coverage xml | ||||
| fi | ||||
| python3 -m coverage xml | ||||
|  |  | |||
|  | @ -3,8 +3,5 @@ | |||
| set -e | ||||
| 
 | ||||
| python3 -m coverage erase | ||||
| if [ $(uname) == "Darwin" ]; then | ||||
|     export CPPFLAGS="-I/usr/local/miniconda/include"; | ||||
| fi | ||||
| make clean | ||||
| make install-coverage | ||||
|  |  | |||
|  | @ -2,12 +2,12 @@ | |||
| 
 | ||||
| aptget_update() | ||||
| { | ||||
|     if [ ! -z $1 ]; then | ||||
|     if [ -n "$1" ]; then | ||||
|         echo "" | ||||
|         echo "Retrying apt-get update..." | ||||
|         echo "" | ||||
|     fi | ||||
|     output=`sudo apt-get update 2>&1` | ||||
|     output=$(sudo apt-get update 2>&1) | ||||
|     echo "$output" | ||||
|     if [[ $output == *[WE]:\ * ]]; then | ||||
|         return 1 | ||||
|  | @ -20,10 +20,10 @@ fi | |||
| set -e | ||||
| 
 | ||||
| if [[ $(uname) != CYGWIN* ]]; then | ||||
|     sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ | ||||
|                              ghostscript libjpeg-turbo-progs libopenjp2-7-dev\ | ||||
|     sudo apt-get -qq install libfreetype6-dev liblcms2-dev libtiff-dev python3-tk\ | ||||
|                              ghostscript libjpeg-turbo8-dev libopenjp2-7-dev\ | ||||
|                              cmake meson imagemagick libharfbuzz-dev libfribidi-dev\ | ||||
|                              sway wl-clipboard libopenblas-dev | ||||
|                              sway wl-clipboard libopenblas-dev nasm | ||||
| fi | ||||
| 
 | ||||
| python3 -m pip install --upgrade pip | ||||
|  | @ -36,14 +36,12 @@ python3 -m pip install -U pytest | |||
| python3 -m pip install -U pytest-cov | ||||
| python3 -m pip install -U pytest-timeout | ||||
| python3 -m pip install pyroma | ||||
| # optional test dependency, only install if there's a binary package. | ||||
| # fails on beta 3.14 and PyPy | ||||
| python3 -m pip install --only-binary=:all: pyarrow || true | ||||
| 
 | ||||
| if [[ $(uname) != CYGWIN* ]]; then | ||||
|     # TODO Update condition when NumPy supports free-threading | ||||
|     if [[ "$PYTHON_GIL" == "0" ]]; then | ||||
|         python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple | ||||
|     else | ||||
|     python3 -m pip install numpy | ||||
|     fi | ||||
| 
 | ||||
|     # PyQt6 doesn't support PyPy3 | ||||
|     if [[ $GHA_PYTHON_VERSION == 3.* ]]; then | ||||
|  | @ -55,7 +53,7 @@ if [[ $(uname) != CYGWIN* ]]; then | |||
|     # Pyroma uses non-isolated build and fails with old setuptools | ||||
|     if [[ $GHA_PYTHON_VERSION == 3.9 ]]; then | ||||
|         # To match pyproject.toml | ||||
|         python3 -m pip install "setuptools>=67.8" | ||||
|         python3 -m pip install "setuptools>=77" | ||||
|     fi | ||||
| 
 | ||||
|     # webp | ||||
|  | @ -67,6 +65,9 @@ if [[ $(uname) != CYGWIN* ]]; then | |||
|     # raqm | ||||
|     pushd depends && ./install_raqm.sh && popd | ||||
| 
 | ||||
|     # libavif | ||||
|     pushd depends && CMAKE_POLICY_VERSION_MINIMUM=3.5 ./install_libavif.sh && popd | ||||
| 
 | ||||
|     # extra test images | ||||
|     pushd depends && ./install_extra_test_images.sh && popd | ||||
| else | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| cibuildwheel==2.20.0 | ||||
| cibuildwheel==2.23.2 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| mypy==1.11.2 | ||||
| mypy==1.15.0 | ||||
| IceSpringPySideStubs-PyQt6 | ||||
| IceSpringPySideStubs-PySide6 | ||||
| ipython | ||||
|  |  | |||
							
								
								
									
										3
									
								
								.ci/test.cmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,3 @@ | |||
| python.exe -c "from PIL import Image" | ||||
| IF ERRORLEVEL 1 EXIT /B | ||||
| python.exe -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests | ||||
|  | @ -4,4 +4,4 @@ set -e | |||
| 
 | ||||
| python3 -c "from PIL import Image" | ||||
| 
 | ||||
| python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE | ||||
| python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests $REVERSE | ||||
|  |  | |||
|  | @ -1,5 +1,26 @@ | |||
| # A clang-format style that approximates Python's PEP 7 | ||||
| # Useful for IDE integration | ||||
| Language: C | ||||
| BasedOnStyle: Google | ||||
| AlwaysBreakAfterReturnType: All | ||||
| AllowShortIfStatementsOnASingleLine: false | ||||
| AlignAfterOpenBracket: BlockIndent | ||||
| BinPackArguments: false | ||||
| BinPackParameters: false | ||||
| BreakBeforeBraces: Attach | ||||
| ColumnLimit: 88 | ||||
| DerivePointerAlignment: false | ||||
| IndentGotoLabels: false | ||||
| IndentWidth: 4 | ||||
| PointerAlignment: Right | ||||
| ReflowComments: true | ||||
| SortIncludes: false | ||||
| SpaceBeforeParens: ControlStatements | ||||
| SpacesInParentheses: false | ||||
| TabWidth: 4 | ||||
| UseTab: Never | ||||
| --- | ||||
| Language: Cpp | ||||
| BasedOnStyle: Google | ||||
| AlwaysBreakAfterReturnType: All | ||||
| AllowShortIfStatementsOnASingleLine: false | ||||
|  | @ -11,7 +32,6 @@ ColumnLimit: 88 | |||
| DerivePointerAlignment: false | ||||
| IndentGotoLabels: false | ||||
| IndentWidth: 4 | ||||
| Language: Cpp | ||||
| PointerAlignment: Right | ||||
| ReflowComments: true | ||||
| SortIncludes: false | ||||
|  |  | |||
							
								
								
									
										5
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -9,7 +9,7 @@ Please send a pull request to the `main` branch. Please include [documentation]( | |||
| - Fork the Pillow repository. | ||||
| - Create a branch from `main`. | ||||
| - Develop bug fixes, features, tests, etc. | ||||
| - Run the test suite. You can enable GitHub Actions (https://github.com/MY-USERNAME/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests. | ||||
| - Run the test suite. You can enable GitHub Actions (https://github.com/MY-USERNAME/Pillow/actions) 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 `main`. | ||||
| 
 | ||||
| ### Guidelines | ||||
|  | @ -17,9 +17,8 @@ Please send a pull request to the `main` branch. Please include [documentation]( | |||
| - Separate code commits from reformatting commits. | ||||
| - Provide tests for any newly added code. | ||||
| - Follow PEP 8. | ||||
| - When committing only documentation changes please include `[ci skip]` in the commit message to avoid running tests on AppVeyor. | ||||
| - When committing only documentation changes please include `[ci skip]` in the commit message to avoid running extra tests. | ||||
| - Include [release notes](https://github.com/python-pillow/Pillow/tree/main/docs/releasenotes) as needed or appropriate with your bug fixes, feature additions and tests. | ||||
| - Do not add to the [changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) for proposed changes, as that is updated after changes are merged. | ||||
| 
 | ||||
| ## Reporting Issues | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								.github/mergify.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -9,7 +9,6 @@ pull_request_rules: | |||
|       - status-success=Windows Test Successful | ||||
|       - status-success=MinGW | ||||
|       - status-success=Cygwin Test Successful | ||||
|       - status-success=continuous-integration/appveyor/pr | ||||
|     actions: | ||||
|       merge: | ||||
|         method: merge | ||||
|  |  | |||
							
								
								
									
										11
									
								
								.github/release-drafter.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -3,18 +3,19 @@ tag-template: "$NEXT_MINOR_VERSION" | |||
| change-template: '- $TITLE #$NUMBER [@$AUTHOR]' | ||||
| 
 | ||||
| categories: | ||||
|   - title: "Dependencies" | ||||
|     label: "Dependency" | ||||
|   - title: "Removals" | ||||
|     label: "Removal" | ||||
|   - title: "Deprecations" | ||||
|     label: "Deprecation" | ||||
|   - title: "Documentation" | ||||
|     label: "Documentation" | ||||
|   - title: "Removals" | ||||
|     label: "Removal" | ||||
|   - title: "Dependencies" | ||||
|     label: "Dependency" | ||||
|   - title: "Testing" | ||||
|     label: "Testing" | ||||
|   - title: "Type hints" | ||||
|     label: "Type hints" | ||||
|   - title: "Other changes" | ||||
| 
 | ||||
| exclude-labels: | ||||
|   - "changelog: skip" | ||||
|  | @ -23,6 +24,4 @@ template: | | |||
| 
 | ||||
|   https://pillow.readthedocs.io/en/stable/releasenotes/$NEXT_MINOR_VERSION.html | ||||
| 
 | ||||
|   ## Changes | ||||
| 
 | ||||
|   $CHANGES | ||||
|  |  | |||
							
								
								
									
										12
									
								
								.github/renovate.json
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|     "$schema": "https://docs.renovatebot.com/renovate-schema.json", | ||||
|     "extends": [ | ||||
|         "config:base" | ||||
|         "config:recommended" | ||||
|     ], | ||||
|     "labels": [ | ||||
|         "Dependency" | ||||
|  | @ -9,9 +9,13 @@ | |||
|     "packageRules": [ | ||||
|         { | ||||
|             "groupName": "github-actions", | ||||
|             "matchManagers": ["github-actions"], | ||||
|             "separateMajorMinor": "false" | ||||
|             "matchManagers": [ | ||||
|                 "github-actions" | ||||
|             ], | ||||
|             "separateMajorMinor": false | ||||
|         } | ||||
|     ], | ||||
|     "schedule": ["on the 3rd day of the month"] | ||||
|     "schedule": [ | ||||
|         "* * 3 * *" | ||||
|     ] | ||||
| } | ||||
|  |  | |||
							
								
								
									
										4
									
								
								.github/workflows/cifuzz.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -6,11 +6,13 @@ on: | |||
|       - "**" | ||||
|     paths: | ||||
|       - ".github/workflows/cifuzz.yml" | ||||
|       - ".github/workflows/wheels-dependencies.sh" | ||||
|       - "**.c" | ||||
|       - "**.h" | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - ".github/workflows/cifuzz.yml" | ||||
|       - ".github/workflows/wheels-dependencies.sh" | ||||
|       - "**.c" | ||||
|       - "**.h" | ||||
|   workflow_dispatch: | ||||
|  | @ -24,8 +26,6 @@ concurrency: | |||
| 
 | ||||
| jobs: | ||||
|   Fuzzing: | ||||
|     # Disabled until google/oss-fuzz#11419 upgrades Python to 3.9+ | ||||
|     if: false | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - name: Build Fuzzers | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -33,6 +33,8 @@ jobs: | |||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Set up Python | ||||
|       uses: actions/setup-python@v5 | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -21,6 +21,8 @@ jobs: | |||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: pre-commit cache | ||||
|       uses: actions/cache@v4 | ||||
|  |  | |||
							
								
								
									
										18
									
								
								.github/workflows/macos-install.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -6,19 +6,19 @@ if [[ "$ImageOS" == "macos13" ]]; then | |||
|     brew uninstall gradle maven | ||||
| fi | ||||
| brew install \ | ||||
|     aom \ | ||||
|     dav1d \ | ||||
|     freetype \ | ||||
|     ghostscript \ | ||||
|     jpeg-turbo \ | ||||
|     libimagequant \ | ||||
|     libjpeg \ | ||||
|     libraqm \ | ||||
|     libtiff \ | ||||
|     little-cms2 \ | ||||
|     openjpeg \ | ||||
|     rav1e \ | ||||
|     svt-av1 \ | ||||
|     webp | ||||
| if [[ "$ImageOS" == "macos13" ]]; then | ||||
|     brew install --ignore-dependencies libraqm | ||||
| else | ||||
|     brew install libraqm | ||||
| fi | ||||
| export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig" | ||||
| 
 | ||||
| python3 -m pip install coverage | ||||
|  | @ -30,6 +30,12 @@ python3 -m pip install -U pytest-cov | |||
| python3 -m pip install -U pytest-timeout | ||||
| python3 -m pip install pyroma | ||||
| python3 -m pip install numpy | ||||
| # optional test dependency, only install if there's a binary package. | ||||
| # fails on beta 3.14 and PyPy | ||||
| python3 -m pip install --only-binary=:all: pyarrow || true | ||||
| 
 | ||||
| # libavif | ||||
| pushd depends && ./install_libavif.sh && popd | ||||
| 
 | ||||
| # extra test images | ||||
| pushd depends && ./install_extra_test_images.sh && popd | ||||
|  |  | |||
							
								
								
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -6,7 +6,7 @@ on: | |||
|   workflow_dispatch: | ||||
| 
 | ||||
| permissions: | ||||
|   issues: write | ||||
|   contents: read | ||||
| 
 | ||||
| concurrency: | ||||
|   group: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | @ -15,6 +15,8 @@ concurrency: | |||
| jobs: | ||||
|   stale: | ||||
|     if: github.repository_owner == 'python-pillow' | ||||
|     permissions: | ||||
|       issues: write | ||||
| 
 | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										9
									
								
								.github/workflows/test-cygwin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -48,9 +48,11 @@ jobs: | |||
| 
 | ||||
|       - name: Checkout Pillow | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
| 
 | ||||
|       - name: Install Cygwin | ||||
|         uses: cygwin/cygwin-install-action@v4 | ||||
|         uses: cygwin/cygwin-install-action@v5 | ||||
|         with: | ||||
|           packages: > | ||||
|             gcc-g++ | ||||
|  | @ -131,11 +133,12 @@ jobs: | |||
|       - name: After success | ||||
|         run: | | ||||
|           bash.exe .ci/after_success.sh | ||||
|           rm C:\cygwin\bin\bash.EXE | ||||
| 
 | ||||
|       - name: Upload coverage | ||||
|         uses: codecov/codecov-action@v4 | ||||
|         uses: codecov/codecov-action@v5 | ||||
|         with: | ||||
|           file: ./coverage.xml | ||||
|           files: ./coverage.xml | ||||
|           flags: GHA_Cygwin | ||||
|           name: Cygwin Python 3.${{ matrix.python-minor-version }} | ||||
|           token: ${{ secrets.CODECOV_ORG_TOKEN }} | ||||
|  |  | |||
							
								
								
									
										30
									
								
								.github/workflows/test-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -29,13 +29,13 @@ concurrency: | |||
| jobs: | ||||
|   build: | ||||
| 
 | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         os: ["ubuntu-latest"] | ||||
|         docker: [ | ||||
|           # Run slower jobs first to give them a headstart and reduce waiting time | ||||
|           ubuntu-22.04-jammy-arm64v8, | ||||
|           ubuntu-24.04-noble-ppc64le, | ||||
|           ubuntu-24.04-noble-s390x, | ||||
|           # Then run the remainder | ||||
|  | @ -44,35 +44,40 @@ jobs: | |||
|           amazon-2023-amd64, | ||||
|           arch, | ||||
|           centos-stream-9-amd64, | ||||
|           centos-stream-10-amd64, | ||||
|           debian-12-bookworm-x86, | ||||
|           debian-12-bookworm-amd64, | ||||
|           fedora-39-amd64, | ||||
|           fedora-40-amd64, | ||||
|           fedora-41-amd64, | ||||
|           fedora-42-amd64, | ||||
|           gentoo, | ||||
|           ubuntu-22.04-jammy-amd64, | ||||
|           ubuntu-24.04-noble-amd64, | ||||
|         ] | ||||
|         dockerTag: [main] | ||||
|         include: | ||||
|           - docker: "ubuntu-22.04-jammy-arm64v8" | ||||
|             qemu-arch: "aarch64" | ||||
|           - docker: "ubuntu-24.04-noble-ppc64le" | ||||
|             qemu-arch: "ppc64le" | ||||
|           - docker: "ubuntu-24.04-noble-s390x" | ||||
|             qemu-arch: "s390x" | ||||
|           - docker: "ubuntu-24.04-noble-arm64v8" | ||||
|             os: "ubuntu-24.04-arm" | ||||
|             dockerTag: main | ||||
| 
 | ||||
|     name: ${{ matrix.docker }} | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Build system information | ||||
|       run: python3 .github/workflows/system-info.py | ||||
| 
 | ||||
|     - name: Set up QEMU | ||||
|       if: "matrix.qemu-arch" | ||||
|       run: | | ||||
|         docker run --rm --privileged aptman/qus -s -- -p ${{ matrix.qemu-arch }} | ||||
|       uses: docker/setup-qemu-action@v3 | ||||
|       with: | ||||
|         platforms: ${{ matrix.qemu-arch }} | ||||
| 
 | ||||
|     - name: Docker pull | ||||
|       run: | | ||||
|  | @ -87,22 +92,21 @@ jobs: | |||
| 
 | ||||
|     - name: After success | ||||
|       run: | | ||||
|         PATH="$PATH:~/.local/bin" | ||||
|         docker start pillow_container | ||||
|         sudo docker cp pillow_container:/Pillow /Pillow | ||||
|         sudo chown -R runner /Pillow | ||||
|         pil_path=`docker exec pillow_container /vpy3/bin/python -c 'import os, PIL;print(os.path.realpath(os.path.dirname(PIL.__file__)))'` | ||||
|         docker stop pillow_container | ||||
|         sudo mkdir -p $pil_path | ||||
|         sudo cp src/PIL/*.py $pil_path | ||||
|         cd /Pillow | ||||
|         .ci/after_success.sh | ||||
|       env: | ||||
|         MATRIX_DOCKER: ${{ matrix.docker }} | ||||
| 
 | ||||
|     - name: Upload coverage | ||||
|       uses: codecov/codecov-action@v4 | ||||
|       uses: codecov/codecov-action@v5 | ||||
|       with: | ||||
|         flags: GHA_Docker | ||||
|         name: ${{ matrix.docker }} | ||||
|         gcov: true | ||||
|         token: ${{ secrets.CODECOV_ORG_TOKEN }} | ||||
| 
 | ||||
|   success: | ||||
|  |  | |||
							
								
								
									
										24
									
								
								.github/workflows/test-mingw.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -46,6 +46,8 @@ jobs: | |||
|     steps: | ||||
|       - name: Checkout Pillow | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
| 
 | ||||
|       - name: Set up shell | ||||
|         run: echo "C:\msys64\usr\bin\" >> $env:GITHUB_PATH | ||||
|  | @ -58,35 +60,35 @@ jobs: | |||
|               mingw-w64-x86_64-gcc \ | ||||
|               mingw-w64-x86_64-ghostscript \ | ||||
|               mingw-w64-x86_64-lcms2 \ | ||||
|               mingw-w64-x86_64-libavif \ | ||||
|               mingw-w64-x86_64-libimagequant \ | ||||
|               mingw-w64-x86_64-libjpeg-turbo \ | ||||
|               mingw-w64-x86_64-libraqm \ | ||||
|               mingw-w64-x86_64-libtiff \ | ||||
|               mingw-w64-x86_64-libwebp \ | ||||
|               mingw-w64-x86_64-openjpeg2 \ | ||||
|               mingw-w64-x86_64-python3-numpy \ | ||||
|               mingw-w64-x86_64-python3-olefile \ | ||||
|               mingw-w64-x86_64-python3-setuptools \ | ||||
|               mingw-w64-x86_64-python-numpy \ | ||||
|               mingw-w64-x86_64-python-olefile \ | ||||
|               mingw-w64-x86_64-python-pip \ | ||||
|               mingw-w64-x86_64-python-pytest \ | ||||
|               mingw-w64-x86_64-python-pytest-cov \ | ||||
|               mingw-w64-x86_64-python-pytest-timeout \ | ||||
|               mingw-w64-x86_64-python-pyqt6 | ||||
| 
 | ||||
|           python3 -m ensurepip | ||||
|           python3 -m pip install pyroma pytest pytest-cov pytest-timeout | ||||
| 
 | ||||
|           pushd depends && ./install_extra_test_images.sh && popd | ||||
| 
 | ||||
|       - name: Build Pillow | ||||
|         run: SETUPTOOLS_USE_DISTUTILS="stdlib" CFLAGS="-coverage" python3 -m pip install . | ||||
|         run: CFLAGS="-coverage" python3 -m pip install . | ||||
| 
 | ||||
|       - name: Test Pillow | ||||
|         run: | | ||||
|           python3 selftest.py --installed | ||||
|           python3 -c "from PIL import Image" | ||||
|           python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests | ||||
|           .ci/test.sh | ||||
| 
 | ||||
|       - name: Upload coverage | ||||
|         uses: codecov/codecov-action@v4 | ||||
|         uses: codecov/codecov-action@v5 | ||||
|         with: | ||||
|           file: ./coverage.xml | ||||
|           files: ./coverage.xml | ||||
|           flags: GHA_Windows | ||||
|           name: "MSYS2 MinGW" | ||||
|           token: ${{ secrets.CODECOV_ORG_TOKEN }} | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.github/workflows/test-valgrind.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -40,6 +40,8 @@ jobs: | |||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Build system information | ||||
|       run: python3 .github/workflows/system-info.py | ||||
|  |  | |||
							
								
								
									
										58
									
								
								.github/workflows/test-windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -31,29 +31,38 @@ env: | |||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: windows-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"] | ||||
|         python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"] | ||||
|         architecture: ["x64"] | ||||
|         os: ["windows-latest"] | ||||
|         include: | ||||
|             # Test the oldest Python on 32-bit | ||||
|             - { python-version: "3.9", architecture: "x86", os: "windows-2019" } | ||||
| 
 | ||||
|     timeout-minutes: 30 | ||||
|     timeout-minutes: 45 | ||||
| 
 | ||||
|     name: Python ${{ matrix.python-version }} | ||||
|     name: Python ${{ matrix.python-version }} (${{ matrix.architecture }}) | ||||
| 
 | ||||
|     steps: | ||||
|     - name: Checkout Pillow | ||||
|       uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Checkout cached dependencies | ||||
|       uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
|         repository: python-pillow/pillow-depends | ||||
|         path: winbuild\depends | ||||
| 
 | ||||
|     - name: Checkout extra test images | ||||
|       uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
|         repository: python-pillow/test-images | ||||
|         path: Tests\test-images | ||||
| 
 | ||||
|  | @ -63,22 +72,25 @@ jobs: | |||
|       with: | ||||
|         python-version: ${{ matrix.python-version }} | ||||
|         allow-prereleases: true | ||||
|         architecture: ${{ matrix.architecture }} | ||||
|         cache: pip | ||||
|         cache-dependency-path: ".github/workflows/test-windows.yml" | ||||
| 
 | ||||
|     - name: Print build system information | ||||
|       run: python3 .github/workflows/system-info.py | ||||
| 
 | ||||
|     - name: Install Python dependencies | ||||
|       run: > | ||||
|         python3 -m pip install | ||||
|         coverage>=7.4.2 | ||||
|         defusedxml | ||||
|         olefile | ||||
|         pyroma | ||||
|         pytest | ||||
|         pytest-cov | ||||
|         pytest-timeout | ||||
|     - name: Upgrade pip | ||||
|       run: | | ||||
|         python3 -m pip install --upgrade pip | ||||
| 
 | ||||
|     - name: Install CPython dependencies | ||||
|       if: "!contains(matrix.python-version, 'pypy') && matrix.architecture != 'x86'" | ||||
|       run: | | ||||
|         python3 -m pip install PyQt6 | ||||
| 
 | ||||
|     - name: Install PyArrow dependency | ||||
|       run: | | ||||
|         python3 -m pip install --only-binary=:all: pyarrow || true | ||||
| 
 | ||||
|     - name: Install dependencies | ||||
|       id: install | ||||
|  | @ -86,8 +98,8 @@ jobs: | |||
|         choco install nasm --no-progress | ||||
|         echo "C:\Program Files\NASM" >> $env:GITHUB_PATH | ||||
| 
 | ||||
|         choco install ghostscript --version=10.3.1 --no-progress | ||||
|         echo "C:\Program Files\gs\gs10.03.1\bin" >> $env:GITHUB_PATH | ||||
|         choco install ghostscript --version=10.5.0 --no-progress | ||||
|         echo "C:\Program Files\gs\gs10.05.0\bin" >> $env:GITHUB_PATH | ||||
| 
 | ||||
|         # Install extra test images | ||||
|         xcopy /S /Y Tests\test-images\* Tests\images | ||||
|  | @ -137,6 +149,10 @@ jobs: | |||
|       if: steps.build-cache.outputs.cache-hit != 'true' | ||||
|       run: "& winbuild\\build\\build_dep_libpng.cmd" | ||||
| 
 | ||||
|     - name: Build dependencies / libavif | ||||
|       if: steps.build-cache.outputs.cache-hit != 'true' && matrix.architecture == 'x64' | ||||
|       run: "& winbuild\\build\\build_dep_libavif.cmd" | ||||
| 
 | ||||
|     # for FreeType WOFF2 font support | ||||
|     - name: Build dependencies / brotli | ||||
|       if: steps.build-cache.outputs.cache-hit != 'true' | ||||
|  | @ -178,7 +194,7 @@ jobs: | |||
|     - name: Build Pillow | ||||
|       run: | | ||||
|         $FLAGS="-C raqm=vendor -C fribidi=vendor" | ||||
|         cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS ." | ||||
|         cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS .[tests]" | ||||
|         & $env:pythonLocation\python.exe selftest.py --installed | ||||
|       shell: pwsh | ||||
| 
 | ||||
|  | @ -190,8 +206,8 @@ jobs: | |||
| 
 | ||||
|     - name: Test Pillow | ||||
|       run: | | ||||
|         path %GITHUB_WORKSPACE%\\winbuild\\build\\bin;%PATH% | ||||
|         python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests | ||||
|         path %GITHUB_WORKSPACE%\winbuild\build\bin;%PATH% | ||||
|         .ci\test.cmd | ||||
|       shell: cmd | ||||
| 
 | ||||
|     - name: Prepare to upload errors | ||||
|  | @ -213,9 +229,9 @@ jobs: | |||
|       shell: pwsh | ||||
| 
 | ||||
|     - name: Upload coverage | ||||
|       uses: codecov/codecov-action@v4 | ||||
|       uses: codecov/codecov-action@v5 | ||||
|       with: | ||||
|         file: ./coverage.xml | ||||
|         files: ./coverage.xml | ||||
|         flags: GHA_Windows | ||||
|         name: ${{ runner.os }} Python ${{ matrix.python-version }} | ||||
|         token: ${{ secrets.CODECOV_ORG_TOKEN }} | ||||
|  |  | |||
							
								
								
									
										26
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -37,11 +37,14 @@ jobs: | |||
|       fail-fast: false | ||||
|       matrix: | ||||
|         os: [ | ||||
|           "macos-14", | ||||
|           "macos-latest", | ||||
|           "ubuntu-latest", | ||||
|         ] | ||||
|         python-version: [ | ||||
|           "pypy3.11", | ||||
|           "pypy3.10", | ||||
|           "3.14", | ||||
|           "3.13t", | ||||
|           "3.13", | ||||
|           "3.12", | ||||
|           "3.11", | ||||
|  | @ -52,21 +55,22 @@ jobs: | |||
|         - { python-version: "3.11", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" } | ||||
|         - { python-version: "3.10", PYTHONOPTIMIZE: 2 } | ||||
|         # Free-threaded | ||||
|         - { os: "ubuntu-latest", python-version: "3.13-dev", disable-gil: true } | ||||
|         - { python-version: "3.13t", disable-gil: true } | ||||
|         # M1 only available for 3.10+ | ||||
|         - { os: "macos-13", python-version: "3.9" } | ||||
|         exclude: | ||||
|         - { os: "macos-14", python-version: "3.9" } | ||||
|         - { os: "macos-latest", python-version: "3.9" } | ||||
| 
 | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     name: ${{ matrix.os }} Python ${{ matrix.python-version }} ${{ matrix.disable-gil && 'free-threaded' || '' }} | ||||
|     name: ${{ matrix.os }} Python ${{ matrix.python-version }} | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Set up Python ${{ matrix.python-version }} | ||||
|       uses: actions/setup-python@v5 | ||||
|       if: "${{ !matrix.disable-gil }}" | ||||
|       with: | ||||
|         python-version: ${{ matrix.python-version }} | ||||
|         allow-prereleases: true | ||||
|  | @ -75,13 +79,6 @@ jobs: | |||
|           ".ci/*.sh" | ||||
|           "pyproject.toml" | ||||
| 
 | ||||
|     - name: Set up Python ${{ matrix.python-version }} (free-threaded) | ||||
|       uses: deadsnakes/action@v3.2.0 | ||||
|       if: "${{ matrix.disable-gil }}" | ||||
|       with: | ||||
|         python-version: ${{ matrix.python-version }} | ||||
|         nogil: ${{ matrix.disable-gil }} | ||||
| 
 | ||||
|     - name: Set PYTHON_GIL | ||||
|       if: "${{ matrix.disable-gil }}" | ||||
|       run: | | ||||
|  | @ -114,7 +111,7 @@ jobs: | |||
|         GHA_PYTHON_VERSION: ${{ matrix.python-version }} | ||||
| 
 | ||||
|     - name: Register gcc problem matcher | ||||
|       if: "matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'" | ||||
|       if: "matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'" | ||||
|       run: echo "::add-matcher::.github/problem-matchers/gcc.json" | ||||
| 
 | ||||
|     - name: Build | ||||
|  | @ -154,11 +151,10 @@ jobs: | |||
|         .ci/after_success.sh | ||||
| 
 | ||||
|     - name: Upload coverage | ||||
|       uses: codecov/codecov-action@v4 | ||||
|       uses: codecov/codecov-action@v5 | ||||
|       with: | ||||
|         flags: ${{ matrix.os == 'ubuntu-latest' && 'GHA_Ubuntu' || 'GHA_macOS' }} | ||||
|         name: ${{ matrix.os }} Python ${{ matrix.python-version }} | ||||
|         gcov: true | ||||
|         token: ${{ secrets.CODECOV_ORG_TOKEN }} | ||||
| 
 | ||||
|   success: | ||||
|  |  | |||
							
								
								
									
										223
									
								
								.github/workflows/wheels-dependencies.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -1,11 +1,33 @@ | |||
| #!/bin/bash | ||||
| # Define custom utilities | ||||
| # Test for macOS with [ -n "$IS_MACOS" ] | ||||
| if [ -z "$IS_MACOS" ]; then | ||||
|     export MB_ML_LIBC=${AUDITWHEEL_POLICY::9} | ||||
|     export MB_ML_VER=${AUDITWHEEL_POLICY:9} | ||||
| 
 | ||||
| # Setup that needs to be done before multibuild utils are invoked | ||||
| PROJECTDIR=$(pwd) | ||||
| if [[ "$(uname -s)" == "Darwin" ]]; then | ||||
|     # Safety check - macOS builds require that CIBW_ARCHS is set, and that it | ||||
|     # only contains a single value (even though cibuildwheel allows multiple | ||||
|     # values in CIBW_ARCHS). | ||||
|     if [[ -z "$CIBW_ARCHS" ]]; then | ||||
|         echo "ERROR: Pillow macOS builds require CIBW_ARCHS be defined." | ||||
|         exit 1 | ||||
|     fi | ||||
|     if [[ "$CIBW_ARCHS" == *" "* ]]; then | ||||
|         echo "ERROR: Pillow macOS builds only support a single architecture in CIBW_ARCHS." | ||||
|         exit 1 | ||||
|     fi | ||||
| 
 | ||||
|     # Build macOS dependencies in `build/darwin` | ||||
|     # Install them into `build/deps/darwin` | ||||
|     WORKDIR=$(pwd)/build/darwin | ||||
|     BUILD_PREFIX=$(pwd)/build/deps/darwin | ||||
| else | ||||
|     # Build prefix will default to /usr/local | ||||
|     WORKDIR=$(pwd)/build | ||||
|     MB_ML_LIBC=${AUDITWHEEL_POLICY::9} | ||||
|     MB_ML_VER=${AUDITWHEEL_POLICY:9} | ||||
| fi | ||||
| export PLAT=$CIBW_ARCHS | ||||
| PLAT="${CIBW_ARCHS:-$AUDITWHEEL_ARCH}" | ||||
| 
 | ||||
| # Define custom utilities | ||||
| source wheels/multibuild/common_utils.sh | ||||
| source wheels/multibuild/library_builders.sh | ||||
| if [ -z "$IS_MACOS" ]; then | ||||
|  | @ -15,113 +37,112 @@ fi | |||
| ARCHIVE_SDIR=pillow-depends-main | ||||
| 
 | ||||
| # Package versions for fresh source builds | ||||
| FREETYPE_VERSION=2.13.2 | ||||
| if [[ "$MB_ML_VER" != 2014 ]]; then | ||||
|     HARFBUZZ_VERSION=9.0.0 | ||||
| else | ||||
|     HARFBUZZ_VERSION=8.5.0 | ||||
| fi | ||||
| LIBPNG_VERSION=1.6.43 | ||||
| JPEGTURBO_VERSION=3.0.3 | ||||
| OPENJPEG_VERSION=2.5.2 | ||||
| XZ_VERSION=5.4.5 | ||||
| TIFF_VERSION=4.6.0 | ||||
| LCMS2_VERSION=2.16 | ||||
| if [[ -n "$IS_MACOS" ]]; then | ||||
|     GIFLIB_VERSION=5.2.2 | ||||
| else | ||||
|     GIFLIB_VERSION=5.2.1 | ||||
| fi | ||||
| if [[ -n "$IS_MACOS" ]] || [[ "$MB_ML_VER" != 2014 ]]; then | ||||
|     ZLIB_VERSION=1.3.1 | ||||
| else | ||||
|     ZLIB_VERSION=1.2.8 | ||||
| fi | ||||
| LIBWEBP_VERSION=1.4.0 | ||||
| FREETYPE_VERSION=2.13.3 | ||||
| HARFBUZZ_VERSION=11.1.0 | ||||
| LIBPNG_VERSION=1.6.47 | ||||
| JPEGTURBO_VERSION=3.1.0 | ||||
| OPENJPEG_VERSION=2.5.3 | ||||
| XZ_VERSION=5.8.1 | ||||
| TIFF_VERSION=4.7.0 | ||||
| LCMS2_VERSION=2.17 | ||||
| ZLIB_VERSION=1.3.1 | ||||
| ZLIB_NG_VERSION=2.2.4 | ||||
| LIBWEBP_VERSION=1.5.0 | ||||
| BZIP2_VERSION=1.0.8 | ||||
| LIBXCB_VERSION=1.17.0 | ||||
| BROTLI_VERSION=1.1.0 | ||||
| 
 | ||||
| if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then | ||||
|     function build_openjpeg { | ||||
|         local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v$OPENJPEG_VERSION.tar.gz openjpeg-$OPENJPEG_VERSION.tar.gz) | ||||
|         (cd $out_dir \ | ||||
|             && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \ | ||||
|             && make install) | ||||
|         touch openjpeg-stamp | ||||
|     } | ||||
| fi | ||||
| function build_pkg_config { | ||||
|     if [ -e pkg-config-stamp ]; then return; fi | ||||
|     # This essentially duplicates the Homebrew recipe | ||||
|     CFLAGS="$CFLAGS -Wno-int-conversion" build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \ | ||||
|         --disable-debug --disable-host-tool --with-internal-glib \ | ||||
|         --with-pc-path=$BUILD_PREFIX/share/pkgconfig:$BUILD_PREFIX/lib/pkgconfig \ | ||||
|         --with-system-include-path=$(xcrun --show-sdk-path --sdk macosx)/usr/include | ||||
|     export PKG_CONFIG=$BUILD_PREFIX/bin/pkg-config | ||||
|     touch pkg-config-stamp | ||||
| } | ||||
| 
 | ||||
| function build_zlib_ng { | ||||
|     if [ -e zlib-stamp ]; then return; fi | ||||
|     build_github zlib-ng/zlib-ng $ZLIB_NG_VERSION --zlib-compat | ||||
| 
 | ||||
|     if [ -n "$IS_MACOS" ]; then | ||||
|         # Ensure that on macOS, the library name is an absolute path, not an | ||||
|         # @rpath, so that delocate picks up the right library (and doesn't need | ||||
|         # DYLD_LIBRARY_PATH to be set). The default Makefile doesn't have an | ||||
|         # option to control the install_name. | ||||
|         install_name_tool -id $BUILD_PREFIX/lib/libz.1.dylib $BUILD_PREFIX/lib/libz.1.dylib | ||||
|     fi | ||||
|     touch zlib-stamp | ||||
| } | ||||
| 
 | ||||
| function build_brotli { | ||||
|     local cmake=$(get_modern_cmake) | ||||
|     if [ -e brotli-stamp ]; then return; fi | ||||
|     local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-$BROTLI_VERSION.tar.gz) | ||||
|     (cd $out_dir \ | ||||
|         && $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \ | ||||
|         && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \ | ||||
|         && make install) | ||||
|     if [[ "$MB_ML_LIBC" == "manylinux" ]]; then | ||||
|         cp /usr/local/lib64/libbrotli* /usr/local/lib | ||||
|         cp /usr/local/lib64/pkgconfig/libbrotli* /usr/local/lib/pkgconfig | ||||
|     fi | ||||
|     touch brotli-stamp | ||||
| } | ||||
| 
 | ||||
| function build_harfbuzz { | ||||
|     if [[ "$HARFBUZZ_VERSION" == 8.5.0 ]]; then | ||||
|         export FREETYPE_LIBS=-lfreetype | ||||
|         export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/ | ||||
|         build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no | ||||
|         export FREETYPE_LIBS="" | ||||
|         export FREETYPE_CFLAGS="" | ||||
|     else | ||||
|         local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz) | ||||
|     if [ -e harfbuzz-stamp ]; then return; fi | ||||
|     python3 -m pip install meson ninja | ||||
| 
 | ||||
|     local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/harfbuzz-$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz) | ||||
|     (cd $out_dir \ | ||||
|             && meson setup build --buildtype=release -Dfreetype=enabled -Dglib=disabled) | ||||
|         && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled) | ||||
|     (cd $out_dir/build \ | ||||
|         && meson install) | ||||
|         if [[ "$MB_ML_LIBC" == "manylinux" ]]; then | ||||
|             cp /usr/local/lib64/libharfbuzz* /usr/local/lib | ||||
|         fi | ||||
|     fi | ||||
|     touch harfbuzz-stamp | ||||
| } | ||||
| 
 | ||||
| function build { | ||||
|     if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then | ||||
|         sudo chown -R runner /usr/local | ||||
|     fi | ||||
|     build_xz | ||||
|     if [ -z "$IS_ALPINE" ] && [ -z "$IS_MACOS" ]; then | ||||
|     if [ -z "$IS_ALPINE" ] && [ -z "$SANITIZER" ] && [ -z "$IS_MACOS" ]; then | ||||
|         yum remove -y zlib-devel | ||||
|     fi | ||||
|     if [[ -n "$IS_MACOS" ]] && [[ "$MACOSX_DEPLOYMENT_TARGET" == "10.10" || "$MACOSX_DEPLOYMENT_TARGET" == "10.13" ]]; then | ||||
|         build_new_zlib | ||||
|     else | ||||
|         build_zlib_ng | ||||
|     fi | ||||
| 
 | ||||
|     build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto | ||||
|     if [ -n "$IS_MACOS" ]; then | ||||
|         build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto | ||||
|         build_simple libXau 1.0.11 https://www.x.org/pub/individual/lib | ||||
|         build_simple libXau 1.0.12 https://www.x.org/pub/individual/lib | ||||
|         build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist | ||||
|         if [[ "$CIBW_ARCHS" == "arm64" ]]; then | ||||
|             cp /usr/local/share/pkgconfig/xcb-proto.pc /usr/local/lib/pkgconfig | ||||
|         fi | ||||
|     else | ||||
|         sed s/\${pc_sysrootdir\}// /usr/local/share/pkgconfig/xcb-proto.pc > /usr/local/lib/pkgconfig/xcb-proto.pc | ||||
|         sed s/\${pc_sysrootdir\}// $BUILD_PREFIX/share/pkgconfig/xcb-proto.pc > $BUILD_PREFIX/lib/pkgconfig/xcb-proto.pc | ||||
|     fi | ||||
|     build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib | ||||
| 
 | ||||
|     build_libjpeg_turbo | ||||
|     if [ -n "$IS_MACOS" ]; then | ||||
|         # Custom tiff build to include jpeg; by default, configure won't include | ||||
|         # headers/libs in the custom macOS prefix. Explicitly disable webp, | ||||
|         # libdeflate and zstd, because on x86_64 macs, it will pick up the | ||||
|         # Homebrew versions of those libraries from /usr/local. | ||||
|         build_simple tiff $TIFF_VERSION https://download.osgeo.org/libtiff tar.gz \ | ||||
|             --with-jpeg-include-dir=$BUILD_PREFIX/include --with-jpeg-lib-dir=$BUILD_PREFIX/lib \ | ||||
|             --disable-webp --disable-libdeflate --disable-zstd | ||||
|     else | ||||
|         build_tiff | ||||
|     fi | ||||
| 
 | ||||
|     build_libpng | ||||
|     build_lcms2 | ||||
|     build_openjpeg | ||||
|     if [ -f /usr/local/lib64/libopenjp2.so ]; then | ||||
|         cp /usr/local/lib64/libopenjp2.so /usr/local/lib | ||||
|     fi | ||||
| 
 | ||||
|     ORIGINAL_CFLAGS=$CFLAGS | ||||
|     CFLAGS="$CFLAGS -O3 -DNDEBUG" | ||||
|     webp_cflags="-O3 -DNDEBUG" | ||||
|     if [[ -n "$IS_MACOS" ]]; then | ||||
|         CFLAGS="$CFLAGS -Wl,-headerpad_max_install_names" | ||||
|         webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names" | ||||
|     fi | ||||
|     build_libwebp | ||||
|     CFLAGS=$ORIGINAL_CFLAGS | ||||
|     CFLAGS="$CFLAGS $webp_cflags" build_simple libwebp $LIBWEBP_VERSION \ | ||||
|         https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \ | ||||
|         --enable-libwebpmux --enable-libwebpdemux | ||||
| 
 | ||||
|     build_brotli | ||||
| 
 | ||||
|  | @ -135,37 +156,47 @@ function build { | |||
|     build_harfbuzz | ||||
| } | ||||
| 
 | ||||
| # Perform all dependency builds in the build subfolder. | ||||
| mkdir -p $WORKDIR | ||||
| pushd $WORKDIR > /dev/null | ||||
| 
 | ||||
| # Any stuff that you need to do before you start building the wheels | ||||
| # Runs in the root directory of this repository. | ||||
| curl -fsSL -o pillow-depends-main.zip https://github.com/python-pillow/pillow-depends/archive/main.zip | ||||
| untar pillow-depends-main.zip | ||||
| if [[ ! -d $WORKDIR/pillow-depends-main ]]; then | ||||
|   if [[ ! -f $PROJECTDIR/pillow-depends-main.zip ]]; then | ||||
|     echo "Download pillow dependency sources..." | ||||
|     curl -fSL -o $PROJECTDIR/pillow-depends-main.zip https://github.com/python-pillow/pillow-depends/archive/main.zip | ||||
|   fi | ||||
|   echo "Unpacking pillow dependency sources..." | ||||
|   untar $PROJECTDIR/pillow-depends-main.zip | ||||
| fi | ||||
| 
 | ||||
| if [[ -n "$IS_MACOS" ]]; then | ||||
|   # libtiff and libxcb cause a conflict with building libtiff and libxcb | ||||
|   # libxau and libxdmcp cause an issue on macOS < 11 | ||||
|   # remove cairo to fix building harfbuzz on arm64 | ||||
|   # remove lcms2 and libpng to fix building openjpeg on arm64 | ||||
|   # remove jpeg-turbo to avoid inclusion on arm64 | ||||
|   # remove webp and zstd to avoid inclusion on x86_64 | ||||
|   # curl from brew requires zstd, use system curl | ||||
|   brew remove --ignore-dependencies libpng libtiff libxcb libxau libxdmcp curl cairo lcms2 zstd | ||||
|   if [[ "$CIBW_ARCHS" == "arm64" ]]; then | ||||
|     brew remove --ignore-dependencies jpeg-turbo | ||||
|   else | ||||
|     brew remove --ignore-dependencies webp | ||||
|   fi | ||||
|     # Homebrew (or similar packaging environments) install can contain some of | ||||
|     # the libraries that we're going to build. However, they may be compiled | ||||
|     # with a MACOSX_DEPLOYMENT_TARGET that doesn't match what we want to use, | ||||
|     # and they may bring in other dependencies that we don't want. The same will | ||||
|     # be true of any other locations on the path. To avoid conflicts, strip the | ||||
|     # path down to the bare minimum (which, on macOS, won't include any | ||||
|     # development dependencies). | ||||
|     export PATH="$BUILD_PREFIX/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" | ||||
|     export CMAKE_PREFIX_PATH=$BUILD_PREFIX | ||||
| 
 | ||||
|   brew install meson pkg-config | ||||
| elif [[ "$MB_ML_LIBC" == "manylinux" ]]; then | ||||
|   if [[ "$HARFBUZZ_VERSION" != 8.5.0 ]]; then | ||||
|     yum install -y meson | ||||
|   fi | ||||
| else | ||||
|   apk add meson | ||||
|     # Ensure the basic structure of the build prefix directory exists. | ||||
|     mkdir -p "$BUILD_PREFIX/bin" | ||||
|     mkdir -p "$BUILD_PREFIX/lib" | ||||
| 
 | ||||
|     # Ensure pkg-config is available | ||||
|     build_pkg_config | ||||
|     # Ensure cmake is available | ||||
|     python3 -m pip install cmake | ||||
| fi | ||||
| 
 | ||||
| wrap_wheel_builder build | ||||
| 
 | ||||
| # Return to the project root to finish the build | ||||
| popd > /dev/null | ||||
| 
 | ||||
| # Append licenses | ||||
| for filename in wheels/dependency_licenses/*; do | ||||
|   echo -e "\n\n----\n\n$(basename $filename | cut -f 1 -d '.')\n" | cat >> LICENSE | ||||
|  |  | |||
							
								
								
									
										3
									
								
								.github/workflows/wheels-test.ps1
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -11,6 +11,9 @@ if ("$venv" -like "*\cibw-run-*\pp*-win_amd64\*") { | |||
| $env:path += ";$pillow\winbuild\build\bin\" | ||||
| & "$venv\Scripts\activate.ps1" | ||||
| & reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\python.exe" /v "GlobalFlag" /t REG_SZ /d "0x02000000" /f | ||||
| if ("$venv" -like "*\cibw-run-*-win_amd64\*") { | ||||
|   & python -m pip install numpy | ||||
| } | ||||
| cd $pillow | ||||
| & python -VV | ||||
| if (!$?) { exit $LASTEXITCODE } | ||||
|  |  | |||
							
								
								
									
										29
									
								
								.github/workflows/wheels-test.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -1,26 +1,31 @@ | |||
| #!/bin/bash | ||||
| set -e | ||||
| 
 | ||||
| # Ensure fribidi is installed by the system. | ||||
| if [[ "$OSTYPE" == "darwin"* ]]; then | ||||
|     brew install fribidi | ||||
|     export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig" | ||||
|     if [ -f /opt/homebrew/lib/libfribidi.dylib ]; then | ||||
|         sudo cp /opt/homebrew/lib/libfribidi.dylib /usr/local/lib | ||||
|     # If Homebrew is on the path during the build, it may leak into the wheels. | ||||
|     # However, we *do* need Homebrew to provide a copy of fribidi for | ||||
|     # testing purposes so that we can verify the fribidi shim works as expected. | ||||
|     if [[ "$(uname -m)" == "x86_64" ]]; then | ||||
|         HOMEBREW_PREFIX=/usr/local | ||||
|     else | ||||
|         HOMEBREW_PREFIX=/opt/homebrew | ||||
|     fi | ||||
|     $HOMEBREW_PREFIX/bin/brew install fribidi | ||||
| 
 | ||||
|     # Add the lib folder for fribidi so that the vendored library can be found. | ||||
|     # Don't use $HOMEWBREW_PREFIX/lib directly - use the lib folder where the | ||||
|     # installed copy of fribidi is cellared. This ensures we don't pick up the | ||||
|     # Homebrew version of any other library that we're dependent on (most notably, | ||||
|     # freetype). | ||||
|     export DYLD_LIBRARY_PATH=$(dirname $(realpath $HOMEBREW_PREFIX/lib/libfribidi.dylib)) | ||||
| elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then | ||||
|     apk add curl fribidi | ||||
| else | ||||
|     yum install -y fribidi | ||||
| fi | ||||
| 
 | ||||
| if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then | ||||
|   # TODO Update condition when NumPy supports free-threading | ||||
|   if [ $(python3 -c "import sysconfig;print(sysconfig.get_config_var('Py_GIL_DISABLED'))") == "1" ]; then | ||||
|     python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple | ||||
|   else | ||||
|     python3 -m pip install numpy | ||||
|   fi | ||||
| fi | ||||
| python3 -m pip install numpy | ||||
| 
 | ||||
| if [ ! -d "test-images-main" ]; then | ||||
|     curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip | ||||
|  |  | |||
							
								
								
									
										116
									
								
								.github/workflows/wheels.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -13,6 +13,7 @@ on: | |||
|     paths: | ||||
|       - ".ci/requirements-cibw.txt" | ||||
|       - ".github/workflows/wheel*" | ||||
|       - "pyproject.toml" | ||||
|       - "setup.py" | ||||
|       - "wheels/*" | ||||
|       - "winbuild/build_prepare.py" | ||||
|  | @ -23,6 +24,7 @@ on: | |||
|     paths: | ||||
|       - ".ci/requirements-cibw.txt" | ||||
|       - ".github/workflows/wheel*" | ||||
|       - "pyproject.toml" | ||||
|       - "setup.py" | ||||
|       - "wheels/*" | ||||
|       - "winbuild/build_prepare.py" | ||||
|  | @ -40,61 +42,7 @@ env: | |||
|   FORCE_COLOR: 1 | ||||
| 
 | ||||
| jobs: | ||||
|   build-1-QEMU-emulated-wheels: | ||||
|     if: github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' | ||||
|     name: aarch64 ${{ matrix.python-version }} ${{ matrix.spec }} | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         python-version: | ||||
|           - pp310 | ||||
|           - cp3{9,10,11} | ||||
|           - cp3{12,13} | ||||
|         spec: | ||||
|           - manylinux2014 | ||||
|           - manylinux_2_28 | ||||
|           - musllinux | ||||
|         exclude: | ||||
|           - { python-version: pp310, spec: musllinux } | ||||
| 
 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           submodules: true | ||||
| 
 | ||||
|       - uses: actions/setup-python@v5 | ||||
|         with: | ||||
|           python-version: "3.x" | ||||
| 
 | ||||
|       # https://github.com/docker/setup-qemu-action | ||||
|       - name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v3 | ||||
| 
 | ||||
|       - name: Install cibuildwheel | ||||
|         run: | | ||||
|           python3 -m pip install -r .ci/requirements-cibw.txt | ||||
| 
 | ||||
|       - name: Build wheels | ||||
|         run: | | ||||
|           python3 -m cibuildwheel --output-dir wheelhouse | ||||
|         env: | ||||
|           # Build only the currently selected Linux architecture (so we can | ||||
|           # parallelise for speed). | ||||
|           CIBW_ARCHS: "aarch64" | ||||
|           # Likewise, select only one Python version per job to speed this up. | ||||
|           CIBW_BUILD: "${{ matrix.python-version }}-${{ matrix.spec == 'musllinux' && 'musllinux' || 'manylinux' }}*" | ||||
|           CIBW_PRERELEASE_PYTHONS: True | ||||
|           # Extra options for manylinux. | ||||
|           CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.spec }} | ||||
|           CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.spec }} | ||||
| 
 | ||||
|       - uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: dist-qemu-${{ matrix.python-version }}-${{ matrix.spec }} | ||||
|           path: ./wheelhouse/*.whl | ||||
| 
 | ||||
|   build-2-native-wheels: | ||||
|   build-native-wheels: | ||||
|     if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow' | ||||
|     name: ${{ matrix.name }} | ||||
|     runs-on: ${{ matrix.os }} | ||||
|  | @ -102,12 +50,23 @@ jobs: | |||
|       fail-fast: false | ||||
|       matrix: | ||||
|         include: | ||||
|           - name: "macOS x86_64" | ||||
|           - name: "macOS 10.10 x86_64" | ||||
|             os: macos-13 | ||||
|             cibw_arch: x86_64 | ||||
|             build: "cp3{9,10,11}*" | ||||
|             macosx_deployment_target: "10.10" | ||||
|           - name: "macOS 10.13 x86_64" | ||||
|             os: macos-13 | ||||
|             cibw_arch: x86_64 | ||||
|             build: "cp3{12,13}*" | ||||
|             macosx_deployment_target: "10.13" | ||||
|           - name: "macOS 10.15 x86_64" | ||||
|             os: macos-13 | ||||
|             cibw_arch: x86_64 | ||||
|             build: "pp3*" | ||||
|             macosx_deployment_target: "10.15" | ||||
|           - name: "macOS arm64" | ||||
|             os: macos-14 | ||||
|             os: macos-latest | ||||
|             cibw_arch: arm64 | ||||
|             macosx_deployment_target: "11.0" | ||||
|           - name: "manylinux2014 and musllinux x86_64" | ||||
|  | @ -118,9 +77,18 @@ jobs: | |||
|             cibw_arch: x86_64 | ||||
|             build: "*manylinux*" | ||||
|             manylinux: "manylinux_2_28" | ||||
|           - name: "manylinux2014 and musllinux aarch64" | ||||
|             os: ubuntu-24.04-arm | ||||
|             cibw_arch: aarch64 | ||||
|           - name: "manylinux_2_28 aarch64" | ||||
|             os: ubuntu-24.04-arm | ||||
|             cibw_arch: aarch64 | ||||
|             build: "*manylinux*" | ||||
|             manylinux: "manylinux_2_28" | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|           submodules: true | ||||
| 
 | ||||
|       - uses: actions/setup-python@v5 | ||||
|  | @ -137,34 +105,42 @@ jobs: | |||
|         env: | ||||
|           CIBW_ARCHS: ${{ matrix.cibw_arch }} | ||||
|           CIBW_BUILD: ${{ matrix.build }} | ||||
|           CIBW_FREE_THREADED_SUPPORT: True | ||||
|           CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy | ||||
|           CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux }} | ||||
|           CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.manylinux }} | ||||
|           CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }} | ||||
|           CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }} | ||||
|           CIBW_PRERELEASE_PYTHONS: True | ||||
|           CIBW_SKIP: pp39-* | ||||
|           MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }} | ||||
| 
 | ||||
|       - uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: dist-${{ matrix.os }}-${{ matrix.cibw_arch }}${{ matrix.manylinux && format('-{0}', matrix.manylinux) }} | ||||
|           name: dist-${{ matrix.os }}${{ matrix.macosx_deployment_target && format('-{0}', matrix.macosx_deployment_target) }}-${{ matrix.cibw_arch }}${{ matrix.manylinux && format('-{0}', matrix.manylinux) }} | ||||
|           path: ./wheelhouse/*.whl | ||||
| 
 | ||||
|   windows: | ||||
|     if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow' | ||||
|     name: Windows ${{ matrix.cibw_arch }} | ||||
|     runs-on: windows-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         include: | ||||
|           - cibw_arch: x86 | ||||
|             os: windows-latest | ||||
|           - cibw_arch: AMD64 | ||||
|             os: windows-latest | ||||
|           - cibw_arch: ARM64 | ||||
|             os: windows-11-arm | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
| 
 | ||||
|       - name: Checkout extra test images | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|           repository: python-pillow/test-images | ||||
|           path: Tests\test-images | ||||
| 
 | ||||
|  | @ -184,7 +160,7 @@ jobs: | |||
|           # Install extra test images | ||||
|           xcopy /S /Y Tests\test-images\* Tests\images | ||||
| 
 | ||||
|           & python.exe winbuild\build_prepare.py -v --no-imagequant --architecture=${{ matrix.cibw_arch }} | ||||
|           & python.exe winbuild\build_prepare.py -v --no-imagequant --no-avif --architecture=${{ matrix.cibw_arch }} | ||||
|         shell: pwsh | ||||
| 
 | ||||
|       - name: Build wheels | ||||
|  | @ -211,8 +187,8 @@ jobs: | |||
|           CIBW_ARCHS: ${{ matrix.cibw_arch }} | ||||
|           CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd" | ||||
|           CIBW_CACHE_PATH: "C:\\cibw" | ||||
|           CIBW_FREE_THREADED_SUPPORT: True | ||||
|           CIBW_PRERELEASE_PYTHONS: True | ||||
|           CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy | ||||
|           CIBW_SKIP: pp39-* | ||||
|           CIBW_TEST_SKIP: "*-win_arm64" | ||||
|           CIBW_TEST_COMMAND: 'docker run --rm | ||||
|             -v {project}:C:\pillow | ||||
|  | @ -240,13 +216,13 @@ jobs: | |||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|       with: | ||||
|         persist-credentials: false | ||||
| 
 | ||||
|     - name: Set up Python | ||||
|       uses: actions/setup-python@v5 | ||||
|       with: | ||||
|         python-version: "3.x" | ||||
|         cache: pip | ||||
|         cache-dependency-path: "Makefile" | ||||
| 
 | ||||
|     - run: make sdist | ||||
| 
 | ||||
|  | @ -257,7 +233,7 @@ jobs: | |||
| 
 | ||||
|   scientific-python-nightly-wheels-publish: | ||||
|     if: github.repository_owner == 'python-pillow' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | ||||
|     needs: [build-2-native-wheels, windows] | ||||
|     needs: [build-native-wheels, windows] | ||||
|     runs-on: ubuntu-latest | ||||
|     name: Upload wheels to scientific-python-nightly-wheels | ||||
|     steps: | ||||
|  | @ -267,14 +243,14 @@ jobs: | |||
|           path: dist | ||||
|           merge-multiple: true | ||||
|       - name: Upload wheels to scientific-python-nightly-wheels | ||||
|         uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # 0.5.0 | ||||
|         uses: scientific-python/upload-nightly-action@b36e8c0c10dbcfd2e05bf95f17ef8c14fd708dbf # 0.6.2 | ||||
|         with: | ||||
|           artifacts_path: dist | ||||
|           anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} | ||||
| 
 | ||||
|   pypi-publish: | ||||
|     if: github.repository_owner == 'python-pillow' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') | ||||
|     needs: [build-1-QEMU-emulated-wheels, build-2-native-wheels, windows, sdist] | ||||
|     needs: [build-native-wheels, windows, sdist] | ||||
|     runs-on: ubuntu-latest | ||||
|     name: Upload release to PyPI | ||||
|     environment: | ||||
|  | @ -290,3 +266,5 @@ jobs: | |||
|           merge-multiple: true | ||||
|       - name: Publish to PyPI | ||||
|         uses: pypa/gh-action-pypi-publish@release/v1 | ||||
|         with: | ||||
|           attestations: true | ||||
|  |  | |||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -19,6 +19,7 @@ lib64/ | |||
| parts/ | ||||
| sdist/ | ||||
| var/ | ||||
| wheelhouse/ | ||||
| *.egg-info/ | ||||
| .installed.cfg | ||||
| *.egg | ||||
|  | @ -90,5 +91,9 @@ Tests/images/msp | |||
| Tests/images/picins | ||||
| Tests/images/sunraster | ||||
| 
 | ||||
| # Test and dependency downloads | ||||
| pillow-depends-main.zip | ||||
| pillow-test-images.zip | ||||
| 
 | ||||
| # pyinstaller | ||||
| *.spec | ||||
|  |  | |||
|  | @ -1,17 +1,17 @@ | |||
| repos: | ||||
|   - repo: https://github.com/astral-sh/ruff-pre-commit | ||||
|     rev: v0.6.3 | ||||
|     rev: v0.11.4 | ||||
|     hooks: | ||||
|       - id: ruff | ||||
|         args: [--exit-non-zero-on-fix] | ||||
| 
 | ||||
|   - repo: https://github.com/psf/black-pre-commit-mirror | ||||
|     rev: 24.8.0 | ||||
|     rev: 25.1.0 | ||||
|     hooks: | ||||
|       - id: black | ||||
| 
 | ||||
|   - repo: https://github.com/PyCQA/bandit | ||||
|     rev: 1.7.9 | ||||
|     rev: 1.8.3 | ||||
|     hooks: | ||||
|     - id: bandit | ||||
|       args: [--severity-level=high] | ||||
|  | @ -24,7 +24,7 @@ repos: | |||
|         exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$) | ||||
| 
 | ||||
|   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|     rev: v18.1.8 | ||||
|     rev: v20.1.0 | ||||
|     hooks: | ||||
|       - id: clang-format | ||||
|         types: [c] | ||||
|  | @ -36,7 +36,7 @@ repos: | |||
|       - id: rst-backticks | ||||
| 
 | ||||
|   - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: v4.6.0 | ||||
|     rev: v5.0.0 | ||||
|     hooks: | ||||
|       - id: check-executables-have-shebangs | ||||
|       - id: check-shebang-scripts-are-executable | ||||
|  | @ -44,35 +44,42 @@ repos: | |||
|       - id: check-json | ||||
|       - id: check-toml | ||||
|       - id: check-yaml | ||||
|         args: [--allow-multiple-documents] | ||||
|       - id: end-of-file-fixer | ||||
|         exclude: ^Tests/images/ | ||||
|       - id: trailing-whitespace | ||||
|         exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ | ||||
| 
 | ||||
|   - repo: https://github.com/python-jsonschema/check-jsonschema | ||||
|     rev: 0.29.2 | ||||
|     rev: 0.32.1 | ||||
|     hooks: | ||||
|       - id: check-github-workflows | ||||
|       - id: check-readthedocs | ||||
|       - id: check-renovate | ||||
| 
 | ||||
|   - repo: https://github.com/woodruffw/zizmor-pre-commit | ||||
|     rev: v1.5.2 | ||||
|     hooks: | ||||
|       - id: zizmor | ||||
| 
 | ||||
|   - repo: https://github.com/sphinx-contrib/sphinx-lint | ||||
|     rev: v0.9.1 | ||||
|     rev: v1.0.0 | ||||
|     hooks: | ||||
|       - id: sphinx-lint | ||||
| 
 | ||||
|   - repo: https://github.com/tox-dev/pyproject-fmt | ||||
|     rev: 2.2.1 | ||||
|     rev: v2.5.1 | ||||
|     hooks: | ||||
|       - id: pyproject-fmt | ||||
| 
 | ||||
|   - repo: https://github.com/abravalheri/validate-pyproject | ||||
|     rev: v0.19 | ||||
|     rev: v0.24.1 | ||||
|     hooks: | ||||
|       - id: validate-pyproject | ||||
|         additional_dependencies: [trove-classifiers>=2024.10.12] | ||||
| 
 | ||||
|   - repo: https://github.com/tox-dev/tox-ini-fmt | ||||
|     rev: 1.3.1 | ||||
|     rev: 1.5.0 | ||||
|     hooks: | ||||
|       - id: tox-ini-fmt | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| version: 2 | ||||
| 
 | ||||
| sphinx: | ||||
|   configuration: docs/conf.py | ||||
| 
 | ||||
| formats: [pdf] | ||||
| 
 | ||||
| build: | ||||
|  |  | |||
							
								
								
									
										75
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						|  | @ -2,9 +2,82 @@ | |||
| Changelog (Pillow) | ||||
| ================== | ||||
| 
 | ||||
| 11.0.0 (unreleased) | ||||
| 11.1.0 and newer | ||||
| ---------------- | ||||
| 
 | ||||
| See GitHub Releases: | ||||
| 
 | ||||
| - https://github.com/python-pillow/Pillow/releases | ||||
| 
 | ||||
| 11.0.0 (2024-10-15) | ||||
| ------------------- | ||||
| 
 | ||||
| - Update licence to MIT-CMU #8460 | ||||
|   [hugovk] | ||||
| 
 | ||||
| - Conditionally define ImageCms type hint to avoid requiring core #8197 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Support writing LONG8 offsets in AppendingTiffWriter #8417 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Use ImageFile.MAXBLOCK when saving TIFF images #8461 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Do not close provided file handles with libtiff when saving #8458 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Support ImageFilter.BuiltinFilter for I;16* images #8438 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Use ImagingCore.ptr instead of ImagingCore.id #8341 | ||||
|   [homm, radarhere, hugovk] | ||||
| 
 | ||||
| - Updated EPS mode when opening images without transparency #8281 | ||||
|   [Yay295, radarhere] | ||||
| 
 | ||||
| - Use transparency when combining P frames from APNGs #8443 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Support all resampling filters when resizing I;16* images #8422 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Free memory on early return #8413 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Cast int before potentially exceeding INT_MAX #8402 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Check image value before use #8400 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Improved copying imagequant libraries #8420 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Use Capsule for WebP saving #8386 | ||||
|   [homm, radarhere] | ||||
| 
 | ||||
| - Fixed writing multiple StripOffsets to TIFF #8317 | ||||
|   [Yay295, radarhere] | ||||
| 
 | ||||
| - Fix dereference before checking for NULL in ImagingTransformAffine #8398 | ||||
|   [PavlNekrasov] | ||||
| 
 | ||||
| - Use transposed size after opening for TIFF images #8390 | ||||
|   [radarhere, homm] | ||||
| 
 | ||||
| - Improve ImageFont error messages #8338 | ||||
|   [yngvem, radarhere, hugovk] | ||||
| 
 | ||||
| - Mention MAX_TEXT_CHUNK limit in PNG error message #8391 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Cast Dib handle to int #8385 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Accept float stroke widths #8369 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Deprecate ICNS (width, height, scale) sizes in favour of load(scale) #8352 | ||||
|   [radarhere] | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						|  | @ -5,9 +5,9 @@ The Python Imaging Library (PIL) is | |||
| 
 | ||||
| Pillow is the friendly PIL fork. It is | ||||
| 
 | ||||
|     Copyright © 2010-2024 by Jeffrey A. Clark and contributors | ||||
|     Copyright © 2010 by Jeffrey A. Clark and contributors | ||||
| 
 | ||||
| Like PIL, Pillow is licensed under the open source HPND License: | ||||
| Like PIL, Pillow is licensed under the open source MIT-CMU License: | ||||
| 
 | ||||
| By obtaining, using, and/or copying this software and/or its associated | ||||
| documentation, you agree that you have read, understood, and will comply | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ graft docs | |||
| graft _custom_build | ||||
| 
 | ||||
| # build/src control detritus | ||||
| exclude .appveyor.yml | ||||
| exclude .clang-format | ||||
| exclude .coveragerc | ||||
| exclude .editorconfig | ||||
|  |  | |||
							
								
								
									
										9
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						|  | @ -17,14 +17,16 @@ coverage: | |||
| .PHONY: 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: htmllive | ||||
| htmllive: | ||||
| 	$(MAKE) -C docs htmllive | ||||
| 
 | ||||
| .PHONY: doccheck | ||||
| doccheck: | ||||
| 	$(MAKE) doc | ||||
|  | @ -45,6 +47,7 @@ help: | |||
| 	@echo "  docserve           run an HTTP server on the docs directory" | ||||
| 	@echo "  html               make HTML docs" | ||||
| 	@echo "  htmlview           open the index page built by the html target in your browser" | ||||
| 	@echo "  htmllive           rebuild and reload HTML files in your browser" | ||||
| 	@echo "  install            make and install" | ||||
| 	@echo "  install-coverage   make and install with C coverage" | ||||
| 	@echo "  lint               run the lint checks" | ||||
|  | @ -117,7 +120,7 @@ lint-fix: | |||
| 	python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black | ||||
| 	python3 -m black . | ||||
| 	python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff | ||||
| 	python3 -m ruff --fix . | ||||
| 	python3 -m ruff check --fix . | ||||
| 
 | ||||
| .PHONY: mypy | ||||
| mypy: | ||||
|  |  | |||
|  | @ -42,16 +42,13 @@ As of 2019, Pillow development is | |||
|             <a href="https://github.com/python-pillow/Pillow/actions/workflows/test-docker.yml"><img | ||||
|                 alt="GitHub Actions build status (Test Docker)" | ||||
|                 src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></a> | ||||
|             <a href="https://ci.appveyor.com/project/python-pillow/Pillow"><img | ||||
|                 alt="AppVeyor CI build status (Windows)" | ||||
|                 src="https://img.shields.io/appveyor/build/python-pillow/Pillow/main.svg?label=Windows%20build"></a> | ||||
|             <a href="https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml"><img | ||||
|                 alt="GitHub Actions build status (Wheels)" | ||||
|                 src="https://github.com/python-pillow/Pillow/workflows/Wheels/badge.svg"></a> | ||||
|             <a href="https://app.codecov.io/gh/python-pillow/Pillow"><img | ||||
|                 alt="Code coverage" | ||||
|                 src="https://codecov.io/gh/python-pillow/Pillow/branch/main/graph/badge.svg"></a> | ||||
|             <a href="https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:pillow"><img | ||||
|             <a href="https://issues.oss-fuzz.com/issues?q=title:pillow"><img | ||||
|                 alt="Fuzzing Status" | ||||
|                 src="https://oss-fuzz-build-logs.storage.googleapis.com/badges/pillow.svg"></a> | ||||
|         </td> | ||||
|  | @ -107,7 +104,7 @@ The core image library is designed for fast access to data stored in a few basic | |||
|   - [Issues](https://github.com/python-pillow/Pillow/issues) | ||||
|   - [Pull requests](https://github.com/python-pillow/Pillow/pulls) | ||||
| - [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html) | ||||
| - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) | ||||
| - [Changelog](https://github.com/python-pillow/Pillow/releases) | ||||
|   - [Pre-fork](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst#pre-fork) | ||||
| 
 | ||||
| ## Report a Vulnerability | ||||
|  |  | |||
|  | @ -9,10 +9,9 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. | |||
| 
 | ||||
| * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154 | ||||
| * [ ] 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 [GitHub Actions](https://github.com/python-pillow/Pillow/actions) to confirm passing tests in `main` branch. | ||||
| * [ ] Check that all the wheel builds pass the tests in the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) jobs by manually triggering them. | ||||
| * [ ] 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 | ||||
|  | @ -34,13 +33,12 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. | |||
| Released as needed for security, installation or critical bug fixes. | ||||
| 
 | ||||
| * [ ] Make necessary changes in `main` branch. | ||||
| * [ ] Update `CHANGES.rst`. | ||||
| * [ ] Check out release branch e.g.: | ||||
|   ```bash | ||||
|   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`. | ||||
| * [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) to confirm passing tests in release branch e.g. `5.2.x`. | ||||
| * [ ] 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.: | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| Pillow Tests | ||||
| Pillow tests | ||||
| ============ | ||||
| 
 | ||||
| Test scripts are named ``test_xxx.py``. Helper classes and functions can be found in ``helper.py``. | ||||
|  |  | |||
|  | @ -9,6 +9,6 @@ from PIL import Image | |||
| 
 | ||||
| def test_j2k_overflow(tmp_path: Path) -> None: | ||||
|     im = Image.new("RGBA", (1024, 131584)) | ||||
|     target = str(tmp_path / "temp.jpc") | ||||
|     target = tmp_path / "temp.jpc" | ||||
|     with pytest.raises(OSError): | ||||
|         im.save(target) | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit sy | |||
| 
 | ||||
| 
 | ||||
| def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None: | ||||
|     f = str(tmp_path / "temp.png") | ||||
|     f = tmp_path / "temp.png" | ||||
|     im = Image.new("L", (xdim, ydim), 0) | ||||
|     im.save(f) | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit sy | |||
| def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None: | ||||
|     dtype = np.uint8 | ||||
|     a = np.zeros((xdim, ydim), dtype=dtype) | ||||
|     f = str(tmp_path / "temp.png") | ||||
|     f = tmp_path / "temp.png" | ||||
|     im = Image.fromarray(a, "L") | ||||
|     im.save(f) | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,19 +3,18 @@ from __future__ import annotations | |||
| import zlib | ||||
| from io import BytesIO | ||||
| 
 | ||||
| import pytest | ||||
| 
 | ||||
| from PIL import Image, ImageFile, PngImagePlugin | ||||
| 
 | ||||
| TEST_FILE = "Tests/images/png_decompression_dos.png" | ||||
| 
 | ||||
| 
 | ||||
| def test_ignore_dos_text() -> None: | ||||
|     ImageFile.LOAD_TRUNCATED_IMAGES = True | ||||
| def test_ignore_dos_text(monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|     monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True) | ||||
| 
 | ||||
|     try: | ||||
|         im = Image.open(TEST_FILE) | ||||
|     with Image.open(TEST_FILE) as im: | ||||
|         im.load() | ||||
|     finally: | ||||
|         ImageFile.LOAD_TRUNCATED_IMAGES = False | ||||
| 
 | ||||
|         assert isinstance(im, PngImagePlugin.PngImageFile) | ||||
|         for s in im.text.values(): | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| from __future__ import annotations | ||||
| 
 | ||||
| import platform | ||||
| import sys | ||||
| 
 | ||||
| from PIL import features | ||||
| 
 | ||||
| from .helper import is_pypy | ||||
| 
 | ||||
| 
 | ||||
| def test_wheel_modules() -> None: | ||||
|     expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp"} | ||||
|  | @ -34,10 +37,13 @@ def test_wheel_features() -> None: | |||
|         "fribidi", | ||||
|         "harfbuzz", | ||||
|         "libjpeg_turbo", | ||||
|         "zlib_ng", | ||||
|         "xcb", | ||||
|     } | ||||
| 
 | ||||
|     if sys.platform == "win32": | ||||
|         expected_features.remove("xcb") | ||||
|     elif sys.platform == "darwin" and not is_pypy() and platform.processor() != "arm": | ||||
|         expected_features.remove("zlib_ng") | ||||
| 
 | ||||
|     assert set(features.get_supported_features()) == expected_features | ||||
|  |  | |||
|  | @ -9,11 +9,11 @@ import os | |||
| import shutil | ||||
| import subprocess | ||||
| import sys | ||||
| import sysconfig | ||||
| import tempfile | ||||
| from collections.abc import Sequence | ||||
| from functools import lru_cache | ||||
| from io import BytesIO | ||||
| from pathlib import Path | ||||
| from typing import Any, Callable | ||||
| 
 | ||||
| import pytest | ||||
|  | @ -96,7 +96,10 @@ def assert_image_equal(a: Image.Image, b: Image.Image, msg: str | None = None) - | |||
| 
 | ||||
| 
 | ||||
| def assert_image_equal_tofile( | ||||
|     a: Image.Image, filename: str, msg: str | None = None, mode: str | None = None | ||||
|     a: Image.Image, | ||||
|     filename: str | Path, | ||||
|     msg: str | None = None, | ||||
|     mode: str | None = None, | ||||
| ) -> None: | ||||
|     with Image.open(filename) as img: | ||||
|         if mode: | ||||
|  | @ -137,21 +140,14 @@ def assert_image_similar( | |||
| 
 | ||||
| def assert_image_similar_tofile( | ||||
|     a: Image.Image, | ||||
|     filename: str, | ||||
|     filename: str | Path, | ||||
|     epsilon: float, | ||||
|     msg: str | None = None, | ||||
|     mode: str | None = None, | ||||
| ) -> None: | ||||
|     with Image.open(filename) as img: | ||||
|         if mode: | ||||
|             img = img.convert(mode) | ||||
|         assert_image_similar(a, img, epsilon, msg) | ||||
| 
 | ||||
| 
 | ||||
| def assert_all_same(items: Sequence[Any], msg: str | None = None) -> None: | ||||
|     assert items.count(items[0]) == len(items), msg | ||||
| 
 | ||||
| 
 | ||||
| def assert_not_all_same(items: Sequence[Any], msg: str | None = None) -> None: | ||||
|     assert items.count(items[0]) != len(items), msg | ||||
| 
 | ||||
|  | @ -327,16 +323,7 @@ def magick_command() -> list[str] | None: | |||
|     return None | ||||
| 
 | ||||
| 
 | ||||
| def on_appveyor() -> bool: | ||||
|     return "APPVEYOR" in os.environ | ||||
| 
 | ||||
| 
 | ||||
| def on_github_actions() -> bool: | ||||
|     return "GITHUB_ACTIONS" in os.environ | ||||
| 
 | ||||
| 
 | ||||
| def on_ci() -> bool: | ||||
|     # GitHub Actions and AppVeyor have "CI" | ||||
|     return "CI" in os.environ | ||||
| 
 | ||||
| 
 | ||||
|  | @ -358,10 +345,6 @@ def is_pypy() -> bool: | |||
|     return hasattr(sys, "pypy_translation_info") | ||||
| 
 | ||||
| 
 | ||||
| def is_mingw() -> bool: | ||||
|     return sysconfig.get_platform() == "mingw" | ||||
| 
 | ||||
| 
 | ||||
| class CachedProperty: | ||||
|     def __init__(self, func: Callable[[Any], Any]) -> None: | ||||
|         self.func = func | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/exif.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/hopper-missing-pixi.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/hopper.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/hopper.heif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/hopper_avif_write.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 30 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/icc_profile.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/icc_profile_none.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot0mir0.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot0mir1.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot1mir0.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot1mir1.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot2mir0.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot2mir1.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot3mir0.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/rot3mir1.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/star.avifs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/star.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/star.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/transparency.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/avif/xmp_tags_orientation.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/drawing_emf_ref_72_144.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 984 B | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/eps/1.bmp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/eps/1_boundingbox_after_imagedata.eps
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/eps/1_second_imagedata.eps
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB | 
| Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB | 
| Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB | 
| Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB | 
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB | 
| Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB | 
							
								
								
									
										260
									
								
								Tests/images/full_gimp_palette.gpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,260 @@ | |||
| GIMP Palette | ||||
| Name: fullpalette | ||||
| Columns: 4 | ||||
| # | ||||
|   0   0   0     Index 0 | ||||
|   1   1   1     Index 1 | ||||
|   2   2   2     Index 2 | ||||
|   3   3   3     Index 3 | ||||
|   4   4   4     Index 4 | ||||
|   5   5   5     Index 5 | ||||
|   6   6   6     Index 6 | ||||
|   7   7   7     Index 7 | ||||
|   8   8   8     Index 8 | ||||
|   9   9   9     Index 9 | ||||
|  10  10  10     Index 10 | ||||
|  11  11  11     Index 11 | ||||
|  12  12  12     Index 12 | ||||
|  13  13  13     Index 13 | ||||
|  14  14  14     Index 14 | ||||
|  15  15  15     Index 15 | ||||
|  16  16  16     Index 16 | ||||
|  17  17  17     Index 17 | ||||
|  18  18  18     Index 18 | ||||
|  19  19  19     Index 19 | ||||
|  20  20  20     Index 20 | ||||
|  21  21  21     Index 21 | ||||
|  22  22  22     Index 22 | ||||
|  23  23  23     Index 23 | ||||
|  24  24  24     Index 24 | ||||
|  25  25  25     Index 25 | ||||
|  26  26  26     Index 26 | ||||
|  27  27  27     Index 27 | ||||
|  28  28  28     Index 28 | ||||
|  29  29  29     Index 29 | ||||
|  30  30  30     Index 30 | ||||
|  31  31  31     Index 31 | ||||
|  32  32  32     Index 32 | ||||
|  33  33  33     Index 33 | ||||
|  34  34  34     Index 34 | ||||
|  35  35  35     Index 35 | ||||
|  36  36  36     Index 36 | ||||
|  37  37  37     Index 37 | ||||
|  38  38  38     Index 38 | ||||
|  39  39  39     Index 39 | ||||
|  40  40  40     Index 40 | ||||
|  41  41  41     Index 41 | ||||
|  42  42  42     Index 42 | ||||
|  43  43  43     Index 43 | ||||
|  44  44  44     Index 44 | ||||
|  45  45  45     Index 45 | ||||
|  46  46  46     Index 46 | ||||
|  47  47  47     Index 47 | ||||
|  48  48  48     Index 48 | ||||
|  49  49  49     Index 49 | ||||
|  50  50  50     Index 50 | ||||
|  51  51  51     Index 51 | ||||
|  52  52  52     Index 52 | ||||
|  53  53  53     Index 53 | ||||
|  54  54  54     Index 54 | ||||
|  55  55  55     Index 55 | ||||
|  56  56  56     Index 56 | ||||
|  57  57  57     Index 57 | ||||
|  58  58  58     Index 58 | ||||
|  59  59  59     Index 59 | ||||
|  60  60  60     Index 60 | ||||
|  61  61  61     Index 61 | ||||
|  62  62  62     Index 62 | ||||
|  63  63  63     Index 63 | ||||
|  64  64  64     Index 64 | ||||
|  65  65  65     Index 65 | ||||
|  66  66  66     Index 66 | ||||
|  67  67  67     Index 67 | ||||
|  68  68  68     Index 68 | ||||
|  69  69  69     Index 69 | ||||
|  70  70  70     Index 70 | ||||
|  71  71  71     Index 71 | ||||
|  72  72  72     Index 72 | ||||
|  73  73  73     Index 73 | ||||
|  74  74  74     Index 74 | ||||
|  75  75  75     Index 75 | ||||
|  76  76  76     Index 76 | ||||
|  77  77  77     Index 77 | ||||
|  78  78  78     Index 78 | ||||
|  79  79  79     Index 79 | ||||
|  80  80  80     Index 80 | ||||
|  81  81  81     Index 81 | ||||
|  82  82  82     Index 82 | ||||
|  83  83  83     Index 83 | ||||
|  84  84  84     Index 84 | ||||
|  85  85  85     Index 85 | ||||
|  86  86  86     Index 86 | ||||
|  87  87  87     Index 87 | ||||
|  88  88  88     Index 88 | ||||
|  89  89  89     Index 89 | ||||
|  90  90  90     Index 90 | ||||
|  91  91  91     Index 91 | ||||
|  92  92  92     Index 92 | ||||
|  93  93  93     Index 93 | ||||
|  94  94  94     Index 94 | ||||
|  95  95  95     Index 95 | ||||
|  96  96  96     Index 96 | ||||
|  97  97  97     Index 97 | ||||
|  98  98  98     Index 98 | ||||
|  99  99  99     Index 99 | ||||
| 100 100 100     Index 100 | ||||
| 101 101 101     Index 101 | ||||
| 102 102 102     Index 102 | ||||
| 103 103 103     Index 103 | ||||
| 104 104 104     Index 104 | ||||
| 105 105 105     Index 105 | ||||
| 106 106 106     Index 106 | ||||
| 107 107 107     Index 107 | ||||
| 108 108 108     Index 108 | ||||
| 109 109 109     Index 109 | ||||
| 110 110 110     Index 110 | ||||
| 111 111 111     Index 111 | ||||
| 112 112 112     Index 112 | ||||
| 113 113 113     Index 113 | ||||
| 114 114 114     Index 114 | ||||
| 115 115 115     Index 115 | ||||
| 116 116 116     Index 116 | ||||
| 117 117 117     Index 117 | ||||
| 118 118 118     Index 118 | ||||
| 119 119 119     Index 119 | ||||
| 120 120 120     Index 120 | ||||
| 121 121 121     Index 121 | ||||
| 122 122 122     Index 122 | ||||
| 123 123 123     Index 123 | ||||
| 124 124 124     Index 124 | ||||
| 125 125 125     Index 125 | ||||
| 126 126 126     Index 126 | ||||
| 127 127 127     Index 127 | ||||
| 128 128 128     Index 128 | ||||
| 129 129 129     Index 129 | ||||
| 130 130 130     Index 130 | ||||
| 131 131 131     Index 131 | ||||
| 132 132 132     Index 132 | ||||
| 133 133 133     Index 133 | ||||
| 134 134 134     Index 134 | ||||
| 135 135 135     Index 135 | ||||
| 136 136 136     Index 136 | ||||
| 137 137 137     Index 137 | ||||
| 138 138 138     Index 138 | ||||
| 139 139 139     Index 139 | ||||
| 140 140 140     Index 140 | ||||
| 141 141 141     Index 141 | ||||
| 142 142 142     Index 142 | ||||
| 143 143 143     Index 143 | ||||
| 144 144 144     Index 144 | ||||
| 145 145 145     Index 145 | ||||
| 146 146 146     Index 146 | ||||
| 147 147 147     Index 147 | ||||
| 148 148 148     Index 148 | ||||
| 149 149 149     Index 149 | ||||
| 150 150 150     Index 150 | ||||
| 151 151 151     Index 151 | ||||
| 152 152 152     Index 152 | ||||
| 153 153 153     Index 153 | ||||
| 154 154 154     Index 154 | ||||
| 155 155 155     Index 155 | ||||
| 156 156 156     Index 156 | ||||
| 157 157 157     Index 157 | ||||
| 158 158 158     Index 158 | ||||
| 159 159 159     Index 159 | ||||
| 160 160 160     Index 160 | ||||
| 161 161 161     Index 161 | ||||
| 162 162 162     Index 162 | ||||
| 163 163 163     Index 163 | ||||
| 164 164 164     Index 164 | ||||
| 165 165 165     Index 165 | ||||
| 166 166 166     Index 166 | ||||
| 167 167 167     Index 167 | ||||
| 168 168 168     Index 168 | ||||
| 169 169 169     Index 169 | ||||
| 170 170 170     Index 170 | ||||
| 171 171 171     Index 171 | ||||
| 172 172 172     Index 172 | ||||
| 173 173 173     Index 173 | ||||
| 174 174 174     Index 174 | ||||
| 175 175 175     Index 175 | ||||
| 176 176 176     Index 176 | ||||
| 177 177 177     Index 177 | ||||
| 178 178 178     Index 178 | ||||
| 179 179 179     Index 179 | ||||
| 180 180 180     Index 180 | ||||
| 181 181 181     Index 181 | ||||
| 182 182 182     Index 182 | ||||
| 183 183 183     Index 183 | ||||
| 184 184 184     Index 184 | ||||
| 185 185 185     Index 185 | ||||
| 186 186 186     Index 186 | ||||
| 187 187 187     Index 187 | ||||
| 188 188 188     Index 188 | ||||
| 189 189 189     Index 189 | ||||
| 190 190 190     Index 190 | ||||
| 191 191 191     Index 191 | ||||
| 192 192 192     Index 192 | ||||
| 193 193 193     Index 193 | ||||
| 194 194 194     Index 194 | ||||
| 195 195 195     Index 195 | ||||
| 196 196 196     Index 196 | ||||
| 197 197 197     Index 197 | ||||
| 198 198 198     Index 198 | ||||
| 199 199 199     Index 199 | ||||
| 200 200 200     Index 200 | ||||
| 201 201 201     Index 201 | ||||
| 202 202 202     Index 202 | ||||
| 203 203 203     Index 203 | ||||
| 204 204 204     Index 204 | ||||
| 205 205 205     Index 205 | ||||
| 206 206 206     Index 206 | ||||
| 207 207 207     Index 207 | ||||
| 208 208 208     Index 208 | ||||
| 209 209 209     Index 209 | ||||
| 210 210 210     Index 210 | ||||
| 211 211 211     Index 211 | ||||
| 212 212 212     Index 212 | ||||
| 213 213 213     Index 213 | ||||
| 214 214 214     Index 214 | ||||
| 215 215 215     Index 215 | ||||
| 216 216 216     Index 216 | ||||
| 217 217 217     Index 217 | ||||
| 218 218 218     Index 218 | ||||
| 219 219 219     Index 219 | ||||
| 220 220 220     Index 220 | ||||
| 221 221 221     Index 221 | ||||
| 222 222 222     Index 222 | ||||
| 223 223 223     Index 223 | ||||
| 224 224 224     Index 224 | ||||
| 225 225 225     Index 225 | ||||
| 226 226 226     Index 226 | ||||
| 227 227 227     Index 227 | ||||
| 228 228 228     Index 228 | ||||
| 229 229 229     Index 229 | ||||
| 230 230 230     Index 230 | ||||
| 231 231 231     Index 231 | ||||
| 232 232 232     Index 232 | ||||
| 233 233 233     Index 233 | ||||
| 234 234 234     Index 234 | ||||
| 235 235 235     Index 235 | ||||
| 236 236 236     Index 236 | ||||
| 237 237 237     Index 237 | ||||
| 238 238 238     Index 238 | ||||
| 239 239 239     Index 239 | ||||
| 240 240 240     Index 240 | ||||
| 241 241 241     Index 241 | ||||
| 242 242 242     Index 242 | ||||
| 243 243 243     Index 243 | ||||
| 244 244 244     Index 244 | ||||
| 245 245 245     Index 245 | ||||
| 246 246 246     Index 246 | ||||
| 247 247 247     Index 247 | ||||
| 248 248 248     Index 248 | ||||
| 249 249 249     Index 249 | ||||
| 250 250 250     Index 250 | ||||
| 251 251 251     Index 251 | ||||
| 252 252 252     Index 252 | ||||
| 253 253 253     Index 253 | ||||
| 254 254 254     Index 254 | ||||
| 255 255 255     Index 255 | ||||
| Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 533 B | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/imagedraw_stroke_float.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/jfif_unit_cm.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 391 B | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/multiline_text_justify.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
|  | @ -7,7 +7,7 @@ import fuzzers | |||
| import packaging | ||||
| import pytest | ||||
| 
 | ||||
| from PIL import Image, UnidentifiedImageError, features | ||||
| from PIL import Image, features | ||||
| from Tests.helper import skip_unless_feature | ||||
| 
 | ||||
| if sys.platform.startswith("win32"): | ||||
|  | @ -32,21 +32,17 @@ def test_fuzz_images(path: str) -> None: | |||
|             fuzzers.fuzz_image(f.read()) | ||||
|             assert True | ||||
|     except ( | ||||
|         # Known exceptions from Pillow | ||||
|         OSError, | ||||
|         SyntaxError, | ||||
|         MemoryError, | ||||
|         ValueError, | ||||
|         NotImplementedError, | ||||
|         OverflowError, | ||||
|     ): | ||||
|         # Known exceptions that are through from Pillow | ||||
|         assert True | ||||
|     except ( | ||||
|         # Known Image.* exceptions | ||||
|         Image.DecompressionBombError, | ||||
|         Image.DecompressionBombWarning, | ||||
|         UnidentifiedImageError, | ||||
|     ): | ||||
|         # Known Image.* exceptions | ||||
|         assert True | ||||
|     finally: | ||||
|         fuzzers.disable_decompressionbomb_error() | ||||
|  |  | |||
							
								
								
									
										164
									
								
								Tests/test_arrow.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,164 @@ | |||
| from __future__ import annotations | ||||
| 
 | ||||
| import pytest | ||||
| 
 | ||||
| from PIL import Image | ||||
| 
 | ||||
| from .helper import hopper | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.parametrize( | ||||
|     "mode, dest_modes", | ||||
|     ( | ||||
|         ("L", ["I", "F", "LA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr", "HSV"]), | ||||
|         ("I", ["L", "F"]),  # Technically I;32 can work for any 4x8bit storage. | ||||
|         ("F", ["I", "L", "LA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr", "HSV"]), | ||||
|         ("LA", ["L", "F"]), | ||||
|         ("RGB", ["L", "F"]), | ||||
|         ("RGBA", ["L", "F"]), | ||||
|         ("RGBX", ["L", "F"]), | ||||
|         ("CMYK", ["L", "F"]), | ||||
|         ("YCbCr", ["L", "F"]), | ||||
|         ("HSV", ["L", "F"]), | ||||
|     ), | ||||
| ) | ||||
| def test_invalid_array_type(mode: str, dest_modes: list[str]) -> None: | ||||
|     img = hopper(mode) | ||||
|     for dest_mode in dest_modes: | ||||
|         with pytest.raises(ValueError): | ||||
|             Image.fromarrow(img, dest_mode, img.size) | ||||
| 
 | ||||
| 
 | ||||
| def test_invalid_array_size() -> None: | ||||
|     img = hopper("RGB") | ||||
| 
 | ||||
|     assert img.size != (10, 10) | ||||
|     with pytest.raises(ValueError): | ||||
|         Image.fromarrow(img, "RGB", (10, 10)) | ||||
| 
 | ||||
| 
 | ||||
| def test_release_schema() -> None: | ||||
|     # these should not error out, valgrind should be clean | ||||
|     img = hopper("L") | ||||
|     schema = img.__arrow_c_schema__() | ||||
|     del schema | ||||
| 
 | ||||
| 
 | ||||
| def test_release_array() -> None: | ||||
|     # these should not error out, valgrind should be clean | ||||
|     img = hopper("L") | ||||
|     array, schema = img.__arrow_c_array__() | ||||
|     del array | ||||
|     del schema | ||||
| 
 | ||||
| 
 | ||||
| def test_readonly() -> None: | ||||
|     img = hopper("L") | ||||
|     reloaded = Image.fromarrow(img, img.mode, img.size) | ||||
|     assert reloaded.readonly == 1 | ||||
|     reloaded._readonly = 0 | ||||
|     assert reloaded.readonly == 1 | ||||
| 
 | ||||
| 
 | ||||
| def test_multiblock_l_image() -> None: | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in single channel mode | ||||
|     size = (4096, 2 * block_size // 4096) | ||||
|     img = Image.new("L", size, 128) | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         (schema, arr) = img.__arrow_c_array__() | ||||
| 
 | ||||
| 
 | ||||
| def test_multiblock_rgba_image() -> None: | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in 4 channel mode | ||||
|     size = (4096, (block_size // 4096) // 2) | ||||
|     img = Image.new("RGBA", size, (128, 127, 126, 125)) | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         (schema, arr) = img.__arrow_c_array__() | ||||
| 
 | ||||
| 
 | ||||
| def test_multiblock_l_schema() -> None: | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in single channel mode | ||||
|     size = (4096, 2 * block_size // 4096) | ||||
|     img = Image.new("L", size, 128) | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         img.__arrow_c_schema__() | ||||
| 
 | ||||
| 
 | ||||
| def test_multiblock_rgba_schema() -> None: | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in 4 channel mode | ||||
|     size = (4096, (block_size // 4096) // 2) | ||||
|     img = Image.new("RGBA", size, (128, 127, 126, 125)) | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         img.__arrow_c_schema__() | ||||
| 
 | ||||
| 
 | ||||
| def test_singleblock_l_image() -> None: | ||||
|     Image.core.set_use_block_allocator(1) | ||||
| 
 | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in 4 channel mode | ||||
|     size = (4096, 2 * (block_size // 4096)) | ||||
|     img = Image.new("L", size, 128) | ||||
|     assert img.im.isblock() | ||||
| 
 | ||||
|     (schema, arr) = img.__arrow_c_array__() | ||||
|     assert schema | ||||
|     assert arr | ||||
| 
 | ||||
|     Image.core.set_use_block_allocator(0) | ||||
| 
 | ||||
| 
 | ||||
| def test_singleblock_rgba_image() -> None: | ||||
|     Image.core.set_use_block_allocator(1) | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in 4 channel mode | ||||
|     size = (4096, (block_size // 4096) // 2) | ||||
|     img = Image.new("RGBA", size, (128, 127, 126, 125)) | ||||
|     assert img.im.isblock() | ||||
| 
 | ||||
|     (schema, arr) = img.__arrow_c_array__() | ||||
|     assert schema | ||||
|     assert arr | ||||
|     Image.core.set_use_block_allocator(0) | ||||
| 
 | ||||
| 
 | ||||
| def test_singleblock_l_schema() -> None: | ||||
|     Image.core.set_use_block_allocator(1) | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in single channel mode | ||||
|     size = (4096, 2 * block_size // 4096) | ||||
|     img = Image.new("L", size, 128) | ||||
|     assert img.im.isblock() | ||||
| 
 | ||||
|     schema = img.__arrow_c_schema__() | ||||
|     assert schema | ||||
|     Image.core.set_use_block_allocator(0) | ||||
| 
 | ||||
| 
 | ||||
| def test_singleblock_rgba_schema() -> None: | ||||
|     Image.core.set_use_block_allocator(1) | ||||
|     block_size = Image.core.get_block_size() | ||||
| 
 | ||||
|     # check a 2 block image in 4 channel mode | ||||
|     size = (4096, (block_size // 4096) // 2) | ||||
|     img = Image.new("RGBA", size, (128, 127, 126, 125)) | ||||
|     assert img.im.isblock() | ||||
| 
 | ||||
|     schema = img.__arrow_c_schema__() | ||||
|     assert schema | ||||
|     Image.core.set_use_block_allocator(0) | ||||
|  | @ -22,6 +22,8 @@ def test_bad() -> None: | |||
|     for f in get_files("b"): | ||||
|         # Assert that there is no unclosed file warning | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter("error") | ||||
| 
 | ||||
|             try: | ||||
|                 with Image.open(f) as im: | ||||
|                     im.load() | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ except ImportError: | |||
| class TestColorLut3DCoreAPI: | ||||
|     def generate_identity_table( | ||||
|         self, channels: int, size: int | tuple[int, int, int] | ||||
|     ) -> tuple[int, int, int, int, list[float]]: | ||||
|     ) -> tuple[int, tuple[int, int, int], list[float]]: | ||||
|         if isinstance(size, tuple): | ||||
|             size_1d, size_2d, size_3d = size | ||||
|         else: | ||||
|  | @ -39,9 +39,7 @@ class TestColorLut3DCoreAPI: | |||
|         ] | ||||
|         return ( | ||||
|             channels, | ||||
|             size_1d, | ||||
|             size_2d, | ||||
|             size_3d, | ||||
|             (size_1d, size_2d, size_3d), | ||||
|             [item for sublist in table for item in sublist], | ||||
|         ) | ||||
| 
 | ||||
|  | @ -89,21 +87,21 @@ class TestColorLut3DCoreAPI: | |||
| 
 | ||||
|         with pytest.raises(ValueError, match=r"size1D \* size2D \* size3D"): | ||||
|             im.im.color_lut_3d( | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 7 | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, (2, 2, 2), [0, 0, 0] * 7 | ||||
|             ) | ||||
| 
 | ||||
|         with pytest.raises(ValueError, match=r"size1D \* size2D \* size3D"): | ||||
|             im.im.color_lut_3d( | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 9 | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, (2, 2, 2), [0, 0, 0] * 9 | ||||
|             ) | ||||
| 
 | ||||
|         with pytest.raises(TypeError): | ||||
|             im.im.color_lut_3d( | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8 | ||||
|                 "RGB", Image.Resampling.BILINEAR, 3, (2, 2, 2), [0, 0, "0"] * 8 | ||||
|             ) | ||||
| 
 | ||||
|         with pytest.raises(TypeError): | ||||
|             im.im.color_lut_3d("RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, 16) | ||||
|             im.im.color_lut_3d("RGB", Image.Resampling.BILINEAR, 3, (2, 2, 2), 16) | ||||
| 
 | ||||
|     @pytest.mark.parametrize( | ||||
|         "lut_mode, table_channels, table_size", | ||||
|  | @ -264,7 +262,7 @@ class TestColorLut3DCoreAPI: | |||
|         assert_image_equal( | ||||
|             Image.merge('RGB', im.split()[::-1]), | ||||
|             im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, | ||||
|                     3, 2, 2, 2, [ | ||||
|                     3, (2, 2, 2), [ | ||||
|                         0, 0, 0,  0, 0, 1, | ||||
|                         0, 1, 0,  0, 1, 1, | ||||
| 
 | ||||
|  | @ -286,7 +284,7 @@ class TestColorLut3DCoreAPI: | |||
| 
 | ||||
|         # fmt: off | ||||
|         transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, | ||||
|                               3, 2, 2, 2, | ||||
|                               3, (2, 2, 2), | ||||
|                               [ | ||||
|                                   -1, -1, -1,   2, -1, -1, | ||||
|                                   -1,  2, -1,   2,  2, -1, | ||||
|  | @ -307,7 +305,7 @@ class TestColorLut3DCoreAPI: | |||
| 
 | ||||
|         # fmt: off | ||||
|         transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, | ||||
|                               3, 2, 2, 2, | ||||
|                               3, (2, 2, 2), | ||||
|                               [ | ||||
|                                   -3, -3, -3,   5, -3, -3, | ||||
|                                   -3,  5, -3,   5,  5, -3, | ||||
|  | @ -388,10 +386,12 @@ class TestColorLut3DFilter: | |||
| 
 | ||||
|         table = numpy.ones((7 * 6 * 5, 3), dtype=numpy.float16) | ||||
|         lut = ImageFilter.Color3DLUT((5, 6, 7), table) | ||||
|         assert isinstance(lut.table, numpy.ndarray) | ||||
|         assert lut.table.shape == (table.size,) | ||||
| 
 | ||||
|         table = numpy.ones((7 * 6 * 5 * 3), dtype=numpy.float16) | ||||
|         lut = ImageFilter.Color3DLUT((5, 6, 7), table) | ||||
|         assert isinstance(lut.table, numpy.ndarray) | ||||
|         assert lut.table.shape == (table.size,) | ||||
| 
 | ||||
|         # Check application | ||||
|  |  | |||
|  | @ -12,19 +12,16 @@ ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS | |||
| 
 | ||||
| 
 | ||||
| class TestDecompressionBomb: | ||||
|     def teardown_method(self) -> None: | ||||
|         Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT | ||||
| 
 | ||||
|     def test_no_warning_small_file(self) -> None: | ||||
|         # Implicit assert: no warning. | ||||
|         # A warning would cause a failure. | ||||
|         with Image.open(TEST_FILE): | ||||
|             pass | ||||
| 
 | ||||
|     def test_no_warning_no_limit(self) -> None: | ||||
|     def test_no_warning_no_limit(self, monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|         # Arrange | ||||
|         # Turn limit off | ||||
|         Image.MAX_IMAGE_PIXELS = None | ||||
|         monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", None) | ||||
|         assert Image.MAX_IMAGE_PIXELS is None | ||||
| 
 | ||||
|         # Act / Assert | ||||
|  | @ -33,18 +30,18 @@ class TestDecompressionBomb: | |||
|         with Image.open(TEST_FILE): | ||||
|             pass | ||||
| 
 | ||||
|     def test_warning(self) -> None: | ||||
|     def test_warning(self, monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|         # Set limit to trigger warning on the test file | ||||
|         Image.MAX_IMAGE_PIXELS = 128 * 128 - 1 | ||||
|         monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", 128 * 128 - 1) | ||||
|         assert Image.MAX_IMAGE_PIXELS == 128 * 128 - 1 | ||||
| 
 | ||||
|         with pytest.warns(Image.DecompressionBombWarning): | ||||
|             with Image.open(TEST_FILE): | ||||
|                 pass | ||||
| 
 | ||||
|     def test_exception(self) -> None: | ||||
|     def test_exception(self, monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|         # Set limit to trigger exception on the test file | ||||
|         Image.MAX_IMAGE_PIXELS = 64 * 128 - 1 | ||||
|         monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", 64 * 128 - 1) | ||||
|         assert Image.MAX_IMAGE_PIXELS == 64 * 128 - 1 | ||||
| 
 | ||||
|         with pytest.raises(Image.DecompressionBombError): | ||||
|  | @ -66,9 +63,9 @@ class TestDecompressionBomb: | |||
|             with pytest.raises(Image.DecompressionBombError): | ||||
|                 im.seek(1) | ||||
| 
 | ||||
|     def test_exception_gif_zero_width(self) -> None: | ||||
|     def test_exception_gif_zero_width(self, monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|         # Set limit to trigger exception on the test file | ||||
|         Image.MAX_IMAGE_PIXELS = 4 * 64 * 128 | ||||
|         monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", 4 * 64 * 128) | ||||
|         assert Image.MAX_IMAGE_PIXELS == 4 * 64 * 128 | ||||
| 
 | ||||
|         with pytest.raises(Image.DecompressionBombError): | ||||
|  |  | |||
|  | @ -36,9 +36,10 @@ def test_version() -> None: | |||
|         else: | ||||
|             assert function(name) == version | ||||
|             if name != "PIL": | ||||
|                 if name == "zlib" and version is not None: | ||||
|                 if version is not None: | ||||
|                     if name == "zlib" and features.check_feature("zlib_ng"): | ||||
|                         version = re.sub(".zlib-ng$", "", version) | ||||
|                 elif name == "libtiff" and version is not None: | ||||
|                     elif name == "libtiff": | ||||
|                         version = re.sub("t$", "", version) | ||||
|                 assert version is None or re.search(r"\d+(\.\d+)*$", version) | ||||
| 
 | ||||
|  | @ -56,17 +57,17 @@ def test_version() -> None: | |||
| 
 | ||||
| def test_webp_transparency() -> None: | ||||
|     with pytest.warns(DeprecationWarning): | ||||
|         assert features.check("transp_webp") == features.check_module("webp") | ||||
|         assert (features.check("transp_webp") or False) == features.check_module("webp") | ||||
| 
 | ||||
| 
 | ||||
| def test_webp_mux() -> None: | ||||
|     with pytest.warns(DeprecationWarning): | ||||
|         assert features.check("webp_mux") == features.check_module("webp") | ||||
|         assert (features.check("webp_mux") or False) == features.check_module("webp") | ||||
| 
 | ||||
| 
 | ||||
| def test_webp_anim() -> None: | ||||
|     with pytest.warns(DeprecationWarning): | ||||
|         assert features.check("webp_anim") == features.check_module("webp") | ||||
|         assert (features.check("webp_anim") or False) == features.check_module("webp") | ||||
| 
 | ||||
| 
 | ||||
| @skip_unless_feature("libjpeg_turbo") | ||||
|  |  | |||