Skip to content

Commit 2f942b8

Browse files
authored
Merge branch 'main' into jxl-support2
2 parents 095b38d + 7d78ac5 commit 2f942b8

27 files changed

Lines changed: 841 additions & 241 deletions

.github/workflows/wheels-dependencies.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ LIBPNG_VERSION=1.6.56
9696
JPEGTURBO_VERSION=3.1.4.1
9797
OPENJPEG_VERSION=2.5.4
9898
JPEGXL_VERSION=0.11.2
99-
XZ_VERSION=5.8.2
99+
XZ_VERSION=5.8.3
100100
ZSTD_VERSION=1.5.7
101101
TIFF_VERSION=4.7.1
102102
LCMS2_VERSION=2.18
496 Bytes
Binary file not shown.

Tests/images/trailer_loop.pdf

1.78 KB
Binary file not shown.

Tests/test_file_psd.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from __future__ import annotations
22

3+
import sys
34
import warnings
45

56
import pytest
67

78
from PIL import Image, PsdImagePlugin
89

9-
from .helper import assert_image_equal_tofile, assert_image_similar, hopper, is_pypy
10+
from .helper import (
11+
assert_image_equal_tofile,
12+
assert_image_similar,
13+
hopper,
14+
is_pypy,
15+
)
1016

1117
test_file = "Tests/images/hopper.psd"
1218

@@ -204,3 +210,17 @@ def test_bounds_crash(test_file: str) -> None:
204210

205211
with pytest.raises(ValueError):
206212
im.load()
213+
214+
215+
def test_bounds_crash_overflow() -> None:
216+
with Image.open("Tests/images/psd-oob-write-overflow.psd") as im:
217+
assert isinstance(im, PsdImagePlugin.PsdImageFile)
218+
im.load()
219+
if sys.maxsize <= 2**32:
220+
with pytest.raises(OverflowError):
221+
im.seek(im.n_frames)
222+
else:
223+
im.seek(im.n_frames)
224+
225+
with pytest.raises(ValueError):
226+
im.load()

Tests/test_file_spider.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414
TEST_FILE = "Tests/images/hopper.spider"
1515

1616

17-
def teardown_module() -> None:
18-
del Image.EXTENSION[".spider"]
19-
20-
2117
def test_sanity() -> None:
2218
with Image.open(TEST_FILE) as im:
2319
im.load()
@@ -67,6 +63,8 @@ def test_save(tmp_path: Path) -> None:
6763
assert im2.size == (128, 128)
6864
assert im2.format == "SPIDER"
6965

66+
del Image.EXTENSION[".spider"]
67+
7068

7169
@pytest.mark.parametrize("size", ((0, 1), (1, 0), (0, 0)))
7270
def test_save_zero(size: tuple[int, int]) -> None:

Tests/test_imagefile.py

Lines changed: 63 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,27 @@ def test_negative_tile_extents(self, xy: tuple[int, int]) -> None:
170170
with pytest.raises(SystemError, match="tile cannot extend outside image"):
171171
ImageFile._save(im, fp, [ImageFile._Tile("raw", xy + (1, 1), 0, "1")])
172172

173+
def test_extents_none(self) -> None:
174+
with Image.open("Tests/images/hopper.jpg") as im:
175+
im.tile = [im.tile[0]._replace(extents=None)]
176+
im.load()
177+
178+
for extents in ("invalid", (0,), ("0", "0", "0", "0")):
179+
with Image.open("Tests/images/hopper.jpg") as im:
180+
im.tile = [im.tile[0]._replace(extents=extents)] # type: ignore[arg-type]
181+
with pytest.raises(ValueError, match="invalid extents"):
182+
im.load()
183+
184+
im2 = Image.new("L", (1, 1))
185+
fp = BytesIO()
186+
tile = ImageFile._Tile("jpeg", None, 0, "L")
187+
ImageFile._save(im2, fp, [tile])
188+
189+
for extents in ("invalid", (0,), ("0", "0", "0", "0")):
190+
tile = tile._replace(extents=extents) # type: ignore[arg-type]
191+
with pytest.raises(ValueError, match="invalid extents"):
192+
ImageFile._save(im2, fp, [tile])
193+
173194
def test_no_format(self) -> None:
174195
buf = BytesIO(b"\x00" * 255)
175196

@@ -295,52 +316,38 @@ def test_setimage(self) -> None:
295316
with pytest.raises(ValueError):
296317
MockPyDecoder.last.set_as_raw(b"\x00")
297318

