mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Merge pull request #7846 from radarhere/type_hints
Added type hints to additional tests
This commit is contained in:
		
						commit
						b7f39076a1
					
				| 
						 | 
					@ -23,7 +23,10 @@ def _get_mem_usage() -> float:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _test_leak(
 | 
					def _test_leak(
 | 
				
			||||||
    min_iterations: int, max_iterations: int, fn: Callable[..., None], *args: Any
 | 
					    min_iterations: int,
 | 
				
			||||||
 | 
					    max_iterations: int,
 | 
				
			||||||
 | 
					    fn: Callable[..., Image.Image | None],
 | 
				
			||||||
 | 
					    *args: Any,
 | 
				
			||||||
) -> None:
 | 
					) -> None:
 | 
				
			||||||
    mem_limit = None
 | 
					    mem_limit = None
 | 
				
			||||||
    for i in range(max_iterations):
 | 
					    for i in range(max_iterations):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ def test_ignore_dos_text() -> None:
 | 
				
			||||||
    finally:
 | 
					    finally:
 | 
				
			||||||
        ImageFile.LOAD_TRUNCATED_IMAGES = False
 | 
					        ImageFile.LOAD_TRUNCATED_IMAGES = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +33,7 @@ def test_dos_text() -> None:
 | 
				
			||||||
        assert msg, "Decompressed Data Too Large"
 | 
					        assert msg, "Decompressed Data Too Large"
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +59,7 @@ def test_dos_total_memory() -> None:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    total_len = 0
 | 
					    total_len = 0
 | 
				
			||||||
 | 
					    assert isinstance(im2, PngImagePlugin.PngImageFile)
 | 
				
			||||||
    for txt in im2.text.values():
 | 
					    for txt in im2.text.values():
 | 
				
			||||||
        total_len += len(txt)
 | 
					        total_len += len(txt)
 | 
				
			||||||
    assert total_len < 64 * 1024 * 1024, "Total text chunks greater than 64M"
 | 
					    assert total_len < 64 * 1024 * 1024, "Total text chunks greater than 64M"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -351,7 +351,7 @@ def is_mingw() -> bool:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CachedProperty:
 | 
					class CachedProperty:
 | 
				
			||||||
    def __init__(self, func: Callable[[Any], None]) -> None:
 | 
					    def __init__(self, func: Callable[[Any], Any]) -> None:
 | 
				
			||||||
        self.func = func
 | 
					        self.func = func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __get__(self, instance: Any, cls: type[Any] | None = None) -> Any:
 | 
					    def __get__(self, instance: Any, cls: type[Any] | None = None) -> Any:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from typing import Literal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import ContainerIO, Image
 | 
					from PIL import ContainerIO, Image
 | 
				
			||||||
