From a56ac4faa878fef90a15d716bb57ea2917c9e4a5 Mon Sep 17 00:00:00 2001 From: Aryaz Eghbali Date: Thu, 5 Sep 2024 13:29:19 +0200 Subject: [PATCH 1/3] Canonical type checking --- Tests/test_image_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index bb6064882..c1b09c659 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -24,7 +24,7 @@ def test_toarray() -> None: def test_with_dtype(dtype: npt.DTypeLike) -> None: ai = numpy.array(im, dtype=dtype) - assert ai.dtype == dtype + assert ai.dtype.type is dtype # assert test("1") == ((100, 128), '|b1', 1600)) assert test("L") == ((100, 128), "|u1", 12800) From f7f3af799be98e453d6faafc6bd9b08f3bb18e79 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 5 Sep 2024 22:53:48 +1000 Subject: [PATCH 2/3] Update CHANGES.rst [ci skip] --- CHANGES.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4958ed701..b43466833 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,30 @@ Changelog (Pillow) 11.0.0 (unreleased) ------------------- +- Return early from BoxBlur if either width or height is zero #8347 + [radarhere] + +- Check text is either string or bytes #8308 + [radarhere] + +- Added writing XMP bytes to JPEG #8286 + [radarhere] + +- Support JPEG2000 RGBA palettes #8256 + [radarhere] + +- Expand C image to match GIF frame image size #8237 + [radarhere] + +- Allow saving I;16 images as PPM #8231 + [radarhere] + +- When IFD is missing, connect get_ifd() dictionary to Exif #8230 + [radarhere] + +- Skip truncated ICO mask if LOAD_TRUNCATED_IMAGES is enabled #8180 + [radarhere] + - Treat unknown JPEG2000 colorspace as unspecified #8343 [radarhere] From 31638ee29aae89452adf30acd41620d8c8d91373 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 5 Sep 2024 23:07:40 +1000 Subject: [PATCH 3/3] Added type hints --- Tests/test_file_gif.py | 4 +++- src/PIL/PngImagePlugin.py | 46 ++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 7c332149d..571fe1b9a 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -1392,7 +1392,9 @@ def test_lzw_bits() -> None: ), ), ) -def test_extents(test_file, loading_strategy) -> None: +def test_extents( + test_file: str, loading_strategy: GifImagePlugin.LoadingStrategy +) -> None: GifImagePlugin.LOADING_STRATEGY = loading_strategy try: with Image.open("Tests/images/" + test_file) as im: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index a87e1a625..c268d7b1a 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -370,21 +370,27 @@ class PngInfo: # PNG image stream (IHDR/IEND) +class _RewindState(NamedTuple): + info: dict[str | tuple[int, int], Any] + tile: list[ImageFile._Tile] + seq_num: int | None + + class PngStream(ChunkStream): - def __init__(self, fp): + def __init__(self, fp: IO[bytes]) -> None: super().__init__(fp) # local copies of Image attributes - self.im_info = {} - self.im_text = {} + self.im_info: dict[str | tuple[int, int], Any] = {} + self.im_text: dict[str, str | iTXt] = {} self.im_size = (0, 0) - self.im_mode = None - self.im_tile = None - self.im_palette = None - self.im_custom_mimetype = None - self.im_n_frames = None - self._seq_num = None - self.rewind_state = None + self.im_mode = "" + self.im_tile: list[ImageFile._Tile] = [] + self.im_palette: tuple[str, bytes] | None = None + self.im_custom_mimetype: str | None = None + self.im_n_frames: int | None = None + self._seq_num: int | None = None + self.rewind_state = _RewindState({}, [], None) self.text_memory = 0 @@ -398,16 +404,16 @@ class PngStream(ChunkStream): raise ValueError(msg) def save_rewind(self) -> None: - self.rewind_state = { - "info": self.im_info.copy(), - "tile": self.im_tile, - "seq_num": self._seq_num, - } + self.rewind_state = _RewindState( + self.im_info.copy(), + self.im_tile, + self._seq_num, + ) def rewind(self) -> None: - self.im_info = self.rewind_state["info"].copy() - self.im_tile = self.rewind_state["tile"] - self._seq_num = self.rewind_state["seq_num"] + self.im_info = self.rewind_state.info.copy() + self.im_tile = self.rewind_state.tile + self._seq_num = self.rewind_state.seq_num def chunk_iCCP(self, pos: int, length: int) -> bytes: # ICC profile @@ -461,11 +467,11 @@ class PngStream(ChunkStream): def chunk_IDAT(self, pos: int, length: int) -> NoReturn: # image data if "bbox" in self.im_info: - tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)] + tile = [ImageFile._Tile("zip", self.im_info["bbox"], pos, self.im_rawmode)] else: if self.im_n_frames is not None: self.im_info["default_image"] = True - tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] + tile = [ImageFile._Tile("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] self.im_tile = tile self.im_idat = length msg = "image data found"