From 9fc3fa82082e13089548446a6f8a9566553e15ea Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:49:05 +0200 Subject: [PATCH 1/8] Add PYI (flake8-pyi) to Ruff --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 518facc34f7..d1d9c82e40e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,6 +106,7 @@ select = [ "ISC", # flake8-implicit-str-concat "LOG", # flake8-logging "PGH", # pygrep-hooks + "PYI", # flake8-pyi "RUF100", # unused noqa (yesqa) "UP", # pyupgrade "W", # pycodestyle warnings From 41ffc1de81e3dcf1b82c249421ada77e60622afe Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:49:46 +0200 Subject: [PATCH 2/8] Fix: PYI044 'from __future__ import annotations' has no effect in stub files, since type checkers automatically treat stubs as having those semantics --- src/PIL/_imagingcms.pyi | 2 -- src/PIL/_imagingft.pyi | 2 -- src/PIL/_imagingmath.pyi | 2 -- src/PIL/_imagingmorph.pyi | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/PIL/_imagingcms.pyi b/src/PIL/_imagingcms.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_imagingcms.pyi +++ b/src/PIL/_imagingcms.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/_imagingmath.pyi b/src/PIL/_imagingmath.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_imagingmath.pyi +++ b/src/PIL/_imagingmath.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/_imagingmorph.pyi b/src/PIL/_imagingmorph.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_imagingmorph.pyi +++ b/src/PIL/_imagingmorph.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... From 5d37d028d31dc6aea6ee64c61c9870df94d75040 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 23 Mar 2024 20:44:43 +0200 Subject: [PATCH 3/8] Fix: PYI044 'from __future__ import annotations' has no effect in stub files, since type checkers automatically treat stubs as having those semantics --- src/PIL/_imaging.pyi | 2 -- src/PIL/_webp.pyi | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/_webp.pyi b/src/PIL/_webp.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_webp.pyi +++ b/src/PIL/_webp.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... From 6ed952b510f256f76ddaf7b43855a85a3e83ca15 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 23 Mar 2024 20:48:46 +0200 Subject: [PATCH 4/8] Fix: PYI024 Use typing.NamedTuple instead of collections.namedtuple --- Tests/test_file_libtiff.py | 46 +++++++++++++++++++++----------------- src/PIL/PdfParser.py | 11 +++++---- src/PIL/TiffTags.py | 12 ++++++++-- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 14504e58999..6c32b5ad427 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -6,8 +6,8 @@ import os import re import sys -from collections import namedtuple from pathlib import Path +from typing import Any, NamedTuple import pytest @@ -243,36 +243,40 @@ def test_additional_metadata(self, tmp_path: Path) -> None: TiffImagePlugin.WRITE_LIBTIFF = False def test_custom_metadata(self, tmp_path: Path) -> None: - tc = namedtuple("tc", "value,type,supported_by_default") + class Tc(NamedTuple): + value: Any + type: int + supported_by_default: bool + custom = { 37000 + k: v for k, v in enumerate( [ - tc(4, TiffTags.SHORT, True), - tc(123456789, TiffTags.LONG, True), - tc(-4, TiffTags.SIGNED_BYTE, False), - tc(-4, TiffTags.SIGNED_SHORT, False), - tc(-123456789, TiffTags.SIGNED_LONG, False), - tc(TiffImagePlugin.IFDRational(4, 7), TiffTags.RATIONAL, True), - tc(4.25, TiffTags.FLOAT, True), - tc(4.25, TiffTags.DOUBLE, True), - tc("custom tag value", TiffTags.ASCII, True), - tc(b"custom tag value", TiffTags.BYTE, True), - tc((4, 5, 6), TiffTags.SHORT, True), - tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True), - tc((-4, 9, 10), TiffTags.SIGNED_BYTE, False), - tc((-4, 5, 6), TiffTags.SIGNED_SHORT, False), - tc( + Tc(4, TiffTags.SHORT, True), + Tc(123456789, TiffTags.LONG, True), + Tc(-4, TiffTags.SIGNED_BYTE, False), + Tc(-4, TiffTags.SIGNED_SHORT, False), + Tc(-123456789, TiffTags.SIGNED_LONG, False), + Tc(TiffImagePlugin.IFDRational(4, 7), TiffTags.RATIONAL, True), + Tc(4.25, TiffTags.FLOAT, True), + Tc(4.25, TiffTags.DOUBLE, True), + Tc("custom tag value", TiffTags.ASCII, True), + Tc(b"custom tag value", TiffTags.BYTE, True), + Tc((4, 5, 6), TiffTags.SHORT, True), + Tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True), + Tc((-4, 9, 10), TiffTags.SIGNED_BYTE, False), + Tc((-4, 5, 6), TiffTags.SIGNED_SHORT, False), + Tc( (-123456789, 9, 34, 234, 219387, -92432323), TiffTags.SIGNED_LONG, False, ), - tc((4.25, 5.25), TiffTags.FLOAT, True), - tc((4.25, 5.25), TiffTags.DOUBLE, True), + Tc((4.25, 5.25), TiffTags.FLOAT, True), + Tc((4.25, 5.25), TiffTags.DOUBLE, True), # array of TIFF_BYTE requires bytes instead of tuple for backwards # compatibility - tc(bytes([4]), TiffTags.BYTE, True), - tc(bytes((4, 9, 10)), TiffTags.BYTE, True), + Tc(bytes([4]), TiffTags.BYTE, True), + Tc(bytes((4, 9, 10)), TiffTags.BYTE, True), ] ) } diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 4c510173814..2542d4e9127 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -8,7 +8,7 @@ import re import time import zlib -from typing import TYPE_CHECKING, Any, List, Union +from typing import TYPE_CHECKING, Any, List, NamedTuple, Union # see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set @@ -81,9 +81,12 @@ def check_format_condition(condition, error_message): raise PdfFormatError(error_message) -class IndirectReference( - collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"]) -): +class IndirectReferenceTuple(NamedTuple): + object_id: int + generation: int + + +class IndirectReference(IndirectReferenceTuple): def __str__(self): return f"{self.object_id} {self.generation} R" diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index b9419393119..3f2220c2ae9 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -18,10 +18,18 @@ ## from __future__ import annotations -from collections import namedtuple +from typing import NamedTuple -class TagInfo(namedtuple("_TagInfo", "value name type length enum")): +class _TagInfo(NamedTuple): + value: int + name: str + type: int + length: int + enum: dict[int, str] + + +class TagInfo(_TagInfo): __slots__: list[str] = [] def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): From 20d8095439c858f9e460c44e476f4e9dc8064ef2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 23 Mar 2024 20:49:43 +0200 Subject: [PATCH 5/8] Fix: PYI041 Use float instead of int | float --- Tests/test_lib_pack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 629a6dc7a87..6a0e704b89a 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -15,7 +15,7 @@ def assert_pack( mode: str, rawmode: str, data: int | bytes, - *pixels: int | float | tuple[int, ...], + *pixels: float | tuple[int, ...], ) -> None: """ data - either raw bytes with data or just number of bytes in rawmode. @@ -239,7 +239,7 @@ def assert_unpack( mode: str, rawmode: str, data: int | bytes, - *pixels: int | float | tuple[int, ...], + *pixels: float | tuple[int, ...], ) -> None: """ data - either raw bytes with data or just number of bytes in rawmode. From eff78cb514f41ca5ee91ae56da3fe44f570b9a87 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 23 Mar 2024 20:52:32 +0200 Subject: [PATCH 6/8] Skip PYI034 as typing.Self is in Python 3.10+ --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d1d9c82e40e..2ef3fb2d1fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,6 +117,7 @@ ignore = [ "E221", # Multiple spaces before operator "E226", # Missing whitespace around arithmetic operator "E241", # Multiple spaces after ',' + "PYI034", # flake8-pyi: typing.Self added in Python 3.10 ] [tool.ruff.lint.per-file-ignores] From 98c7d90af613a9d5e726361303ceea86b2998bda Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:16:31 +0200 Subject: [PATCH 7/8] Variables can be None; update comment Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- pyproject.toml | 2 +- src/PIL/TiffTags.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2ef3fb2d1fd..c660752c923 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,7 +117,7 @@ ignore = [ "E221", # Multiple spaces before operator "E226", # Missing whitespace around arithmetic operator "E241", # Multiple spaces after ',' - "PYI034", # flake8-pyi: typing.Self added in Python 3.10 + "PYI034", # flake8-pyi: typing.Self added in Python 3.11 ] [tool.ruff.lint.per-file-ignores] diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 3f2220c2ae9..469d4def9d5 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -22,11 +22,11 @@ class _TagInfo(NamedTuple): - value: int + value: int | None name: str - type: int - length: int - enum: dict[int, str] + type: int | None + length: int | None + enum: dict[int, str] | None class TagInfo(_TagInfo): From 73bf04474fce2bbbc89311ee4b35b4faa9ae3a8d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:35:28 +0200 Subject: [PATCH 8/8] enum can't be None Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/TiffTags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 469d4def9d5..89fad703343 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -26,7 +26,7 @@ class _TagInfo(NamedTuple): name: str type: int | None length: int | None - enum: dict[int, str] | None + enum: dict[str, int] class TagInfo(_TagInfo):