From 26be2ee1e459125ea8dfbf6a5476814215783639 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Apr 2025 15:47:35 +1000 Subject: [PATCH] Lint fixes --- src/PIL/AniImagePlugin.py | 49 ++++++++++++----------- src/PIL/CurImagePlugin.py | 82 ++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 57 deletions(-) diff --git a/src/PIL/AniImagePlugin.py b/src/PIL/AniImagePlugin.py index e66d5ec23..7e70decc4 100644 --- a/src/PIL/AniImagePlugin.py +++ b/src/PIL/AniImagePlugin.py @@ -2,6 +2,7 @@ from __future__ import annotations import struct from io import BytesIO +from typing import IO, Any from PIL import BmpImagePlugin, CurImagePlugin, Image, ImageFile from PIL._binary import i32le as i32 @@ -10,11 +11,11 @@ from PIL._binary import o16le as o16 from PIL._binary import o32le as o32 -def _accept(s): - return s[:4] == b"RIFF" +def _accept(prefix: bytes) -> bool: + return prefix[:4] == b"RIFF" -def _save_frame(im: Image.Image, fp: BytesIO, filename: str, info: dict): +def _save_frame(im: Image.Image, fp: BytesIO, info: dict[str, Any]) -> None: fp.write(b"\0\0\2\0") bmp = True s = info.get( @@ -64,7 +65,9 @@ def _save_frame(im: Image.Image, fp: BytesIO, filename: str, info: dict): if bits != 32: and_mask = Image.new("1", size) ImageFile._save( - and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))] + and_mask, + image_io, + [ImageFile._Tile("raw", (0, 0) + size, 0, ("1", 0, -1))], ) else: frame.alpha = True @@ -87,7 +90,7 @@ def _save_frame(im: Image.Image, fp: BytesIO, filename: str, info: dict): fp.seek(current) -def _write_single_frame(im: Image.Image, fp: BytesIO, filename: str): +def _write_single_frame(im: Image.Image, fp: IO[bytes]) -> None: fp.write(b"anih") anih = o32(36) + o32(36) + (o32(1) * 2) + (o32(0) * 4) + o32(60) + o32(1) fp.write(anih) @@ -99,7 +102,7 @@ def _write_single_frame(im: Image.Image, fp: BytesIO, filename: str): fp.write(b"icon" + o32(0)) icon_offset = fp.tell() with BytesIO(b"") as icon_fp: - _save_frame(im, icon_fp, filename, im.encoderinfo) + _save_frame(im, icon_fp, im.encoderinfo) icon_fp.seek(0) icon_data = icon_fp.read() @@ -117,7 +120,7 @@ def _write_single_frame(im: Image.Image, fp: BytesIO, filename: str): fp.seek(fram_end) -def _write_multiple_frames(im: Image.Image, fp: BytesIO, filename: str): +def _write_multiple_frames(im: Image.Image, fp: IO[bytes]) -> None: anih_offset = fp.tell() fp.write(b"anih" + o32(36)) fp.write(o32(0) * 9) @@ -132,7 +135,7 @@ def _write_multiple_frames(im: Image.Image, fp: BytesIO, filename: str): fp.write(b"icon" + o32(0)) icon_offset = fp.tell() with BytesIO(b"") as icon_fp: - _save_frame(frame, icon_fp, filename, im.encoderinfo) + _save_frame(frame, icon_fp, im.encoderinfo) icon_fp.seek(0) icon_data = icon_fp.read() @@ -216,7 +219,7 @@ def _write_multiple_frames(im: Image.Image, fp: BytesIO, filename: str): fp.seek(fram_end) -def _write_info(im: Image.Image, fp: BytesIO, filename: str): +def _write_info(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: fp.write(b"LIST" + o32(0)) list_offset = fp.tell() @@ -263,7 +266,7 @@ def _write_info(im: Image.Image, fp: BytesIO, filename: str): fp.seek(info_end) -def _save(im: Image.Image, fp: BytesIO, filename: str): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: fp.write(b"RIFF\x00\x00\x00\x00") riff_offset = fp.tell() @@ -272,9 +275,9 @@ def _save(im: Image.Image, fp: BytesIO, filename: str): frames = im.encoderinfo.get("append_images", []) if frames: - _write_multiple_frames(im, fp, filename) + _write_multiple_frames(im, fp) else: - _write_single_frame(im, fp, filename) + _write_single_frame(im, fp) pass riff_end = fp.tell() @@ -352,7 +355,8 @@ class AniFile: if self.rate is None: self.rate = [self.anih["iDispRate"] for i in range(self.anih["nFrames"])] - def frame(self, frame): + def frame(self, frame: int) -> CurImagePlugin.CurImageFile: + assert self.anih is not None if frame > self.anih["nFrames"]: msg = "Frame index out of animation bounds" raise EOFError(msg) @@ -361,13 +365,12 @@ class AniFile: self.buf.seek(offset) data = self.buf.read(size) - im = CurImagePlugin.CurImageFile(BytesIO(data)) - return im + return CurImagePlugin.CurImageFile(BytesIO(data)) - def sizes(self): + def sizes(self) -> list[int]: return [data["size"] for data in self.image_data] - def hotspots(self): + def hotspots(self) -> None: pass @@ -398,10 +401,12 @@ class AniImageFile(ImageFile.ImageFile): format = "ANI" format_description = "Windows Animated Cursor" - def _open(self): + def _open(self) -> None: self.ani = AniFile(self.fp) self.info["seq"] = self.ani.seq self.info["rate"] = self.ani.rate + + assert self.ani.anih is not None self.info["frames"] = self.ani.anih["nFrames"] self.frame = 0 @@ -410,24 +415,24 @@ class AniImageFile(ImageFile.ImageFile): self.size = self.im.size @property - def size(self): + def size(self) -> tuple[int, int]: return self._size @size.setter - def size(self, value): + def size(self, value: tuple[int, int]) -> None: if value not in self.info["sizes"]: msg = "This is not one of the allowed sizes of this image" raise ValueError(msg) self._size = value - def load(self): + def load(self) -> None: im = self.ani.frame(self.frame) self.info["sizes"] = im.info["sizes"] self.info["hotspots"] = im.info["hotspots"] self.im = im.im self._mode = im.mode - def seek(self, frame): + def seek(self, frame: int) -> None: if frame > self.info["frames"] - 1 or frame < 0: msg = "Frame index out of animation bounds" raise EOFError(msg) diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index 1f2c326dd..3a897171e 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -18,6 +18,7 @@ from __future__ import annotations from io import BytesIO +from typing import IO, NamedTuple from . import BmpImagePlugin, IcoImagePlugin, Image, ImageFile from ._binary import i16le as i16 @@ -32,7 +33,7 @@ from ._binary import o32le as o32 _MAGIC = b"\x00\x00\x02\x00" -def _save(im: Image.Image, fp: BytesIO, filename: str): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: fp.write(_MAGIC) bmp = im.encoderinfo.get("bitmap_format", "") == "bmp" s = im.encoderinfo.get( @@ -82,7 +83,9 @@ def _save(im: Image.Image, fp: BytesIO, filename: str): if bits != 32: and_mask = Image.new("1", size) ImageFile._save( - and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))] + and_mask, + image_io, + [ImageFile._Tile("raw", (0, 0) + size, 0, ("1", 0, -1))], ) else: frame.alpha = True @@ -109,10 +112,24 @@ def _accept(prefix: bytes) -> bool: return prefix.startswith(_MAGIC) +class IconHeader(NamedTuple): + width: int + height: int + nb_color: int + reserved: int + bpp: int + x_hotspot: int + y_hotspot: int + size: int + offset: int + dim: tuple[int, int] + square: int + + ## # Image plugin for Windows Cursor files. class CurFile(IcoImagePlugin.IcoFile): - def __init__(self, buf: BytesIO()): + def __init__(self, buf: IO[bytes]) -> None: """ Parse image from file-like object containing cur file data """ @@ -133,24 +150,12 @@ class CurFile(IcoImagePlugin.IcoFile): for _ in range(self.nb_items): s = buf.read(16) - icon_header = { - "width": s[0], - "height": s[1], - "nb_color": s[2], # No. of colors in image (0 if >=8bpp) - "reserved": s[3], - "x_hotspot": i16(s, 4), - "y_hotspot": i16(s, 6), - "size": i32(s, 8), - "offset": i32(s, 12), - } - # See Wikipedia - for j in ("width", "height"): - if not icon_header[j]: - icon_header[j] = 256 + width = s[0] or 256 + height = s[1] or 256 - icon_header["dim"] = (icon_header["width"], icon_header["height"]) - icon_header["square"] = icon_header["width"] * icon_header["height"] + size = i32(s, 8) + square = width * height # TODO: This needs further investigation. Cursor files do not really # specify their bpp like ICO's as those bits are used for the y_hotspot. @@ -158,28 +163,35 @@ class CurFile(IcoImagePlugin.IcoFile): # of pixels * 1bpp) and dividing by the number of pixels. This seems # to work well so far. BITMAP_INFO_HEADER_SIZE = 40 - bpp_without_and = ( - (icon_header["size"] - BITMAP_INFO_HEADER_SIZE) * 8 - ) // icon_header["square"] - + bpp_without_and = ((size - BITMAP_INFO_HEADER_SIZE) * 8) // square if bpp_without_and != 32: - icon_header["bpp"] = ( - (icon_header["size"] - BITMAP_INFO_HEADER_SIZE) * 8 - - icon_header["square"] - ) // icon_header["square"] + bpp = ((size - BITMAP_INFO_HEADER_SIZE) * 8 - square) // square else: - icon_header["bpp"] = bpp_without_and + bpp = bpp_without_and + + icon_header = IconHeader( + width=width, + height=height, + nb_color=s[2], # No. of colors in image (0 if >=8bpp) + reserved=s[3], + bpp=bpp, + x_hotspot=i16(s, 4), + y_hotspot=i16(s, 6), + size=size, + offset=i32(s, 12), + dim=(width, height), + square=square, + ) self.entry.append(icon_header) - self.entry = sorted(self.entry, key=lambda x: x["square"]) - self.entry.reverse() + self.entry = sorted(self.entry, key=lambda x: x.square, reverse=True) - def sizes(self): - return [(h["width"], h["height"]) for h in self.entry] + def sizes(self) -> list[tuple[int, int]]: + return [(h.width, h.height) for h in self.entry] - def hotspots(self): - return [(h["x_hotspot"], h["y_hotspot"]) for h in self.entry] + def hotspots(self) -> list[tuple[int, int]]: + return [(h.x_hotspot, h.y_hotspot) for h in self.entry] class CurImageFile(IcoImagePlugin.IcoImageFile): @@ -213,7 +225,7 @@ class CurImageFile(IcoImagePlugin.IcoImageFile): self.info["sizes"] = self.ico.sizes() self.info["hotspots"] = self.ico.hotspots() if len(self.ico.entry) > 0: - self.size = self.ico.entry[0]["dim"] + self.size = self.ico.entry[0].dim else: msg = "No cursors were found" raise TypeError(msg)