Skip to content

Commit 5d6f22d

Browse files
authored
Merge pull request #7790 from radarhere/pyproject
Removed mypy excludes, except for olefile-related files
2 parents 6782a07 + 112a5a4 commit 5d6f22d

File tree

15 files changed

+174
-102
lines changed

15 files changed

+174
-102
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ exclude_also =
1010
if DEBUG:
1111
# Don't complain about compatibility code for missing optional dependencies
1212
except ImportError
13+
if TYPE_CHECKING:
14+
@abc.abstractmethod
1315

1416
[run]
1517
omit =

pyproject.toml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,6 @@ warn_redundant_casts = true
141141
warn_unreachable = true
142142
warn_unused_ignores = true
143143
exclude = [
144-
'^src/PIL/_tkinter_finder.py$',
145-
'^src/PIL/DdsImagePlugin.py$',
146144
'^src/PIL/FpxImagePlugin.py$',
147-
'^src/PIL/Image.py$',
148-
'^src/PIL/ImageQt.py$',
149-
'^src/PIL/ImImagePlugin.py$',
150145
'^src/PIL/MicImagePlugin.py$',
151-
'^src/PIL/PdfParser.py$',
152-
'^src/PIL/PyAccess.py$',
153-
'^src/PIL/TiffImagePlugin.py$',
154-
'^src/PIL/TiffTags.py$',
155-
'^src/PIL/WebPImagePlugin.py$',
156146
]

src/PIL/DdsImagePlugin.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,17 @@ class D3DFMT(IntEnum):
270270
# Backward compatibility layer
271271
module = sys.modules[__name__]
272272
for item in DDSD:
273+
assert item.name is not None
273274
setattr(module, "DDSD_" + item.name, item.value)
274-
for item in DDSCAPS:
275-
setattr(module, "DDSCAPS_" + item.name, item.value)
276-
for item in DDSCAPS2:
277-
setattr(module, "DDSCAPS2_" + item.name, item.value)
278-
for item in DDPF:
279-
setattr(module, "DDPF_" + item.name, item.value)
275+
for item1 in DDSCAPS:
276+
assert item1.name is not None
277+
setattr(module, "DDSCAPS_" + item1.name, item1.value)
278+
for item2 in DDSCAPS2:
279+
assert item2.name is not None
280+
setattr(module, "DDSCAPS2_" + item2.name, item2.value)
281+
for item3 in DDPF:
282+
assert item3.name is not None
283+
setattr(module, "DDPF_" + item3.name, item3.value)
280284

281285
DDS_FOURCC = DDPF.FOURCC
282286
DDS_RGB = DDPF.RGB

src/PIL/ImImagePlugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@
9393
for i in ["32S"]:
9494
OPEN[f"L {i} image"] = ("I", f"I;{i}")
9595
OPEN[f"L*{i} image"] = ("I", f"I;{i}")
96-
for i in range(2, 33):
97-
OPEN[f"L*{i} image"] = ("F", f"F;{i}")
96+
for j in range(2, 33):
97+
OPEN[f"L*{j} image"] = ("F", f"F;{j}")
9898

9999

100100
# --------------------------------------------------------------------

src/PIL/Image.py

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from __future__ import annotations
2828

29+
import abc
2930
import atexit
3031
import builtins
3132
import io
@@ -40,11 +41,8 @@
4041
from collections.abc import Callable, MutableMapping
4142
from enum import IntEnum
4243
from pathlib import Path
43-
44-
try:
45-
from defusedxml import ElementTree
46-
except ImportError:
47-
ElementTree = None
44+
from types import ModuleType
45+
from typing import IO, TYPE_CHECKING, Any
4846

4947
# VERSION was removed in Pillow 6.0.0.
5048
# PILLOW_VERSION was removed in Pillow 9.0.0.
@@ -60,6 +58,12 @@
6058
from ._binary import i32le, o32be, o32le
6159
from ._util import DeferredError, is_path
6260

61+
ElementTree: ModuleType | None
62+
try:
63+
from defusedxml import ElementTree
64+
except ImportError:
65+
ElementTree = None
66+
6367
logger = logging.getLogger(__name__)
6468

6569

@@ -110,6 +114,7 @@ class DecompressionBombError(Exception):
110114

111115

112116
USE_CFFI_ACCESS = False
117+
cffi: ModuleType | None
113118
try:
114119
import cffi
115120
except ImportError:
@@ -211,14 +216,22 @@ class Quantize(IntEnum):
211216
# --------------------------------------------------------------------
212217
# Registries
213218

214-
ID = []
215-
OPEN = {}
216-
MIME = {}
217-
SAVE = {}
218-
SAVE_ALL = {}
219-
EXTENSION = {}
220-
DECODERS = {}
221-
ENCODERS = {}
219+
if TYPE_CHECKING:
220+
from . import ImageFile
221+
ID: list[str] = []
222+
OPEN: dict[
223+
str,
224+
tuple[
225+
Callable[[IO[bytes], str | bytes], ImageFile.ImageFile],
226+
Callable[[bytes], bool] | None,
227+
],
228+
] = {}
229+
MIME: dict[str, str] = {}
230+
SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
231+
SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
232+
EXTENSION: dict[str, str] = {}
233+
DECODERS: dict[str, object] = {}
234+
ENCODERS: dict[str, object] = {}
222235

223236
# --------------------------------------------------------------------
224237
# Modes
@@ -2383,12 +2396,12 @@ def save(self, fp, format=None, **params) -> None:
23832396
may have been created, and may contain partial data.
23842397
"""
23852398

2386-
filename = ""
2399+
filename: str | bytes = ""
23872400
open_fp = False
23882401
if isinstance(fp, Path):
23892402
filename = str(fp)
23902403
open_fp = True
2391-
elif is_path(fp):
2404+
elif isinstance(fp, (str, bytes)):
23922405
filename = fp
23932406
open_fp = True
23942407
elif fp == sys.stdout:
@@ -2398,7 +2411,7 @@ def save(self, fp, format=None, **params) -> None:
23982411
pass
23992412
if not filename and hasattr(fp, "name") and is_path(fp.name):
24002413
# only set the name for metadata purposes
2401-
filename = fp.name
2414+
filename = os.path.realpath(os.fspath(fp.name))
24022415

24032416
# may mutate self!
24042417
self._ensure_mutable()
@@ -2409,7 +2422,8 @@ def save(self, fp, format=None, **params) -> None:
24092422

24102423
preinit()
24112424

2412-
ext = os.path.splitext(filename)[1].lower()
2425+
filename_ext = os.path.splitext(filename)[1].lower()
2426+
ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext
24132427

24142428
if not format:
24152429
if ext not in EXTENSION:
@@ -2451,7 +2465,7 @@ def save(self, fp, format=None, **params) -> None:
24512465
if open_fp:
24522466
fp.close()
24532467

2454-
def seek(self, frame) -> Image:
2468+
def seek(self, frame) -> None:
24552469
"""
24562470
Seeks to the given frame in this sequence file. If you seek
24572471
beyond the end of the sequence, the method raises an
@@ -2511,10 +2525,8 @@ def split(self) -> tuple[Image, ...]:
25112525

25122526
self.load()
25132527
if self.im.bands == 1:
2514-
ims = [self.copy()]
2515-
else:
2516-
ims = map(self._new, self.im.split())
2517-
return tuple(ims)
2528+
return (self.copy(),)
2529+
return tuple(map(self._new, self.im.split()))
25182530

25192531
def getchannel(self, channel):
25202532
"""
@@ -2871,7 +2883,14 @@ class ImageTransformHandler:
28712883
(for use with :py:meth:`~PIL.Image.Image.transform`)
28722884
"""
28732885

2874-
pass
2886+
@abc.abstractmethod
2887+
def transform(
2888+
self,
2889+
size: tuple[int, int],
2890+
image: Image,
2891+
**options: dict[str, str | int | tuple[int, ...] | list[int]],
2892+
) -> Image:
2893+
pass
28752894

28762895

28772896
# --------------------------------------------------------------------
@@ -3243,11 +3262,9 @@ def open(fp, mode="r", formats=None) -> Image:
32433262
raise TypeError(msg)
32443263

32453264
exclusive_fp = False
3246-
filename = ""
3247-
if isinstance(fp, Path):
3248-
filename = str(fp.resolve())
3249-
elif is_path(fp):
3250-
filename = fp
3265+
filename: str | bytes = ""
3266+
if is_path(fp):
3267+
filename = os.path.realpath(os.fspath(fp))
32513268

32523269
if filename:
32533270
fp = builtins.open(filename, "rb")
@@ -3421,7 +3438,11 @@ def merge(mode, bands):
34213438
# Plugin registry
34223439

34233440

3424-
def register_open(id, factory, accept=None) -> None:
3441+
def register_open(
3442+
id,
3443+
factory: Callable[[IO[bytes], str | bytes], ImageFile.ImageFile],
3444+
accept: Callable[[bytes], bool] | None = None,
3445+
) -> None:
34253446
"""
34263447
Register an image file plugin. This function should not be used
34273448
in application code.
@@ -3631,7 +3652,13 @@ def _apply_env_variables(env=None):
36313652
atexit.register(core.clear_cache)
36323653

36333654

3634-
class Exif(MutableMapping):
3655+
if TYPE_CHECKING:
3656+
_ExifBase = MutableMapping[int, Any]
3657+
else:
3658+
_ExifBase = MutableMapping
3659+
3660+
3661+
class Exif(_ExifBase):
36353662
"""
36363663
This class provides read and write access to EXIF image data::
36373664

src/PIL/ImageQt.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,26 @@
1919

2020
import sys
2121
from io import BytesIO
22+
from typing import Callable
2223

2324
from . import Image
2425
from ._util import is_path
2526

27+
qt_version: str | None
2628
qt_versions = [
2729
["6", "PyQt6"],
2830
["side6", "PySide6"],
2931
]
3032

3133
# If a version has already been imported, attempt it first
32-
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
33-
for qt_version, qt_module in qt_versions:
34+
qt_versions.sort(key=lambda version: version[1] in sys.modules, reverse=True)
35+
for version, qt_module in qt_versions:
3436
try:
37+
QBuffer: type
38+
QIODevice: type
39+
QImage: type
40+
QPixmap: type
41+
qRgba: Callable[[int, int, int, int], int]
3542
if qt_module == "PyQt6":
3643
from PyQt6.QtCore import QBuffer, QIODevice
3744
from PyQt6.QtGui import QImage, QPixmap, qRgba
@@ -41,6 +48,7 @@
4148
except (ImportError, RuntimeError):
4249
continue
4350
qt_is_installed = True
51+
qt_version = version
4452
break
4553
else:
4654
qt_is_installed = False

src/PIL/ImageShow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ class UnixViewer(Viewer):
184184

185185
@abc.abstractmethod
186186
def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]:
187-
pass # pragma: no cover
187+
pass
188188

189189
def get_command(self, file: str, **options: Any) -> str:
190190
command = self.get_command_ex(file, **options)[0]

src/PIL/PdfParser.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
import time
1010
import zlib
11+
from typing import TYPE_CHECKING, Any, List, Union
1112

1213

1314
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
@@ -239,12 +240,18 @@ def __bytes__(self):
239240
return bytes(result)
240241

241242

242-
class PdfArray(list):
243+
class PdfArray(List[Any]):
243244
def __bytes__(self):
244245
return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]"
245246

246247

247-
class PdfDict(collections.UserDict):
248+
if TYPE_CHECKING:
249+
_DictBase = collections.UserDict[Union[str, bytes], Any]
250+
else:
251+
_DictBase = collections.UserDict
252+
253+
254+
class PdfDict(_DictBase):
248255
def __setattr__(self, key, value):
249256
if key == "data":
250257
collections.UserDict.__setattr__(self, key, value)

src/PIL/PyAccess.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
from ._deprecate import deprecate
2727

28+
FFI: type
2829
try:
2930
from cffi import FFI
3031

0 commit comments

Comments
 (0)