diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 575d911def5..0b88526ff17 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -165,7 +165,7 @@ def test_reduce() -> None: assert callable(im.reduce) im.reduce = 2 # type: ignore[assignment, method-assign] - assert im.reduce == 2 + assert im.reduce == 2 # type: ignore[comparison-overlap] im.load() assert im.size == (160, 120) 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_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/pyproject.toml b/pyproject.toml index cc616bc547c..12f6fab3c2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -216,11 +216,10 @@ testpaths = [ [tool.mypy] python_version = "3.10" pretty = true -disallow_any_generics = true -disallow_untyped_defs = true +strict = true +disallow_subclassing_any = false +disallow_untyped_calls = false enable_error_code = "ignore-without-code" -extra_checks = true -follow_imports = "silent" -warn_redundant_casts = true +no_implicit_reexport = false +warn_return_any = false warn_unreachable = true -warn_unused_ignores = true diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 2effb816cfb..5ca78c5d7a5 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -203,7 +203,9 @@ def _open(self) -> None: imagedata_size: tuple[int, int] | None = None byte_arr = bytearray(255) - bytes_mv = memoryview(byte_arr) + # the extra `bytes` annotation here works around several false positive + # `comparison-overlap` mypy errors + bytes_mv: bytes | memoryview = memoryview(byte_arr) bytes_read = 0 reading_header_comments = True reading_trailer_comments = False diff --git a/src/PIL/Image.py b/src/PIL/Image.py index fdcc680b900..6d79e280677 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -69,6 +69,8 @@ from types import ModuleType from typing import Any, Literal + from ._typing import Buffer + logger = logging.getLogger(__name__) @@ -931,7 +933,7 @@ def tobitmap(self, name: str = "image") -> bytes: def frombytes( self, - data: bytes | bytearray | SupportsArrayInterface, + data: Buffer | SupportsArrayInterface, decoder_name: str = "raw", *args: Any, ) -> None: @@ -3232,7 +3234,7 @@ def new( def frombytes( mode: str, size: tuple[int, int], - data: bytes | bytearray | SupportsArrayInterface, + data: Buffer | SupportsArrayInterface, decoder_name: str = "raw", *args: Any, ) -> Image: diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 78abe3c77be..3b29c47c9e7 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -41,7 +41,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from ._typing import StrOrBytesPath + from ._typing import Buffer, StrOrBytesPath logger = logging.getLogger(__name__) @@ -839,7 +839,7 @@ 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: Buffer | Image.SupportsArrayInterface) -> tuple[int, int]: """ Override to perform the decoding process. @@ -852,7 +852,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 @@ -893,7 +896,7 @@ class PyEncoder(PyCodec): def pushes_fd(self) -> bool: return self._pushes_fd - def encode(self, bufsize: int) -> tuple[int, int, bytes]: + def encode(self, bufsize: int) -> tuple[int, int, bytes | bytearray]: """ Override to perform the encoding process. diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 98c28f29f1d..18f3f73c30f 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -19,6 +19,7 @@ from __future__ import annotations from . import Image +from ._typing import Buffer class HDC: @@ -183,7 +184,7 @@ def paste( else: self.image.paste(im.im) - def frombytes(self, buffer: bytes) -> None: + def frombytes(self, buffer: Buffer) -> None: """ Load display memory contents from byte data. diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 2c9031469ad..1fdf6ec8d52 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -14,6 +14,8 @@ if TYPE_CHECKING: from typing import IO + from ._typing import Buffer + _DictBase = collections.UserDict[str | bytes, Any] else: _DictBase = collections.UserDict @@ -316,7 +318,7 @@ def __bytes__(self) -> bytes: class PdfStream: - def __init__(self, dictionary: PdfDict, buf: bytes) -> None: + def __init__(self, dictionary: PdfDict, buf: Buffer) -> None: self.dictionary = dictionary self.buf = buf diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 307bc97ff65..5b17216156a 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -169,12 +169,12 @@ def _read_block(self) -> bytes: return self.fd.read(ImageFile.SAFEBLOCK) - def _find_comment_end(self, block: bytes, start: int = 0) -> int: + def _find_comment_end(self, block: bytes | bytearray, start: int = 0) -> int: a = block.find(b"\n", start) b = block.find(b"\r", start) return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) - def _ignore_comments(self, block: bytes) -> bytes: + def _ignore_comments(self, block: bytes | bytearray) -> bytes | bytearray: if self._comment_spans: # Finish current comment while block: @@ -217,7 +217,7 @@ def _decode_bitonal(self) -> bytearray: total_bytes = self.state.xsize * self.state.ysize while len(data) != total_bytes: - block = self._read_block() # read next block + block: bytes | bytearray = self._read_block() # read next block if not block: # eof break @@ -241,9 +241,9 @@ def _decode_blocks(self, maxval: int) -> bytearray: bands = Image.getmodebands(self.mode) total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count - half_token = b"" + half_token: bytes | bytearray = b"" while len(data) != total_bytes: - block = self._read_block() # read next block + block: bytes | bytearray = self._read_block() # read next block if not block: if half_token: block = bytearray(b" ") # flush half_token diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index d0709b1198a..f6e2e831cab 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -151,7 +151,7 @@ def _delta(self, left: int, right: int) -> int: result -= 256 return result - def encode(self, bufsize: int) -> tuple[int, int, bytes]: + def encode(self, bufsize: int) -> tuple[int, int, bytearray]: assert self.im is not None self._previously_seen_pixels = {0: (0, 0, 0, 0)}