From a040a92a4983fb29516fbae00c0e7f90094ca853 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 eb53829d21f..24d3c45f259 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 2f624f477805ae070d5103ae6808dc7643a912d8 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 --- Tests/benchmarks.py | 2 +- src/PIL/EpsImagePlugin.py | 4 +++- src/PIL/ImageGrab.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 0c67d298dcb..12e8ef76fc2 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from collections.abc import Callable - from pytest_benchmark.fixture import ( # type: ignore[import-not-found] + from pytest_benchmark.fixture import ( # type: ignore[unused-ignore, import-not-found] BenchmarkFixture, ) 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 35af1ef5885840b16d771eac56d47f907c8f2e0b 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 24d3c45f259..e305fe79992 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 cc0b19186a1f020199464ef4970dbf0dc52aa1f8 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 a2c25af7fe8..32fff053346 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,5 +28,23 @@ jobs: uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.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