298-
def test_extents_none(self) -> None:
319+
@pytest.mark.parametrize(
320+
"extents",
321+
(
322+
(-10, yoff, xoff + xsize, yoff + ysize),
323+
(xoff, -10, xoff + xsize, yoff + ysize),
324+
(xoff, yoff, -10, yoff + ysize),
325+
(xoff, yoff, xoff + xsize, -10),
326+
(xoff, yoff, xoff + xsize + 100, yoff + ysize),
327+
(xoff, yoff, xoff + xsize, yoff + ysize + 100),
328+
),
329+
)
330+
def test_extents(self, extents: tuple[int, int, int, int]) -> None:
299331
buf = BytesIO(b"\x00" * 255)
300332

301333
im = MockImageFile(buf)
302-
im.tile = [ImageFile._Tile("MOCK", None, 32, None)]
303-
304-
im.load()
305-
306-
assert MockPyDecoder.last.state.xoff == 0
307-
assert MockPyDecoder.last.state.yoff == 0
308-
assert MockPyDecoder.last.state.xsize == 200
309-
assert MockPyDecoder.last.state.ysize == 200
310-
311-
def test_negsize(self) -> None:
312-
buf = BytesIO(b"\x00" * 255)
313-
314-
im = MockImageFile(buf)
315-
im.tile = [ImageFile._Tile("MOCK", (xoff, yoff, -10, yoff + ysize), 32, None)]
334+
im.tile = [ImageFile._Tile("MOCK", extents, 32, None)]
316335

317336
with pytest.raises(ValueError):
318337
im.load()
319338

320-
im.tile = [ImageFile._Tile("MOCK", (xoff, yoff, xoff + xsize, -10), 32, None)]
321-
with pytest.raises(ValueError):
322-
im.load()
323-
324-
def test_oversize(self) -> None:
339+
def test_extents_none(self) -> None:
325340
buf = BytesIO(b"\x00" * 255)
326341

327342
im = MockImageFile(buf)
328-
im.tile = [
329-
ImageFile._Tile(
330-
"MOCK", (xoff, yoff, xoff + xsize + 100, yoff + ysize), 32, None
331-
)
332-
]
343+
im.tile = [ImageFile._Tile("MOCK", None, 32, None)]
333344

334-
with pytest.raises(ValueError):
335-
im.load()
345+
im.load()
336346

337-
im.tile = [
338-
ImageFile._Tile(
339-
"MOCK", (xoff, yoff, xoff + xsize, yoff + ysize + 100), 32, None
340-
)
341-
]
342-
with pytest.raises(ValueError):
343-
im.load()
347+
assert MockPyDecoder.last.state.xoff == 0
348+
assert MockPyDecoder.last.state.yoff == 0
349+
assert MockPyDecoder.last.state.xsize == 200
350+
assert MockPyDecoder.last.state.ysize == 200
344351

345352
def test_decode(self) -> None:
346353
decoder = ImageFile.PyDecoder("")
@@ -371,72 +378,47 @@ def test_setimage(self) -> None:
371378
assert MockPyEncoder.last.state.xsize == xsize
372379
assert MockPyEncoder.last.state.ysize == ysize
373380

374-
def test_extents_none(self) -> None:
375-
buf = BytesIO(b"\x00" * 255)
376-
377-
im = MockImageFile(buf)
378-
im.tile = [ImageFile._Tile("MOCK", None, 32, None)]
379-
380-
fp = BytesIO()
381-
ImageFile._save(im, fp, [ImageFile._Tile("MOCK", None, 0, "RGB")])
382-
383-
assert MockPyEncoder.last
384-
assert MockPyEncoder.last.state.xoff == 0
385-
assert MockPyEncoder.last.state.yoff == 0
386-
assert MockPyEncoder.last.state.xsize == 200
387-
assert MockPyEncoder.last.state.ysize == 200
388-
389-
def test_negsize(self) -> None:
381+
@pytest.mark.parametrize(
382+
"extents",
383+
(
384+
(-10, yoff, xoff + xsize, yoff + ysize),
385+
(xoff, -10, xoff + xsize, yoff + ysize),
386+
(xoff, yoff, -10, yoff + ysize),
387+
(xoff, yoff, xoff + xsize, -10),
388+
(xoff, yoff, xoff + xsize + 100, yoff + ysize),
389+
(xoff, yoff, xoff + xsize, yoff + ysize + 100),
390+
),
391+
)
392+
def test_extents(self, extents: tuple[int, int, int, int]) -> None:
390393
buf = BytesIO(b"\x00" * 255)
391394

392395
im = MockImageFile(buf)
393396

394397
fp = BytesIO()
395398
MockPyEncoder.last = None
396399
with pytest.raises(ValueError):
397-
ImageFile._save(
398-
im,
399-
fp,
400-
[ImageFile._Tile("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")],
401-
)
400+
ImageFile._save(im, fp, [ImageFile._Tile("MOCK", extents, 0, "RGB")])
402401
last: MockPyEncoder | None = MockPyEncoder.last
403402
assert last
404403
assert last.cleanup_called
405404

406405
with pytest.raises(ValueError):
407-
ImageFile._save(
408-
im,
409-
fp,
410-
[ImageFile._Tile("MOCK", (xoff, yoff, xoff + xsize, -10), 0, "RGB")],
411-
)
406+
ImageFile._save(im, fp, [ImageFile._Tile("MOCK", extents, 0, "RGB")])
412407

413-
def test_oversize(self) -> None:
408+
def test_extents_none(self) -> None:
414409
buf = BytesIO(b"\x00" * 255)
415410

416411
im = MockImageFile(buf)
412+
im.tile = [ImageFile._Tile("MOCK", None, 32, None)]
417413

418414
fp = BytesIO()
419-
with pytest.raises(ValueError):
420-
ImageFile._save(
421-
im,
422-
fp,
423-
[
424-
ImageFile._Tile(
425-
"MOCK", (xoff, yoff, xoff + xsize + 100, yoff + ysize), 0, "RGB"
426-
)
427-
],
428-
)
415+
ImageFile._save(im, fp, [ImageFile._Tile("MOCK", None, 0, "RGB")])
429416

430-
with pytest.raises(ValueError):
431-
ImageFile._save(
432-
im,
433-
fp,
434-
[
435-
ImageFile._Tile(
436-
"MOCK", (xoff, yoff, xoff + xsize, yoff + ysize + 100), 0, "RGB"
437-
)
438-
],
439-
)
417+
assert MockPyEncoder.last
418+
assert MockPyEncoder.last.state.xoff == 0
419+
assert MockPyEncoder.last.state.yoff == 0
420+
assert MockPyEncoder.last.state.xsize == 200
421+
assert MockPyEncoder.last.state.ysize == 200
440422

441423
def test_encode(self) -> None:
442424
encoder = ImageFile.PyEncoder("")

Tests/test_imagepath.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def test_path() -> None:
5151
[0.0, 1.0],
5252
((0, 1),),
5353
[(0, 1)],
54+
[[0, 1]],
5455
((0.0, 1.0),),
5556
[(0.0, 1.0)],
5657
array.array("f", [0, 1]),
@@ -68,6 +69,34 @@ def test_path_constructors(
6869
assert list(p) == [(0.0, 1.0)]
6970

7071

72+
@pytest.mark.parametrize(
73+
"coords, expected",
74+
(
75+
([[0, 1], [2, 3]], [(0.0, 1.0), (2.0, 3.0)]),
76+
([[0.0, 1.0], [2.0, 3.0]], [(0.0, 1.0), (2.0, 3.0)]),
77+
),
78+
)
79+
def test_path_list_of_lists(
80+
coords: list[list[float]], expected: list[tuple[float, float]]
81+
) -> None:
82+
p = ImagePath.Path(coords)
83+
assert list(p) == expected
84+
85+
86+
@pytest.mark.parametrize(
87+
"coords, message",
88+
(
89+
([[1, 2, 3]], "coordinate list must contain exactly 2 coordinates"),
90+
([[1]], "coordinate list must contain exactly 2 coordinates"),
91+
([[[1, 2], [3, 4]]], "coordinate list must contain numbers"),
92+
([["a", "b"]], "coordinate list must contain numbers"),
93+
),
94+
)
95+
def test_invalid_list_coords(coords: list[list[object]], message: str) -> None:
96+
with pytest.raises(ValueError, match=message):
97+
ImagePath.Path(coords)
98+
99+
71100
def test_invalid_path_constructors() -> None:
72101
# Arrange / Act
73102
with pytest.raises(ValueError, match="incorrect coordinate type"):

0 commit comments

Comments
 (0)