Skip to content

Commit

Permalink
Added type hints
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed May 18, 2024
1 parent 8c7be25 commit 8a3a72e
Show file tree
Hide file tree
Showing 21 changed files with 135 additions and 94 deletions.
4 changes: 4 additions & 0 deletions docs/reference/ImageFile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ Classes
:undoc-members:
:show-inheritance:

.. autoclass:: PIL.ImageFile.StubHandler()
:members:
:show-inheritance:

.. autoclass:: PIL.ImageFile.StubImageFile()
:members:
:show-inheritance:
Expand Down
22 changes: 12 additions & 10 deletions src/PIL/BlpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class AlphaEncoding(IntEnum):
DXT5 = 7


def unpack_565(i):
def unpack_565(i: int) -> tuple[int, int, int]:
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3


Expand Down Expand Up @@ -284,7 +284,8 @@ def decode(self, buffer):
raise OSError(msg) from e
return -1, 0

def _read_blp_header(self):
def _read_blp_header(self) -> None:
assert self.fd is not None
self.fd.seek(4)
(self._blp_compression,) = struct.unpack("<i", self._safe_read(4))

Expand All @@ -303,10 +304,10 @@ def _read_blp_header(self):
self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))

def _safe_read(self, length):
def _safe_read(self, length: int) -> bytes:
return ImageFile._safe_read(self.fd, length)

def _read_palette(self):
def _read_palette(self) -> list[tuple[int, int, int, int]]:
ret = []
for i in range(256):
try:
Expand Down Expand Up @@ -349,29 +350,30 @@ def _load(self) -> None:
msg = f"Unsupported BLP compression {repr(self._blp_encoding)}"
raise BLPFormatError(msg)

def _decode_jpeg_stream(self):
def _decode_jpeg_stream(self) -> None:
from .JpegImagePlugin import JpegImageFile