| 
						 | 
					@ -22,14 +24,14 @@ def test_isatty() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize(
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
    "mode, expected_value",
 | 
					    "mode, expected_position",
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
        (0, 33),
 | 
					        (0, 33),
 | 
				
			||||||
        (1, 66),
 | 
					        (1, 66),
 | 
				
			||||||
        (2, 100),
 | 
					        (2, 100),
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def test_seek_mode(mode: int, expected_value: int) -> None:
 | 
					def test_seek_mode(mode: Literal[0, 1, 2], expected_position: int) -> None:
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    with open(TEST_FILE, "rb") as fh:
 | 
					    with open(TEST_FILE, "rb") as fh:
 | 
				
			||||||
        container = ContainerIO.ContainerIO(fh, 22, 100)
 | 
					        container = ContainerIO.ContainerIO(fh, 22, 100)
 | 
				
			||||||
| 
						 | 
					@ -39,7 +41,7 @@ def test_seek_mode(mode: int, expected_value: int) -> None:
 | 
				
			||||||
        container.seek(33, mode)
 | 
					        container.seek(33, mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Assert
 | 
					        # Assert
 | 
				
			||||||
        assert container.tell() == expected_value
 | 
					        assert container.tell() == expected_position
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bytesmode", (True, False))
 | 
					@pytest.mark.parametrize("bytesmode", (True, False))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import warnings
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from types import ModuleType
 | 
					from types import ModuleType
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any, cast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,14 +45,20 @@ TEST_FILE = "Tests/images/hopper.jpg"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@skip_unless_feature("jpg")
 | 
					@skip_unless_feature("jpg")
 | 
				
			||||||
class TestFileJpeg:
 | 
					class TestFileJpeg:
 | 
				
			||||||
    def roundtrip(self, im: Image.Image, **options: Any) -> Image.Image:
 | 
					    def roundtrip_with_bytes(
 | 
				
			||||||
 | 
					        self, im: Image.Image, **options: Any
 | 
				
			||||||
 | 
					    ) -> tuple[JpegImagePlugin.JpegImageFile, int]:
 | 
				
			||||||
        out = BytesIO()
 | 
					        out = BytesIO()
 | 
				
			||||||
        im.save(out, "JPEG", **options)
 | 
					        im.save(out, "JPEG", **options)
 | 
				
			||||||
        test_bytes = out.tell()
 | 
					        test_bytes = out.tell()
 | 
				
			||||||
        out.seek(0)
 | 
					        out.seek(0)
 | 
				
			||||||
        im = Image.open(out)
 | 
					        reloaded = cast(JpegImagePlugin.JpegImageFile, Image.open(out))
 | 
				
			||||||
        im.bytes = test_bytes  # for testing only
 | 
					        return reloaded, test_bytes
 | 
				
			||||||
        return im
 | 
					
 | 
				
			||||||
 | 
					    def roundtrip(
 | 
				
			||||||
 | 
					        self, im: Image.Image, **options: Any
 | 
				
			||||||
 | 
					    ) -> JpegImagePlugin.JpegImageFile:
 | 
				
			||||||
 | 
					        return self.roundtrip_with_bytes(im, **options)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Image:
 | 
					    def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Image:
 | 
				
			||||||
        """Generates a very hard to compress file
 | 
					        """Generates a very hard to compress file
 | 
				
			||||||
| 
						 | 
					@ -246,13 +252,13 @@ class TestFileJpeg:
 | 
				
			||||||
            im.save(f, progressive=True, quality=94, exif=b" " * 43668)
 | 
					            im.save(f, progressive=True, quality=94, exif=b" " * 43668)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_optimize(self) -> None:
 | 
					    def test_optimize(self) -> None:
 | 
				
			||||||
        im1 = self.roundtrip(hopper())
 | 
					        im1, im1_bytes = self.roundtrip_with_bytes(hopper())
 | 
				
			||||||
        im2 = self.roundtrip(hopper(), optimize=0)
 | 
					        im2, im2_bytes = self.roundtrip_with_bytes(hopper(), optimize=0)
 | 
				
			||||||
        im3 = self.roundtrip(hopper(), optimize=1)
 | 
					        im3, im3_bytes = self.roundtrip_with_bytes(hopper(), optimize=1)
 | 
				
			||||||
        assert_image_equal(im1, im2)
 | 
					        assert_image_equal(im1, im2)
 | 
				
			||||||
        assert_image_equal(im1, im3)
 | 
					        assert_image_equal(im1, im3)
 | 
				
			||||||
        assert im1.bytes >= im2.bytes
 | 
					        assert im1_bytes >= im2_bytes
 | 
				
			||||||
        assert im1.bytes >= im3.bytes
 | 
					        assert im1_bytes >= im3_bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_optimize_large_buffer(self, tmp_path: Path) -> None:
 | 
					    def test_optimize_large_buffer(self, tmp_path: Path) -> None:
 | 
				
			||||||
        # https://github.com/python-pillow/Pillow/issues/148
 | 
					        # https://github.com/python-pillow/Pillow/issues/148
 | 
				
			||||||
| 
						 | 
					@ -262,15 +268,15 @@ class TestFileJpeg:
 | 
				
			||||||
        im.save(f, format="JPEG", optimize=True)
 | 
					        im.save(f, format="JPEG", optimize=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_progressive(self) -> None:
 | 
					    def test_progressive(self) -> None:
 | 
				
			||||||
        im1 = self.roundtrip(hopper())
 | 
					        im1, im1_bytes = self.roundtrip_with_bytes(hopper())
 | 
				
			||||||
        im2 = self.roundtrip(hopper(), progressive=False)
 | 
					        im2 = self.roundtrip(hopper(), progressive=False)
 | 
				
			||||||
        im3 = self.roundtrip(hopper(), progressive=True)
 | 
					        im3, im3_bytes = self.roundtrip_with_bytes(hopper(), progressive=True)
 | 
				
			||||||
        assert not im1.info.get("progressive")
 | 
					        assert not im1.info.get("progressive")
 | 
				
			||||||
        assert not im2.info.get("progressive")
 | 
					        assert not im2.info.get("progressive")
 | 
				
			||||||
        assert im3.info.get("progressive")
 | 
					        assert im3.info.get("progressive")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_image_equal(im1, im3)
 | 
					        assert_image_equal(im1, im3)
 | 
				
			||||||
        assert im1.bytes >= im3.bytes
 | 
					        assert im1_bytes >= im3_bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_progressive_large_buffer(self, tmp_path: Path) -> None:
 | 
					    def test_progressive_large_buffer(self, tmp_path: Path) -> None:
 | 
				
			||||||
        f = str(tmp_path / "temp.jpg")
 | 
					        f = str(tmp_path / "temp.jpg")
 | 
				
			||||||
| 
						 | 
					@ -341,6 +347,7 @@ class TestFileJpeg:
 | 
				
			||||||
            assert exif.get_ifd(0x8825) == {}
 | 
					            assert exif.get_ifd(0x8825) == {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            transposed = ImageOps.exif_transpose(im)
 | 
					            transposed = ImageOps.exif_transpose(im)
 | 
				
			||||||
 | 
					        assert transposed is not None
 | 
				
			||||||
        exif = transposed.getexif()
 | 
					        exif = transposed.getexif()
 | 
				
			||||||
        assert exif.get_ifd(0x8825) == {}
 | 
					        assert exif.get_ifd(0x8825) == {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -419,14 +426,14 @@ class TestFileJpeg:
 | 
				
			||||||
        assert im3.info.get("progression")
 | 
					        assert im3.info.get("progression")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_quality(self) -> None:
 | 
					    def test_quality(self) -> None:
 | 
				
			||||||
        im1 = self.roundtrip(hopper())
 | 
					        im1, im1_bytes = self.roundtrip_with_bytes(hopper())
 | 
				
			||||||
        im2 = self.roundtrip(hopper(), quality=50)
 | 
					        im2, im2_bytes = self.roundtrip_with_bytes(hopper(), quality=50)
 | 
				
			||||||
        assert_image(im1, im2.mode, im2.size)
 | 
					        assert_image(im1, im2.mode, im2.size)
 | 
				
			||||||
        assert im1.bytes >= im2.bytes
 | 
					        assert im1_bytes >= im2_bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im3 = self.roundtrip(hopper(), quality=0)
 | 
					        im3, im3_bytes = self.roundtrip_with_bytes(hopper(), quality=0)
 | 
				
			||||||
        assert_image(im1, im3.mode, im3.size)
 | 
					        assert_image(im1, im3.mode, im3.size)
 | 
				
			||||||
        assert im2.bytes > im3.bytes
 | 
					        assert im2_bytes > im3_bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_smooth(self) -> None:
 | 
					    def test_smooth(self) -> None:
 | 
				
			||||||
        im1 = self.roundtrip(hopper())
 | 
					        im1 = self.roundtrip(hopper())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,10 +40,8 @@ test_card.load()
 | 
				
			||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
 | 
					def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
 | 
				
			||||||
    out = BytesIO()
 | 
					    out = BytesIO()
 | 
				
			||||||
    im.save(out, "JPEG2000", **options)
 | 
					    im.save(out, "JPEG2000", **options)
 | 
				
			||||||
    test_bytes = out.tell()
 | 
					 | 
				
			||||||
    out.seek(0)
 | 
					    out.seek(0)
 | 
				
			||||||
    with Image.open(out) as im:
 | 
					    with Image.open(out) as im:
 | 
				
			||||||
        im.bytes = test_bytes  # for testing only
 | 
					 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
    return im
 | 
					    return im
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,7 +75,9 @@ def test_invalid_file() -> None:
 | 
				
			||||||
def test_bytesio() -> None:
 | 
					def test_bytesio() -> None:
 | 
				
			||||||
    with open("Tests/images/test-card-lossless.jp2", "rb") as f:
 | 
					    with open("Tests/images/test-card-lossless.jp2", "rb") as f:
 | 
				
			||||||
        data = BytesIO(f.read())
 | 
					        data = BytesIO(f.read())
 | 
				
			||||||
    assert_image_similar_tofile(test_card, data, 1.0e-3)
 | 
					    with Image.open(data) as im:
 | 
				
			||||||
 | 
					        im.load()
 | 
				
			||||||
 | 
					        assert_image_similar(im, test_card, 1.0e-3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# These two test pre-written JPEG 2000 files that were not written with
 | 
					# These two test pre-written JPEG 2000 files that were not written with
 | 
				
			||||||
| 
						 | 
					@ -340,6 +340,7 @@ def test_parser_feed() -> None:
 | 
				
			||||||
    p.feed(data)
 | 
					    p.feed(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
 | 
					    assert p.image is not None
 | 
				
			||||||
    assert p.image.size == (640, 480)
 | 
					    assert p.image.size == (640, 480)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ from .helper import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@skip_unless_feature("libtiff")
 | 
					@skip_unless_feature("libtiff")
 | 
				
			||||||
class LibTiffTestCase:
 | 
					class LibTiffTestCase:
 | 
				
			||||||
    def _assert_noerr(self, tmp_path: Path, im: Image.Image) -> None:
 | 
					    def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> None:
 | 
				
			||||||
        """Helper tests that assert basic sanity about the g4 tiff reading"""
 | 
					        """Helper tests that assert basic sanity about the g4 tiff reading"""
 | 
				
			||||||
        # 1 bit
 | 
					        # 1 bit
 | 
				
			||||||
        assert im.mode == "1"
 | 
					        assert im.mode == "1"
 | 
				
			||||||
| 
						 | 
					@ -524,7 +524,8 @@ class TestFileLibTiff(LibTiffTestCase):
 | 
				
			||||||
            im.save(out, compression=compression)
 | 
					            im.save(out, compression=compression)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_fp_leak(self) -> None:
 | 
					    def test_fp_leak(self) -> None:
 | 
				
			||||||
        im = Image.open("Tests/images/hopper_g4_500.tif")
 | 
					        im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif")
 | 
				
			||||||
 | 
					        assert im is not None
 | 
				
			||||||
        fn = im.fp.fileno()
 | 
					        fn = im.fp.fileno()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        os.fstat(fn)
 | 
					        os.fstat(fn)
 | 
				
			||||||
| 
						 | 
					@ -716,6 +717,7 @@ class TestFileLibTiff(LibTiffTestCase):
 | 
				
			||||||
                f.write(src.read())
 | 
					                f.write(src.read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im = Image.open(tmpfile)
 | 
					        im = Image.open(tmpfile)
 | 
				
			||||||
 | 
					        assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
				
			||||||
        im.n_frames
 | 
					        im.n_frames
 | 
				
			||||||
        im.close()
 | 
					        im.close()
 | 
				
			||||||
        # Should not raise PermissionError.
 | 
					        # Should not raise PermissionError.
 | 
				
			||||||
| 
						 | 
					@ -1097,6 +1099,7 @@ class TestFileLibTiff(LibTiffTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with Image.open(out) as im:
 | 
					        with Image.open(out) as im:
 | 
				
			||||||
            # Assert that there are multiple strips
 | 
					            # Assert that there are multiple strips
 | 
				
			||||||
 | 
					            assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
				
			||||||
            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))
 | 
				
			||||||
| 
						 | 
					@ -1113,6 +1116,7 @@ class TestFileLibTiff(LibTiffTestCase):
 | 
				
			||||||
            im.save(out, **arguments)
 | 
					            im.save(out, **arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            with Image.open(out) as im:
 | 
					            with Image.open(out) as im:
 | 
				
			||||||
 | 
					                assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
				
			||||||
                assert len(im.tag_v2[STRIPOFFSETS]) == 1
 | 
					                assert len(im.tag_v2[STRIPOFFSETS]) == 1
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            TiffImagePlugin.STRIP_SIZE = 65536
 | 
					            TiffImagePlugin.STRIP_SIZE = 65536
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,11 @@ from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import warnings
 | 
					import warnings
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any, cast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image
 | 
					from PIL import Image, MpoImagePlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import (
 | 
					from .helper import (
 | 
				
			||||||
    assert_image_equal,
 | 
					    assert_image_equal,
 | 
				
			||||||
| 
						 | 
					@ -20,14 +20,11 @@ test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
 | 
				
			||||||
pytestmark = skip_unless_feature("jpg")
 | 
					pytestmark = skip_unless_feature("jpg")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
 | 
					def roundtrip(im: Image.Image, **options: Any) -> MpoImagePlugin.MpoImageFile:
 | 
				
			||||||
    out = BytesIO()
 | 
					    out = BytesIO()
 | 
				
			||||||
    im.save(out, "MPO", **options)
 | 
					    im.save(out, "MPO", **options)
 | 
				
			||||||
    test_bytes = out.tell()
 | 
					 | 
				
			||||||
    out.seek(0)
 | 
					    out.seek(0)
 | 
				
			||||||
    im = Image.open(out)
 | 
					    return cast(MpoImagePlugin.MpoImageFile, Image.open(out))
 | 
				
			||||||
    im.bytes = test_bytes  # for testing only
 | 
					 | 
				
			||||||
    return im
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("test_file", test_files)
 | 
					@pytest.mark.parametrize("test_file", test_files)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import zlib
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from types import ModuleType
 | 
					from types import ModuleType
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any, cast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,11 +59,11 @@ def load(data: bytes) -> Image.Image:
 | 
				
			||||||
    return Image.open(BytesIO(data))
 | 
					    return Image.open(BytesIO(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
 | 
					def roundtrip(im: Image.Image, **options: Any) -> PngImagePlugin.PngImageFile:
 | 
				
			||||||
    out = BytesIO()
 | 
					    out = BytesIO()
 | 
				
			||||||
    im.save(out, "PNG", **options)
 | 
					    im.save(out, "PNG", **options)
 | 
				
			||||||
    out.seek(0)
 | 
					    out.seek(0)
 | 
				
			||||||
    return Image.open(out)
 | 
					    return cast(PngImagePlugin.PngImageFile, Image.open(out))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@skip_unless_feature("zlib")
 | 
					@skip_unless_feature("zlib")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
 | 
					from PIL import Image, ImageSequence, SpiderImagePlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import assert_image_equal_tofile, hopper, is_pypy
 | 
					from .helper import assert_image_equal, hopper, is_pypy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST_FILE = "Tests/images/hopper.spider"
 | 
					TEST_FILE = "Tests/images/hopper.spider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,7 +152,7 @@ def test_nonstack_dos() -> None:
 | 
				
			||||||
            assert i <= 1, "Non-stack DOS file test failed"
 | 
					            assert i <= 1, "Non-stack DOS file test failed"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# for issue #4093
 | 
					# for issue #4093s
 | 
				
			||||||
def test_odd_size() -> None:
 | 
					def test_odd_size() -> None:
 | 
				
			||||||
    data = BytesIO()
 | 
					    data = BytesIO()
 | 
				
			||||||
    width = 100
 | 
					    width = 100
 | 
				
			||||||
| 
						 | 
					@ -160,4 +160,5 @@ def test_odd_size() -> None:
 | 
				
			||||||
    im.save(data, format="SPIDER")
 | 
					    im.save(data, format="SPIDER")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    data.seek(0)
 | 
					    data.seek(0)
 | 
				
			||||||
    assert_image_equal_tofile(im, data)
 | 
					    with Image.open(data) as im2:
 | 
				
			||||||
 | 
					        assert_image_equal(im, im2)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -623,6 +623,7 @@ class TestFileTiff:
 | 
				
			||||||
        im.save(outfile, tiffinfo={278: 256})
 | 
					        im.save(outfile, tiffinfo={278: 256})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with Image.open(outfile) as im:
 | 
					        with Image.open(outfile) as im:
 | 
				
			||||||
 | 
					            assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
				
			||||||
            assert im.tag_v2[278] == 256
 | 
					            assert im.tag_v2[278] == 256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_strip_raw(self) -> None:
 | 
					    def test_strip_raw(self) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,13 +138,13 @@ class TestImage:
 | 
				
			||||||
        assert im.height == 2
 | 
					        assert im.height == 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(AttributeError):
 | 
					        with pytest.raises(AttributeError):
 | 
				
			||||||
            im.size = (3, 4)
 | 
					            im.size = (3, 4)  # type: ignore[misc]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_set_mode(self) -> None:
 | 
					    def test_set_mode(self) -> None:
 | 
				
			||||||
        im = Image.new("RGB", (1, 1))
 | 
					        im = Image.new("RGB", (1, 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(AttributeError):
 | 
					        with pytest.raises(AttributeError):
 | 
				
			||||||
            im.mode = "P"
 | 
					            im.mode = "P"  # type: ignore[misc]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_invalid_image(self) -> None:
 | 
					    def test_invalid_image(self) -> None:
 | 
				
			||||||
        im = io.BytesIO(b"")
 | 
					        im = io.BytesIO(b"")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ from .helper import assert_image_equal, hopper, is_win32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
 | 
					# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
 | 
				
			||||||
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
 | 
					# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
 | 
				
			||||||
 | 
					cffi: ModuleType | None
 | 
				
			||||||
if os.environ.get("PYTHONOPTIMIZE") == "2":
 | 
					if os.environ.get("PYTHONOPTIMIZE") == "2":
 | 
				
			||||||
    cffi = None
 | 
					    cffi = None
 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import warnings
 | 
					import warnings
 | 
				
			||||||
from typing import Generator
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,17 +16,14 @@ pytestmark = pytest.mark.skipif(
 | 
				
			||||||
    not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
 | 
					    not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ims = [
 | 
				
			||||||
@pytest.fixture
 | 
					 | 
				
			||||||
def test_images() -> Generator[Image.Image, None, None]:
 | 
					 | 
				
			||||||
    ims = [
 | 
					 | 
				
			||||||
    hopper(),
 | 
					    hopper(),
 | 
				
			||||||
    Image.open("Tests/images/transparent.png"),
 | 
					    Image.open("Tests/images/transparent.png"),
 | 
				
			||||||
    Image.open("Tests/images/7x13.png"),
 | 
					    Image.open("Tests/images/7x13.png"),
 | 
				
			||||||
    ]
 | 
					]
 | 
				
			||||||
    try:
 | 
					
 | 
				
			||||||
        yield ims
 | 
					
 | 
				
			||||||
    finally:
 | 
					def teardown_module() -> None:
 | 
				
			||||||
    for im in ims:
 | 
					    for im in ims:
 | 
				
			||||||
        im.close()
 | 
					        im.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,26 +40,26 @@ def roundtrip(expected: Image.Image) -> None:
 | 
				
			||||||
        assert_image_equal(result, expected.convert("RGB"))
 | 
					        assert_image_equal(result, expected.convert("RGB"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity_1(test_images: Generator[Image.Image, None, None]) -> None:
 | 
					def test_sanity_1() -> None:
 | 
				
			||||||
    for im in test_images:
 | 
					    for im in ims:
 | 
				
			||||||
        roundtrip(im.convert("1"))
 | 
					        roundtrip(im.convert("1"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity_rgb(test_images: Generator[Image.Image, None, None]) -> None:
 | 
					def test_sanity_rgb() -> None:
 | 
				
			||||||
    for im in test_images:
 | 
					    for im in ims:
 | 
				
			||||||
        roundtrip(im.convert("RGB"))
 | 
					        roundtrip(im.convert("RGB"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity_rgba(test_images: Generator[Image.Image, None, None]) -> None:
 | 
					def test_sanity_rgba() -> None:
 | 
				
			||||||
    for im in test_images:
 | 
					    for im in ims:
 | 
				
			||||||
        roundtrip(im.convert("RGBA"))
 | 
					        roundtrip(im.convert("RGBA"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity_l(test_images: Generator[Image.Image, None, None]) -> None:
 | 
					def test_sanity_l() -> None:
 | 
				
			||||||
    for im in test_images:
 | 
					    for im in ims:
 | 
				
			||||||
        roundtrip(im.convert("L"))
 | 
					        roundtrip(im.convert("L"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity_p(test_images: Generator[Image.Image, None, None]) -> None:
 | 
					def test_sanity_p() -> None:
 | 
				
			||||||
    for im in test_images:
 | 
					    for im in ims:
 | 
				
			||||||
        roundtrip(im.convert("P"))
 | 
					        roundtrip(im.convert("P"))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ class TestImagingPaste:
 | 
				
			||||||
    def assert_9points_paste(
 | 
					    def assert_9points_paste(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        im: Image.Image,
 | 
					        im: Image.Image,
 | 
				
			||||||
        im2: Image.Image,
 | 
					        im2: Image.Image | str | tuple[int, ...],
 | 
				
			||||||
        mask: Image.Image,
 | 
					        mask: Image.Image,
 | 
				
			||||||
        expected: list[tuple[int, int, int, int]],
 | 
					        expected: list[tuple[int, int, int, int]],
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -237,7 +237,7 @@ class TestCoreResampleConsistency:
 | 
				
			||||||
        im = Image.new(mode, (512, 9), fill)
 | 
					        im = Image.new(mode, (512, 9), fill)
 | 
				
			||||||
        return im.resize((9, 512), Image.Resampling.LANCZOS), im.load()[0, 0]
 | 
					        return im.resize((9, 512), Image.Resampling.LANCZOS), im.load()[0, 0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run_case(self, case: tuple[Image.Image, Image.Image]) -> None:
 | 
					    def run_case(self, case: tuple[Image.Image, int | tuple[int, ...]]) -> None:
 | 
				
			||||||
        channel, color = case
 | 
					        channel, color = case
 | 
				
			||||||
        px = channel.load()
 | 
					        px = channel.load()
 | 
				
			||||||
        for x in range(channel.size[0]):
 | 
					        for x in range(channel.size[0]):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,7 +154,7 @@ class TestImagingCoreResize:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_unknown_filter(self) -> None:
 | 
					    def test_unknown_filter(self) -> None:
 | 
				
			||||||
        with pytest.raises(ValueError):
 | 
					        with pytest.raises(ValueError):
 | 
				
			||||||
            self.resize(hopper(), (10, 10), 9)
 | 
					            self.resize(hopper(), (10, 10), 9)  # type: ignore[arg-type]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_cross_platform(self, tmp_path: Path) -> None:
 | 
					    def test_cross_platform(self, tmp_path: Path) -> None:
 | 
				
			||||||
        # This test is intended for only check for consistent behaviour across
 | 
					        # This test is intended for only check for consistent behaviour across
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,15 +73,16 @@ def test_lut(op: str) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_no_operator_loaded() -> None:
 | 
					def test_no_operator_loaded() -> None:
 | 
				
			||||||
 | 
					    im = Image.new("L", (1, 1))
 | 
				
			||||||
    mop = ImageMorph.MorphOp()
 | 
					    mop = ImageMorph.MorphOp()
 | 
				
			||||||
    with pytest.raises(Exception) as e:
 | 
					    with pytest.raises(Exception) as e:
 | 
				
			||||||
        mop.apply(None)
 | 
					        mop.apply(im)
 | 
				
			||||||
    assert str(e.value) == "No operator loaded"
 | 
					    assert str(e.value) == "No operator loaded"
 | 
				
			||||||
    with pytest.raises(Exception) as e:
 | 
					    with pytest.raises(Exception) as e:
 | 
				
			||||||
        mop.match(None)
 | 
					        mop.match(im)
 | 
				
			||||||
    assert str(e.value) == "No operator loaded"
 | 
					    assert str(e.value) == "No operator loaded"
 | 
				
			||||||
    with pytest.raises(Exception) as e:
 | 
					    with pytest.raises(Exception) as e:
 | 
				
			||||||
        mop.save_lut(None)
 | 
					        mop.save_lut("")
 | 
				
			||||||
    assert str(e.value) == "No operator loaded"
 | 
					    assert str(e.value) == "No operator loaded"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,12 @@ from .helper import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Deformer:
 | 
					class Deformer(ImageOps.SupportsGetMesh):
 | 
				
			||||||
    def getmesh(self, im: Image.Image) -> list[tuple[tuple[int, ...], tuple[int, ...]]]:
 | 
					    def getmesh(
 | 
				
			||||||
 | 
					        self, im: Image.Image
 | 
				
			||||||
 | 
					    ) -> list[
 | 
				
			||||||
 | 
					        tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]]
 | 
				
			||||||
 | 
					    ]:
 | 
				
			||||||
        x, y = im.size
 | 
					        x, y = im.size
 | 
				
			||||||
        return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]
 | 
					        return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -376,6 +380,7 @@ def test_exif_transpose() -> None:
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        original_exif = im.info["exif"]
 | 
					                        original_exif = im.info["exif"]
 | 
				
			||||||
                    transposed_im = ImageOps.exif_transpose(im)
 | 
					                    transposed_im = ImageOps.exif_transpose(im)
 | 
				
			||||||
 | 
					                    assert transposed_im is not None
 | 
				
			||||||
                    assert_image_similar(base_im, transposed_im, 17)
 | 
					                    assert_image_similar(base_im, transposed_im, 17)
 | 
				
			||||||
                    if orientation_im is base_im:
 | 
					                    if orientation_im is base_im:
 | 
				
			||||||
                        assert "exif" not in im.info
 | 
					                        assert "exif" not in im.info
 | 
				
			||||||
| 
						 | 
					@ -387,6 +392,7 @@ def test_exif_transpose() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # Repeat the operation to test that it does not keep transposing
 | 
					                    # Repeat the operation to test that it does not keep transposing
 | 
				
			||||||
                    transposed_im2 = ImageOps.exif_transpose(transposed_im)
 | 
					                    transposed_im2 = ImageOps.exif_transpose(transposed_im)
 | 
				
			||||||
 | 
					                    assert transposed_im2 is not None
 | 
				
			||||||
                    assert_image_equal(transposed_im2, transposed_im)
 | 
					                    assert_image_equal(transposed_im2, transposed_im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            check(base_im)
 | 
					            check(base_im)
 | 
				
			||||||
| 
						 | 
					@ -402,6 +408,7 @@ def test_exif_transpose() -> None:
 | 
				
			||||||
            assert im.getexif()[0x0112] == 3
 | 
					            assert im.getexif()[0x0112] == 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            transposed_im = ImageOps.exif_transpose(im)
 | 
					            transposed_im = ImageOps.exif_transpose(im)
 | 
				
			||||||
 | 
					            assert transposed_im is not None
 | 
				
			||||||
            assert 0x0112 not in transposed_im.getexif()
 | 
					            assert 0x0112 not in transposed_im.getexif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            transposed_im._reload_exif()
 | 
					            transposed_im._reload_exif()
 | 
				
			||||||
| 
						 | 
					@ -414,12 +421,14 @@ def test_exif_transpose() -> None:
 | 
				
			||||||
        assert im.getexif()[0x0112] == 3
 | 
					        assert im.getexif()[0x0112] == 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        transposed_im = ImageOps.exif_transpose(im)
 | 
					        transposed_im = ImageOps.exif_transpose(im)
 | 
				
			||||||
 | 
					        assert transposed_im is not None
 | 
				
			||||||
        assert 0x0112 not in transposed_im.getexif()
 | 
					        assert 0x0112 not in transposed_im.getexif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Orientation set directly on Image.Exif
 | 
					    # Orientation set directly on Image.Exif
 | 
				
			||||||
    im = hopper()
 | 
					    im = hopper()
 | 
				
			||||||
    im.getexif()[0x0112] = 3
 | 
					    im.getexif()[0x0112] = 3
 | 
				
			||||||
    transposed_im = ImageOps.exif_transpose(im)
 | 
					    transposed_im = ImageOps.exif_transpose(im)
 | 
				
			||||||
 | 
					    assert transposed_im is not None
 | 
				
			||||||
    assert 0x0112 not in transposed_im.getexif()
 | 
					    assert 0x0112 not in transposed_im.getexif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -499,7 +508,7 @@ def test_autocontrast_mask_real_input() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_autocontrast_preserve_tone() -> None:
 | 
					def test_autocontrast_preserve_tone() -> None:
 | 
				
			||||||
    def autocontrast(mode: str, preserve_tone: bool) -> Image.Image:
 | 
					    def autocontrast(mode: str, preserve_tone: bool) -> list[int]:
 | 
				
			||||||
        im = hopper(mode)
 | 
					        im = hopper(mode)
 | 
				
			||||||
        return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
 | 
					        return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,8 @@ def test_filter_api(test_images: dict[str, Image.Image]) -> None:
 | 
				
			||||||
    assert i.mode == "RGB"
 | 
					    assert i.mode == "RGB"
 | 
				
			||||||
    assert i.size == (128, 128)
 | 
					    assert i.size == (128, 128)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
 | 
					    test_filter2 = ImageFilter.UnsharpMask(2.0, 125, 8)
 | 
				
			||||||
    i = im.filter(test_filter)
 | 
					    i = im.filter(test_filter2)
 | 
				
			||||||
    assert i.mode == "RGB"
 | 
					    assert i.mode == "RGB"
 | 
				
			||||||
    assert i.size == (128, 128)
 | 
					    assert i.size == (128, 128)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ def test_sanity(tmp_path: Path) -> None:
 | 
				
			||||||
    assert index == 1
 | 
					    assert index == 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(AttributeError):
 | 
					    with pytest.raises(AttributeError):
 | 
				
			||||||
        ImageSequence.Iterator(0)
 | 
					        ImageSequence.Iterator(0)  # type: ignore[arg-type]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_iterator() -> None:
 | 
					def test_iterator() -> None:
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,7 @@ def test_consecutive() -> None:
 | 
				
			||||||
        for frame in ImageSequence.Iterator(im):
 | 
					        for frame in ImageSequence.Iterator(im):
 | 
				
			||||||
            if first_frame is None:
 | 
					            if first_frame is None:
 | 
				
			||||||
                first_frame = frame.copy()
 | 
					                first_frame = frame.copy()
 | 
				
			||||||
 | 
					        assert first_frame is not None
 | 
				
			||||||
        for frame in ImageSequence.Iterator(im):
 | 
					        for frame in ImageSequence.Iterator(im):
 | 
				
			||||||
            assert_image_equal(frame, first_frame)
 | 
					            assert_image_equal(frame, first_frame)
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,10 +68,11 @@ def test_show_without_viewers() -> None:
 | 
				
			||||||
def test_viewer() -> None:
 | 
					def test_viewer() -> None:
 | 
				
			||||||
    viewer = ImageShow.Viewer()
 | 
					    viewer = ImageShow.Viewer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert viewer.get_format(None) is None
 | 
					    im = Image.new("L", (1, 1))
 | 
				
			||||||
 | 
					    assert viewer.get_format(im) is None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(NotImplementedError):
 | 
					    with pytest.raises(NotImplementedError):
 | 
				
			||||||
        viewer.get_command(None)
 | 
					        viewer.get_command("")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("viewer", ImageShow._viewers)
 | 
					@pytest.mark.parametrize("viewer", ImageShow._viewers)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ def test_basic(tmp_path: Path, mode: str) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_tobytes() -> None:
 | 
					def test_tobytes() -> None:
 | 
				
			||||||
    def tobytes(mode: str) -> Image.Image:
 | 
					    def tobytes(mode: str) -> bytes:
 | 
				
			||||||
        return Image.new(mode, (1, 1), 1).tobytes()
 | 
					        return Image.new(mode, (1, 1), 1).tobytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    order = 1 if Image._ENDIAN == "<" else -1
 | 
					    order = 1 if Image._ENDIAN == "<" else -1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,9 +47,8 @@ def test_tiff_crashes(test_file: str) -> None:
 | 
				
			||||||
        with Image.open(test_file) as im:
 | 
					        with Image.open(test_file) as im:
 | 
				
			||||||
            im.load()
 | 
					            im.load()
 | 
				
			||||||
    except FileNotFoundError:
 | 
					    except FileNotFoundError:
 | 
				
			||||||
        if not on_ci():
 | 
					        if on_ci():
 | 
				
			||||||
            pytest.skip("test image not found")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
            raise
 | 
					            raise
 | 
				
			||||||
 | 
					        pytest.skip("test image not found")
 | 
				
			||||||
    except OSError:
 | 
					    except OSError:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ from ._deprecate import deprecate
 | 
				
			||||||
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
 | 
					split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
 | 
				
			||||||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
 | 
					field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gs_binary = None
 | 
					gs_binary: str | bool | None = None
 | 
				
			||||||
gs_windows_binary = None
 | 
					gs_windows_binary = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,7 @@ class DecompressionBombError(Exception):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image
 | 
					# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image
 | 
				
			||||||
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3)
 | 
					MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -384,7 +384,7 @@ class Parser:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    incremental = None
 | 
					    incremental = None
 | 
				
			||||||
    image = None
 | 
					    image: Image.Image | None = None
 | 
				
			||||||
    data = None
 | 
					    data = None
 | 
				
			||||||
    decoder = None
 | 
					    decoder = None
 | 
				
			||||||
    offset = 0
 | 
					    offset = 0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user