mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-10 16:22:22 +03:00
Merge branch 'main' into valgrind-leakcheck
This commit is contained in:
commit
5b854b2332
|
@ -66,7 +66,7 @@ if [[ $(uname) != CYGWIN* ]]; then
|
||||||
pushd depends && ./install_raqm.sh && popd
|
pushd depends && ./install_raqm.sh && popd
|
||||||
|
|
||||||
# libavif
|
# libavif
|
||||||
pushd depends && CMAKE_POLICY_VERSION_MINIMUM=3.5 ./install_libavif.sh && popd
|
pushd depends && ./install_libavif.sh && popd
|
||||||
|
|
||||||
# extra test images
|
# extra test images
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
2
.github/workflows/wheels-dependencies.sh
vendored
2
.github/workflows/wheels-dependencies.sh
vendored
|
@ -39,7 +39,7 @@ ARCHIVE_SDIR=pillow-depends-main
|
||||||
# Package versions for fresh source builds
|
# Package versions for fresh source builds
|
||||||
FREETYPE_VERSION=2.13.3
|
FREETYPE_VERSION=2.13.3
|
||||||
HARFBUZZ_VERSION=11.2.1
|
HARFBUZZ_VERSION=11.2.1
|
||||||
LIBPNG_VERSION=1.6.47
|
LIBPNG_VERSION=1.6.48
|
||||||
JPEGTURBO_VERSION=3.1.0
|
JPEGTURBO_VERSION=3.1.0
|
||||||
OPENJPEG_VERSION=2.5.3
|
OPENJPEG_VERSION=2.5.3
|
||||||
XZ_VERSION=5.8.1
|
XZ_VERSION=5.8.1
|
||||||
|
|
|
@ -161,6 +161,12 @@ def assert_tuple_approx_equal(
|
||||||
pytest.fail(msg + ": " + repr(actuals) + " != " + repr(targets))
|
pytest.fail(msg + ": " + repr(actuals) + " != " + repr(targets))
|
||||||
|
|
||||||
|
|
||||||
|
def timeout_unless_slower_valgrind(timeout: float) -> pytest.MarkDecorator:
|
||||||
|
if "PILLOW_VALGRIND_TEST" in os.environ:
|
||||||
|
return pytest.mark.pil_noop_mark()
|
||||||
|
return pytest.mark.timeout(timeout)
|
||||||
|
|
||||||
|
|
||||||
def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
|
def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
|
||||||
reason = f"{feature} not available"
|
reason = f"{feature} not available"
|
||||||
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from .helper import (
|
||||||
is_win32,
|
is_win32,
|
||||||
mark_if_feature_version,
|
mark_if_feature_version,
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
)
|
)
|
||||||
|
|
||||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||||
|
@ -398,7 +399,7 @@ def test_emptyline() -> None:
|
||||||
assert image.format == "EPS"
|
assert image.format == "EPS"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(timeout=5)
|
@timeout_unless_slower_valgrind(5)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_file",
|
"test_file",
|
||||||
["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
|
["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
|
||||||
|
|
|
@ -7,7 +7,12 @@ import pytest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image, ImageFile
|
from PIL import FliImagePlugin, Image, ImageFile
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_equal_tofile, is_pypy
|
from .helper import (
|
||||||
|
assert_image_equal,
|
||||||
|
assert_image_equal_tofile,
|
||||||
|
is_pypy,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
|
)
|
||||||
|
|
||||||
# created as an export of a palette image from Gimp2.6
|
# created as an export of a palette image from Gimp2.6
|
||||||
# save as...-> hopper.fli, default options.
|
# save as...-> hopper.fli, default options.
|
||||||
|
@ -189,7 +194,7 @@ def test_seek() -> None:
|
||||||
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
|
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.timeout(timeout=3)
|
@timeout_unless_slower_valgrind(3)
|
||||||
def test_timeouts(test_file: str) -> None:
|
def test_timeouts(test_file: str) -> None:
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
|
|
|
@ -32,6 +32,7 @@ from .helper import (
|
||||||
is_win32,
|
is_win32,
|
||||||
mark_if_feature_version,
|
mark_if_feature_version,
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
)
|
)
|
||||||
|
|
||||||
ElementTree: ModuleType | None
|
ElementTree: ModuleType | None
|
||||||
|
@ -1033,10 +1034,7 @@ class TestFileJpeg:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
im.save(f, xmp=b"1" * 65505)
|
im.save(f, xmp=b"1" * 65505)
|
||||||
|
|
||||||
@pytest.mark.timeout(timeout=1)
|
@timeout_unless_slower_valgrind(1)
|
||||||
@pytest.mark.xfail(
|
|
||||||
"PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower"
|
|
||||||
)
|
|
||||||
def test_eof(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_eof(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
# Even though this decoder never says that it is finished
|
# Even though this decoder never says that it is finished
|
||||||
# the image should still end when there is no new data
|
# the image should still end when there is no new data
|
||||||
|
|
|
@ -13,7 +13,12 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image, PdfParser, features
|
from PIL import Image, PdfParser, features
|
||||||
|
|
||||||
from .helper import hopper, mark_if_feature_version, skip_unless_feature
|
from .helper import (
|
||||||
|
hopper,
|
||||||
|
mark_if_feature_version,
|
||||||
|
skip_unless_feature,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def helper_save_as_pdf(tmp_path: Path, mode: str, **kwargs: Any) -> str:
|
def helper_save_as_pdf(tmp_path: Path, mode: str, **kwargs: Any) -> str:
|
||||||
|
@ -339,8 +344,7 @@ def test_pdf_append_to_bytesio() -> None:
|
||||||
assert len(f.getvalue()) > initial_size
|
assert len(f.getvalue()) > initial_size
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(1)
|
@timeout_unless_slower_valgrind(1)
|
||||||
@pytest.mark.skipif("PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower")
|
|
||||||
@pytest.mark.parametrize("newline", (b"\r", b"\n"))
|
@pytest.mark.parametrize("newline", (b"\r", b"\n"))
|
||||||
def test_redos(newline: bytes) -> None:
|
def test_redos(newline: bytes) -> None:
|
||||||
malicious = b" trailer<<>>" + newline * 3456
|
malicious = b" trailer<<>>" + newline * 3456
|
||||||
|
|
|
@ -26,6 +26,7 @@ from .helper import (
|
||||||
hopper,
|
hopper,
|
||||||
is_pypy,
|
is_pypy,
|
||||||
is_win32,
|
is_win32,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
)
|
)
|
||||||
|
|
||||||
ElementTree: ModuleType | None
|
ElementTree: ModuleType | None
|
||||||
|
@ -988,11 +989,8 @@ class TestFileTiff:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
@pytest.mark.timeout(6)
|
@timeout_unless_slower_valgrind(6)
|
||||||
@pytest.mark.filterwarnings("ignore:Truncated File Read")
|
@pytest.mark.filterwarnings("ignore:Truncated File Read")
|
||||||
@pytest.mark.xfail(
|
|
||||||
"PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower"
|
|
||||||
)
|
|
||||||
def test_timeout(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_timeout(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
with Image.open("Tests/images/timeout-6646305047838720") as im:
|
with Image.open("Tests/images/timeout-6646305047838720") as im:
|
||||||
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
|
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
|
||||||
|
@ -1004,10 +1002,7 @@ class TestFileTiff:
|
||||||
"Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif",
|
"Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.timeout(2)
|
@timeout_unless_slower_valgrind(2)
|
||||||
@pytest.mark.xfail(
|
|
||||||
"PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower"
|
|
||||||
)
|
|
||||||
def test_oom(self, test_file: str) -> None:
|
def test_oom(self, test_file: str) -> None:
|
||||||
with pytest.raises(UnidentifiedImageError):
|
with pytest.raises(UnidentifiedImageError):
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
|
|
|
@ -34,6 +34,7 @@ from .helper import (
|
||||||
is_win32,
|
is_win32,
|
||||||
mark_if_feature_version,
|
mark_if_feature_version,
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
|
timeout_unless_slower_valgrind,
|
||||||
)
|
)
|
||||||
|
|
||||||
ElementTree: ModuleType | None
|
ElementTree: ModuleType | None
|
||||||
|
@ -572,10 +573,7 @@ class TestImage:
|
||||||
i = Image.new("RGB", [1, 1])
|
i = Image.new("RGB", [1, 1])
|
||||||
assert isinstance(i.size, tuple)
|
assert isinstance(i.size, tuple)
|
||||||
|
|
||||||
@pytest.mark.timeout(0.75)
|
@timeout_unless_slower_valgrind(0.75)
|
||||||
@pytest.mark.skipif(
|
|
||||||
"PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower"
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize("size", ((0, 100000000), (100000000, 0)))
|
@pytest.mark.parametrize("size", ((0, 100000000), (100000000, 0)))
|
||||||
def test_empty_image(self, size: tuple[int, int]) -> None:
|
def test_empty_image(self, size: tuple[int, int]) -> None:
|
||||||
Image.new("RGB", size)
|
Image.new("RGB", size)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
@ -8,7 +7,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont, _util, features
|
from PIL import Image, ImageDraw, ImageFont, _util, features
|
||||||
|
|
||||||
from .helper import assert_image_equal_tofile
|
from .helper import assert_image_equal_tofile, timeout_unless_slower_valgrind
|
||||||
|
|
||||||
fonts = [ImageFont.load_default_imagefont()]
|
fonts = [ImageFont.load_default_imagefont()]
|
||||||
if not features.check_module("freetype2"):
|
if not features.check_module("freetype2"):
|
||||||
|
@ -73,8 +72,7 @@ def test_decompression_bomb() -> None:
|
||||||
font.getmask("A" * 1_000_000)
|
font.getmask("A" * 1_000_000)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(4)
|
@timeout_unless_slower_valgrind(4)
|
||||||
@pytest.mark.xfail("PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower")
|
|
||||||
def test_oom() -> None:
|
def test_oom() -> None:
|
||||||
glyph = struct.pack(
|
glyph = struct.pack(
|
||||||
">hhhhhhhhhh", 1, 0, -32767, -32767, 32767, 32767, -32767, -32767, 32767, 32767
|
">hhhhhhhhhh", 1, 0, -32767, -32767, 32767, 32767, -32767, -32767, 32767, 32767
|
||||||
|
|
19
setup.py
19
setup.py
|
@ -224,13 +224,14 @@ def _add_directory(
|
||||||
path.insert(where, subdir)
|
path.insert(where, subdir)
|
||||||
|
|
||||||
|
|
||||||
def _find_include_file(self: pil_build_ext, include: str) -> int:
|
def _find_include_file(self: pil_build_ext, include: str) -> str | None:
|
||||||
for directory in self.compiler.include_dirs:
|
for directory in self.compiler.include_dirs:
|
||||||
_dbg("Checking for include file %s in %s", (include, directory))
|
_dbg("Checking for include file %s in %s", (include, directory))
|
||||||
if os.path.isfile(os.path.join(directory, include)):
|
path = os.path.join(directory, include)
|
||||||
|
if os.path.isfile(path):
|
||||||
_dbg("Found %s", include)
|
_dbg("Found %s", include)
|
||||||
return 1
|
return path
|
||||||
return 0
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _find_library_file(self: pil_build_ext, library: str) -> str | None:
|
def _find_library_file(self: pil_build_ext, library: str) -> str | None:
|
||||||
|
@ -852,9 +853,13 @@ class pil_build_ext(build_ext):
|
||||||
|
|
||||||
if feature.want("avif"):
|
if feature.want("avif"):
|
||||||
_dbg("Looking for avif")
|
_dbg("Looking for avif")
|
||||||
if _find_include_file(self, "avif/avif.h"):
|
if avif_h := _find_include_file(self, "avif/avif.h"):
|
||||||
if _find_library_file(self, "avif"):
|
with open(avif_h, "rb") as fp:
|
||||||
feature.set("avif", "avif")
|
major_version = int(
|
||||||
|
fp.read().split(b"#define AVIF_VERSION_MAJOR ")[1].split()[0]
|
||||||
|
)
|
||||||
|
if major_version >= 1 and _find_library_file(self, "avif"):
|
||||||
|
feature.set("avif", "avif")
|
||||||
|
|
||||||
for f in feature:
|
for f in feature:
|
||||||
if not feature.get(f) and feature.require(f):
|
if not feature.get(f) and feature.require(f):
|
||||||
|
|
|
@ -81,7 +81,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# check placable header
|
# check placable header
|
||||||
s = self.fp.read(80)
|
s = self.fp.read(44)
|
||||||
|
|
||||||
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
||||||
# placeable windows metafile
|
# placeable windows metafile
|
||||||
|
|
|
@ -118,7 +118,7 @@ V = {
|
||||||
"LCMS2": "2.17",
|
"LCMS2": "2.17",
|
||||||
"LIBAVIF": "1.3.0",
|
"LIBAVIF": "1.3.0",
|
||||||
"LIBIMAGEQUANT": "4.3.4",
|
"LIBIMAGEQUANT": "4.3.4",
|
||||||
"LIBPNG": "1.6.47",
|
"LIBPNG": "1.6.48",
|
||||||
"LIBWEBP": "1.5.0",
|
"LIBWEBP": "1.5.0",
|
||||||
"OPENJPEG": "2.5.3",
|
"OPENJPEG": "2.5.3",
|
||||||
"TIFF": "4.7.0",
|
"TIFF": "4.7.0",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user