(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
jpeg_header = self._safe_read(jpeg_header_size)
assert self.fd is not None
self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
data = self._safe_read(self._blp_lengths[0])
data = jpeg_header + data
data = BytesIO(data)
image = JpegImageFile(data)
image = JpegImageFile(BytesIO(data))
Image._decompression_bomb_check(image.size)
if image.mode == "CMYK":
decoder_name, extents, offset, args = image.tile[0]
image.tile = [(decoder_name, extents, offset, (args[0], "CMYK"))]
r, g, b = image.convert("RGB").split()
image = Image.merge("RGB", (b, g, r))
self.set_as_raw(image.tobytes())
reversed_image = Image.merge("RGB", (b, g, r))
self.set_as_raw(reversed_image.tobytes())


class BLP2Decoder(_BLPBaseDecoder):
def _load(self):
def _load(self) -> None:
palette = self._read_palette()

assert self.fd is not None
self.fd.seek(self._blp_offsets[0])

if self._blp_compression == 1:
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/BufrStubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
_handler = None


def register_handler(handler):
def register_handler(handler: ImageFile.StubHandler) -> None:
"""
Install application-specific BUFR image handler.
Expand Down Expand Up @@ -54,7 +54,7 @@ def _open(self) -> None:
if loader:
loader.open(self)

def _load(self):
def _load(self) -> ImageFile.StubHandler | None:
return _handler


Expand Down
4 changes: 2 additions & 2 deletions src/PIL/DcxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DcxImageFile(PcxImageFile):
format_description = "Intel DCX"
_close_exclusive_fp_after_loading = False

def _open(self):
def _open(self) -> None:
# Header
s = self.fp.read(4)
if not _accept(s):
Expand All @@ -58,7 +58,7 @@ def _open(self):
self._offset.append(offset)

self._fp = self.fp
self.frame = None
self.frame = -1
self.n_frames = len(self._offset)
self.is_animated = self.n_frames > 1
self.seek(0)
Expand Down
9 changes: 5 additions & 4 deletions src/PIL/GimpPaletteFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from __future__ import annotations

import re
from typing import IO

from ._binary import o8

Expand All @@ -25,8 +26,8 @@ class GimpPaletteFile:

rawmode = "RGB"

def __init__(self, fp):
self.palette = [o8(i) * 3 for i in range(256)]
def __init__(self, fp: IO[bytes]) -> None:
palette = [o8(i) * 3 for i in range(256)]

if fp.readline()[:12] != b"GIMP Palette":
msg = "not a GIMP palette file"
Expand All @@ -49,9 +50,9 @@ def __init__(self, fp):
msg = "bad palette entry"
raise ValueError(msg)

self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])

self.palette = b"".join(self.palette)
self.palette = b"".join(palette)

def getpalette(self) -> tuple[bytes, str]:
return self.palette, self.rawmode
4 changes: 2 additions & 2 deletions src/PIL/GribStubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
_handler = None


def register_handler(handler):
def register_handler(handler: ImageFile.StubHandler) -> None:
"""
Install application-specific GRIB image handler.
Expand Down Expand Up @@ -54,7 +54,7 @@ def _open(self) -> None:
if loader:
loader.open(self)

def _load(self):
def _load(self) -> ImageFile.StubHandler | None:
return _handler


Expand Down
8 changes: 5 additions & 3 deletions src/PIL/Hdf5StubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
#
from __future__ import annotations

from typing import IO

from . import Image, ImageFile

_handler = None


def register_handler(handler):
def register_handler(handler: ImageFile.StubHandler) -> None:
"""
Install application-specific HDF5 image handler.
Expand Down Expand Up @@ -54,11 +56,11 @@ def _open(self) -> None:
if loader:
loader.open(self)

def _load(self):
def _load(self) -> ImageFile.StubHandler | None:
return _handler


def _save(im, fp, filename):
def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None:
if _handler is None or not hasattr(_handler, "save"):
msg = "HDF5 save handler not installed"
raise OSError(msg)
Expand Down
4 changes: 3 additions & 1 deletion src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1948,7 +1948,9 @@ def putalpha(self, alpha):

self.im.putband(alpha.im, band)

def putdata(self, data, scale=1.0, offset=0.0):
def putdata(
self, data: Sequence[float], scale: float = 1.0, offset: float = 0.0
) -> None:
"""
Copies pixel data from a flattened sequence object into the image. The
values should start at the upper left corner (0, 0), continue to the
Expand Down
10 changes: 10 additions & 0 deletions src/PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#
from __future__ import annotations

import abc
import io
import itertools
import struct
Expand Down Expand Up @@ -347,6 +348,15 @@ def _seek_check(self, frame):
return self.tell() != frame


class StubHandler:
def open(self, im: StubImageFile) -> None:
pass

Check warning on line 353 in src/PIL/ImageFile.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/ImageFile.py#L353

Added line #L353 was not covered by tests

@abc.abstractmethod
def load(self, im: StubImageFile) -> Image.Image:
pass


class StubImageFile(ImageFile):
"""
Base class for stub image loaders.
Expand Down
17 changes: 10 additions & 7 deletions src/PIL/ImageFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import abc
import functools
from typing import Sequence


class Filter:
Expand Down Expand Up @@ -79,7 +80,7 @@ class RankFilter(Filter):

name = "Rank"

def __init__(self, size, rank):
def __init__(self, size: int, rank: int) -> None:
self.size = size
self.rank = rank

Expand All @@ -101,7 +102,7 @@ class MedianFilter(RankFilter):

name = "Median"

def __init__(self, size=3):
def __init__(self, size: int = 3) -> None:
self.size = size
self.rank = size * size // 2

Expand All @@ -116,7 +117,7 @@ class MinFilter(RankFilter):

name = "Min"

def __init__(self, size=3):
def __init__(self, size: int = 3) -> None:
self.size = size
self.rank = 0

Expand All @@ -131,7 +132,7 @@ class MaxFilter(RankFilter):

name = "Max"

def __init__(self, size=3):
def __init__(self, size: int = 3) -> None:
self.size = size
self.rank = size * size - 1

Expand All @@ -147,7 +148,7 @@ class ModeFilter(Filter):

name = "Mode"

def __init__(self, size=3):
def __init__(self, size: int = 3) -> None:
self.size = size

def filter(self, image):
Expand All @@ -165,7 +166,7 @@ class GaussianBlur(MultibandFilter):

name = "GaussianBlur"

def __init__(self, radius=2):
def __init__(self, radius: float | Sequence[float] = 2) -> None:
self.radius = radius

def filter(self, image):
Expand Down Expand Up @@ -228,7 +229,9 @@ class UnsharpMask(MultibandFilter):

name = "UnsharpMask"

def __init__(self, radius=2, percent=150, threshold=3):
def __init__(
self, radius: float = 2, percent: int = 150, threshold: int = 3
) -> None:
self.radius = radius
self.percent = percent
self.threshold = threshold
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/ImageFont.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def getname(self):
"""
return self.font.family, self.font.style

def getmetrics(self):
def getmetrics(self) -> tuple[int, int]:
"""
:return: A tuple of the font ascent (the distance from the baseline to
the highest outline point) and descent (the distance from the
Expand Down Expand Up @@ -628,7 +628,7 @@ def font_variant(
layout_engine=layout_engine or self.layout_engine,
)

def get_variation_names(self):
def get_variation_names(self) -> list[bytes]:
"""
:returns: A list of the named styles in a variation font.
:exception OSError: If the font is not a variation font.
Expand Down
14 changes: 7 additions & 7 deletions src/PIL/ImagePalette.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from __future__ import annotations

import array
from typing import Sequence
from typing import IO, Sequence

from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile

Expand Down Expand Up @@ -166,7 +166,7 @@ def getcolor(self, color, image=None) -> int:
msg = f"unknown color specifier: {repr(color)}"
raise ValueError(msg)

def save(self, fp):
def save(self, fp: str | IO[str]) -> None:
"""Save palette to text file.
.. warning:: This method is experimental.
Expand Down Expand Up @@ -213,29 +213,29 @@ def make_linear_lut(black, white):
raise NotImplementedError(msg) # FIXME


def make_gamma_lut(exp):
def make_gamma_lut(exp: float) -> list[int]:
return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)]


def negative(mode="RGB"):
def negative(mode: str = "RGB") -> ImagePalette:
palette = list(range(256 * len(mode)))
palette.reverse()
return ImagePalette(mode, [i // len(mode) for i in palette])


def random(mode="RGB"):
def random(mode: str = "RGB") -> ImagePalette:
from random import randint

palette = [randint(0, 255) for _ in range(256 * len(mode))]
return ImagePalette(mode, palette)


def sepia(white="#fff0c0"):
def sepia(white: str = "#fff0c0") -> ImagePalette:
bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)]
return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)])


def wedge(mode="RGB"):
def wedge(mode: str = "RGB") -> ImagePalette:
palette = list(range(256 * len(mode)))
return ImagePalette(mode, [i // len(mode) for i in palette])

Expand Down
Loading

0 comments on commit 8a3a72e

Please sign in to comment.