diff --git a/.ci/requirements-mypy.txt b/.ci/requirements-mypy.txt index ad78bcb67a3..88099650693 100644 --- a/.ci/requirements-mypy.txt +++ b/.ci/requirements-mypy.txt @@ -1,4 +1,4 @@ -mypy==1.20.2 +mypy==2.1.0 arro3-compute arro3-core IceSpringPySideStubs-PyQt6 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 5103a767278..6bc142a5ec8 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -1054,7 +1054,8 @@ def test_eof(self, monkeypatch: pytest.MonkeyPatch) -> None: # the image should still end when there is no new data class InfiniteMockPyDecoder(ImageFile.PyDecoder): def decode( - self, buffer: bytes | Image.SupportsArrayInterface + self, + buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface, ) -> tuple[int, int]: return 0, 0 diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index a2218673b44..8bb4ff8db92 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -40,7 +40,8 @@ def helper_save_as_pdf(tmp_path: Path, mode: str, **kwargs: Any) -> str: with open(outfile, "rb") as fp: contents = fp.read() size = tuple( - float(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split() + int(float(d)) + for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split() ) assert im.size == size diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py index 5f4a704f213..1a9a34e84f1 100644 --- a/Tests/test_format_lab.py +++ b/Tests/test_format_lab.py @@ -19,9 +19,9 @@ def test_white() -> None: assert k == (255, 128, 128) - assert L == (255,) * 100 - assert a == (128,) * 100 - assert b == (128,) * 100 + assert L == (255.0,) * 100 + assert a == (128.0,) * 100 + assert b == (128.0,) * 100 def test_green() -> None: diff --git a/Tests/test_image_frombytes.py b/Tests/test_image_frombytes.py index 98c0ea0b43c..06b4c5b3118 100644 --- a/Tests/test_image_frombytes.py +++ b/Tests/test_image_frombytes.py @@ -11,7 +11,7 @@ def test_sanity(data_type: str) -> None: im1 = hopper() - data = im1.tobytes() + data: bytes | memoryview = im1.tobytes() if data_type == "memoryview": data = memoryview(data) im2 = Image.frombytes(im1.mode, im1.size, data) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index a30fb18b80a..d73ab34282b 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -278,9 +278,9 @@ def test_simple_lab() -> None: a_data = i_lab.get_flattened_data(1) b_data = i_lab.get_flattened_data(2) - assert l_data == (137,) * 100 - assert a_data == (128,) * 100 - assert b_data == (128,) * 100 + assert l_data == (137.0,) * 100 + assert a_data == (128.0,) * 100 + assert b_data == (128.0,) * 100 def test_lab_color() -> None: diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 5177059055a..e0352dd26be 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -260,7 +260,9 @@ def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: # eof return -1, 0 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 40947470708..2a8f20ce82e 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -821,7 +821,7 @@ def test_variation_set_by_axes(font: ImageFont.FreeTypeFont) -> None: ids=("ls", "ms", "rs", "ma", "mt", "mm", "mb", "md"), ) def test_anchor( - layout_engine: ImageFont.Layout, anchor: str, left: int, top: int + layout_engine: ImageFont.Layout, anchor: str, left: float, top: float ) -> None: name, text = "quick", "Quick" path = f"Tests/images/test_anchor_{name}_{anchor}.png" diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index e7af160ddd2..36df32aff66 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -116,7 +116,7 @@ def test_dib_frombytes_tobytes_roundtrip(self) -> None: # Act # Make one the same as the using tobytes()/frombytes() - test_buffer = dib1.tobytes() + test_buffer: bytes | memoryview = dib1.tobytes() for datatype in ("bytes", "memoryview"): if datatype == "memoryview": test_buffer = memoryview(test_buffer) diff --git a/docs/example/DdsImagePlugin.py b/docs/example/DdsImagePlugin.py index e0557976c28..3f01721f3ea 100644 --- a/docs/example/DdsImagePlugin.py +++ b/docs/example/DdsImagePlugin.py @@ -258,7 +258,9 @@ def load_seek(self, pos: int) -> None: class DXT1Decoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None try: self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize)) @@ -271,7 +273,9 @@ def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int class DXT5Decoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None try: self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize)) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 6bb92edf891..19de6cafdbf 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -295,7 +295,9 @@ def _open(self) -> None: class _BLPBaseDecoder(abc.ABC, ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: try: self._read_header() self._load() diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index a6724cab437..c2f619bfc12 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -324,7 +324,9 @@ def _open(self) -> None: class BmpRleDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None rle4 = self.args[1] data = bytearray() diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 312f602a6b1..ca0bafd3ec4 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -488,7 +488,9 @@ def load_seek(self, pos: int) -> None: class DdsRgbDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None bitcount, masks = self.args diff --git a/src/PIL/FitsImagePlugin.py b/src/PIL/FitsImagePlugin.py index e918407784d..c2af3f0ebe0 100644 --- a/src/PIL/FitsImagePlugin.py +++ b/src/PIL/FitsImagePlugin.py @@ -126,7 +126,9 @@ def _parse_headers( class FitsGzipDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None with gzip.open(self.fd) as fp: value = fp.read(self.state.xsize * self.state.ysize * 4) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 81add2f7a41..d7fbc45ce57 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -931,7 +931,7 @@ def tobitmap(self, name: str = "image") -> bytes: def frombytes( self, - data: bytes | bytearray | SupportsArrayInterface, + data: bytes | bytearray | memoryview | SupportsArrayInterface, decoder_name: str = "raw", *args: Any, ) -> None: @@ -3241,7 +3241,7 @@ def new( def frombytes( mode: str, size: tuple[int, int], - data: bytes | bytearray | SupportsArrayInterface, + data: bytes | bytearray | memoryview | SupportsArrayInterface, decoder_name: str = "raw", *args: Any, ) -> Image: diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index c70d93f3c97..a1709295fdd 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -836,7 +836,9 @@ class PyDecoder(PyCodec): def pulls_fd(self) -> bool: return self._pulls_fd - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: """ Override to perform the decoding process. @@ -849,7 +851,10 @@ def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int raise NotImplementedError(msg) def set_as_raw( - self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = () + self, + data: bytes | bytearray, + rawmode: str | None = None, + extra: tuple[Any, ...] = (), ) -> None: """ Convenience method to set the internal image from a stream of raw data diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 9326eeeda9d..71c5845e96b 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -189,10 +189,10 @@ def __init__(self, radius: float | Sequence[float] = 2) -> None: def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: xy = self.radius + if xy == (0, 0) or xy == 0: + return image.copy() if isinstance(xy, (int, float)): xy = (xy, xy) - if xy == (0, 0): - return image.copy() return image.gaussian_blur(xy) @@ -221,10 +221,10 @@ def __init__(self, radius: float | Sequence[float]) -> None: def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: xy = self.radius + if xy == (0, 0) or xy == 0: + return image.copy() if isinstance(xy, (int, float)): xy = (xy, xy) - if xy == (0, 0): - return image.copy() return image.box_blur(xy) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 7705608e3ec..2734f68b173 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -23,7 +23,7 @@ from . import Image -_viewers = [] +_viewers: list[Viewer] = [] def register(viewer: type[Viewer] | Viewer, order: int = 1) -> None: diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 98c28f29f1d..29263617d0b 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -183,7 +183,7 @@ def paste( else: self.image.paste(im.im) - def frombytes(self, buffer: bytes) -> None: + def frombytes(self, buffer: bytes | memoryview) -> None: """ Load display memory contents from byte data. diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index fa0f52fe8db..47fe615299a 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -112,7 +112,9 @@ class MspDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None img = io.BytesIO() diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 99ff2699977..e4e094e715a 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -824,7 +824,7 @@ def interpret_name(cls, raw: bytes, as_text: bool = False) -> str | bytes: @classmethod def get_value( cls, - data: bytes | bytearray | mmap.mmap, + data: bytes | bytearray | memoryview | mmap.mmap, offset: int, expect_indirect: IndirectReference | None = None, max_nesting: int = -1, @@ -902,7 +902,7 @@ def get_value( if stream_len is None or not isinstance(stream_len, int): msg = f"bad or missing Length in stream dict ({stream_len})" raise PdfFormatError(msg) - stream_data = data[m.end() : m.end() + stream_len] + stream_data = bytes(data[m.end() : m.end() + stream_len]) m = cls.re_stream_end.match(data, m.end() + stream_len) check_format_condition(m is not None, "stream end not found") assert m is not None @@ -985,7 +985,7 @@ def get_value( @classmethod def get_literal_string( - cls, data: bytes | bytearray | mmap.mmap, offset: int + cls, data: bytes | bytearray | memoryview | mmap.mmap, offset: int ) -> tuple[bytes, int]: nesting_depth = 0 result = bytearray() diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 307bc97ff65..dc1f237f927 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -246,7 +246,7 @@ def _decode_blocks(self, maxval: int) -> bytearray: block = self._read_block() # read next block if not block: if half_token: - block = bytearray(b" ") # flush half_token + block = b" " # flush half_token else: # eof break @@ -284,7 +284,9 @@ def _decode_blocks(self, maxval: int) -> bytearray: break return data - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: self._comment_spans = False if self.mode == "1": data = self._decode_bitonal() @@ -300,7 +302,9 @@ def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int class PpmDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None data = bytearray() diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index d0709b1198a..c564935b7e4 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -51,7 +51,9 @@ def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 self._previously_seen_pixels[hash_value] = value - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None self._previously_seen_pixels = {} @@ -224,7 +226,7 @@ def encode(self, bufsize: int) -> tuple[int, int, bytes]: data += self._write_run() data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding - return len(data), 0, data + return len(data), 0, bytes(data) Image.register_open(QoiImageFile.format, QoiImageFile, _accept) diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 853022150ae..05ef5ab297e 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -198,7 +198,9 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: class SGI16Decoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None assert self.im is not None diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 11d90699d10..b8bf2282d74 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -37,7 +37,7 @@ import os import struct import sys -from typing import IO, Any, cast +from typing import IO, Any from . import Image, ImageFile from ._util import DeferredError @@ -193,7 +193,7 @@ def seek(self, frame: int) -> None: def convert2byte(self, depth: int = 255) -> Image.Image: extrema = self.getextrema() assert isinstance(extrema[0], float) - minimum, maximum = cast(tuple[float, float], extrema) + minimum, maximum = extrema m: float = 1 if maximum != minimum: m = depth / (maximum - minimum) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 5094faa1325..6a1d947664e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -318,8 +318,8 @@ def _limit_signed_rational( ## # Wrapper for TIFF IFDs. -_load_dispatch = {} -_write_dispatch = {} +_load_dispatch: dict[int, tuple[int, _LoaderFunc]] = {} +_write_dispatch: dict[int, Callable[..., Any]] = {} def _delegate(op: str) -> Any: diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 3be240fbc1a..29ad2cb9cd4 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -118,7 +118,9 @@ def load_read(self, read_bytes: int) -> bytes: class XpmDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + def decode( + self, buffer: bytes | bytearray | memoryview | Image.SupportsArrayInterface + ) -> tuple[int, int]: assert self.fd is not None data = bytearray()