Skip to content

Commit

Permalink
Merge branch 'main' into libjpeg-turbo
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Jun 7, 2024
2 parents 47580f2 + d25aaed commit 13a33dc
Show file tree
Hide file tree
Showing 133 changed files with 1,471 additions and 1,109 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ install:
- 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.01-win64.zip
- 7z x nasm-win64.zip -oc:\
- choco install ghostscript --version=10.3.0
- choco install ghostscript --version=10.3.1
- path c:\nasm-2.16.01;C:\Program Files\gs\gs10.00.0\bin;%PATH%
- cd c:\pillow\winbuild\
- ps: |
Expand Down
2 changes: 1 addition & 1 deletion .ci/requirements-cibw.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cibuildwheel==2.17.0
cibuildwheel==2.18.1
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 88
DerivePointerAlignment: false
IndentGotoLabels: false
IndentWidth: 4
Language: Cpp
PointerAlignment: Right
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 @@ -86,7 +86,7 @@ jobs:
choco install nasm --no-progress
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
choco install ghostscript --version=10.3.0 --no-progress
choco install ghostscript --version=10.3.1 --no-progress
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
# Install extra test images
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/wheels-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ARCHIVE_SDIR=pillow-depends-main

# Package versions for fresh source builds
FREETYPE_VERSION=2.13.2
HARFBUZZ_VERSION=8.4.0
HARFBUZZ_VERSION=8.5.0
LIBPNG_VERSION=1.6.43
JPEGTURBO_VERSION=3.0.3
OPENJPEG_VERSION=2.5.2
Expand All @@ -33,9 +33,9 @@ if [[ -n "$IS_MACOS" ]] || [[ "$MB_ML_VER" != 2014 ]]; then
else
ZLIB_VERSION=1.2.8
fi
LIBWEBP_VERSION=1.3.2
LIBWEBP_VERSION=1.4.0
BZIP2_VERSION=1.0.8
LIBXCB_VERSION=1.16.1
LIBXCB_VERSION=1.17.0
BROTLI_VERSION=1.1.0

if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
Expand Down Expand Up @@ -70,7 +70,7 @@ function build {
fi
build_new_zlib

build_simple xcb-proto 1.16.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
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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wheels-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then
else
yum install -y fribidi
fi
if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then
if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ] && !([[ "$OSTYPE" == "darwin"* ]] && [[ $(python3 --version) == *"3.13."* ]]); then
python3 -m pip install numpy
fi

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- cp310
- cp311
- cp312
- cp313
spec:
- manylinux2014
- manylinux_2_28
Expand Down Expand Up @@ -80,6 +81,7 @@ jobs:
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 }}
Expand Down Expand Up @@ -133,6 +135,7 @@ jobs:
CIBW_BUILD: ${{ matrix.build }}
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
CIBW_PRERELEASE_PYTHONS: True
CIBW_SKIP: pp38-*
CIBW_TEST_SKIP: cp38-macosx_arm64
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
Expand Down Expand Up @@ -204,6 +207,7 @@ jobs:
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
CIBW_CACHE_PATH: "C:\\cibw"
CIBW_PRERELEASE_PYTHONS: True
CIBW_SKIP: pp38-*
CIBW_TEST_SKIP: "*-win_arm64"
CIBW_TEST_COMMAND: 'docker run --rm
Expand Down
19 changes: 13 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
rev: v0.4.7
hooks:
- id: ruff
args: [--exit-non-zero-on-fix]

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

Expand All @@ -23,13 +23,20 @@ repos:
- id: remove-tabs
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v18.1.5
hooks:
- id: clang-format
types: [c]
exclude: ^src/thirdparty/

- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
- id: rst-backticks

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
Expand All @@ -43,7 +50,7 @@ repos:
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.1
rev: 0.28.4
hooks:
- id: check-github-workflows
- id: check-readthedocs
Expand All @@ -55,12 +62,12 @@ repos:
- id: sphinx-lint

- repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.7.0
rev: 1.8.0
hooks:
- id: pyproject-fmt

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.16
rev: v0.18
hooks:
- id: validate-pyproject

Expand Down
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ Changelog (Pillow)
10.4.0 (unreleased)
-------------------

- Added ImageDraw circle() #8085
[void4, hugovk, radarhere]

- Add mypy target to Makefile #8077
[Yay295]

- Added more modes to Image.MODES #7984
[radarhere]

- Deprecate BGR;15, BGR;16 and BGR;24 modes #7978
[radarhere, hugovk]

Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,8 @@ lint-fix:
python3 -m black .
python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff
python3 -m ruff --fix .

.PHONY: mypy
mypy:
python3 -c "import tox" > /dev/null 2>&1 || python3 -m pip install tox
python3 -m tox -e mypy
52 changes: 11 additions & 41 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,6 @@
uploader = "github_actions"


modes = (
"1",
"L",
"LA",
"La",
"P",
"PA",
"F",
"I",
"I;16",
"I;16L",
"I;16B",
"I;16N",
"RGB",
"RGBA",
"RGBa",
"RGBX",
"BGR;15",
"BGR;16",
"BGR;24",
"CMYK",
"YCbCr",
"HSV",
"LAB",
)


def upload(a: Image.Image, b: Image.Image) -> str | None:
if uploader == "show":
# local img.show for errors.
Expand Down Expand Up @@ -201,12 +174,13 @@ def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
def skip_unless_feature_version(
feature: str, required: str, reason: str | None = None
) -> pytest.MarkDecorator:
if not features.check(feature):
version = features.version(feature)
if version is None:
return pytest.mark.skip(f"{feature} not available")
if reason is None:
reason = f"{feature} is older than {required}"
version_required = parse_version(required)
version_available = parse_version(features.version(feature))
version_available = parse_version(version)
return pytest.mark.skipif(version_available < version_required, reason=reason)


Expand All @@ -216,12 +190,13 @@ def mark_if_feature_version(
version_blacklist: str,
reason: str | None = None,
) -> pytest.MarkDecorator:
if not features.check(feature):
version = features.version(feature)
if version is None:
return pytest.mark.pil_noop_mark()
if reason is None:
reason = f"{feature} is {version_blacklist}"
version_required = parse_version(version_blacklist)
version_available = parse_version(features.version(feature))
version_available = parse_version(version)
if (
version_available.major == version_required.major
and version_available.minor == version_required.minor
Expand All @@ -247,16 +222,11 @@ def _get_mem_usage(self) -> float:
from resource import RUSAGE_SELF, getrusage

mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == "darwin":
# man 2 getrusage:
# ru_maxrss
# This is the maximum resident set size utilized (in bytes).
return mem / 1024 # Kb
# linux
# man 2 getrusage
# ru_maxrss (since Linux 2.6.32)
# This is the maximum resident set size used (in kilobytes).
return mem # Kb
# man 2 getrusage:
# ru_maxrss
# This is the maximum resident set size utilized
# in bytes on macOS, in kilobytes on Linux
return mem / 1024 if sys.platform == "darwin" else mem

def _test_leak(self, core: Callable[[], None]) -> None:
start_mem = self._get_mem_usage()
Expand Down
5 changes: 3 additions & 2 deletions Tests/oss-fuzz/test_fuzzers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@

if sys.platform.startswith("win32"):
pytest.skip("Fuzzer is linux only", allow_module_level=True)
if features.check("libjpeg_turbo"):
version = packaging.version.parse(features.version("libjpeg_turbo"))
libjpeg_turbo_version = features.version("libjpeg_turbo")
if libjpeg_turbo_version is not None:
version = packaging.version.parse(libjpeg_turbo_version)
if version.major == 2 and version.minor == 0:
pytestmark = pytest.mark.valgrind_known_error(
reason="Known failing with libjpeg_turbo 2.0"
Expand Down
10 changes: 7 additions & 3 deletions Tests/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_version() -> None:
# Check the correctness of the convenience function
# and the format of version numbers

def test(name: str, function: Callable[[str], bool]) -> None:
def test(name: str, function: Callable[[str], str | None]) -> None:
version = features.version(name)
if not features.check(name):
assert version is None
Expand Down Expand Up @@ -67,12 +67,16 @@ def test_webp_anim() -> None:

@skip_unless_feature("libjpeg_turbo")
def test_libjpeg_turbo_version() -> None:
assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo"))
version = features.version("libjpeg_turbo")
assert version is not None
assert re.search(r"\d+\.\d+\.\d+$", version)


@skip_unless_feature("libimagequant")
def test_libimagequant_version() -> None:
assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant"))
version = features.version("libimagequant")
assert version is not None
assert re.search(r"\d+\.\d+\.\d+$", version)


@pytest.mark.parametrize("feature", features.modules)
Expand Down
4 changes: 1 addition & 3 deletions Tests/test_file_eps.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,7 @@ def test_readline_psfile(tmp_path: Path) -> None:
strings = ["something", "else", "baz", "bif"]

def _test_readline(t: EpsImagePlugin.PSFile, ending: str) -> None:
ending = "Failure with line ending: %s" % (
"".join("%s" % ord(s) for s in ending)
)
ending = f"Failure with line ending: {''.join(str(ord(s)) for s in ending)}"
assert t.readline().strip("\r\n") == "something", ending
assert t.readline().strip("\r\n") == "else", ending
assert t.readline().strip("\r\n") == "baz", ending
Expand Down
5 changes: 3 additions & 2 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,10 +1252,11 @@ def test_palette_save_L(tmp_path: Path) -> None:

im = hopper("P")
im_l = Image.frombytes("L", im.size, im.tobytes())
palette = bytes(im.getpalette())
palette = im.getpalette()
assert palette is not None

out = str(tmp_path / "temp.gif")
im_l.save(out, palette=palette)
im_l.save(out, palette=bytes(palette))

with Image.open(out) as reloaded:
assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
Expand Down
8 changes: 5 additions & 3 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Im

def test_sanity(self) -> None:
# internal version number
assert re.search(r"\d+\.\d+$", features.version_codec("jpg"))
version = features.version_codec("jpg")
assert version is not None
assert re.search(r"\d+\.\d+$", version)

with Image.open(TEST_FILE) as im:
im.load()
Expand Down Expand Up @@ -152,7 +154,7 @@ def test_cmyk(self) -> None:
assert k > 0.9

def test_rgb(self) -> None:
def getchannels(im: Image.Image) -> tuple[int, int, int]:
def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, int, int]:
return tuple(v[0] for v in im.layer)

im = hopper()
Expand Down Expand Up @@ -441,7 +443,7 @@ def test_smooth(self) -> None:
assert_image(im1, im2.mode, im2.size)

def test_subsampling(self) -> None:
def getsampling(im: Image.Image):
def getsampling(im: JpegImagePlugin.JpegImageFile):
layer = im.layer
return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]

Expand Down
4 changes: 3 additions & 1 deletion Tests/test_file_jpeg2k.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ def roundtrip(im: Image.Image, **options: Any) -> Image.Image:

def test_sanity() -> None:
# Internal version number
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000"))
version = features.version_codec("jpg_2000")
assert version is not None
assert re.search(r"\d+\.\d+\.\d+$", version)

with Image.open("Tests/images/test-card-lossless.jp2") as im:
px = im.load()
Expand Down
7 changes: 5 additions & 2 deletions Tests/test_file_libtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> No

class TestFileLibTiff(LibTiffTestCase):
def test_version(self) -> None:
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff"))
version = features.version_codec("libtiff")
assert version is not None
assert re.search(r"\d+\.\d+\.\d+$", version)

def test_g4_tiff(self, tmp_path: Path) -> None:
"""Test the ordinary file path load path"""
Expand Down Expand Up @@ -666,7 +668,8 @@ def save_bytesio(compression: str | None = None) -> None:
pilim.save(buffer_io, format="tiff", compression=compression)
buffer_io.seek(0)

assert_image_similar_tofile(pilim, buffer_io, 0)
with Image.open(buffer_io) as saved_im:
assert_image_similar(pilim, saved_im, 0)

save_bytesio()
save_bytesio("raw")
Expand Down
Loading

0 comments on commit 13a33dc

Please sign in to comment.