Use monkeypatch (#8707)

Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
This commit is contained in:
Andrew Murray 2025-01-27 21:17:51 +11:00 committed by GitHub
parent 569b785371
commit e19a1496c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 153 additions and 193 deletions

View File

@ -3,26 +3,25 @@ from __future__ import annotations
import zlib import zlib
from io import BytesIO from io import BytesIO
import pytest
from PIL import Image, ImageFile, PngImagePlugin from PIL import Image, ImageFile, PngImagePlugin
TEST_FILE = "Tests/images/png_decompression_dos.png" TEST_FILE = "Tests/images/png_decompression_dos.png"
def test_ignore_dos_text() -> None: def test_ignore_dos_text(monkeypatch: pytest.MonkeyPatch) -> None:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: with Image.open(TEST_FILE) as im:
im = Image.open(TEST_FILE)
im.load() im.load()
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
assert isinstance(im, PngImagePlugin.PngImageFile) assert isinstance(im, PngImagePlugin.PngImageFile)
for s in im.text.values(): for s in im.text.values():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M" assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
for s in im.info.values(): for s in im.info.values():
assert len(s) < 1024 * 1024, "Text chunk larger than 1M" assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
def test_dos_text() -> None: def test_dos_text() -> None:

View File

@ -12,19 +12,16 @@ ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS
class TestDecompressionBomb: class TestDecompressionBomb:
def teardown_method(self) -> None:
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
def test_no_warning_small_file(self) -> None: def test_no_warning_small_file(self) -> None:
# Implicit assert: no warning. # Implicit assert: no warning.
# A warning would cause a failure. # A warning would cause a failure.
with Image.open(TEST_FILE): with Image.open(TEST_FILE):
pass pass
def test_no_warning_no_limit(self) -> None: def test_no_warning_no_limit(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange # Arrange
# Turn limit off # Turn limit off
Image.MAX_IMAGE_PIXELS = None monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", None)
assert Image.MAX_IMAGE_PIXELS is None assert Image.MAX_IMAGE_PIXELS is None
# Act / Assert # Act / Assert
@ -33,18 +30,18 @@ class TestDecompressionBomb:
with Image.open(TEST_FILE): with Image.open(TEST_FILE):
pass pass
def test_warning(self) -> None: def test_warning(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Set limit to trigger warning on the test file # 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 assert Image.MAX_IMAGE_PIXELS == 128 * 128 - 1
with pytest.warns(Image.DecompressionBombWarning): with pytest.warns(Image.DecompressionBombWarning):
with Image.open(TEST_FILE): with Image.open(TEST_FILE):
pass pass
def test_exception(self) -> None: def test_exception(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Set limit to trigger exception on the test file # 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 assert Image.MAX_IMAGE_PIXELS == 64 * 128 - 1
with pytest.raises(Image.DecompressionBombError): with pytest.raises(Image.DecompressionBombError):
@ -66,9 +63,9 @@ class TestDecompressionBomb:
with pytest.raises(Image.DecompressionBombError): with pytest.raises(Image.DecompressionBombError):
im.seek(1) 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 # 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 assert Image.MAX_IMAGE_PIXELS == 4 * 64 * 128
with pytest.raises(Image.DecompressionBombError): with pytest.raises(Image.DecompressionBombError):

View File

@ -35,22 +35,19 @@ def test_sanity() -> None:
assert im.is_animated assert im.is_animated
def test_prefix_chunk() -> None: def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: with Image.open(animated_test_file_with_prefix_chunk) as im:
with Image.open(animated_test_file_with_prefix_chunk) as im: assert im.mode == "P"
assert im.mode == "P" assert im.size == (320, 200)
assert im.size == (320, 200) assert im.format == "FLI"
assert im.format == "FLI" assert im.info["duration"] == 171
assert im.info["duration"] == 171 assert im.is_animated
assert im.is_animated
palette = im.getpalette() palette = im.getpalette()
assert palette[3:6] == [255, 255, 255] assert palette[3:6] == [255, 255, 255]
assert palette[381:384] == [204, 204, 12] assert palette[381:384] == [204, 204, 12]
assert palette[765:] == [252, 0, 0] assert palette[765:] == [252, 0, 0]
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
@pytest.mark.skipif(is_pypy(), reason="Requires CPython") @pytest.mark.skipif(is_pypy(), reason="Requires CPython")

View File

@ -109,7 +109,7 @@ def test_palette_not_needed_for_second_frame() -> None:
assert_image_similar(im, hopper("L").convert("RGB"), 8) assert_image_similar(im, hopper("L").convert("RGB"), 8)
def test_strategy() -> None: def test_strategy(monkeypatch: pytest.MonkeyPatch) -> None:
with Image.open("Tests/images/iss634.gif") as im: with Image.open("Tests/images/iss634.gif") as im:
expected_rgb_always = im.convert("RGB") expected_rgb_always = im.convert("RGB")
@ -119,35 +119,36 @@ def test_strategy() -> None:
im.seek(1) im.seek(1)
expected_different = im.convert("RGB") expected_different = im.convert("RGB")
try: monkeypatch.setattr(
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_ALWAYS GifImagePlugin, "LOADING_STRATEGY", GifImagePlugin.LoadingStrategy.RGB_ALWAYS
with Image.open("Tests/images/iss634.gif") as im: )
assert im.mode == "RGB" with Image.open("Tests/images/iss634.gif") as im:
assert_image_equal(im, expected_rgb_always) assert im.mode == "RGB"
assert_image_equal(im, expected_rgb_always)
with Image.open("Tests/images/chi.gif") as im: with Image.open("Tests/images/chi.gif") as im:
assert im.mode == "RGBA" assert im.mode == "RGBA"
assert_image_equal(im, expected_rgb_always_rgba) assert_image_equal(im, expected_rgb_always_rgba)
GifImagePlugin.LOADING_STRATEGY = ( monkeypatch.setattr(
GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY GifImagePlugin,
) "LOADING_STRATEGY",
# Stay in P mode with only a global palette GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY,
with Image.open("Tests/images/chi.gif") as im: )
assert im.mode == "P" # Stay in P mode with only a global palette
with Image.open("Tests/images/chi.gif") as im:
assert im.mode == "P"
im.seek(1) im.seek(1)
assert im.mode == "P" assert im.mode == "P"
assert_image_equal(im.convert("RGB"), expected_different) assert_image_equal(im.convert("RGB"), expected_different)
# Change to RGB mode when a frame has an individual palette # Change to RGB mode when a frame has an individual palette
with Image.open("Tests/images/iss634.gif") as im: with Image.open("Tests/images/iss634.gif") as im:
assert im.mode == "P" assert im.mode == "P"
im.seek(1) im.seek(1)
assert im.mode == "RGB" assert im.mode == "RGB"
finally:
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_FIRST
def test_optimize() -> None: def test_optimize() -> None:
@ -555,17 +556,15 @@ def test_dispose_background_transparency() -> None:
def test_transparent_dispose( def test_transparent_dispose(
loading_strategy: GifImagePlugin.LoadingStrategy, loading_strategy: GifImagePlugin.LoadingStrategy,
expected_colors: tuple[tuple[int | tuple[int, int, int, int], ...]], expected_colors: tuple[tuple[int | tuple[int, int, int, int], ...]],
monkeypatch: pytest.MonkeyPatch,
) -> None: ) -> None:
GifImagePlugin.LOADING_STRATEGY = loading_strategy monkeypatch.setattr(GifImagePlugin, "LOADING_STRATEGY", loading_strategy)
try: with Image.open("Tests/images/transparent_dispose.gif") as img:
with Image.open("Tests/images/transparent_dispose.gif") as img: for frame in range(3):
for frame in range(3): img.seek(frame)
img.seek(frame) for x in range(3):
for x in range(3): color = img.getpixel((x, 0))
color = img.getpixel((x, 0)) assert color == expected_colors[frame][x]
assert color == expected_colors[frame][x]
finally:
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_FIRST
def test_dispose_previous() -> None: def test_dispose_previous() -> None:
@ -1398,24 +1397,23 @@ def test_lzw_bits() -> None:
), ),
) )
def test_extents( def test_extents(
test_file: str, loading_strategy: GifImagePlugin.LoadingStrategy test_file: str,
loading_strategy: GifImagePlugin.LoadingStrategy,
monkeypatch: pytest.MonkeyPatch,
) -> None: ) -> None:
GifImagePlugin.LOADING_STRATEGY = loading_strategy monkeypatch.setattr(GifImagePlugin, "LOADING_STRATEGY", loading_strategy)
try: with Image.open("Tests/images/" + test_file) as im:
with Image.open("Tests/images/" + test_file) as im: assert im.size == (100, 100)
assert im.size == (100, 100)
# Check that n_frames does not change the size # Check that n_frames does not change the size
assert im.n_frames == 2 assert im.n_frames == 2
assert im.size == (100, 100) assert im.size == (100, 100)
im.seek(1) im.seek(1)
assert im.size == (150, 150) assert im.size == (150, 150)
im.load() im.load()
assert im.im.size == (150, 150) assert im.im.size == (150, 150)
finally:
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_FIRST
def test_missing_background() -> None: def test_missing_background() -> None:

