Skip to content

Commit

Permalink
Merge branch 'main' into mode_enums
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere authored Feb 19, 2025
2 parents e76fcd5 + 769a14d commit cad6b3d
Show file tree
Hide file tree
Showing 127 changed files with 1,506 additions and 1,398 deletions.
4 changes: 2 additions & 2 deletions .ci/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/macos-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ brew install \
ghostscript \
jpeg-turbo \
libimagequant \
libraqm \
libtiff \
little-cms2 \
openjpeg \
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
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ jobs:
matrix:
os: ["ubuntu-latest"]
docker: [
# Run slower jobs first to give them a headstart and reduce waiting time
ubuntu-24.04-noble-ppc64le,
ubuntu-24.04-noble-s390x,
# Then run the remainder
alpine,
amazon-2-amd64,
amazon-2023-amd64,
Expand All @@ -56,9 +52,13 @@ jobs:
dockerTag: [main]
include:
- docker: "ubuntu-24.04-noble-ppc64le"
os: "ubuntu-22.04"
qemu-arch: "ppc64le"
dockerTag: main
- docker: "ubuntu-24.04-noble-s390x"
os: "ubuntu-22.04"
qemu-arch: "s390x"
dockerTag: main
- docker: "ubuntu-24.04-noble-arm64v8"
os: "ubuntu-24.04-arm"
dockerTag: main
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/test-mingw.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ jobs:
mingw-w64-x86_64-gcc \
mingw-w64-x86_64-ghostscript \
mingw-w64-x86_64-lcms2 \
mingw-w64-x86_64-libimagequant \
mingw-w64-x86_64-libjpeg-turbo \
mingw-w64-x86_64-libraqm \
mingw-w64-x86_64-libtiff \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
architecture: ["x64"]
os: ["windows-latest"]
include:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
"ubuntu-latest",
]
python-version: [
"pypy3.11",
"pypy3.10",
"3.14",
"3.13t",
Expand Down
29 changes: 16 additions & 13 deletions .github/workflows/wheels-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ ARCHIVE_SDIR=pillow-depends-main

# Package versions for fresh source builds
FREETYPE_VERSION=2.13.3
HARFBUZZ_VERSION=10.1.0
LIBPNG_VERSION=1.6.45
HARFBUZZ_VERSION=10.2.0
LIBPNG_VERSION=1.6.46
JPEGTURBO_VERSION=3.1.0
OPENJPEG_VERSION=2.5.3
XZ_VERSION=5.6.3
XZ_VERSION=5.6.4
TIFF_VERSION=4.6.0
LCMS2_VERSION=2.16
ZLIB_NG_VERSION=2.2.3
ZLIB_NG_VERSION=2.2.4
LIBWEBP_VERSION=1.5.0
BZIP2_VERSION=1.0.8
LIBXCB_VERSION=1.17.0
Expand All @@ -54,13 +54,10 @@ BROTLI_VERSION=1.1.0
function build_pkg_config {
if [ -e pkg-config-stamp ]; then return; fi
# This essentially duplicates the Homebrew recipe
ORIGINAL_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -Wno-int-conversion"
build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
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
CFLAGS=$ORIGINAL_CFLAGS
export PKG_CONFIG=$BUILD_PREFIX/bin/pkg-config
touch pkg-config-stamp
}
Expand All @@ -72,6 +69,14 @@ function build_zlib_ng {
&& ./configure --prefix=$BUILD_PREFIX --zlib-compat \
&& make -j4 \
&& make install)

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
}

Expand Down Expand Up @@ -130,15 +135,13 @@ function build {
build_lcms2
build_openjpeg

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_simple libwebp $LIBWEBP_VERSION \
CFLAGS="$CFLAGS $webp_cflags" build_simple libwebp $LIBWEBP_VERSION \
https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \
--enable-libwebpmux --enable-libwebpdemux
CFLAGS=$ORIGINAL_CFLAGS

build_brotli

Expand Down
14 changes: 7 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6
rev: v0.9.4
hooks:
- id: ruff
args: [--exit-non-zero-on-fix]

- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.10.0
rev: 25.1.0
hooks:
- id: black

- repo: https://github.com/PyCQA/bandit
rev: 1.8.0
rev: 1.8.2
hooks:
- id: bandit
args: [--severity-level=high]
Expand All @@ -24,7 +24,7 @@ repos:
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v19.1.6
rev: v19.1.7
hooks:
- id: clang-format
types: [c]
Expand All @@ -50,14 +50,14 @@ repos:
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.30.0
rev: 0.31.1
hooks:
- id: check-github-workflows
- id: check-readthedocs
- id: check-renovate

- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.0.0
rev: v1.3.0
hooks:
- id: zizmor

Expand All @@ -78,7 +78,7 @@ repos:
additional_dependencies: [trove-classifiers>=2024.10.12]

- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 1.4.1
rev: 1.5.0
hooks:
- id: tox-ini-fmt

Expand Down
21 changes: 10 additions & 11 deletions Tests/check_png_dos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@
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():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
assert isinstance(im, PngImagePlugin.PngImageFile)
for s in im.text.values():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"

for s in im.info.values():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
for s in im.info.values():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"


def test_dos_text() -> None:
Expand Down
5 changes: 0 additions & 5 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import shutil
import subprocess
import sys
import sysconfig
import tempfile
from collections.abc import Sequence
from functools import lru_cache
Expand Down Expand Up @@ -342,10 +341,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
Expand Down
Binary file added Tests/images/multiline_text_justify.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 9 additions & 11 deletions Tests/test_color_lut.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
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:
Expand All @@ -39,9 +39,7 @@ def generate_identity_table(
]
return (
channels,
size_1d,
size_2d,
size_3d,
(size_1d, size_2d, size_3d),
[item for sublist in table for item in sublist],
)

Expand Down Expand Up @@ -89,21 +87,21 @@ def test_wrong_args(self) -> None:

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",
Expand Down Expand Up @@ -264,7 +262,7 @@ def test_channels_order(self) -> None:
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,

Expand All @@ -286,7 +284,7 @@ def test_overflow(self) -> None:

# 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,
Expand All @@ -307,7 +305,7 @@ def test_overflow(self) -> None:

# 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,
Expand Down
19 changes: 8 additions & 11 deletions Tests/test_decompression_bomb.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,16 @@


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
Expand All @@ -33,18 +30,18 @@ def test_no_warning_no_limit(self) -> None:
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):
Expand All @@ -66,9 +63,9 @@ def test_exception_gif_extents(self) -> None:
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):
Expand Down
Loading

0 comments on commit cad6b3d

Please sign in to comment.