From f5cc6fcac3287966aa131d23446f62c146486cea Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 27 Jan 2026 14:15:56 +0100 Subject: [PATCH 1/4] enable the mypy's strict mode --- pyproject.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 76182deb21c..50aac7c41f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,16 +187,15 @@ lint.isort.required-imports = [ max_supported_python = "3.15" [tool.mypy] -follow_imports = "silent" python_version = "3.11" -disallow_any_generics = true -disallow_untyped_defs = true -warn_redundant_casts = true -warn_unused_ignores = true +disallow_subclassing_any = false +disallow_untyped_calls = false +warn_return_any = false warn_unreachable = true enable_error_code = "ignore-without-code" -extra_checks = true +strict = true pretty = true +no_implicit_reexport = false [tool.pytest] addopts = [ "-ra", "--color=auto" ] From 66c291f85ea005b57771a46a6fc6129b4299bf6e Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 27 Jan 2026 14:16:33 +0100 Subject: [PATCH 2/4] Fix mypy errors Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> Co-authored-by: Aarni Koskela --- src/PIL/EpsImagePlugin.py | 4 +++- src/PIL/ImageGrab.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 363ba19e130..5dc508979b0 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/ImageGrab.py b/src/PIL/ImageGrab.py index 52cbfea406d..0a638bb3504 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -79,7 +79,7 @@ def grab( scale = 1 if scale_down else 2 im_cropped = im.resize( ((right - left) * scale, (bottom - top) * scale), - box=tuple(coord * 2 for coord in bbox), + box=(left * 2, top * 2, right * 2, bottom * 2), ) else: im_cropped = im.crop(bbox) From 0125be06a7e1abade4f7bbb04437988d87bafcc5 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 21 Jul 2026 08:23:50 +0300 Subject: [PATCH 3/4] Remove python_version mypy configuration; fix issues arising on 3.11-3.14 --- Tests/conftest.py | 4 ++-- pyproject.toml | 1 - src/PIL/GifImagePlugin.py | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 1f32bbedff3..9795375d9f8 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -10,7 +10,7 @@ gil_enabled_at_start = True if FREE_THREADED_BUILD: - gil_enabled_at_start = sys._is_gil_enabled() # type: ignore[attr-defined] + gil_enabled_at_start = sys._is_gil_enabled() # type: ignore[attr-defined,unused-ignore] def pytest_report_header(config: pytest.Config) -> str: @@ -28,7 +28,7 @@ def pytest_terminal_summary(terminalreporter: pytest.TerminalReporter) -> None: if ( FREE_THREADED_BUILD and not gil_enabled_at_start - and sys._is_gil_enabled() # type: ignore[attr-defined] + and sys._is_gil_enabled() # type: ignore[attr-defined,unused-ignore] ): tr = terminalreporter tr.ensure_newline() diff --git a/pyproject.toml b/pyproject.toml index 50aac7c41f6..d1e09e977a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,7 +187,6 @@ lint.isort.required-imports = [ max_supported_python = "3.15" [tool.mypy] -python_version = "3.11" disallow_subclassing_any = false disallow_untyped_calls = false warn_return_any = false diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b8db5d83284..41c537cc1e6 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -1195,8 +1195,9 @@ class Collector(BytesIO): data = [] def write(self, data: Buffer) -> int: - self.data.append(data) - return len(data) + data_bytes = bytes(data) + self.data.append(data_bytes) + return len(data_bytes) im.load() # make sure raster data is available From 0c2f232671af6f683ef306a4c9230edbe95c980e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 22 Jul 2026 08:35:48 +0300 Subject: [PATCH 4/4] CI: separate lint + mypy step; run mypy with all supported Python versions --- .github/workflows/lint.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3b2b34acd52..fac8eecbdbc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,5 +28,23 @@ jobs: uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 - name: Lint run: uvx --with tox-uv tox -e lint + mypy: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [ + "3.14", + "3.13", + "3.12", + "3.11", + ] + name: Mypy + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: Mypy - run: uvx --with tox-uv tox -e mypy + run: uvx --python=${{ matrix.python-version }} --with tox-uv tox -e mypy