This commit is contained in:
Bruno Verachten 2026-05-15 05:18:52 -07:00 committed by GitHub
commit 52cd716e75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 49 additions and 20 deletions

View File

@ -205,6 +205,12 @@ function build_libavif {
-DAVIF_CODEC_DAV1D=LOCAL
)
fi
# libaom's riscv64 RVV code calls functions without declarations, which
# GCC 14 (manylinux_2_39) treats as errors. Disable arch-specific AOM
# optimizations for riscv64; QEMU-based builds don't benefit from them.
if [[ "$(uname -m)" == "riscv64" ]]; then
libavif_cmake_flags+=(-DAOM_TARGET_CPU=generic)
fi
local out_dir=$(fetch_unpack https://github.com/AOMediaCodec/libavif/archive/refs/tags/v$LIBAVIF_VERSION.tar.gz libavif-$LIBAVIF_VERSION.tar.gz)
@ -268,6 +274,13 @@ function build {
fi
build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib
# libjpeg-turbo 3.1.4.1 simdcoverage.c references riscv64 RVV SIMD
# functions that are only in upstream main and not yet released. Disable
# SIMD for riscv64 to avoid the build error; there is no production
# riscv64 SIMD support in this version anyway.
if [[ "$(uname -m)" == "riscv64" ]]; then
HOST_CMAKE_FLAGS="${HOST_CMAKE_FLAGS} -DWITH_SIMD=FALSE"
fi
build_libjpeg_turbo
if [[ -n "$IS_MACOS" ]]; then
# Custom tiff build to include jpeg; by default, configure won't include

View File

@ -36,7 +36,7 @@ concurrency:
cancel-in-progress: true
env:
EXPECTED_DISTS: 66
EXPECTED_DISTS: 68
FORCE_COLOR: 1
jobs:
@ -91,6 +91,12 @@ jobs:
os: ubuntu-24.04-arm
cibw_arch: aarch64
build: "*musllinux*"
- name: "manylinux_2_39 riscv64"
platform: linux
os: ubuntu-latest
cibw_arch: riscv64
build: "cp3{13,14}-manylinux*"
manylinux: "manylinux_2_39"
- name: "iOS arm64 device"
platform: ios
os: macos-latest
@ -113,6 +119,12 @@ jobs:
with:
python-version: "3.x"
- name: Set up QEMU
if: matrix.cibw_arch == 'riscv64'
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
with:
platforms: riscv64
- name: Install cibuildwheel
run: |
python3 -m pip install -r .ci/requirements-cibw.txt
@ -125,6 +137,7 @@ jobs:
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_BUILD: ${{ matrix.build }}
CIBW_ENABLE: cpython-prerelease pypy
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ matrix.manylinux }}
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

View File

@ -163,8 +163,11 @@ def assert_tuple_approx_equal(
pytest.fail(msg + ": " + repr(actuals) + " != " + repr(targets))
def timeout_unless_slower_valgrind(timeout: float) -> pytest.MarkDecorator:
if "PILLOW_VALGRIND_TEST" in os.environ:
def timeout_unless_slower(timeout: float) -> pytest.MarkDecorator:
if (
"PILLOW_VALGRIND_TEST" in os.environ
or os.environ.get("AUDITWHEEL_ARCH") == "riscv64"
):
return pytest.mark.pil_noop_mark()
return pytest.mark.timeout(timeout)

View File

@ -16,7 +16,7 @@ from .helper import (
is_win32,
mark_if_feature_version,
skip_unless_feature,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
@ -411,7 +411,7 @@ def test_emptyline() -> None:
assert image.format == "EPS"
@timeout_unless_slower_valgrind(5)
@timeout_unless_slower(5)
@pytest.mark.parametrize(
"test_file",
["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],

View File

@ -11,7 +11,7 @@ from .helper import (
assert_image_equal,
assert_image_equal_tofile,
is_pypy,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
# created as an export of a palette image from Gimp2.6
@ -196,7 +196,7 @@ def test_seek() -> None:
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
],
)
@timeout_unless_slower_valgrind(3)
@timeout_unless_slower(3)
def test_timeouts(test_file: str) -> None:
with open(test_file, "rb") as f:
with Image.open(f) as im:

View File

@ -31,7 +31,7 @@ from .helper import (
is_win32,
mark_if_feature_version,
skip_unless_feature,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
ElementTree: ModuleType | None
@ -1048,7 +1048,7 @@ class TestFileJpeg:
with pytest.raises(ValueError):
im.save(f, xmp=b"1" * 65505)
@timeout_unless_slower_valgrind(1)
@timeout_unless_slower(1)
def test_eof(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Even though this decoder never says that it is finished
# the image should still end when there is no new data

View File

@ -17,7 +17,7 @@ from .helper import (
hopper,
mark_if_feature_version,
skip_unless_feature,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
@ -344,7 +344,7 @@ def test_pdf_append_to_bytesio() -> None:
assert len(f.getvalue()) > initial_size
@timeout_unless_slower_valgrind(1)
@timeout_unless_slower(1)
@pytest.mark.parametrize("newline", (b"\r", b"\n"))
def test_redos(newline: bytes) -> None:
malicious = b" trailer<<>>" + newline * 3456

View File

@ -28,7 +28,7 @@ from .helper import (
hopper,
is_pypy,
is_win32,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
ElementTree: ModuleType | None
@ -1015,7 +1015,7 @@ class TestFileTiff:
with pytest.raises(OSError):
im.load()
@timeout_unless_slower_valgrind(6)
@timeout_unless_slower(6)
@pytest.mark.filterwarnings("ignore:Truncated File Read")
def test_timeout(self, monkeypatch: pytest.MonkeyPatch) -> None:
with Image.open("Tests/images/timeout-6646305047838720") as im:
@ -1028,7 +1028,7 @@ class TestFileTiff:
"Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif",
],
)
@timeout_unless_slower_valgrind(2)
@timeout_unless_slower(2)
def test_oom(self, test_file: str) -> None:
with pytest.raises(UnidentifiedImageError):
with pytest.warns(UserWarning, match="Corrupt EXIF data"):

View File

@ -34,7 +34,7 @@ from .helper import (
is_win32,
mark_if_feature_version,
skip_unless_feature,
timeout_unless_slower_valgrind,
timeout_unless_slower,
)
ElementTree: ModuleType | None
@ -577,7 +577,7 @@ class TestImage:
i = Image.new("RGB", [1, 1])
assert isinstance(i.size, tuple)
@timeout_unless_slower_valgrind(0.75)
@timeout_unless_slower(0.75)
@pytest.mark.parametrize("size", ((0, 100000000), (100000000, 0)))
def test_empty_image(self, size: tuple[int, int]) -> None:
Image.new("RGB", size)

View File

@ -7,7 +7,7 @@ import pytest
from PIL import Image, ImageDraw, ImageFont, _util, features
from .helper import assert_image_equal_tofile, timeout_unless_slower_valgrind
from .helper import assert_image_equal_tofile, timeout_unless_slower
fonts = [ImageFont.load_default_imagefont()]
if not features.check_module("freetype2"):
@ -78,7 +78,7 @@ def test_decompression_bomb() -> None:
font.getmask("A" * 1_000_000)
@timeout_unless_slower_valgrind(4)
@timeout_unless_slower(4)
def test_oom() -> None:
glyph = struct.pack(
">hhhhhhhhhh", 1, 0, -32767, -32767, 32767, 32767, -32767, -32767, 32767, 32767

View File

@ -7,7 +7,7 @@ import pytest
from PIL import Image, ImageMorph, _imagingmorph
from .helper import assert_image_equal_tofile, hopper, timeout_unless_slower_valgrind
from .helper import assert_image_equal_tofile, hopper, timeout_unless_slower
def string_to_img(image_string: str) -> Image.Image:
@ -266,7 +266,7 @@ def test_unknown_pattern() -> None:
@pytest.mark.parametrize(
"pattern", ("a pattern with a syntax error", "4:(" + "X" * 30000)
)
@timeout_unless_slower_valgrind(1)
@timeout_unless_slower(1)
def test_pattern_syntax_error(pattern: str) -> None:
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")