mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Added type hints (#8204)
Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
This commit is contained in:
parent
7e1a6be767
commit
f3c3e52797
|
@ -16,8 +16,11 @@ def test_sanity() -> None:
|
|||
|
||||
|
||||
def test_register() -> None:
|
||||
# Test registering a viewer that is not a class
|
||||
ImageShow.register("not a class")
|
||||
# Test registering a viewer that is an instance
|
||||
class TestViewer(ImageShow.Viewer):
|
||||
pass
|
||||
|
||||
ImageShow.register(TestViewer())
|
||||
|
||||
# Restore original state
|
||||
ImageShow._viewers.pop()
|
||||
|
|
|
@ -338,7 +338,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
msg = "cannot determine EPS bounding box"
|
||||
raise OSError(msg)
|
||||
|
||||
def _find_offset(self, fp):
|
||||
def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]:
|
||||
s = fp.read(4)
|
||||
|
||||
if s == b"%!PS":
|
||||
|
@ -361,7 +361,9 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
|
||||
return length, offset
|
||||
|
||||
def load(self, scale=1, transparency=False):
|
||||
def load(
|
||||
self, scale: int = 1, transparency: bool = False
|
||||
) -> Image.core.PixelAccess | None:
|
||||
# Load EPS via Ghostscript
|
||||
if self.tile:
|
||||
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
|
||||
|
|
|
@ -45,7 +45,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
format_description = "Autodesk FLI/FLC Animation"
|
||||
_close_exclusive_fp_after_loading = False
|
||||
|
||||
def _open(self):
|
||||
def _open(self) -> None:
|
||||
# HEAD
|
||||
s = self.fp.read(128)
|
||||
if not (_accept(s) and s[20:22] == b"\x00\x00"):
|
||||
|
@ -83,7 +83,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
if i16(s, 4) == 0xF1FA:
|
||||
# look for palette chunk
|
||||
number_of_subchunks = i16(s, 6)
|
||||
chunk_size = None
|
||||
chunk_size: int | None = None
|
||||
for _ in range(number_of_subchunks):
|
||||
if chunk_size is not None:
|
||||
self.fp.seek(chunk_size - 6, os.SEEK_CUR)
|
||||
|
@ -96,8 +96,9 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
if not chunk_size:
|
||||
break
|
||||
|
||||
palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
|
||||
self.palette = ImagePalette.raw("RGB", b"".join(palette))
|
||||
self.palette = ImagePalette.raw(
|
||||
"RGB", b"".join(o8(r) + o8(g) + o8(b) for (r, g, b) in palette)
|
||||
)
|
||||
|
||||
# set things up to decode first frame
|
||||
self.__frame = -1
|
||||
|
@ -105,7 +106,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
self.__rewind = self.fp.tell()
|
||||
self.seek(0)
|
||||
|
||||
def _palette(self, palette, shift):
|
||||
def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None:
|
||||
# load palette
|
||||
|
||||
i = 0
|
||||
|
|
|
@ -231,7 +231,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
self._fp = self.fp
|
||||
self.fp = None
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if not self.fp:
|
||||
self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
# Data is an uncompressed block of w * h * bytes/pixel
|
||||
self._data_size = width * height * color_depth
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if not self.im:
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.frombytes(self.fp.read(self._data_size))
|
||||
|
|
|
@ -34,11 +34,13 @@ MAGIC = b"icns"
|
|||
HEADERSIZE = 8
|
||||
|
||||
|
||||
def nextheader(fobj):
|
||||
def nextheader(fobj: IO[bytes]) -> tuple[bytes, int]:
|
||||
return struct.unpack(">4sI", fobj.read(HEADERSIZE))
|
||||
|
||||
|
||||
def read_32t(fobj, start_length, size):
|
||||
def read_32t(
|
||||
fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int]
|
||||
) -> dict[str, Image.Image]:
|
||||
# The 128x128 icon seems to have an extra header for some reason.
|
||||
(start, length) = start_length
|
||||
fobj.seek(start)
|
||||
|
@ -49,7 +51,9 @@ def read_32t(fobj, start_length, size):
|
|||
return read_32(fobj, (start + 4, length - 4), size)
|
||||
|
||||
|
||||
def read_32(fobj, start_length, size):
|
||||
def read_32(
|
||||
fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int]
|
||||
) -> dict[str, Image.Image]:
|
||||
"""
|
||||
Read a 32bit RGB icon resource. Seems to be either uncompressed or
|
||||
an RLE packbits-like scheme.
|
||||
|
@ -72,14 +76,14 @@ def read_32(fobj, start_length, size):
|
|||
byte = fobj.read(1)
|
||||
if not byte:
|
||||
break
|
||||
byte = byte[0]
|
||||
if byte & 0x80:
|
||||
blocksize = byte - 125
|
||||
byte_int = byte[0]
|
||||
if byte_int & 0x80:
|
||||
blocksize = byte_int - 125
|
||||
byte = fobj.read(1)
|
||||
for i in range(blocksize):
|
||||
data.append(byte)
|
||||
else:
|
||||
blocksize = byte + 1
|
||||
blocksize = byte_int + 1
|
||||
data.append(fobj.read(blocksize))
|
||||
bytesleft -= blocksize
|
||||
if bytesleft <= 0:
|
||||
|
@ -92,7 +96,9 @@ def read_32(fobj, start_length, size):
|
|||
return {"RGB": im}
|
||||
|
||||
|
||||
def read_mk(fobj, start_length, size):
|
||||
def read_mk(
|
||||
fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int]
|
||||
) -> dict[str, Image.Image]:
|
||||
# Alpha masks seem to be uncompressed
|
||||
start = start_length[0]
|
||||
fobj.seek(start)
|
||||
|
@ -102,10 +108,14 @@ def read_mk(fobj, start_length, size):
|
|||
return {"A": band}
|
||||
|
||||
|
||||
def read_png_or_jpeg2000(fobj, start_length, size):
|
||||
def read_png_or_jpeg2000(
|
||||
fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int]
|
||||
) -> dict[str, Image.Image]:
|
||||
(start, length) = start_length
|
||||
fobj.seek(start)
|
||||
sig = fobj.read(12)
|
||||
|
||||
im: Image.Image
|
||||
if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a":
|
||||
fobj.seek(start)
|
||||
im = PngImagePlugin.PngImageFile(fobj)
|
||||
|
@ -164,12 +174,12 @@ class IcnsFile:
|
|||
],
|
||||
}
|
||||
|
||||
def __init__(self, fobj):
|
||||
def __init__(self, fobj: IO[bytes]) -> None:
|
||||
"""
|
||||
fobj is a file-like object as an icns resource
|
||||
"""
|
||||
# signature : (start, length)
|
||||
self.dct = dct = {}
|
||||
self.dct = {}
|
||||
self.fobj = fobj
|
||||
sig, filesize = nextheader(fobj)
|
||||
if not _accept(sig):
|
||||
|
@ -183,11 +193,11 @@ class IcnsFile:
|
|||
raise SyntaxError(msg)
|
||||
i += HEADERSIZE
|
||||
blocksize -= HEADERSIZE
|
||||
dct[sig] = (i, blocksize)
|
||||
self.dct[sig] = (i, blocksize)
|
||||
fobj.seek(blocksize, io.SEEK_CUR)
|
||||
i += blocksize
|
||||
|
||||
def itersizes(self):
|
||||
def itersizes(self) -> list[tuple[int, int, int]]:
|
||||
sizes = []
|
||||
for size, fmts in self.SIZES.items():
|
||||
for fmt, reader in fmts:
|
||||
|
@ -196,14 +206,14 @@ class IcnsFile:
|
|||
break
|
||||
return sizes
|
||||
|
||||
def bestsize(self):
|
||||
def bestsize(self) -> tuple[int, int, int]:
|
||||
sizes = self.itersizes()
|
||||
if not sizes:
|
||||
msg = "No 32bit icon resources found"
|
||||
raise SyntaxError(msg)
|
||||
return max(sizes)
|
||||
|
||||
def dataforsize(self, size):
|
||||
def dataforsize(self, size: tuple[int, int, int]) -> dict[str, Image.Image]:
|
||||
"""
|
||||
Get an icon resource as {channel: array}. Note that
|
||||
the arrays are bottom-up like windows bitmaps and will likely
|
||||
|
@ -216,18 +226,20 @@ class IcnsFile:
|
|||
dct.update(reader(self.fobj, desc, size))
|
||||
return dct
|
||||
|
||||
def getimage(self, size=None):
|
||||
def getimage(
|
||||
self, size: tuple[int, int] | tuple[int, int, int] | None = None
|
||||
) -> Image.Image:
|
||||
if size is None:
|
||||
size = self.bestsize()
|
||||
if len(size) == 2:
|
||||
elif len(size) == 2:
|
||||
size = (size[0], size[1], 1)
|
||||
channels = self.dataforsize(size)
|
||||
|
||||
im = channels.get("RGBA", None)
|
||||
im = channels.get("RGBA")
|
||||
if im:
|
||||
return im
|
||||
|
||||
im = channels.get("RGB").copy()
|
||||
im = channels["RGB"].copy()
|
||||
try:
|
||||
im.putalpha(channels["A"])
|
||||
except KeyError:
|
||||
|
@ -268,7 +280,7 @@ class IcnsImageFile(ImageFile.ImageFile):
|
|||
return self._size
|
||||
|
||||
@size.setter
|
||||
def size(self, value):
|
||||
def size(self, value) -> None:
|
||||
info_size = value
|
||||
if info_size not in self.info["sizes"] and len(info_size) == 2:
|
||||
info_size = (info_size[0], info_size[1], 1)
|
||||
|
@ -287,7 +299,7 @@ class IcnsImageFile(ImageFile.ImageFile):
|
|||
raise ValueError(msg)
|
||||
self._size = value
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if len(self.size) == 3:
|
||||
self.best_size = self.size
|
||||
self.size = (
|
||||
|
|
|
@ -120,7 +120,7 @@ def _accept(prefix: bytes) -> bool:
|
|||
|
||||
|
||||
class IcoFile:
|
||||
def __init__(self, buf):
|
||||
def __init__(self, buf) -> None:
|
||||
"""
|
||||
Parse image from file-like object containing ico file data
|
||||
"""
|
||||
|
@ -177,19 +177,19 @@ class IcoFile:
|
|||
# ICO images are usually squares
|
||||
self.entry = sorted(self.entry, key=lambda x: x["square"], reverse=True)
|
||||
|
||||
def sizes(self):
|
||||
def sizes(self) -> set[tuple[int, int]]:
|
||||
"""
|
||||
Get a list of all available icon sizes and color depths.
|
||||
"""
|
||||
return {(h["width"], h["height"]) for h in self.entry}
|
||||
|
||||
def getentryindex(self, size, bpp=False):
|
||||
def getentryindex(self, size: tuple[int, int], bpp: int | bool = False) -> int:
|
||||
for i, h in enumerate(self.entry):
|
||||
if size == h["dim"] and (bpp is False or bpp == h["color_depth"]):
|
||||
return i
|
||||
return 0
|
||||
|
||||
def getimage(self, size, bpp=False):
|
||||
def getimage(self, size: tuple[int, int], bpp: int | bool = False) -> Image.Image:
|
||||
"""
|
||||
Get an image from the icon
|
||||
"""
|
||||
|
@ -321,7 +321,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
|||
raise ValueError(msg)
|
||||
self._size = value
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self.im is not None and self.im.size == self.size:
|
||||
# Already loaded
|
||||
return Image.Image.load(self)
|
||||
|
@ -341,6 +341,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
|||
self.info["sizes"] = set(sizes)
|
||||
|
||||
self.size = im.size
|
||||
return None
|
||||
|
||||
def load_seek(self, pos: int) -> None:
|
||||
# Flag the ImageFile.Parser so that it
|
||||
|
|
|
@ -1395,7 +1395,7 @@ class Image:
|
|||
|
||||
def getcolors(
|
||||
self, maxcolors: int = 256
|
||||
) -> list[tuple[int, int]] | list[tuple[int, float]] | None:
|
||||
) -> list[tuple[int, tuple[int, ...]]] | list[tuple[int, float]] | None:
|
||||
"""
|
||||
Returns a list of colors used in this image.
|
||||
|
||||
|
@ -1412,7 +1412,7 @@ class Image:
|
|||
self.load()
|
||||
if self.mode in ("1", "L", "P"):
|
||||
h = self.im.histogram()
|
||||
out = [(h[i], i) for i in range(256) if h[i]]
|
||||
out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]]
|
||||
if len(out) > maxcolors:
|
||||
return None
|
||||
return out
|
||||
|
|
|
@ -26,7 +26,7 @@ from . import Image
|
|||
_viewers = []
|
||||
|
||||
|
||||
def register(viewer, order: int = 1) -> None:
|
||||
def register(viewer: type[Viewer] | Viewer, order: int = 1) -> None:
|
||||
"""
|
||||
The :py:func:`register` function is used to register additional viewers::
|
||||
|
||||
|
@ -40,11 +40,8 @@ def register(viewer, order: int = 1) -> None:
|
|||
Zero or a negative integer to prepend this viewer to the list,
|
||||
a positive integer to append it.
|
||||
"""
|
||||
try:
|
||||
if issubclass(viewer, Viewer):
|
||||
viewer = viewer()
|
||||
except TypeError:
|
||||
pass # raised if viewer wasn't a class
|
||||
if isinstance(viewer, type) and issubclass(viewer, Viewer):
|
||||
viewer = viewer()
|
||||
if order > 0:
|
||||
_viewers.append(viewer)
|
||||
else:
|
||||
|
|
|
@ -148,7 +148,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
if tag == (8, 10):
|
||||
self.tile = [("iptc", (0, 0) + self.size, offset, compression)]
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if len(self.tile) != 1 or self.tile[0][0] != "iptc":
|
||||
return ImageFile.ImageFile.load(self)
|
||||
|
||||
|
@ -176,6 +176,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
with Image.open(o) as _im:
|
||||
_im.load()
|
||||
self.im = _im.im
|
||||
return None
|
||||
|
||||
|
||||
Image.register_open(IptcImageFile.format, IptcImageFile)
|
||||
|
|
|
@ -299,7 +299,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
def reduce(self, value):
|
||||
self._reduce = value
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self.tile and self._reduce:
|
||||
power = 1 << self._reduce
|
||||
adjust = power >> 1
|
||||
|
|
|
@ -1232,7 +1232,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
val = val[math.ceil((10 + n + size) / 2) * 2 :]
|
||||
return blocks
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self.tile and self.use_load_libtiff:
|
||||
return self._load_libtiff()
|
||||
return super().load()
|
||||
|
|
|
@ -50,7 +50,7 @@ class WalImageFile(ImageFile.ImageFile):
|
|||
if next_name:
|
||||
self.info["next_name"] = next_name
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if not self.im:
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
|
||||
|
|
|
@ -144,7 +144,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
while self.__physical_frame < frame:
|
||||
self._get_next() # Advance to the requested frame
|
||||
|
||||
def load(self):
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if _webp.HAVE_WEBPANIM:
|
||||
if self.__loaded != self.__logical_frame:
|
||||
self._seek(self.__logical_frame)
|
||||
|
|
Loading…
Reference in New Issue
Block a user