From 71029803e74c2148567a1a2dda7f8e0e3c49d27c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Apr 2024 21:57:29 +1000 Subject: [PATCH 1/4] Corrected check for libtiff feature --- Tests/test_tiff_ifdrational.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index f6adae3e6..7cbc1a266 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -3,10 +3,12 @@ from __future__ import annotations from fractions import Fraction from pathlib import Path -from PIL import Image, TiffImagePlugin, features +import pytest + +from PIL import Image, TiffImagePlugin from PIL.TiffImagePlugin import IFDRational -from .helper import hopper +from .helper import hopper, skip_unless_feature def _test_equal(num, denom, target) -> None: @@ -52,18 +54,17 @@ def test_nonetype() -> None: assert xres and yres -def test_ifd_rational_save(tmp_path: Path) -> None: - methods = [True] - if features.check("libtiff"): - methods.append(False) +@pytest.mark.parametrize( + "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) +) +def test_ifd_rational_save(tmp_path: Path, libtiff: bool) -> None: + im = hopper() + out = str(tmp_path / "temp.tiff") + res = IFDRational(301, 1) - for libtiff in methods: - TiffImagePlugin.WRITE_LIBTIFF = libtiff + TiffImagePlugin.WRITE_LIBTIFF = libtiff + im.save(out, dpi=(res, res), compression="raw") + TiffImagePlugin.WRITE_LIBTIFF = False - im = hopper() - out = str(tmp_path / "temp.tiff") - res = IFDRational(301, 1) - im.save(out, dpi=(res, res), compression="raw") - - with Image.open(out) as reloaded: - assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) + with Image.open(out) as reloaded: + assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) From 28f436c94d21c445230c50bd5d16e898e12febd5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Apr 2024 17:57:40 +1000 Subject: [PATCH 2/4] Use monkeypatch to set READ_LIBTIFF and WRITE_LIBTIFF --- Tests/test_file_libtiff.py | 100 +++++++++++++++------------------ Tests/test_imagesequence.py | 5 +- Tests/test_tiff_ifdrational.py | 7 ++- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 6c32b5ad4..4db0bfed5 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -185,7 +185,9 @@ class TestFileLibTiff(LibTiffTestCase): assert field in reloaded, f"{field} not in metadata" @pytest.mark.valgrind_known_error(reason="Known invalid metadata") - def test_additional_metadata(self, tmp_path: Path) -> None: + def test_additional_metadata( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: # these should not crash. Seriously dummy data, most of it doesn't make # any sense, so we're running up against limits where we're asking # libtiff to do stupid things. @@ -236,12 +238,10 @@ class TestFileLibTiff(LibTiffTestCase): del new_ifd[338] out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out, tiffinfo=new_ifd) - TiffImagePlugin.WRITE_LIBTIFF = False - def test_custom_metadata(self, tmp_path: Path) -> None: class Tc(NamedTuple): value: Any @@ -343,24 +343,24 @@ class TestFileLibTiff(LibTiffTestCase): # Should not segfault im.save(outfile) - def test_xmlpacket_tag(self, tmp_path: Path) -> None: - TiffImagePlugin.WRITE_LIBTIFF = True + def test_xmlpacket_tag( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) out = str(tmp_path / "temp.tif") hopper().save(out, tiffinfo={700: b"xmlpacket tag"}) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: if 700 in reloaded.tag_v2: assert reloaded.tag_v2[700] == b"xmlpacket tag" - def test_int_dpi(self, tmp_path: Path) -> None: + def test_int_dpi(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: # issue #1765 im = hopper("RGB") out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out, dpi=(72, 72)) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert reloaded.info["dpi"] == (72.0, 72.0) @@ -422,13 +422,13 @@ class TestFileLibTiff(LibTiffTestCase): assert "temp.tif" == reread.tag_v2[269] assert "temp.tif" == reread.tag[269][0] - def test_12bit_rawmode(self) -> None: + def test_12bit_rawmode(self, monkeypatch: pytest.MonkeyPatch) -> None: """Are we generating the same interpretation of the image as Imagemagick is?""" - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/12bit.cropped.tif") as im: im.load() - TiffImagePlugin.READ_LIBTIFF = False + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", False) # to make the target -- # convert 12bit.cropped.tif -depth 16 tmp.tif # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif @@ -514,12 +514,13 @@ class TestFileLibTiff(LibTiffTestCase): assert_image_equal_tofile(im, out) @pytest.mark.parametrize("im", (hopper("P"), Image.new("P", (1, 1), "#000"))) - def test_palette_save(self, im: Image.Image, tmp_path: Path) -> None: + def test_palette_save( + self, im: Image.Image, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: # colormap/palette tag @@ -548,9 +549,9 @@ class TestFileLibTiff(LibTiffTestCase): with pytest.raises(OSError): os.close(fn) - def test_multipage(self) -> None: + def test_multipage(self, monkeypatch: pytest.MonkeyPatch) -> None: # issue #862 - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: # file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue @@ -569,11 +570,9 @@ class TestFileLibTiff(LibTiffTestCase): assert im.size == (20, 20) assert im.convert("RGB").getpixel((0, 0)) == (0, 0, 255) - TiffImagePlugin.READ_LIBTIFF = False - - def test_multipage_nframes(self) -> None: + def test_multipage_nframes(self, monkeypatch: pytest.MonkeyPatch) -> None: # issue #862 - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: frames = im.n_frames assert frames == 3 @@ -582,10 +581,8 @@ class TestFileLibTiff(LibTiffTestCase): # Should not raise ValueError: I/O operation on closed file im.load() - TiffImagePlugin.READ_LIBTIFF = False - - def test_multipage_seek_backwards(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test_multipage_seek_backwards(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: im.seek(1) im.load() @@ -593,24 +590,21 @@ class TestFileLibTiff(LibTiffTestCase): im.seek(0) assert im.convert("RGB").getpixel((0, 0)) == (0, 128, 0) - TiffImagePlugin.READ_LIBTIFF = False - - def test__next(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test__next(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/hopper.tif") as im: assert not im.tag.next im.load() assert not im.tag.next - def test_4bit(self) -> None: + def test_4bit(self, monkeypatch: pytest.MonkeyPatch) -> None: # Arrange test_file = "Tests/images/hopper_gray_4bpp.tif" original = hopper("L") # Act - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open(test_file) as im: - TiffImagePlugin.READ_LIBTIFF = False # Assert assert im.size == (128, 128) @@ -650,12 +644,12 @@ class TestFileLibTiff(LibTiffTestCase): assert im2.mode == "L" assert_image_equal(im, im2) - def test_save_bytesio(self) -> None: + def test_save_bytesio(self, monkeypatch: pytest.MonkeyPatch) -> None: # PR 1011 # Test TIFF saving to io.BytesIO() object. - TiffImagePlugin.WRITE_LIBTIFF = True - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) # Generate test image pilim = hopper() @@ -672,9 +666,6 @@ class TestFileLibTiff(LibTiffTestCase): save_bytesio("packbits") save_bytesio("tiff_lzw") - TiffImagePlugin.WRITE_LIBTIFF = False - TiffImagePlugin.READ_LIBTIFF = False - def test_save_ycbcr(self, tmp_path: Path) -> None: im = hopper("YCbCr") outfile = str(tmp_path / "temp.tif") @@ -694,15 +685,16 @@ class TestFileLibTiff(LibTiffTestCase): if Image.core.libtiff_support_custom_tags: assert reloaded.tag_v2[34665] == 125456 - def test_crashing_metadata(self, tmp_path: Path) -> None: + def test_crashing_metadata( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: # issue 1597 with Image.open("Tests/images/rdf.tif") as im: out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) # this shouldn't crash im.save(out, format="TIFF") - TiffImagePlugin.WRITE_LIBTIFF = False def test_page_number_x_0(self, tmp_path: Path) -> None: # Issue 973 @@ -733,20 +725,19 @@ class TestFileLibTiff(LibTiffTestCase): # Should not raise PermissionError. os.remove(tmpfile) - def test_read_icc(self) -> None: + def test_read_icc(self, monkeypatch: pytest.MonkeyPatch) -> None: with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc = img.info.get("icc_profile") assert icc is not None - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc_libtiff = img.info.get("icc_profile") assert icc_libtiff is not None - TiffImagePlugin.READ_LIBTIFF = False assert icc == icc_libtiff - def test_write_icc(self, tmp_path: Path) -> None: + def test_write_icc(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: def check_write(libtiff: bool) -> None: - TiffImagePlugin.WRITE_LIBTIFF = libtiff + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc_profile = img.info["icc_profile"] @@ -756,10 +747,9 @@ class TestFileLibTiff(LibTiffTestCase): with Image.open(out) as reloaded: assert icc_profile == reloaded.info["icc_profile"] - libtiffs = [] + libtiffs = [False] if Image.core.libtiff_support_custom_tags: libtiffs.append(True) - libtiffs.append(False) for libtiff in libtiffs: check_write(libtiff) @@ -840,12 +830,13 @@ class TestFileLibTiff(LibTiffTestCase): assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") - def test_sampleformat_write(self, tmp_path: Path) -> None: + def test_sampleformat_write( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: im = Image.new("F", (1, 1)) out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert reloaded.mode == "F" @@ -1091,15 +1082,14 @@ class TestFileLibTiff(LibTiffTestCase): with Image.open(out) as im: im.load() - def test_realloc_overflow(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test_realloc_overflow(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: with pytest.raises(OSError) as e: im.load() # Assert that the error code is IMAGING_CODEC_MEMORY assert str(e.value) == "-9" - TiffImagePlugin.READ_LIBTIFF = False @pytest.mark.parametrize("compression", ("tiff_adobe_deflate", "jpeg")) def test_save_multistrip(self, compression: str, tmp_path: Path) -> None: diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 7f3a3d141..18070377f 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -60,10 +60,9 @@ def test_tiff() -> None: @skip_unless_feature("libtiff") -def test_libtiff() -> None: - TiffImagePlugin.READ_LIBTIFF = True +def test_libtiff(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) _test_multipage_tiff() - TiffImagePlugin.READ_LIBTIFF = False def test_consecutive() -> None: diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index 7cbc1a266..ae80b98b8 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -57,14 +57,15 @@ def test_nonetype() -> None: @pytest.mark.parametrize( "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) ) -def test_ifd_rational_save(tmp_path: Path, libtiff: bool) -> None: +def test_ifd_rational_save( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool +) -> None: im = hopper() out = str(tmp_path / "temp.tiff") res = IFDRational(301, 1) - TiffImagePlugin.WRITE_LIBTIFF = libtiff + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) im.save(out, dpi=(res, res), compression="raw") - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) From 533f78e0a255c77357e2694419d6b0f5ea6bcd05 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Apr 2024 07:47:14 +1000 Subject: [PATCH 3/4] Parametrize test --- Tests/test_file_libtiff.py | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 4db0bfed5..71f1b6f1d 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -735,24 +735,31 @@ class TestFileLibTiff(LibTiffTestCase): assert icc_libtiff is not None assert icc == icc_libtiff - def test_write_icc(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: - def check_write(libtiff: bool) -> None: - monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) + @pytest.mark.parametrize( + "libtiff", + ( + pytest.param( + True, + marks=pytest.mark.skipif( + not Image.core.libtiff_support_custom_tags, + reason="Custom tags not supported by older libtiff", + ), + ), + False, + ), + ) + def test_write_icc( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool + ) -> None: + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) - with Image.open("Tests/images/hopper.iccprofile.tif") as img: - icc_profile = img.info["icc_profile"] + with Image.open("Tests/images/hopper.iccprofile.tif") as img: + icc_profile = img.info["icc_profile"] - out = str(tmp_path / "temp.tif") - img.save(out, icc_profile=icc_profile) - with Image.open(out) as reloaded: - assert icc_profile == reloaded.info["icc_profile"] - - libtiffs = [False] - if Image.core.libtiff_support_custom_tags: - libtiffs.append(True) - - for libtiff in libtiffs: - check_write(libtiff) + out = str(tmp_path / "temp.tif") + img.save(out, icc_profile=icc_profile) + with Image.open(out) as reloaded: + assert icc_profile == reloaded.info["icc_profile"] def test_multipage_compression(self) -> None: with Image.open("Tests/images/compression.tif") as im: From 11ac0c1703cb9f46f9de9ac692f008255222d7b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Apr 2024 17:15:10 +1000 Subject: [PATCH 4/4] Combine tests through parametrization --- Tests/test_imagesequence.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 18070377f..9b37435eb 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -47,7 +47,11 @@ def test_iterator_min_frame() -> None: assert i[index] == next(i) -def _test_multipage_tiff() -> None: +@pytest.mark.parametrize( + "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) +) +def test_multipage_tiff(monkeypatch: pytest.MonkeyPatch, libtiff: bool) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", libtiff) with Image.open("Tests/images/multipage.tiff") as im: for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() @@ -55,16 +59,6 @@ def _test_multipage_tiff() -> None: frame.convert("RGB") -def test_tiff() -> None: - _test_multipage_tiff() - - -@skip_unless_feature("libtiff") -def test_libtiff(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) - _test_multipage_tiff() - - def test_consecutive() -> None: with Image.open("Tests/images/multipage.tiff") as im: first_frame = None