mirror of
https://github.com/python-pillow/Pillow.git
synced 2026-01-11 19:21:18 +03:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627d8743b7 | ||
|
|
dcd52ebf65 | ||
|
|
d6e0a8d174 | ||
|
|
2210714a43 | ||
|
|
3d7801417a | ||
|
|
a85d3b135d | ||
|
|
932aa68d2a | ||
|
|
fe236d77a5 | ||
|
|
bc0e2c0e61 | ||
|
|
e66dd607f0 | ||
|
|
d5d8a91597 | ||
|
|
b8351fde41 | ||
|
|
36cf82ae76 | ||
|
|
525842215f | ||
|
|
844b10f894 | ||
|
|
555fb8371c | ||
|
|
0a1d6c3c61 | ||
|
|
00ec73dfd1 | ||
|
|
e924cfd181 | ||
|
|
2360d0df17 | ||
|
|
499b796556 | ||
|
|
5b677ca1c6 | ||
|
|
b71109d435 | ||
|
|
4337139f0c | ||
|
|
72931475f2 | ||
|
|
79357a2718 | ||
|
|
3abb62ed29 | ||
|
|
d06c8b3591 | ||
|
|
6a9960e8c1 |
|
|
@ -1,4 +1,4 @@
|
||||||
mypy==1.19.0
|
mypy==1.19.1
|
||||||
arro3-compute
|
arro3-compute
|
||||||
arro3-core
|
arro3-core
|
||||||
IceSpringPySideStubs-PyQt6
|
IceSpringPySideStubs-PyQt6
|
||||||
|
|
@ -9,7 +9,6 @@ packaging
|
||||||
pyarrow-stubs
|
pyarrow-stubs
|
||||||
pybind11
|
pybind11
|
||||||
pytest
|
pytest
|
||||||
sphinx
|
|
||||||
types-atheris
|
types-atheris
|
||||||
types-defusedxml
|
types-defusedxml
|
||||||
types-olefile
|
types-olefile
|
||||||
|
|
|
||||||
1
.github/renovate.json
vendored
1
.github/renovate.json
vendored
|
|
@ -6,6 +6,7 @@
|
||||||
"labels": [
|
"labels": [
|
||||||
"Dependency"
|
"Dependency"
|
||||||
],
|
],
|
||||||
|
"minimumReleaseAge": "7 days",
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"groupName": "github-actions",
|
"groupName": "github-actions",
|
||||||
|
|
|
||||||
4
.github/workflows/cifuzz.yml
vendored
4
.github/workflows/cifuzz.yml
vendored
|
|
@ -44,13 +44,13 @@ jobs:
|
||||||
language: python
|
language: python
|
||||||
dry-run: false
|
dry-run: false
|
||||||
- name: Upload New Crash
|
- name: Upload New Crash
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: artifacts
|
||||||
path: ./out/artifacts
|
path: ./out/artifacts
|
||||||
- name: Upload Legacy Crash
|
- name: Upload Legacy Crash
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: steps.run.outcome == 'success'
|
if: steps.run.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: crash
|
name: crash
|
||||||
|
|
|
||||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
|
|
@ -49,7 +49,7 @@ jobs:
|
||||||
run: python3 .github/workflows/system-info.py
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: Cache libimagequant
|
- name: Cache libimagequant
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-libimagequant
|
id: cache-libimagequant
|
||||||
with:
|
with:
|
||||||
path: ~/cache-libimagequant
|
path: ~/cache-libimagequant
|
||||||
|
|
|
||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.x"
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
- name: Lint
|
- name: Lint
|
||||||
|
|
|
||||||
11
.github/workflows/test-windows.yml
vendored
11
.github/workflows/test-windows.yml
vendored
|
|
@ -112,7 +112,7 @@ jobs:
|
||||||
|
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
id: build-cache
|
id: build-cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: winbuild\build
|
path: winbuild\build
|
||||||
key:
|
key:
|
||||||
|
|
@ -188,8 +188,9 @@ jobs:
|
||||||
# trim ~150MB for each job
|
# trim ~150MB for each job
|
||||||
- name: Optimize build cache
|
- name: Optimize build cache
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: rmdir /S /Q winbuild\build\src
|
run: |
|
||||||
shell: cmd
|
rm -rf winbuild\build\src
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build Pillow
|
- name: Build Pillow
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -206,9 +207,7 @@ jobs:
|
||||||
|
|
||||||
- name: Test Pillow
|
- name: Test Pillow
|
||||||
run: |
|
run: |
|
||||||
path %GITHUB_WORKSPACE%\winbuild\build\bin;%PATH%
|
|
||||||
.ci\test.cmd
|
.ci\test.cmd
|
||||||
shell: cmd
|
|
||||||
|
|
||||||
- name: Prepare to upload errors
|
- name: Prepare to upload errors
|
||||||
if: failure()
|
if: failure()
|
||||||
|
|
@ -217,7 +216,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: errors
|
name: errors
|
||||||
|
|
|
||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
|
@ -92,7 +92,7 @@ jobs:
|
||||||
|
|
||||||
- name: Cache libimagequant
|
- name: Cache libimagequant
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-libimagequant
|
id: cache-libimagequant
|
||||||
with:
|
with:
|
||||||
path: ~/cache-libimagequant
|
path: ~/cache-libimagequant
|
||||||
|
|
@ -143,7 +143,7 @@ jobs:
|
||||||
mkdir -p Tests/errors
|
mkdir -p Tests/errors
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: errors
|
name: errors
|
||||||
|
|
|
||||||
2
.github/workflows/wheels-dependencies.sh
vendored
2
.github/workflows/wheels-dependencies.sh
vendored
|
|
@ -267,7 +267,7 @@ function build {
|
||||||
|
|
||||||
build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto
|
build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto
|
||||||
if [[ -n "$IS_MACOS" ]]; then
|
if [[ -n "$IS_MACOS" ]]; then
|
||||||
build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto
|
build_simple xorgproto 2025.1 https://www.x.org/pub/individual/proto
|
||||||
build_simple libXau 1.0.12 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
|
build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist
|
||||||
else
|
else
|
||||||
|
|
|
||||||
48
.github/workflows/wheels.yml
vendored
48
.github/workflows/wheels.yml
vendored
|
|
@ -134,7 +134,7 @@ jobs:
|
||||||
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
|
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
|
||||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v5
|
- uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: dist-${{ matrix.name }}
|
name: dist-${{ matrix.name }}
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
|
|
@ -186,24 +186,18 @@ jobs:
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
run: |
|
run: |
|
||||||
setlocal EnableDelayedExpansion
|
for f in winbuild/build/license/*; do
|
||||||
for %%f in (winbuild\build\license\*) do (
|
name=$(basename "${f%.*}")
|
||||||
set x=%%~nf
|
# Skip FriBiDi license, it is not included in the wheel.
|
||||||
rem Skip FriBiDi license, it is not included in the wheel.
|
[[ $name == fribidi* ]] && continue
|
||||||
set fribidi=!x:~0,7!
|
# Skip imagequant license, it is not included in the wheel.
|
||||||
if NOT !fribidi!==fribidi (
|
[[ $name == libimagequant* ]] && continue
|
||||||
rem Skip imagequant license, it is not included in the wheel.
|
echo "" >> LICENSE
|
||||||
set libimagequant=!x:~0,13!
|
echo "===== $name =====" >> LICENSE
|
||||||
if NOT !libimagequant!==libimagequant (
|
echo "" >> LICENSE
|
||||||
echo. >> LICENSE
|
cat "$f" >> LICENSE
|
||||||
echo ===== %%~nf ===== >> LICENSE
|
done
|
||||||
echo. >> LICENSE
|
cmd //c "winbuild\\build\\build_env.cmd && $pythonLocation\\python.exe -m cibuildwheel . --output-dir wheelhouse"
|
||||||
type %%f >> LICENSE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
call winbuild\\build\\build_env.cmd
|
|
||||||
%pythonLocation%\python.exe -m cibuildwheel . --output-dir wheelhouse
|
|
||||||
env:
|
env:
|
||||||
CIBW_ARCHS: ${{ matrix.cibw_arch }}
|
CIBW_ARCHS: ${{ matrix.cibw_arch }}
|
||||||
CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
|
CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
|
||||||
|
|
@ -217,16 +211,16 @@ jobs:
|
||||||
-e CI -e GITHUB_ACTIONS
|
-e CI -e GITHUB_ACTIONS
|
||||||
mcr.microsoft.com/windows/servercore:ltsc2022
|
mcr.microsoft.com/windows/servercore:ltsc2022
|
||||||
powershell C:\pillow\.github\workflows\wheels-test.ps1 %CD%\..\venv-test'
|
powershell C:\pillow\.github\workflows\wheels-test.ps1 %CD%\..\venv-test'
|
||||||
shell: cmd
|
shell: bash
|
||||||
|
|
||||||
- name: Upload wheels
|
- name: Upload wheels
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: dist-windows-${{ matrix.cibw_arch }}
|
name: dist-windows-${{ matrix.cibw_arch }}
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
|
|
||||||
- name: Upload fribidi.dll
|
- name: Upload fribidi.dll
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: fribidi-windows-${{ matrix.cibw_arch }}
|
name: fribidi-windows-${{ matrix.cibw_arch }}
|
||||||
path: winbuild\build\bin\fribidi*
|
path: winbuild\build\bin\fribidi*
|
||||||
|
|
@ -246,7 +240,7 @@ jobs:
|
||||||
|
|
||||||
- run: make sdist
|
- run: make sdist
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v5
|
- uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: dist-sdist
|
name: dist-sdist
|
||||||
path: dist/*.tar.gz
|
path: dist/*.tar.gz
|
||||||
|
|
@ -256,7 +250,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Count dists
|
name: Count dists
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: dist-*
|
pattern: dist-*
|
||||||
path: dist
|
path: dist
|
||||||
|
|
@ -275,13 +269,13 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Upload wheels to scientific-python-nightly-wheels
|
name: Upload wheels to scientific-python-nightly-wheels
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: dist-!(sdist)*
|
pattern: dist-!(sdist)*
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Upload wheels to scientific-python-nightly-wheels
|
- name: Upload wheels to scientific-python-nightly-wheels
|
||||||
uses: scientific-python/upload-nightly-action@b36e8c0c10dbcfd2e05bf95f17ef8c14fd708dbf # 0.6.2
|
uses: scientific-python/upload-nightly-action@5748273c71e2d8d3a61f3a11a16421c8954f9ecf # 0.6.3
|
||||||
with:
|
with:
|
||||||
artifacts_path: dist
|
artifacts_path: dist
|
||||||
anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }}
|
anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }}
|
||||||
|
|
@ -297,7 +291,7 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: dist-*
|
pattern: dist-*
|
||||||
path: dist
|
path: dist
|
||||||
|
|
|
||||||
2
.github/zizmor.yml
vendored
2
.github/zizmor.yml
vendored
|
|
@ -1,7 +1,5 @@
|
||||||
# https://docs.zizmor.sh/configuration/
|
# https://docs.zizmor.sh/configuration/
|
||||||
rules:
|
rules:
|
||||||
obfuscation:
|
|
||||||
disable: true
|
|
||||||
unpinned-uses:
|
unpinned-uses:
|
||||||
config:
|
config:
|
||||||
policies:
|
policies:
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.14.7
|
rev: v0.14.10
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check
|
- id: ruff-check
|
||||||
args: [--exit-non-zero-on-fix]
|
args: [--exit-non-zero-on-fix]
|
||||||
|
|
||||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
rev: 25.11.0
|
rev: 25.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ repos:
|
||||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: v21.1.6
|
rev: v21.1.8
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
types: [c]
|
types: [c]
|
||||||
|
|
@ -51,14 +51,14 @@ repos:
|
||||||
exclude: ^\.github/.*TEMPLATE|^Tests/(fonts|images)/
|
exclude: ^\.github/.*TEMPLATE|^Tests/(fonts|images)/
|
||||||
|
|
||||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||||
rev: 0.35.0
|
rev: 0.36.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-github-workflows
|
- id: check-github-workflows
|
||||||
- id: check-readthedocs
|
- id: check-readthedocs
|
||||||
- id: check-renovate
|
- id: check-renovate
|
||||||
|
|
||||||
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
||||||
rev: v1.18.0
|
rev: v1.19.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: zizmor
|
- id: zizmor
|
||||||
|
|
||||||
|
|
@ -76,10 +76,10 @@ repos:
|
||||||
rev: v0.24.1
|
rev: v0.24.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: validate-pyproject
|
- id: validate-pyproject
|
||||||
additional_dependencies: [tomli, trove-classifiers>=2024.10.12]
|
additional_dependencies: [trove-classifiers>=2024.10.12]
|
||||||
|
|
||||||
- repo: https://github.com/tox-dev/tox-ini-fmt
|
- repo: https://github.com/tox-dev/tox-ini-fmt
|
||||||
rev: 1.7.0
|
rev: 1.7.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: tox-ini-fmt
|
- id: tox-ini-fmt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ def test_seek_tell() -> None:
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
assert layer_number == 2
|
assert layer_number == 2
|
||||||
|
|
||||||
|
|
||||||
def test_seek_eoferror() -> None:
|
def test_seek_eoferror() -> None:
|
||||||
|
|
@ -138,7 +138,7 @@ def test_icc_profile() -> None:
|
||||||
assert "icc_profile" in im.info
|
assert "icc_profile" in im.info
|
||||||
|
|
||||||
icc_profile = im.info["icc_profile"]
|
icc_profile = im.info["icc_profile"]
|
||||||
assert len(icc_profile) == 3144
|
assert len(icc_profile) == 3144
|
||||||
|
|
||||||
|
|
||||||
def test_no_icc_profile() -> None:
|
def test_no_icc_profile() -> None:
|
||||||
|
|
@ -158,17 +158,16 @@ def test_combined_larger_than_size() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_file,raises",
|
"test_file",
|
||||||
[
|
[
|
||||||
("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError),
|
"Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd",
|
||||||
("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError),
|
"Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_crashes(test_file: str, raises: type[Exception]) -> None:
|
def test_crashes(test_file: str) -> None:
|
||||||
with open(test_file, "rb") as f:
|
with pytest.raises(OSError):
|
||||||
with pytest.raises(raises):
|
with Image.open(test_file):
|
||||||
with Image.open(f):
|
pass
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
@ -179,8 +178,7 @@ def test_crashes(test_file: str, raises: type[Exception]) -> None:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_layer_crashes(test_file: str) -> None:
|
def test_layer_crashes(test_file: str) -> None:
|
||||||
with open(test_file, "rb") as f:
|
with Image.open(test_file) as im:
|
||||||
with Image.open(f) as im:
|
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
||||||
assert isinstance(im, PsdImagePlugin.PsdImageFile)
|
with pytest.raises(SyntaxError):
|
||||||
with pytest.raises(SyntaxError):
|
im.layers
|
||||||
im.layers
|
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,22 @@ def test_sanity() -> None:
|
||||||
draw.rectangle(list(range(4)))
|
draw.rectangle(list(range(4)))
|
||||||
|
|
||||||
|
|
||||||
def test_valueerror() -> None:
|
def test_new_color() -> None:
|
||||||
with Image.open("Tests/images/chi.gif") as im:
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
assert im.palette is not None
|
||||||
|
assert len(im.palette.colors) == 249
|
||||||
|
|
||||||
|
# Test drawing a new color onto the palette
|
||||||
draw.line((0, 0), fill=(0, 0, 0))
|
draw.line((0, 0), fill=(0, 0, 0))
|
||||||
|
assert im.palette is not None
|
||||||
|
assert len(im.palette.colors) == 250
|
||||||
|
assert im.palette.dirty
|
||||||
|
|
||||||
|
# Test drawing another new color, now that the palette is dirty
|
||||||
|
draw.point((0, 0), fill=(1, 0, 0))
|
||||||
|
assert len(im.palette.colors) == 251
|
||||||
|
assert im.convert("RGB").getpixel((0, 0)) == (1, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
def test_mode_mismatch() -> None:
|
def test_mode_mismatch() -> None:
|
||||||
|
|
|
||||||
|
|
@ -231,15 +231,15 @@ def test_negate() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_incorrect_mode() -> None:
|
def test_incorrect_mode() -> None:
|
||||||
im = hopper()
|
|
||||||
mop = ImageMorph.MorphOp(op_name="erosion8")
|
mop = ImageMorph.MorphOp(op_name="erosion8")
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
with hopper() as im:
|
||||||
mop.apply(im)
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
mop.apply(im)
|
||||||
mop.match(im)
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
mop.match(im)
|
||||||
mop.get_on_pixels(im)
|
with pytest.raises(ValueError, match="Image mode must be 1 or L"):
|
||||||
|
mop.get_on_pixels(im)
|
||||||
|
|
||||||
|
|
||||||
def test_add_patterns() -> None:
|
def test_add_patterns() -> None:
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,15 @@ import pytest
|
||||||
|
|
||||||
from PIL import __version__
|
from PIL import __version__
|
||||||
|
|
||||||
|
TYPE_CHECKING = False
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from importlib.metadata import PackageMetadata
|
||||||
|
|
||||||
pyroma = pytest.importorskip("pyroma", reason="Pyroma not installed")
|
pyroma = pytest.importorskip("pyroma", reason="Pyroma not installed")
|
||||||
|
|
||||||
|
|
||||||
def map_metadata_keys(md):
|
def map_metadata_keys(md: PackageMetadata) -> dict[str, str | list[str] | None]:
|
||||||
# Convert installed wheel metadata into canonical Core Metadata 2.4 format.
|
# Convert installed wheel metadata into canonical Core Metadata 2.4 format.
|
||||||
# This was a utility method in pyroma 4.3.3; it was removed in 5.0.
|
# This was a utility method in pyroma 4.3.3; it was removed in 5.0.
|
||||||
# This implementation is constructed from the relevant logic from
|
# This implementation is constructed from the relevant logic from
|
||||||
|
|
@ -17,16 +22,16 @@ def map_metadata_keys(md):
|
||||||
# upstream to Pyroma as https://github.com/regebro/pyroma/pull/116,
|
# upstream to Pyroma as https://github.com/regebro/pyroma/pull/116,
|
||||||
# so it may be possible to simplify this test in future.
|
# so it may be possible to simplify this test in future.
|
||||||
data = {}
|
data = {}
|
||||||
for key in set(md.keys()):
|
for key in set(md):
|
||||||
value = md.get_all(key)
|
value = md.get_all(key)
|
||||||
key = pyroma.projectdata.normalize(key)
|
key = pyroma.projectdata.normalize(key)
|
||||||
|
|
||||||
if len(value) == 1:
|
if value is not None and len(value) == 1:
|
||||||
value = value[0]
|
first_value = value[0]
|
||||||
if value.strip() == "UNKNOWN":
|
if first_value.strip() != "UNKNOWN":
|
||||||
continue
|
data[key] = first_value
|
||||||
|
else:
|
||||||
data[key] = value
|
data[key] = value
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import subprocess
|
||||||
|
|
||||||
TYPE_CHECKING = False
|
TYPE_CHECKING = False
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from sphinx.application import Sphinx
|
from typing import Any
|
||||||
|
|
||||||
DOC_NAME_REGEX = re.compile(r"releasenotes/\d+\.\d+\.\d+")
|
DOC_NAME_REGEX = re.compile(r"releasenotes/\d+\.\d+\.\d+")
|
||||||
VERSION_TITLE_REGEX = re.compile(r"^(\d+\.\d+\.\d+)\n-+\n")
|
VERSION_TITLE_REGEX = re.compile(r"^(\d+\.\d+\.\d+)\n-+\n")
|
||||||
|
|
@ -28,7 +28,7 @@ def get_date_for(git_version: str) -> str | None:
|
||||||
return out.split()[0]
|
return out.split()[0]
|
||||||
|
|
||||||
|
|
||||||
def add_date(app: Sphinx, doc_name: str, source: list[str]) -> None:
|
def add_date(app: Any, doc_name: str, source: list[str]) -> None:
|
||||||
if DOC_NAME_REGEX.match(doc_name) and (m := VERSION_TITLE_REGEX.match(source[0])):
|
if DOC_NAME_REGEX.match(doc_name) and (m := VERSION_TITLE_REGEX.match(source[0])):
|
||||||
old_title = m.group(1)
|
old_title = m.group(1)
|
||||||
|
|
||||||
|
|
@ -43,6 +43,6 @@ def add_date(app: Sphinx, doc_name: str, source: list[str]) -> None:
|
||||||
source[0] = result
|
source[0] = result
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> dict[str, bool]:
|
def setup(app: Any) -> dict[str, bool]:
|
||||||
app.connect("source-read", add_date)
|
app.connect("source-read", add_date)
|
||||||
return {"parallel_read_safe": True}
|
return {"parallel_read_safe": True}
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,7 @@ testpaths = [
|
||||||
python_version = "3.10"
|
python_version = "3.10"
|
||||||
pretty = true
|
pretty = true
|
||||||
disallow_any_generics = true
|
disallow_any_generics = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
enable_error_code = "ignore-without-code"
|
enable_error_code = "ignore-without-code"
|
||||||
extra_checks = true
|
extra_checks = true
|
||||||
follow_imports = "silent"
|
follow_imports = "silent"
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ def testimage() -> None:
|
||||||
('R', 'G', 'B')
|
('R', 'G', 'B')
|
||||||
>>> im.getbbox()
|
>>> im.getbbox()
|
||||||
(0, 0, 128, 128)
|
(0, 0, 128, 128)
|
||||||
>>> len(im.getdata())
|
>>> len(im.get_flattened_data())
|
||||||
16384
|
16384
|
||||||
>>> im.getextrema()
|
>>> im.getextrema()
|
||||||
((0, 255), (0, 255), (0, 255))
|
((0, 255), (0, 255), (0, 255))
|
||||||
|
|
|
||||||
3
setup.py
3
setup.py
|
|
@ -363,7 +363,6 @@ class pil_build_ext(build_ext):
|
||||||
("disable-platform-guessing", None, "Disable platform guessing"),
|
("disable-platform-guessing", None, "Disable platform guessing"),
|
||||||
("debug", None, "Debug logging"),
|
("debug", None, "Debug logging"),
|
||||||
]
|
]
|
||||||
+ [("add-imaging-libs=", None, "Add libs to _imaging build")]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -374,7 +373,6 @@ class pil_build_ext(build_ext):
|
||||||
self.disable_platform_guessing = self.check_configuration(
|
self.disable_platform_guessing = self.check_configuration(
|
||||||
"platform-guessing", "disable"
|
"platform-guessing", "disable"
|
||||||
)
|
)
|
||||||
self.add_imaging_libs = ""
|
|
||||||
build_ext.initialize_options(self)
|
build_ext.initialize_options(self)
|
||||||
for x in self.feature:
|
for x in self.feature:
|
||||||
setattr(self, f"disable_{x}", self.check_configuration(x, "disable"))
|
setattr(self, f"disable_{x}", self.check_configuration(x, "disable"))
|
||||||
|
|
@ -901,7 +899,6 @@ class pil_build_ext(build_ext):
|
||||||
# core library
|
# core library
|
||||||
|
|
||||||
libs: list[str | bool | None] = []
|
libs: list[str | bool | None] = []
|
||||||
libs.extend(self.add_imaging_libs.split())
|
|
||||||
defs: list[tuple[str, str | None]] = []
|
defs: list[tuple[str, str | None]] = []
|
||||||
if feature.get("tiff"):
|
if feature.get("tiff"):
|
||||||
libs.append(feature.get("tiff"))
|
libs.append(feature.get("tiff"))
|
||||||
|
|
|
||||||
|
|
@ -892,7 +892,9 @@ class Image:
|
||||||
else:
|
else:
|
||||||
self.im.putpalettealphas(self.info["transparency"])
|
self.im.putpalettealphas(self.info["transparency"])
|
||||||
self.palette.mode = "RGBA"
|
self.palette.mode = "RGBA"
|
||||||
else:
|
elif self.palette.mode != mode:
|
||||||
|
# If the palette rawmode is different to the mode,
|
||||||
|
# then update the Python palette data
|
||||||
self.palette.palette = self.im.getpalette(
|
self.palette.palette = self.im.getpalette(
|
||||||
self.palette.mode, self.palette.mode
|
self.palette.mode, self.palette.mode
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Master version for Pillow
|
# Master version for Pillow
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
__version__ = "12.1.0"
|
__version__ = "12.2.0.dev0"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user