From cc83cc8ec866ac0308fc8fc16ff16893540f77e2 Mon Sep 17 00:00:00 2001
From: Andrew Murray <radarhere@users.noreply.github.com>
Date: Sat, 22 Jun 2024 10:09:11 +1000
Subject: [PATCH] Updated type hints

---
 Tests/helper.py              |  4 ++--
 Tests/test_file_jpeg.py      |  4 +++-
 Tests/test_file_png.py       | 16 +++++++---------
 Tests/test_file_ppm.py       | 12 ++++--------
 Tests/test_file_tiff.py      |  2 +-
 Tests/test_font_leaks.py     |  2 +-
 Tests/test_image.py          |  8 ++++----
 Tests/test_image_draft.py    |  4 +++-
 Tests/test_image_paste.py    |  5 +++++
 Tests/test_image_point.py    |  2 +-
 Tests/test_image_putdata.py  |  8 ++++----
 Tests/test_image_resample.py |  2 +-
 Tests/test_imagecms.py       | 11 ++++++-----
 Tests/test_imagedraw.py      |  2 +-
 Tests/test_imagedraw2.py     |  5 +++--
 Tests/test_imagefile.py      |  2 +-
 Tests/test_imagefont.py      |  6 +++---
 Tests/test_imagefontpil.py   |  7 ++++++-
 Tests/test_imagepalette.py   |  2 +-
 Tests/test_psdraw.py         | 12 ++++--------
 src/PIL/Image.py             | 30 +++++++++++++++++++-----------
 src/PIL/ImageDraw.py         |  4 +++-
 src/PIL/ImageFont.py         | 12 ++++++++----
 src/PIL/ImageQt.py           |  2 +-
 src/PIL/ImageTransform.py    |  2 +-
 src/PIL/JpegImagePlugin.py   |  2 +-
 src/PIL/TiffTags.py          |  6 ++++--
 27 files changed, 98 insertions(+), 76 deletions(-)

diff --git a/Tests/helper.py b/Tests/helper.py
index fe337c09f5c..605a214d400 100644
--- a/Tests/helper.py
+++ b/Tests/helper.py
@@ -18,7 +18,7 @@
 import pytest
 from packaging.version import parse as parse_version
 
-from PIL import Image, ImageMath, features
+from PIL import Image, ImageFile, ImageMath, features
 
 logger = logging.getLogger(__name__)
 
@@ -240,7 +240,7 @@ def _test_leak(self, core: Callable[[], None]) -> None:
 # helpers
 
 
-def fromstring(data: bytes) -> Image.Image:
+def fromstring(data: bytes) -> ImageFile.ImageFile:
     return Image.open(BytesIO(data))
 
 
diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py
index 02d0b29d8c8..dbd91751712 100644
--- a/Tests/test_file_jpeg.py
+++ b/Tests/test_file_jpeg.py
@@ -1033,8 +1033,10 @@ def test_separate_tables(self) -> None:
 
     def test_repr_jpeg(self) -> None:
         im = hopper()
+        b = im._repr_jpeg_()
+        assert b is not None
 
-        with Image.open(BytesIO(im._repr_jpeg_())) as repr_jpeg:
+        with Image.open(BytesIO(b)) as repr_jpeg:
             assert repr_jpeg.format == "JPEG"
             assert_image_similar(im, repr_jpeg, 17)
 
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
index 46714a0f00b..0f09768028e 100644
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -535,8 +535,10 @@ def test_roundtrip_no_icc_profile(self) -> None:
 
     def test_repr_png(self) -> None:
         im = hopper()
+        b = im._repr_png_()
+        assert b is not None
 
-        with Image.open(BytesIO(im._repr_png_())) as repr_png:
+        with Image.open(BytesIO(b)) as repr_png:
             assert repr_png.format == "PNG"
             assert_image_equal(im, repr_png)
 
@@ -768,14 +770,10 @@ def test_seek(self) -> None:
     def test_save_stdout(self, buffer: bool) -> None:
         old_stdout = sys.stdout
 
-        if buffer:
+        class MyStdOut:
+            buffer = BytesIO()
 
-            class MyStdOut:
-                buffer = BytesIO()
-
-            mystdout = MyStdOut()
-        else:
-            mystdout = BytesIO()
+        mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
 
         sys.stdout = mystdout
 
@@ -785,7 +783,7 @@ class MyStdOut:
         # Reset stdout
         sys.stdout = old_stdout
 
-        if buffer:
+        if isinstance(mystdout, MyStdOut):
             mystdout = mystdout.buffer
         with Image.open(mystdout) as reloaded:
             assert_image_equal_tofile(reloaded, TEST_PNG_FILE)
diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py
index 1bfd0434e96..0a61830a40a 100644
--- a/Tests/test_file_ppm.py
+++ b/Tests/test_file_ppm.py
@@ -368,14 +368,10 @@ def test_mimetypes(tmp_path: Path) -> None:
 def test_save_stdout(buffer: bool) -> None:
     old_stdout = sys.stdout
 
-    if buffer:
+    class MyStdOut:
+        buffer = BytesIO()
 
-        class MyStdOut:
-            buffer = BytesIO()
-
-        mystdout = MyStdOut()
-    else:
-        mystdout = BytesIO()
+    mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
 
     sys.stdout = mystdout
 
@@ -385,7 +381,7 @@ class MyStdOut:
     # Reset stdout
     sys.stdout = old_stdout
 
-    if buffer:
+    if isinstance(mystdout, MyStdOut):
         mystdout = mystdout.buffer
     with Image.open(mystdout) as reloaded:
         assert_image_equal_tofile(reloaded, TEST_FILE)
diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py
index 17dab25e91b..08ff334ac45 100644
--- a/Tests/test_file_tiff.py
+++ b/Tests/test_file_tiff.py
@@ -120,7 +120,7 @@ def test_seek_too_large(self) -> None:
     def test_set_legacy_api(self) -> None:
         ifd = TiffImagePlugin.ImageFileDirectory_v2()
         with pytest.raises(Exception) as e:
-            ifd.legacy_api = None
+            ifd.legacy_api = False
         assert str(e.value) == "Not allowing setting of legacy api"
 
     def test_xyres_tiff(self) -> None:
diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py
index 3fb92a62edb..ab8a7f9ecca 100644
--- a/Tests/test_font_leaks.py
+++ b/Tests/test_font_leaks.py
@@ -34,7 +34,7 @@ class TestDefaultFontLeak(TestTTypeFontLeak):
 
     def test_leak(self) -> None:
         if features.check_module("freetype2"):
-            ImageFont.core = _util.DeferredError(ImportError)
+            ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
         try:
             default_font = ImageFont.load_default()
         finally:
diff --git a/Tests/test_image.py b/Tests/test_image.py
index 35a7a49a876..a8f61d67700 100644
--- a/Tests/test_image.py
+++ b/Tests/test_image.py
@@ -393,13 +393,13 @@ def test_alpha_inplace(self) -> None:
 
         # errors
         with pytest.raises(ValueError):
-            source.alpha_composite(over, "invalid source")
+            source.alpha_composite(over, "invalid destination")  # type: ignore[arg-type]
         with pytest.raises(ValueError):
-            source.alpha_composite(over, (0, 0), "invalid destination")
+            source.alpha_composite(over, (0, 0), "invalid source")  # type: ignore[arg-type]
         with pytest.raises(ValueError):
-            source.alpha_composite(over, 0)
+            source.alpha_composite(over, 0)  # type: ignore[arg-type]
         with pytest.raises(ValueError):
-            source.alpha_composite(over, (0, 0), 0)
+            source.alpha_composite(over, (0, 0), 0)  # type: ignore[arg-type]
         with pytest.raises(ValueError):
             source.alpha_composite(over, (0, 0), (0, -1))
 
diff --git a/Tests/test_image_draft.py b/Tests/test_image_draft.py
index 1ce1a7cd8f6..c9d8c93f314 100644
--- a/Tests/test_image_draft.py
+++ b/Tests/test_image_draft.py
@@ -16,7 +16,9 @@ def draft_roundtrip(
     im = Image.new(in_mode, in_size)
     data = tostring(im, "JPEG")
     im = fromstring(data)
-    mode, box = im.draft(req_mode, req_size)
+    result = im.draft(req_mode, req_size)
+    assert result is not None
+    box = result[1]
     scale, _ = im.decoderconfig
     assert box[:2] == (0, 0)
     assert (im.width - scale) < box[2] <= im.width
diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py
index d8f6b65e0d8..01cb880cb31 100644
--- a/Tests/test_image_paste.py
+++ b/Tests/test_image_paste.py
@@ -338,3 +338,8 @@ def test_different_sizes(self) -> None:
 
         im.copy().paste(im2)
         im.copy().paste(im2, (0, 0))
+
+    def test_incorrect_abbreviated_form(self) -> None:
+        im = Image.new("L", (1, 1))
+        with pytest.raises(ValueError):
+            im.paste(im, im, im)
diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py
index 05f209351d2..a5d5a15dbab 100644
--- a/Tests/test_image_point.py
+++ b/Tests/test_image_point.py
@@ -61,4 +61,4 @@ def test_f_lut() -> None:
 def test_f_mode() -> None:
     im = hopper("F")
     with pytest.raises(ValueError):
-        im.point(None)
+        im.point([])
diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py
index 5e57e4c4c8f..dad26ef144c 100644
--- a/Tests/test_image_putdata.py
+++ b/Tests/test_image_putdata.py
@@ -113,13 +113,13 @@ def test_array_F() -> None:
 def test_not_flattened() -> None:
     im = Image.new("L", (1, 1))
     with pytest.raises(TypeError):
-        im.putdata([[0]])  # type: ignore[list-item]
+        im.putdata([[0]])
     with pytest.raises(TypeError):
-        im.putdata([[0]], 2)  # type: ignore[list-item]
+        im.putdata([[0]], 2)
 
     with pytest.raises(TypeError):
         im = Image.new("I", (1, 1))
-        im.putdata([[0]])  # type: ignore[list-item]
+        im.putdata([[0]])
     with pytest.raises(TypeError):
         im = Image.new("F", (1, 1))
-        im.putdata([[0]])  # type: ignore[list-item]
+        im.putdata([[0]])
diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py
index 9b3bdf3306f..d6055b577e3 100644
--- a/Tests/test_image_resample.py
+++ b/Tests/test_image_resample.py
@@ -445,7 +445,7 @@ def test_wrong_arguments(self, resample: Image.Resampling) -> None:
         im.resize((32, 32), resample, (20, 20, 100, 20))
 
         with pytest.raises(TypeError, match="must be sequence of length 4"):
-            im.resize((32, 32), resample, (im.width, im.height))
+            im.resize((32, 32), resample, (im.width, im.height))  # type: ignore[arg-type]
 
         with pytest.raises(ValueError, match="can't be negative"):
             im.resize((32, 32), resample, (-20, 20, 100, 100))
diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py
index 55f72c3b95c..8a25460d36a 100644
--- a/Tests/test_imagecms.py
+++ b/Tests/test_imagecms.py
@@ -103,7 +103,7 @@ def test_sanity() -> None:
 
 
 def test_flags() -> None:
-    assert ImageCms.Flags.NONE == 0
+    assert ImageCms.Flags.NONE.value == 0
     assert ImageCms.Flags.GRIDPOINTS(0) == ImageCms.Flags.NONE
     assert ImageCms.Flags.GRIDPOINTS(256) == ImageCms.Flags.NONE
 
@@ -569,9 +569,9 @@ def create_test_image() -> Image.Image:
             for delta in nine_grid_deltas:
                 channel_data.paste(
                     channel_pattern,
-                    tuple(
-                        paste_offset[c] + delta[c] * channel_pattern.size[c]
-                        for c in range(2)
+                    (
+                        paste_offset[0] + delta[0] * channel_pattern.size[0],
+                        paste_offset[1] + delta[1] * channel_pattern.size[1],
                     ),
                 )
             chans.append(channel_data)
@@ -642,7 +642,8 @@ def test_auxiliary_channels_isolated() -> None:
                 # convert with and without AUX data, test colors are equal
                 src_colorSpace = cast(Literal["LAB", "XYZ", "sRGB"], src_format[1])
                 source_profile = ImageCms.createProfile(src_colorSpace)
-                destination_profile = ImageCms.createProfile(dst_format[1])
+                dst_colorSpace = cast(Literal["LAB", "XYZ", "sRGB"], dst_format[1])
+                destination_profile = ImageCms.createProfile(dst_colorSpace)
                 source_image = src_format[3]
                 test_transform = ImageCms.buildTransform(
                     source_profile,
diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py
index 8b55df992e1..a24aa16caba 100644
--- a/Tests/test_imagedraw.py
+++ b/Tests/test_imagedraw.py
@@ -1581,7 +1581,7 @@ def test_compute_regular_polygon_vertices_input_error_handling(
     error_message: str,
 ) -> None:
     with pytest.raises(expected_error) as e:
-        ImageDraw._compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
+        ImageDraw._compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)  # type: ignore[arg-type]
     assert str(e.value) == error_message
 
 
diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py
index 3171eb9aec1..35fe0cf40dd 100644
--- a/Tests/test_imagedraw2.py
+++ b/Tests/test_imagedraw2.py
@@ -51,9 +51,10 @@ def test_sanity() -> None:
     pen = ImageDraw2.Pen("blue", width=7)
     draw.line(list(range(10)), pen)
 
-    draw, handler = ImageDraw.getdraw(im)
+    draw2, handler = ImageDraw.getdraw(im)
+    assert draw2 is not None
     pen = ImageDraw2.Pen("blue", width=7)
-    draw.line(list(range(10)), pen)
+    draw2.line(list(range(10)), pen)
 
 
 @pytest.mark.parametrize("bbox", BBOX)
diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py
index c9dba294359..00cdec95255 100644
--- a/Tests/test_imagefile.py
+++ b/Tests/test_imagefile.py
@@ -381,7 +381,7 @@ def test_oversize(self) -> None:
     def test_encode(self) -> None:
         encoder = ImageFile.PyEncoder(None)
         with pytest.raises(NotImplementedError):
-            encoder.encode(None)
+            encoder.encode(0)
 
         bytes_consumed, errcode = encoder.encode_to_pyfd()
         assert bytes_consumed == 0
diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py
index 73cad513e62..9f5c3984bad 100644
--- a/Tests/test_imagefont.py
+++ b/Tests/test_imagefont.py
@@ -209,7 +209,7 @@ def test_getlength(
         assert length == length_raqm
 
 
-def test_float_size() -> None:
+def test_float_size(layout_engine: ImageFont.Layout) -> None:
     lengths = []
     for size in (48, 48.5, 49):
         f = ImageFont.truetype(
@@ -494,8 +494,8 @@ def test_default_font() -> None:
     assert_image_equal_tofile(im, "Tests/images/default_font_freetype.png")
 
 
-@pytest.mark.parametrize("mode", (None, "1", "RGBA"))
-def test_getbbox(font: ImageFont.FreeTypeFont, mode: str | None) -> None:
+@pytest.mark.parametrize("mode", ("", "1", "RGBA"))
+def test_getbbox(font: ImageFont.FreeTypeFont, mode: str) -> None:
     assert (0, 4, 12, 16) == font.getbbox("A", mode)
 
 
diff --git a/Tests/test_imagefontpil.py b/Tests/test_imagefontpil.py
index 3b1c14b4e18..c4a39d1faf2 100644
--- a/Tests/test_imagefontpil.py
+++ b/Tests/test_imagefontpil.py
@@ -14,7 +14,7 @@
 
 def setup_module() -> None:
     if features.check_module("freetype2"):
-        ImageFont.core = _util.DeferredError(ImportError)
+        ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
 
 
 def teardown_module() -> None:
@@ -76,3 +76,8 @@ def test_oom() -> None:
     font = ImageFont.ImageFont()
     font._load_pilfont_data(fp, Image.new("L", (1, 1)))
     font.getmask("A" * 1_000_000)
+
+
+def test_freetypefont_without_freetype() -> None:
+    with pytest.raises(ImportError):
+        ImageFont.truetype("Tests/fonts/FreeMono.ttf")
diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py
index 8e2db15aab7..17f68710a48 100644
--- a/Tests/test_imagepalette.py
+++ b/Tests/test_imagepalette.py
@@ -45,7 +45,7 @@ def test_getcolor() -> None:
 
     # Test unknown color specifier
     with pytest.raises(ValueError):
-        palette.getcolor("unknown")
+        palette.getcolor("unknown")  # type: ignore[arg-type]
 
 
 def test_getcolor_rgba_color_rgb_palette() -> None:
diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py
index 64dfb2c95fb..c3afa908937 100644
--- a/Tests/test_psdraw.py
+++ b/Tests/test_psdraw.py
@@ -54,14 +54,10 @@ def test_stdout(buffer: bool) -> None:
     # Temporarily redirect stdout
     old_stdout = sys.stdout
 
-    if buffer:
+    class MyStdOut:
+        buffer = BytesIO()
 
-        class MyStdOut:
-            buffer = BytesIO()
-
-        mystdout = MyStdOut()
-    else:
-        mystdout = BytesIO()
+    mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO()
 
     sys.stdout = mystdout
 
@@ -71,6 +67,6 @@ class MyStdOut:
     # Reset stdout
     sys.stdout = old_stdout
 
-    if buffer:
+    if isinstance(mystdout, MyStdOut):
         mystdout = mystdout.buffer
     assert mystdout.getvalue() != b""
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
index 57da58522e7..6e2f3865039 100644
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -1168,7 +1168,7 @@ def convert_transparency(
     def quantize(
         self,
         colors: int = 256,
-        method: Quantize | None = None,
+        method: int | None = None,
         kmeans: int = 0,
         palette=None,
         dither: Dither = Dither.FLOYDSTEINBERG,
@@ -1309,7 +1309,7 @@ def _crop(
         return im.crop((x0, y0, x1, y1))
 
     def draft(
-        self, mode: str | None, size: tuple[int, int]
+        self, mode: str | None, size: tuple[int, int] | None
     ) -> tuple[str, tuple[int, int, float, float]] | None:
         """
         Configures the image file loader so it returns a version of the
@@ -1744,7 +1744,7 @@ def entropy(self, mask=None, extrema=None):
     def paste(
         self,
         im: Image | str | float | tuple[float, ...],
-        box: tuple[int, int, int, int] | tuple[int, int] | None = None,
+        box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None,
         mask: Image | None = None,
     ) -> None:
         """
@@ -1786,10 +1786,14 @@ def paste(
         :param mask: An optional mask image.
         """
 
-        if isImageType(box) and mask is None:
+        if isImageType(box):
+            if mask is not None:
+                msg = "If using second argument as mask, third argument must be None"
+                raise ValueError(msg)
             # abbreviated paste(im, mask) syntax
             mask = box
             box = None
+        assert not isinstance(box, Image)
 
         if box is None:
             box = (0, 0)
@@ -1995,7 +1999,10 @@ def putalpha(self, alpha):
         self.im.putband(alpha.im, band)
 
     def putdata(
-        self, data: Sequence[float], scale: float = 1.0, offset: float = 0.0
+        self,
+        data: Sequence[float] | Sequence[Sequence[int]],
+        scale: float = 1.0,
+        offset: float = 0.0,
     ) -> None:
         """
         Copies pixel data from a flattened sequence object into the image. The
@@ -2656,7 +2663,7 @@ def thumbnail(
         self,
         size: tuple[float, float],
         resample: Resampling = Resampling.BICUBIC,
-        reducing_gap: float = 2.0,
+        reducing_gap: float | None = 2.0,
     ) -> None:
         """
         Make this image into a thumbnail.  This method modifies the
@@ -2717,11 +2724,12 @@ def round_aspect(number, key):
             return x, y
 
         box = None
+        final_size: tuple[int, int]
         if reducing_gap is not None:
             preserved_size = preserve_aspect_ratio()
             if preserved_size is None:
                 return
-            size = preserved_size
+            final_size = preserved_size
 
             res = self.draft(
                 None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap))
@@ -2735,13 +2743,13 @@ def round_aspect(number, key):
             preserved_size = preserve_aspect_ratio()
             if preserved_size is None:
                 return
-            size = preserved_size
+            final_size = preserved_size
 
-        if self.size != size:
-            im = self.resize(size, resample, box=box, reducing_gap=reducing_gap)
+        if self.size != final_size:
+            im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap)
 
             self.im = im.im
-            self._size = size
+            self._size = final_size
             self._mode = self.im.mode
 
         self.readonly = 0
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
index 91f8988387d..a471a7ffde9 100644
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -62,7 +62,9 @@
 
 
 class ImageDraw:
-    font = None
+    font: (
+        ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None
+    ) = None
 
     def __init__(self, im: Image.Image, mode: str | None = None) -> None:
         """
diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py
index fa5608e6cd8..61e7a7abec4 100644
--- a/src/PIL/ImageFont.py
+++ b/src/PIL/ImageFont.py
@@ -33,11 +33,12 @@
 import warnings
 from enum import IntEnum
 from io import BytesIO
+from types import ModuleType
 from typing import IO, TYPE_CHECKING, Any, BinaryIO
 
 from . import Image
 from ._typing import StrOrBytesPath
-from ._util import is_path
+from ._util import DeferredError, is_path
 
 if TYPE_CHECKING:
     from . import ImageFile
@@ -53,11 +54,10 @@ class Layout(IntEnum):
 MAX_STRING_LENGTH = 1_000_000
 
 
+core: ModuleType | DeferredError
 try:
     from . import _imagingft as core
 except ImportError as ex:
-    from ._util import DeferredError
-
     core = DeferredError.new(ex)
 
 
@@ -199,6 +199,7 @@ class FreeTypeFont:
     """FreeType font wrapper (requires _imagingft service)"""
 
     font: Font
+    font_bytes: bytes
 
     def __init__(
         self,
@@ -210,6 +211,9 @@ def __init__(
     ) -> None:
         # FIXME: use service provider instead
 
+        if isinstance(core, DeferredError):
+            raise core.ex
+
         if size <= 0:
             msg = "font size must be greater than 0"
             raise ValueError(msg)
@@ -903,7 +907,7 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont:
     :return: A font object.
     """
     f: FreeTypeFont | ImageFont
-    if core.__class__.__name__ == "module" or size is not None:
+    if isinstance(core, ModuleType) or size is not None:
         f = truetype(
             BytesIO(
                 base64.b64decode(
diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py
index 293ba4941e3..a0c16b7656b 100644
--- a/src/PIL/ImageQt.py
+++ b/src/PIL/ImageQt.py
@@ -196,7 +196,7 @@ def __init__(self, im):
                 self.setColorTable(im_data["colortable"])
 
 
-def toqimage(im):
+def toqimage(im) -> ImageQt:
     return ImageQt(im)
 
 
diff --git a/src/PIL/ImageTransform.py b/src/PIL/ImageTransform.py
index 80a6116b7cf..ffd7916745c 100644
--- a/src/PIL/ImageTransform.py
+++ b/src/PIL/ImageTransform.py
@@ -24,7 +24,7 @@ class Transform(Image.ImageTransformHandler):
 
     method: Image.Transform
 
-    def __init__(self, data: Sequence[int]) -> None:
+    def __init__(self, data: Sequence[Any]) -> None:
         self.data = data
 
     def getdata(self) -> tuple[Image.Transform, Sequence[int]]:
diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py
index 7a32f1cdec0..9d6811cb759 100644
--- a/src/PIL/JpegImagePlugin.py
+++ b/src/PIL/JpegImagePlugin.py
@@ -428,7 +428,7 @@ def load_read(self, read_bytes: int) -> bytes:
         return s
 
     def draft(
-        self, mode: str | None, size: tuple[int, int]
+        self, mode: str | None, size: tuple[int, int] | None
     ) -> tuple[str, tuple[int, int, float, float]] | None:
         if len(self.tile) != 1:
             return None
diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py
index 89fad703343..e318c87398c 100644
--- a/src/PIL/TiffTags.py
+++ b/src/PIL/TiffTags.py
@@ -89,7 +89,7 @@ def lookup(tag, group=None):
 IFD = 13
 LONG8 = 16
 
-TAGS_V2 = {
+_tags_v2 = {
     254: ("NewSubfileType", LONG, 1),
     255: ("SubfileType", SHORT, 1),
     256: ("ImageWidth", LONG, 1),
@@ -425,9 +425,11 @@ def lookup(tag, group=None):
     50784: "Alias Layer Metadata",
 }
 
+TAGS_V2: dict[int, TagInfo] = {}
+
 
 def _populate():
-    for k, v in TAGS_V2.items():
+    for k, v in _tags_v2.items():
         # Populate legacy structure.
         TAGS[k] = v[0]
         if len(v) == 4:
