Added type hints

This commit is contained in:
Andrew Murray 2024-05-13 18:47:51 +10:00
parent e39ee95f56
commit a8d154877d
13 changed files with 51 additions and 44 deletions

View File

@ -132,7 +132,7 @@ class FliImageFile(ImageFile.ImageFile):
for f in range(self.__frame + 1, frame + 1): for f in range(self.__frame + 1, frame + 1):
self._seek(f) self._seek(f)
def _seek(self, frame): def _seek(self, frame: int) -> None:
if frame == 0: if frame == 0:
self.__frame = -1 self.__frame = -1
self._fp.seek(self.__rewind) self._fp.seek(self.__rewind)

View File

@ -82,7 +82,7 @@ class GifImageFile(ImageFile.ImageFile):
return self.fp.read(s[0]) return self.fp.read(s[0])
return None return None
def _is_palette_needed(self, p): def _is_palette_needed(self, p: bytes) -> bool:
for i in range(0, len(p), 3): for i in range(0, len(p), 3):
if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): if not (i // 3 == p[i] == p[i + 1] == p[i + 2]):
return True return True
@ -474,7 +474,7 @@ class GifImageFile(ImageFile.ImageFile):
RAWMODE = {"1": "L", "L": "L", "P": "P"} RAWMODE = {"1": "L", "L": "L", "P": "P"}
def _normalize_mode(im): def _normalize_mode(im: Image.Image) -> Image.Image:
""" """
Takes an image (or frame), returns an image in a mode that is appropriate Takes an image (or frame), returns an image in a mode that is appropriate
for saving in a Gif. for saving in a Gif.
@ -887,7 +887,7 @@ def _get_optimize(im, info):
return used_palette_colors return used_palette_colors
def _get_color_table_size(palette_bytes): def _get_color_table_size(palette_bytes: bytes) -> int:
# calculate the palette size for the header # calculate the palette size for the header
if not palette_bytes: if not palette_bytes:
return 0 return 0
@ -897,7 +897,7 @@ def _get_color_table_size(palette_bytes):
return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1
def _get_header_palette(palette_bytes): def _get_header_palette(palette_bytes: bytes) -> bytes:
""" """
Returns the palette, null padded to the next power of 2 (*3) bytes Returns the palette, null padded to the next power of 2 (*3) bytes
suitable for direct inclusion in the GIF header suitable for direct inclusion in the GIF header
@ -915,7 +915,7 @@ def _get_header_palette(palette_bytes):
return palette_bytes return palette_bytes
def _get_palette_bytes(im): def _get_palette_bytes(im: Image.Image) -> bytes:
""" """
Gets the palette for inclusion in the gif header Gets the palette for inclusion in the gif header

View File

@ -53,5 +53,5 @@ class GimpPaletteFile:
self.palette = b"".join(self.palette) self.palette = b"".join(self.palette)
def getpalette(self): def getpalette(self) -> tuple[bytes, str]:
return self.palette, self.rawmode return self.palette, self.rawmode

View File

@ -271,11 +271,11 @@ class ImImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))]
@property @property
def n_frames(self): def n_frames(self) -> int:
return self.info[FRAMES] return self.info[FRAMES]
@property @property
def is_animated(self): def is_animated(self) -> bool:
return self.info[FRAMES] > 1 return self.info[FRAMES] > 1
def seek(self, frame: int) -> None: def seek(self, frame: int) -> None:

View File

@ -34,7 +34,7 @@ from __future__ import annotations
import math import math
import numbers import numbers
import struct import struct
from typing import Sequence, cast from typing import TYPE_CHECKING, Sequence, cast
from . import Image, ImageColor from . import Image, ImageColor
from ._typing import Coords from ._typing import Coords
@ -92,7 +92,10 @@ class ImageDraw:
self.fontmode = "L" # aliasing is okay for other modes self.fontmode = "L" # aliasing is okay for other modes
self.fill = False self.fill = False
def getfont(self): if TYPE_CHECKING:
from . import ImageFont
def getfont(self) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
""" """
Get the current default font. Get the current default font.

View File

@ -544,7 +544,7 @@ class Color3DLUT(MultibandFilter):
_copy_table=False, _copy_table=False,
) )
def __repr__(self): def __repr__(self) -> str:
r = [ r = [
f"{self.__class__.__name__} from {self.table.__class__.__name__}", f"{self.__class__.__name__} from {self.table.__class__.__name__}",
"size={:d}x{:d}x{:d}".format(*self.size), "size={:d}x{:d}x{:d}".format(*self.size),

View File

@ -66,7 +66,7 @@ class ImagePalette:
def colors(self, colors): def colors(self, colors):
self._colors = colors self._colors = colors
def copy(self): def copy(self) -> ImagePalette:
new = ImagePalette() new = ImagePalette()
new.mode = self.mode new.mode = self.mode
@ -77,7 +77,7 @@ class ImagePalette:
return new return new
def getdata(self): def getdata(self) -> tuple[str, bytes]:
""" """
Get palette contents in format suitable for the low-level Get palette contents in format suitable for the low-level
``im.putpalette`` primitive. ``im.putpalette`` primitive.
@ -88,7 +88,7 @@ class ImagePalette:
return self.rawmode, self.palette return self.rawmode, self.palette
return self.mode, self.tobytes() return self.mode, self.tobytes()
def tobytes(self): def tobytes(self) -> bytes:
"""Convert palette to bytes. """Convert palette to bytes.
.. warning:: This method is experimental. .. warning:: This method is experimental.

View File

@ -136,7 +136,7 @@ class PhotoImage:
except Exception: except Exception:
pass # ignore internal errors pass # ignore internal errors
def __str__(self): def __str__(self) -> str:
""" """
Get the Tkinter photo image identifier. This method is automatically Get the Tkinter photo image identifier. This method is automatically
called by Tkinter whenever a PhotoImage object is passed to a Tkinter called by Tkinter whenever a PhotoImage object is passed to a Tkinter
@ -146,7 +146,7 @@ class PhotoImage:
""" """
return str(self.__photo) return str(self.__photo)
def width(self): def width(self) -> int:
""" """
Get the width of the image. Get the width of the image.
@ -154,7 +154,7 @@ class PhotoImage:
""" """
return self.__size[0] return self.__size[0]
def height(self): def height(self) -> int:
""" """
Get the height of the image. Get the height of the image.
@ -227,7 +227,7 @@ class BitmapImage:
except Exception: except Exception:
pass # ignore internal errors pass # ignore internal errors
def width(self): def width(self) -> int:
""" """
Get the width of the image. Get the width of the image.
@ -235,7 +235,7 @@ class BitmapImage:
""" """
return self.__size[0] return self.__size[0]
def height(self): def height(self) -> int:
""" """
Get the height of the image. Get the height of the image.
@ -243,7 +243,7 @@ class BitmapImage:
""" """
return self.__size[1] return self.__size[1]
def __str__(self): def __str__(self) -> str:
""" """
Get the Tkinter bitmap image identifier. This method is automatically Get the Tkinter bitmap image identifier. This method is automatically
called by Tkinter whenever a BitmapImage object is passed to a Tkinter called by Tkinter whenever a BitmapImage object is passed to a Tkinter

View File

@ -63,12 +63,12 @@ class BoxReader:
data = self._read_bytes(size) data = self._read_bytes(size)
return struct.unpack(field_format, data) return struct.unpack(field_format, data)
def read_boxes(self): def read_boxes(self) -> BoxReader:
size = self.remaining_in_box size = self.remaining_in_box
data = self._read_bytes(size) data = self._read_bytes(size)
return BoxReader(io.BytesIO(data), size) return BoxReader(io.BytesIO(data), size)
def has_next_box(self): def has_next_box(self) -> bool:
if self.has_length: if self.has_length:
return self.fp.tell() + self.remaining_in_box < self.length return self.fp.tell() + self.remaining_in_box < self.length
else: else:

View File

@ -87,10 +87,10 @@ class IndirectReferenceTuple(NamedTuple):
class IndirectReference(IndirectReferenceTuple): class IndirectReference(IndirectReferenceTuple):
def __str__(self): def __str__(self) -> str:
return f"{self.object_id} {self.generation} R" return f"{self.object_id} {self.generation} R"
def __bytes__(self): def __bytes__(self) -> bytes:
return self.__str__().encode("us-ascii") return self.__str__().encode("us-ascii")
def __eq__(self, other): def __eq__(self, other):
@ -108,7 +108,7 @@ class IndirectReference(IndirectReferenceTuple):
class IndirectObjectDef(IndirectReference): class IndirectObjectDef(IndirectReference):
def __str__(self): def __str__(self) -> str:
return f"{self.object_id} {self.generation} obj" return f"{self.object_id} {self.generation} obj"
@ -150,7 +150,7 @@ class XrefTable:
def __contains__(self, key): def __contains__(self, key):
return key in self.existing_entries or key in self.new_entries return key in self.existing_entries or key in self.new_entries
def __len__(self): def __len__(self) -> int:
return len( return len(
set(self.existing_entries.keys()) set(self.existing_entries.keys())
| set(self.new_entries.keys()) | set(self.new_entries.keys())
@ -211,7 +211,7 @@ class PdfName:
else: else:
self.name = name.encode("us-ascii") self.name = name.encode("us-ascii")
def name_as_str(self): def name_as_str(self) -> str:
return self.name.decode("us-ascii") return self.name.decode("us-ascii")
def __eq__(self, other): def __eq__(self, other):
@ -222,7 +222,7 @@ class PdfName:
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
def __repr__(self): def __repr__(self) -> str:
return f"{self.__class__.__name__}({repr(self.name)})" return f"{self.__class__.__name__}({repr(self.name)})"
@classmethod @classmethod
@ -231,7 +231,7 @@ class PdfName:
allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"}
def __bytes__(self): def __bytes__(self) -> bytes:
result = bytearray(b"/") result = bytearray(b"/")
for b in self.name: for b in self.name:
if b in self.allowed_chars: if b in self.allowed_chars:
@ -242,7 +242,7 @@ class PdfName:
class PdfArray(List[Any]): class PdfArray(List[Any]):
def __bytes__(self): def __bytes__(self) -> bytes:
return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]"
@ -286,7 +286,7 @@ class PdfDict(_DictBase):
value = time.gmtime(calendar.timegm(value) + offset) value = time.gmtime(calendar.timegm(value) + offset)
return value return value
def __bytes__(self): def __bytes__(self) -> bytes:
out = bytearray(b"<<") out = bytearray(b"<<")
for key, value in self.items(): for key, value in self.items():
if value is None: if value is None:
@ -304,7 +304,7 @@ class PdfBinary:
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
def __bytes__(self): def __bytes__(self) -> bytes:
return b"<%s>" % b"".join(b"%02X" % b for b in self.data) return b"<%s>" % b"".join(b"%02X" % b for b in self.data)

View File

@ -37,6 +37,7 @@ from __future__ import annotations
import os import os
import struct import struct
import sys import sys
from typing import TYPE_CHECKING
from . import Image, ImageFile from . import Image, ImageFile
@ -157,11 +158,11 @@ class SpiderImageFile(ImageFile.ImageFile):
self._fp = self.fp # FIXME: hack self._fp = self.fp # FIXME: hack
@property @property
def n_frames(self): def n_frames(self) -> int:
return self._nimages return self._nimages
@property @property
def is_animated(self): def is_animated(self) -> bool:
return self._nimages > 1 return self._nimages > 1
# 1st image index is zero (although SPIDER imgnumber starts at 1) # 1st image index is zero (although SPIDER imgnumber starts at 1)
@ -191,8 +192,11 @@ class SpiderImageFile(ImageFile.ImageFile):
b = -m * minimum b = -m * minimum
return self.point(lambda i, m=m, b=b: i * m + b).convert("L") return self.point(lambda i, m=m, b=b: i * m + b).convert("L")
if TYPE_CHECKING:
from . import ImageTk
# returns a ImageTk.PhotoImage object, after rescaling to 0..255 # returns a ImageTk.PhotoImage object, after rescaling to 0..255
def tkPhotoImage(self): def tkPhotoImage(self) -> ImageTk.PhotoImage:
from . import ImageTk from . import ImageTk
return ImageTk.PhotoImage(self.convert2byte(), palette=256) return ImageTk.PhotoImage(self.convert2byte(), palette=256)

View File

@ -381,7 +381,7 @@ class IFDRational(Rational):
f = self._val.limit_denominator(max_denominator) f = self._val.limit_denominator(max_denominator)
return f.numerator, f.denominator return f.numerator, f.denominator
def __repr__(self): def __repr__(self) -> str:
return str(float(self._val)) return str(float(self._val))
def __hash__(self): def __hash__(self):
@ -603,7 +603,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
self._next = None self._next = None
self._offset = None self._offset = None
def __str__(self): def __str__(self) -> str:
return str(dict(self)) return str(dict(self))
def named(self): def named(self):
@ -617,7 +617,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
for code, value in self.items() for code, value in self.items()
} }
def __len__(self): def __len__(self) -> int:
return len(set(self._tagdata) | set(self._tags_v2)) return len(set(self._tagdata) | set(self._tags_v2))
def __getitem__(self, tag): def __getitem__(self, tag):
@ -1041,7 +1041,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
ifd.next = original.next # an indicator for multipage tiffs ifd.next = original.next # an indicator for multipage tiffs
return ifd return ifd
def to_v2(self): def to_v2(self) -> ImageFileDirectory_v2:
"""Returns an """Returns an
:py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2`
instance with the same data as is contained in the original instance with the same data as is contained in the original
@ -1061,7 +1061,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
def __contains__(self, tag): def __contains__(self, tag):
return tag in self._tags_v1 or tag in self._tagdata return tag in self._tags_v1 or tag in self._tagdata
def __len__(self): def __len__(self) -> int:
return len(set(self._tagdata) | set(self._tags_v1)) return len(set(self._tagdata) | set(self._tags_v1))
def __iter__(self): def __iter__(self):
@ -1154,7 +1154,7 @@ class TiffImageFile(ImageFile.ImageFile):
Image._decompression_bomb_check(self.size) Image._decompression_bomb_check(self.size)
self.im = Image.core.new(self.mode, self.size) self.im = Image.core.new(self.mode, self.size)
def _seek(self, frame): def _seek(self, frame: int) -> None:
self.fp = self._fp self.fp = self._fp
# reset buffered io handle in case fp # reset buffered io handle in case fp
@ -2003,7 +2003,7 @@ class AppendingTiffWriter:
self.close() self.close()
return False return False
def tell(self): def tell(self) -> int:
return self.f.tell() - self.offsetOfNewPage return self.f.tell() - self.offsetOfNewPage
def seek(self, offset, whence=io.SEEK_SET): def seek(self, offset, whence=io.SEEK_SET):

View File

@ -144,7 +144,7 @@ class WebPImageFile(ImageFile.ImageFile):
timestamp -= duration timestamp -= duration
return data, timestamp, duration return data, timestamp, duration
def _seek(self, frame): def _seek(self, frame: int) -> None:
if self.__physical_frame == frame: if self.__physical_frame == frame:
return # Nothing to do return # Nothing to do
if frame < self.__physical_frame: if frame < self.__physical_frame: