Skip to content

Commit

Permalink
Merge pull request #8134 from radarhere/type_hint
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Jun 18, 2024
2 parents cde0524 + be73b13 commit 5d3338f
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/PIL/BdfFontFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def bdf_char(
class BdfFontFile(FontFile.FontFile):
"""Font file plugin for the X11 BDF format."""

def __init__(self, fp: BinaryIO):
def __init__(self, fp: BinaryIO) -> None:
super().__init__()

s = fp.readline()
Expand Down
20 changes: 11 additions & 9 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@
import math
import numbers
import struct
from types import ModuleType
from typing import TYPE_CHECKING, AnyStr, Sequence, cast

from . import Image, ImageColor
from ._deprecate import deprecate
from ._typing import Coords

if TYPE_CHECKING:
from . import ImageDraw2, ImageFont

"""
A simple 2D drawing interface for PIL images.
<p>
Expand Down Expand Up @@ -93,9 +97,6 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None:
self.fontmode = "L" # aliasing is okay for other modes
self.fill = False

if TYPE_CHECKING:
from . import ImageFont

def getfont(
self,
) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont:
Expand Down Expand Up @@ -879,7 +880,7 @@ def multiline_textbbox(
return bbox


def Draw(im, mode: str | None = None) -> ImageDraw:
def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
"""
A simple 2D drawing interface for PIL images.
Expand All @@ -891,7 +892,7 @@ def Draw(im, mode: str | None = None) -> ImageDraw:
defaults to the mode of the image.
"""
try:
return im.getdraw(mode)
return getattr(im, "getdraw")(mode)
except AttributeError:
return ImageDraw(im, mode)

Expand All @@ -903,7 +904,9 @@ def Draw(im, mode: str | None = None) -> ImageDraw:
Outline = None


def getdraw(im=None, hints=None):
def getdraw(
im: Image.Image | None = None, hints: list[str] | None = None
) -> tuple[ImageDraw2.Draw | None, ModuleType]:
"""
:param im: The image to draw in.
:param hints: An optional list of hints. Deprecated.
Expand All @@ -913,9 +916,8 @@ def getdraw(im=None, hints=None):
deprecate("'hints' parameter", 12)
from . import ImageDraw2

if im:
im = ImageDraw2.Draw(im)
return im, ImageDraw2
draw = ImageDraw2.Draw(im) if im is not None else None
return draw, ImageDraw2


def floodfill(
Expand Down
12 changes: 8 additions & 4 deletions src/PIL/ImagePalette.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def palette(self, palette):
self._palette = palette

@property
def colors(self):
def colors(self) -> dict[tuple[int, int, int] | tuple[int, int, int, int], int]:
if self._colors is None:
mode_len = len(self.mode)
self._colors = {}
Expand All @@ -66,7 +66,9 @@ def colors(self):
return self._colors

@colors.setter
def colors(self, colors):
def colors(
self, colors: dict[tuple[int, int, int] | tuple[int, int, int, int], int]
) -> None:
self._colors = colors

def copy(self) -> ImagePalette:
Expand Down Expand Up @@ -107,11 +109,13 @@ def tobytes(self) -> bytes:
# Declare tostring as an alias for tobytes
tostring = tobytes

def _new_color_index(self, image=None, e=None):
def _new_color_index(
self, image: Image.Image | None = None, e: Exception | None = None
) -> int:
if not isinstance(self.palette, bytearray):
self._palette = bytearray(self.palette)
index = len(self.palette) // 3
special_colors = ()
special_colors: tuple[int | tuple[int, ...] | None, ...] = ()
if image:
special_colors = (
image.info.get("background"),
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/MicImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _open(self) -> None:
msg = "not an MIC file; no image entries"
raise SyntaxError(msg)

self.frame = None
self.frame = -1
self._n_frames = len(self.images)
self.is_animated = self._n_frames > 1

Expand All @@ -85,7 +85,7 @@ def seek(self, frame):

self.frame = frame

def tell(self):
def tell(self) -> int:
return self.frame

def close(self) -> None:
Expand Down
63 changes: 42 additions & 21 deletions src/PIL/PngImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import warnings
import zlib
from enum import IntEnum
from typing import IO, Any
from typing import IO, TYPE_CHECKING, Any, NoReturn

from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
from ._binary import i16be as i16
Expand All @@ -48,6 +48,9 @@
from ._binary import o16be as o16
from ._binary import o32be as o32

if TYPE_CHECKING:
from . import _imaging

logger = logging.getLogger(__name__)

is_cid = re.compile(rb"\w\w\w\w").match
Expand Down Expand Up @@ -249,6 +252,9 @@ class iTXt(str):
"""

lang: str | bytes | None
tkey: str | bytes | None

@staticmethod
def __new__(cls, text, lang=None, tkey=None):
"""
Expand All @@ -270,10 +276,10 @@ class PngInfo:
"""

def __init__(self):
self.chunks = []
def __init__(self) -> None:
self.chunks: list[tuple[bytes, bytes, bool]] = []

def add(self, cid, data, after_idat=False):
def add(self, cid: bytes, data: bytes, after_idat: bool = False) -> None:
"""Appends an arbitrary chunk. Use with caution.
:param cid: a byte string, 4 bytes long.
Expand All @@ -283,12 +289,16 @@ def add(self, cid, data, after_idat=False):
"""

chunk = [cid, data]
if after_idat:
chunk.append(True)
self.chunks.append(tuple(chunk))
self.chunks.append((cid, data, after_idat))

def add_itxt(self, key, value, lang="", tkey="", zip=False):
def add_itxt(
self,
key: str | bytes,
value: str | bytes,
lang: str | bytes = "",
tkey: str | bytes = "",
zip: bool = False,
) -> None:
"""Appends an iTXt chunk.
:param key: latin-1 encodable text key name
Expand Down Expand Up @@ -316,7 +326,9 @@ def add_itxt(self, key, value, lang="", tkey="", zip=False):
else:
self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value)

def add_text(self, key, value, zip=False):
def add_text(
self, key: str | bytes, value: str | bytes | iTXt, zip: bool = False
) -> None:
"""Appends a text chunk.
:param key: latin-1 encodable text key name
Expand All @@ -326,7 +338,13 @@ def add_text(self, key, value, zip=False):
"""
if isinstance(value, iTXt):
return self.add_itxt(key, value, value.lang, value.tkey, zip=zip)
return self.add_itxt(
key,
value,
value.lang if value.lang is not None else b"",
value.tkey if value.tkey is not None else b"",
zip=zip,
)

# The tEXt chunk stores latin-1 text
if not isinstance(value, bytes):
Expand Down Expand Up @@ -434,7 +452,7 @@ def chunk_IHDR(self, pos: int, length: int) -> bytes:
raise SyntaxError(msg)
return s

def chunk_IDAT(self, pos, length):
def chunk_IDAT(self, pos: int, length: int) -> NoReturn:
# image data
if "bbox" in self.im_info:
tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)]
Expand All @@ -447,7 +465,7 @@ def chunk_IDAT(self, pos, length):
msg = "image data found"
raise EOFError(msg)

def chunk_IEND(self, pos, length):
def chunk_IEND(self, pos: int, length: int) -> NoReturn:
msg = "end of PNG image"
raise EOFError(msg)

Expand Down Expand Up @@ -821,7 +839,10 @@ def seek(self, frame: int) -> None:
msg = "no more images in APNG file"
raise EOFError(msg) from e

def _seek(self, frame, rewind=False):
def _seek(self, frame: int, rewind: bool = False) -> None:
assert self.png is not None

self.dispose: _imaging.ImagingCore | None
if frame == 0:
if rewind:
self._fp.seek(self.__rewind)
Expand Down Expand Up @@ -906,14 +927,14 @@ def _seek(self, frame, rewind=False):
if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS:
self.dispose_op = Disposal.OP_BACKGROUND

self.dispose = None
if self.dispose_op == Disposal.OP_PREVIOUS:
self.dispose = self._prev_im.copy()
self.dispose = self._crop(self.dispose, self.dispose_extent)
if self._prev_im:
self.dispose = self._prev_im.copy()
self.dispose = self._crop(self.dispose, self.dispose_extent)
elif self.dispose_op == Disposal.OP_BACKGROUND:
self.dispose = Image.core.fill(self.mode, self.size)
self.dispose = self._crop(self.dispose, self.dispose_extent)
else:
self.dispose = None

def tell(self) -> int:
return self.__frame
Expand Down Expand Up @@ -1026,7 +1047,7 @@ def _getexif(self) -> dict[str, Any] | None:
return None
return self.getexif()._get_merged_dict()

def getexif(self):
def getexif(self) -> Image.Exif:
if "exif" not in self.info:
self.load()

Expand Down Expand Up @@ -1346,7 +1367,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
chunk(fp, cid, data)
elif cid[1:2].islower():
# Private chunk
after_idat = info_chunk[2:3]
after_idat = len(info_chunk) == 3 and info_chunk[2]
if not after_idat:
chunk(fp, cid, data)

Expand Down Expand Up @@ -1425,7 +1446,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
cid, data = info_chunk[:2]
if cid[1:2].islower():
# Private chunk
after_idat = info_chunk[2:3]
after_idat = len(info_chunk) == 3 and info_chunk[2]
if after_idat:
chunk(fp, cid, data)

Expand Down
31 changes: 19 additions & 12 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
from collections.abc import MutableMapping
from fractions import Fraction
from numbers import Number, Rational
from typing import IO, TYPE_CHECKING, Any, Callable
from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn

from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags
from ._binary import i16be as i16
Expand Down Expand Up @@ -384,7 +384,7 @@ def limit_rational(self, max_denominator):
def __repr__(self) -> str:
return str(float(self._val))

def __hash__(self):
def __hash__(self) -> int:
return self._val.__hash__()

def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -551,7 +551,12 @@ class ImageFileDirectory_v2(_IFDv2Base):
_load_dispatch: dict[int, Callable[[ImageFileDirectory_v2, bytes, bool], Any]] = {}
_write_dispatch: dict[int, Callable[..., Any]] = {}

def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
def __init__(
self,
ifh: bytes = b"II\052\0\0\0\0\0",
prefix: bytes | None = None,
group: int | None = None,
) -> None:
"""Initialize an ImageFileDirectory.
To construct an ImageFileDirectory from a real file, pass the 8-byte
Expand All @@ -575,7 +580,7 @@ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
raise SyntaxError(msg)
self._bigtiff = ifh[2] == 43
self.group = group
self.tagtype = {}
self.tagtype: dict[int, int] = {}
""" Dictionary of tag types """
self.reset()
(self.next,) = (
Expand All @@ -587,18 +592,18 @@ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
offset = property(lambda self: self._offset)

@property
def legacy_api(self):
def legacy_api(self) -> bool:
return self._legacy_api

@legacy_api.setter
def legacy_api(self, value):
def legacy_api(self, value: bool) -> NoReturn:
msg = "Not allowing setting of legacy api"
raise Exception(msg)

def reset(self):
self._tags_v1 = {} # will remain empty if legacy_api is false
self._tags_v2 = {} # main tag storage
self._tagdata = {}
def reset(self) -> None:
self._tags_v1: dict[int, Any] = {} # will remain empty if legacy_api is false
self._tags_v2: dict[int, Any] = {} # main tag storage
self._tagdata: dict[int, bytes] = {}
self.tagtype = {} # added 2008-06-05 by Florian Hoech
self._next = None
self._offset = None
Expand Down Expand Up @@ -2039,7 +2044,7 @@ def skipIFDs(self) -> None:
num_tags = self.readShort()
self.f.seek(num_tags * 12, os.SEEK_CUR)

def write(self, data):
def write(self, data: bytes) -> int | None:
return self.f.write(data)

def readShort(self) -> int:
Expand Down Expand Up @@ -2122,7 +2127,9 @@ def fixIFD(self) -> None:
# skip the locally stored value that is not an offset
self.f.seek(4, os.SEEK_CUR)

def fixOffsets(self, count, isShort=False, isLong=False):
def fixOffsets(
self, count: int, isShort: bool = False, isLong: bool = False
) -> None:
if not isShort and not isLong:
msg = "offset is neither short nor long"
raise RuntimeError(msg)
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/_imaging.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ class ImagingDraw:
class PixelAccess:
def __getattr__(self, name: str) -> Any: ...

def font(image, glyphdata: bytes) -> ImagingFont: ...
def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ...
def __getattr__(name: str) -> Any: ...
Loading

0 comments on commit 5d3338f

Please sign in to comment.