View File

@ -243,26 +243,23 @@ def test_draw_reloaded(tmp_path: Path) -> None:
assert_image_equal_tofile(im, "Tests/images/hopper_draw.ico") assert_image_equal_tofile(im, "Tests/images/hopper_draw.ico")
def test_truncated_mask() -> None: def test_truncated_mask(monkeypatch: pytest.MonkeyPatch) -> None:
# 1 bpp # 1 bpp
with open("Tests/images/hopper_mask.ico", "rb") as fp: with open("Tests/images/hopper_mask.ico", "rb") as fp:
data = fp.read() data = fp.read()
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
data = data[:-3] data = data[:-3]
try: with Image.open(io.BytesIO(data)) as im:
with Image.open(io.BytesIO(data)) as im: assert im.mode == "1"
assert im.mode == "1"
# 32 bpp # 32 bpp
output = io.BytesIO() output = io.BytesIO()
expected = hopper("RGBA") expected = hopper("RGBA")
expected.save(output, "ico", bitmap_format="bmp") expected.save(output, "ico", bitmap_format="bmp")
data = output.getvalue()[:-1] data = output.getvalue()[:-1]
with Image.open(io.BytesIO(data)) as im: with Image.open(io.BytesIO(data)) as im:
assert im.mode == "RGB" assert im.mode == "RGB"
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False

View File

@ -530,12 +530,13 @@ class TestFileJpeg:
@mark_if_feature_version( @mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
) )
def test_truncated_jpeg_should_read_all_the_data(self) -> None: def test_truncated_jpeg_should_read_all_the_data(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
filename = "Tests/images/truncated_jpeg.jpg" filename = "Tests/images/truncated_jpeg.jpg"
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
with Image.open(filename) as im: with Image.open(filename) as im:
im.load() im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False
assert im.getbbox() is not None assert im.getbbox() is not None
def test_truncated_jpeg_throws_oserror(self) -> None: def test_truncated_jpeg_throws_oserror(self) -> None:
@ -1024,7 +1025,7 @@ class TestFileJpeg:
im.save(f, xmp=b"1" * 65505) im.save(f, xmp=b"1" * 65505)
@pytest.mark.timeout(timeout=1) @pytest.mark.timeout(timeout=1)
def test_eof(self) -> 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
class InfiniteMockPyDecoder(ImageFile.PyDecoder): class InfiniteMockPyDecoder(ImageFile.PyDecoder):
@ -1039,9 +1040,8 @@ class TestFileJpeg:
im.tile = [ im.tile = [
ImageFile._Tile("INFINITE", (0, 0, 128, 128), 0, ("RGB", 0, 1)), ImageFile._Tile("INFINITE", (0, 0, 128, 128), 0, ("RGB", 0, 1)),
] ]
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
im.load() im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False
def test_separate_tables(self) -> None: def test_separate_tables(self) -> None:
im = hopper() im = hopper()

View File

@ -181,14 +181,11 @@ def test_load_dpi() -> None:
assert "dpi" not in im.info assert "dpi" not in im.info
def test_restricted_icc_profile() -> None: def test_restricted_icc_profile(monkeypatch: pytest.MonkeyPatch) -> None:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: # JPEG2000 image with a restricted ICC profile and a known colorspace
# JPEG2000 image with a restricted ICC profile and a known colorspace with Image.open("Tests/images/balloon_eciRGBv2_aware.jp2") as im:
with Image.open("Tests/images/balloon_eciRGBv2_aware.jp2") as im: assert im.mode == "RGB"
assert im.mode == "RGB"
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
@pytest.mark.skipif( @pytest.mark.skipif(

View File

@ -1156,23 +1156,22 @@ class TestFileLibTiff(LibTiffTestCase):
assert len(im.tag_v2[STRIPOFFSETS]) > 1 assert len(im.tag_v2[STRIPOFFSETS]) > 1
@pytest.mark.parametrize("argument", (True, False)) @pytest.mark.parametrize("argument", (True, False))
def test_save_single_strip(self, argument: bool, tmp_path: Path) -> None: def test_save_single_strip(
self, argument: bool, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
im = hopper("RGB").resize((256, 256)) im = hopper("RGB").resize((256, 256))
out = str(tmp_path / "temp.tif") out = str(tmp_path / "temp.tif")
if not argument: if not argument:
TiffImagePlugin.STRIP_SIZE = 2**18 monkeypatch.setattr(TiffImagePlugin, "STRIP_SIZE", 2**18)
try: arguments: dict[str, str | int] = {"compression": "tiff_adobe_deflate"}
arguments: dict[str, str | int] = {"compression": "tiff_adobe_deflate"} if argument:
if argument: arguments["strip_size"] = 2**18
arguments["strip_size"] = 2**18 im.save(out, "TIFF", **arguments)
im.save(out, "TIFF", **arguments)
with Image.open(out) as im: with Image.open(out) as im:
assert isinstance(im, TiffImagePlugin.TiffImageFile) assert isinstance(im, TiffImagePlugin.TiffImageFile)
assert len(im.tag_v2[STRIPOFFSETS]) == 1 assert len(im.tag_v2[STRIPOFFSETS]) == 1
finally:
TiffImagePlugin.STRIP_SIZE = 65536
@pytest.mark.parametrize("compression", ("tiff_adobe_deflate", None)) @pytest.mark.parametrize("compression", ("tiff_adobe_deflate", None))
def test_save_zero(self, compression: str | None, tmp_path: Path) -> None: def test_save_zero(self, compression: str | None, tmp_path: Path) -> None:

View File

@ -363,7 +363,7 @@ class TestFilePng:
with pytest.raises((OSError, SyntaxError)): with pytest.raises((OSError, SyntaxError)):
im.verify() im.verify()
def test_verify_ignores_crc_error(self) -> None: def test_verify_ignores_crc_error(self, monkeypatch: pytest.MonkeyPatch) -> None:
# check ignores crc errors in ancillary chunks # check ignores crc errors in ancillary chunks
chunk_data = chunk(b"tEXt", b"spam") chunk_data = chunk(b"tEXt", b"spam")
@ -373,24 +373,20 @@ class TestFilePng:
with pytest.raises(SyntaxError): with pytest.raises(SyntaxError):
PngImagePlugin.PngImageFile(BytesIO(image_data)) PngImagePlugin.PngImageFile(BytesIO(image_data))
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: im = load(image_data)
im = load(image_data) assert im is not None
assert im is not None
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
def test_verify_not_ignores_crc_error_in_required_chunk(self) -> None: def test_verify_not_ignores_crc_error_in_required_chunk(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
# check does not ignore crc errors in required chunks # check does not ignore crc errors in required chunks
image_data = MAGIC + IHDR[:-1] + b"q" + TAIL image_data = MAGIC + IHDR[:-1] + b"q" + TAIL
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: with pytest.raises(SyntaxError):
with pytest.raises(SyntaxError): PngImagePlugin.PngImageFile(BytesIO(image_data))
PngImagePlugin.PngImageFile(BytesIO(image_data))
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
def test_roundtrip_dpi(self) -> None: def test_roundtrip_dpi(self) -> None:
# Check dpi roundtripping # Check dpi roundtripping
@ -600,7 +596,7 @@ class TestFilePng:
(b"prIV", b"VALUE3", True), (b"prIV", b"VALUE3", True),
] ]
def test_textual_chunks_after_idat(self) -> None: def test_textual_chunks_after_idat(self, monkeypatch: pytest.MonkeyPatch) -> None:
with Image.open("Tests/images/hopper.png") as im: with Image.open("Tests/images/hopper.png") as im:
assert "comment" in im.text assert "comment" in im.text
for k, v in { for k, v in {
@ -614,18 +610,17 @@ class TestFilePng:
with pytest.raises(OSError): with pytest.raises(OSError):
assert isinstance(im.text, dict) assert isinstance(im.text, dict)
# Raises an EOFError in load_end
with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"}
# Raises a UnicodeDecodeError in load_end # Raises a UnicodeDecodeError in load_end
with Image.open("Tests/images/truncated_image.png") as im: with Image.open("Tests/images/truncated_image.png") as im:
# The file is truncated # The file is truncated
with pytest.raises(OSError): with pytest.raises(OSError):
im.text im.text
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
assert isinstance(im.text, dict) assert isinstance(im.text, dict)
ImageFile.LOAD_TRUNCATED_IMAGES = False
# Raises an EOFError in load_end
with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"}
def test_unknown_compression_method(self) -> None: def test_unknown_compression_method(self) -> None:
with pytest.raises(SyntaxError, match="Unknown compression method"): with pytest.raises(SyntaxError, match="Unknown compression method"):
@ -651,15 +646,16 @@ class TestFilePng:
@pytest.mark.parametrize( @pytest.mark.parametrize(
"cid", (b"IHDR", b"sRGB", b"pHYs", b"acTL", b"fcTL", b"fdAT") "cid", (b"IHDR", b"sRGB", b"pHYs", b"acTL", b"fcTL", b"fdAT")
) )
def test_truncated_chunks(self, cid: bytes) -> None: def test_truncated_chunks(
self, cid: bytes, monkeypatch: pytest.MonkeyPatch
) -> None:
fp = BytesIO() fp = BytesIO()
with PngImagePlugin.PngStream(fp) as png: with PngImagePlugin.PngStream(fp) as png:
with pytest.raises(ValueError): with pytest.raises(ValueError):
png.call(cid, 0, 0) png.call(cid, 0, 0)
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
png.call(cid, 0, 0) png.call(cid, 0, 0)
ImageFile.LOAD_TRUNCATED_IMAGES = False
@pytest.mark.parametrize("save_all", (True, False)) @pytest.mark.parametrize("save_all", (True, False))
def test_specify_bits(self, save_all: bool, tmp_path: Path) -> None: def test_specify_bits(self, save_all: bool, tmp_path: Path) -> None:
@ -789,17 +785,14 @@ class TestFilePng:
with Image.open(mystdout) as reloaded: with Image.open(mystdout) as reloaded:
assert_image_equal_tofile(reloaded, TEST_PNG_FILE) assert_image_equal_tofile(reloaded, TEST_PNG_FILE)
def test_truncated_end_chunk(self) -> None: def test_truncated_end_chunk(self, monkeypatch: pytest.MonkeyPatch) -> None:
with Image.open("Tests/images/truncated_end_chunk.png") as im: with Image.open("Tests/images/truncated_end_chunk.png") as im:
with pytest.raises(OSError): with pytest.raises(OSError):
im.load() im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: with Image.open("Tests/images/truncated_end_chunk.png") as im:
with Image.open("Tests/images/truncated_end_chunk.png") as im: assert_image_equal_tofile(im, "Tests/images/hopper.png")
assert_image_equal_tofile(im, "Tests/images/hopper.png")
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS") @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
@ -808,11 +801,11 @@ class TestTruncatedPngPLeaks(PillowLeakTestCase):
mem_limit = 2 * 1024 # max increase in K mem_limit = 2 * 1024 # max increase in K
iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs
def test_leak_load(self) -> None: def test_leak_load(self, monkeypatch: pytest.MonkeyPatch) -> None:
with open("Tests/images/hopper.png", "rb") as f: with open("Tests/images/hopper.png", "rb") as f:
DATA = BytesIO(f.read(16 * 1024)) DATA = BytesIO(f.read(16 * 1024))
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
with Image.open(DATA) as im: with Image.open(DATA) as im:
im.load() im.load()
@ -820,7 +813,4 @@ class TestTruncatedPngPLeaks(PillowLeakTestCase):
with Image.open(DATA) as im: with Image.open(DATA) as im:
im.load() im.load()
try: self._test_leak(core)
self._test_leak(core)
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False

View File

@ -939,11 +939,10 @@ class TestFileTiff:
@pytest.mark.timeout(6) @pytest.mark.timeout(6)
@pytest.mark.filterwarnings("ignore:Truncated File Read") @pytest.mark.filterwarnings("ignore:Truncated File Read")
def test_timeout(self) -> 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:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
im.load() im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_file", "test_file",

View File

@ -28,9 +28,9 @@ except ImportError:
class TestUnsupportedWebp: class TestUnsupportedWebp:
def test_unsupported(self) -> None: def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None:
if HAVE_WEBP: if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = False monkeypatch.setattr(WebPImagePlugin, "SUPPORTED", False)
file_path = "Tests/images/hopper.webp" file_path = "Tests/images/hopper.webp"
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
@ -38,9 +38,6 @@ class TestUnsupportedWebp:
with Image.open(file_path): with Image.open(file_path):
pass pass
if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = True
@skip_unless_feature("webp") @skip_unless_feature("webp")
class TestFileWebp: class TestFileWebp:

View File

@ -191,13 +191,10 @@ class TestImageFile:
im.load() im.load()
@skip_unless_feature("zlib") @skip_unless_feature("zlib")
def test_truncated_without_errors(self) -> None: def test_truncated_without_errors(self, monkeypatch: pytest.MonkeyPatch) -> None:
with Image.open("Tests/images/truncated_image.png") as im: with Image.open("Tests/images/truncated_image.png") as im:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: im.load()
im.load()
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
@skip_unless_feature("zlib") @skip_unless_feature("zlib")
def test_broken_datastream_with_errors(self) -> None: def test_broken_datastream_with_errors(self) -> None:
@ -206,13 +203,12 @@ class TestImageFile:
im.load() im.load()
@skip_unless_feature("zlib") @skip_unless_feature("zlib")
def test_broken_datastream_without_errors(self) -> None: def test_broken_datastream_without_errors(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
with Image.open("Tests/images/broken_data_stream.png") as im: with Image.open("Tests/images/broken_data_stream.png") as im:
ImageFile.LOAD_TRUNCATED_IMAGES = True monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
try: im.load()
im.load()
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False
class MockPyDecoder(ImageFile.PyDecoder): class MockPyDecoder(ImageFile.PyDecoder):

View File

@ -7,36 +7,30 @@ import pytest
from PIL import Image from PIL import Image
def test_overflow() -> None: def test_overflow(monkeypatch: pytest.MonkeyPatch) -> None:
# There is the potential to overflow comparisons in map.c # There is the potential to overflow comparisons in map.c
# if there are > SIZE_MAX bytes in the image or if # if there are > SIZE_MAX bytes in the image or if
# the file encodes an offset that makes # the file encodes an offset that makes
# (offset + size(bytes)) > SIZE_MAX # (offset + size(bytes)) > SIZE_MAX
# Note that this image triggers the decompression bomb warning: # Note that this image triggers the decompression bomb warning:
max_pixels = Image.MAX_IMAGE_PIXELS monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", None)
Image.MAX_IMAGE_PIXELS = None
# This image hits the offset test. # This image hits the offset test.
with Image.open("Tests/images/l2rgb_read.bmp") as im: with Image.open("Tests/images/l2rgb_read.bmp") as im:
with pytest.raises((ValueError, MemoryError, OSError)): with pytest.raises((ValueError, MemoryError, OSError)):
im.load() im.load()
Image.MAX_IMAGE_PIXELS = max_pixels
def test_tobytes(monkeypatch: pytest.MonkeyPatch) -> None:
def test_tobytes() -> None:
# Note that this image triggers the decompression bomb warning: # Note that this image triggers the decompression bomb warning:
max_pixels = Image.MAX_IMAGE_PIXELS monkeypatch.setattr(Image, "MAX_IMAGE_PIXELS", None)
Image.MAX_IMAGE_PIXELS = None
# Previously raised an access violation on Windows # Previously raised an access violation on Windows
with Image.open("Tests/images/l2rgb_read.bmp") as im: with Image.open("Tests/images/l2rgb_read.bmp") as im:
with pytest.raises((ValueError, MemoryError, OSError)): with pytest.raises((ValueError, MemoryError, OSError)):
im.tobytes() im.tobytes()
Image.MAX_IMAGE_PIXELS = max_pixels
@pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system") @pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system")
def test_ysize() -> None: def test_ysize() -> None: