Skip to content

Commit

Permalink
Added type hints
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Aug 14, 2024
1 parent 497080f commit 8afb7dd
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 35 deletions.
1 change: 1 addition & 0 deletions .ci/requirements-mypy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ipython
numpy
packaging
pytest
sphinx
types-defusedxml
types-olefile
types-setuptools
29 changes: 16 additions & 13 deletions docs/example/DdsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import struct
from io import BytesIO
from typing import IO

from PIL import Image, ImageFile

Expand Down Expand Up @@ -94,26 +95,26 @@
DXT5_FOURCC = 0x35545844


def _decode565(bits):
def _decode565(bits: int) -> tuple[int, int, int]:
a = ((bits >> 11) & 0x1F) << 3
b = ((bits >> 5) & 0x3F) << 2
c = (bits & 0x1F) << 3
return a, b, c


def _c2a(a, b):
def _c2a(a: int, b: int) -> int:
return (2 * a + b) // 3


def _c2b(a, b):
def _c2b(a: int, b: int) -> int:
return (a + b) // 2


def _c3(a, b):
def _c3(a: int, b: int) -> int:
return (2 * b + a) // 3


def _dxt1(data, width, height):
def _dxt1(data: IO[bytes], width: int, height: int) -> bytes:
# TODO implement this function as pixel format in decode.c
ret = bytearray(4 * width * height)

Expand Down Expand Up @@ -151,7 +152,7 @@ def _dxt1(data, width, height):
return bytes(ret)


def _dxtc_alpha(a0, a1, ac0, ac1, ai):
def _dxtc_alpha(a0: int, a1: int, ac0: int, ac1: int, ai: int) -> int:
if ai <= 12:
ac = (ac0 >> ai) & 7
elif ai == 15:
Expand All @@ -175,7 +176,7 @@ def _dxtc_alpha(a0, a1, ac0, ac1, ai):
return alpha


def _dxt5(data, width, height):
def _dxt5(data: IO[bytes], width: int, height: int) -> bytes:
# TODO implement this function as pixel format in decode.c
ret = bytearray(4 * width * height)

Expand Down Expand Up @@ -211,7 +212,7 @@ class DdsImageFile(ImageFile.ImageFile):
format = "DDS"
format_description = "DirectDraw Surface"

def _open(self):
def _open(self) -> None:
if not _accept(self.fp.read(4)):
msg = "not a DDS file"
raise SyntaxError(msg)
Expand Down Expand Up @@ -242,19 +243,20 @@ def _open(self):
elif fourcc == b"DXT5":
self.decoder = "DXT5"
else:
msg = f"Unimplemented pixel format {fourcc}"
msg = f"Unimplemented pixel format {repr(fourcc)}"
raise NotImplementedError(msg)

self.tile = [(self.decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]

def load_seek(self, pos):
def load_seek(self, pos: int) -> None:
pass


class DXT1Decoder(ImageFile.PyDecoder):
_pulls_fd = True

def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
assert self.fd is not None
try:
self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize))
except struct.error as e:
Expand All @@ -266,7 +268,8 @@ def decode(self, buffer):
class DXT5Decoder(ImageFile.PyDecoder):
_pulls_fd = True

def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
assert self.fd is not None
try:
self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize))
except struct.error as e:
Expand All @@ -279,7 +282,7 @@ def decode(self, buffer):
Image.register_decoder("DXT5", DXT5Decoder)


def _accept(prefix):
def _accept(prefix: bytes) -> bool:
return prefix[:4] == b"DDS "


Expand Down
36 changes: 21 additions & 15 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,11 @@ def __setstate__(self, state: list[float | Fraction]) -> None:
__int__ = _delegate("__int__")


def _register_loader(idx: int, size: int):
def decorator(func):
_LoaderFunc = Callable[["ImageFileDirectory_v2", bytes, bool], Any]

Check warning on line 459 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L459

Added line #L459 was not covered by tests


def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc]:
def decorator(func: _LoaderFunc) -> _LoaderFunc:

Check warning on line 463 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L462-L463

Added lines #L462 - L463 were not covered by tests
from .TiffTags import TYPES

if func.__name__.startswith("load_"):
Expand All @@ -482,12 +485,13 @@ def _register_basic(idx_fmt_name: tuple[int, str, str]) -> None:
idx, fmt, name = idx_fmt_name
TYPES[idx] = name
size = struct.calcsize(f"={fmt}")
_load_dispatch[idx] = ( # noqa: F821
size,
lambda self, data, legacy_api=True: (
self._unpack(f"{len(data) // size}{fmt}", data)
),
)

def basic_handler(

Check warning on line 489 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L489

Added line #L489 was not covered by tests
self: ImageFileDirectory_v2, data: bytes, legacy_api: bool = True
) -> tuple[Any, ...]:
return self._unpack(f"{len(data) // size}{fmt}", data)

Check warning on line 492 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L492

Added line #L492 was not covered by tests

_load_dispatch[idx] = size, basic_handler # noqa: F821

Check warning on line 494 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L494

Added line #L494 was not covered by tests
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
b"".join(self._pack(fmt, value) for value in values)
)
Expand Down Expand Up @@ -560,7 +564,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
"""

_load_dispatch: dict[int, Callable[[ImageFileDirectory_v2, bytes, bool], Any]] = {}
_load_dispatch: dict[int, tuple[int, _LoaderFunc]] = {}

Check warning on line 567 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L567

Added line #L567 was not covered by tests
_write_dispatch: dict[int, Callable[..., Any]] = {}

def __init__(
Expand Down Expand Up @@ -653,10 +657,10 @@ def __getitem__(self, tag):
def __contains__(self, tag: object) -> bool:
return tag in self._tags_v2 or tag in self._tagdata

def __setitem__(self, tag: int, value) -> None:
def __setitem__(self, tag: int, value: Any) -> None:

Check warning on line 660 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L660

Added line #L660 was not covered by tests
self._setitem(tag, value, self.legacy_api)

def _setitem(self, tag: int, value, legacy_api: bool) -> None:
def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None:

Check warning on line 663 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L663

Added line #L663 was not covered by tests
basetypes = (Number, bytes, str)

info = TiffTags.lookup(tag, self.group)
Expand Down Expand Up @@ -744,10 +748,10 @@ def __delitem__(self, tag: int) -> None:
def __iter__(self) -> Iterator[int]:
return iter(set(self._tagdata) | set(self._tags_v2))

def _unpack(self, fmt: str, data: bytes):
def _unpack(self, fmt: str, data: bytes) -> tuple[Any, ...]:

Check warning on line 751 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L751

Added line #L751 was not covered by tests
return struct.unpack(self._endian + fmt, data)

def _pack(self, fmt: str, *values) -> bytes:
def _pack(self, fmt: str, *values: Any) -> bytes:

Check warning on line 754 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L754

Added line #L754 was not covered by tests
return struct.pack(self._endian + fmt, *values)

list(
Expand Down Expand Up @@ -824,7 +828,9 @@ def write_undefined(self, value: bytes | int | IFDRational) -> bytes:
return value

@_register_loader(10, 8)
def load_signed_rational(self, data: bytes, legacy_api: bool = True):
def load_signed_rational(

Check warning on line 831 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L831

Added line #L831 was not covered by tests
self, data: bytes, legacy_api: bool = True
) -> tuple[tuple[int, int] | IFDRational, ...]:
vals = self._unpack(f"{len(data) // 4}l", data)

def combine(a: int, b: int) -> tuple[int, int] | IFDRational:
Expand Down Expand Up @@ -1088,7 +1094,7 @@ def __len__(self) -> int:
def __iter__(self) -> Iterator[int]:
return iter(set(self._tagdata) | set(self._tags_v1))

def __setitem__(self, tag: int, value) -> None:
def __setitem__(self, tag: int, value: Any) -> None:

Check warning on line 1097 in src/PIL/TiffImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/TiffImagePlugin.py#L1097

Added line #L1097 was not covered by tests
for legacy_api in (False, True):
self._setitem(tag, value, legacy_api)

Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ deps =
extras =
typing
commands =
mypy src Tests {posargs}
mypy docs src winbuild Tests {posargs}
12 changes: 6 additions & 6 deletions winbuild/build_prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import shutil
import struct
import subprocess
from typing import Any


def cmd_cd(path: str) -> str:
Expand Down Expand Up @@ -43,21 +44,19 @@ def cmd_nmake(
target: str = "",
params: list[str] | None = None,
) -> str:
params = "" if params is None else " ".join(params)

return " ".join(
[
"{nmake}",
"-nologo",
f'-f "{makefile}"' if makefile is not None else "",
f"{params}",
f'{" ".join(params)}' if params is not None else "",
f'"{target}"',
]
)


def cmds_cmake(
target: str | tuple[str, ...] | list[str], *params, build_dir: str = "."
target: str | tuple[str, ...] | list[str], *params: str, build_dir: str = "."
) -> list[str]:
if not isinstance(target, str):
target = " ".join(target)
Expand Down Expand Up @@ -129,7 +128,7 @@ def cmd_msbuild(


# dependencies, listed in order of compilation
DEPS = {
DEPS: dict[str, dict[str, Any]] = {
"libjpeg": {
"url": f"{SF_PROJECTS}/libjpeg-turbo/files/{V['JPEGTURBO']}/"
f"libjpeg-turbo-{V['JPEGTURBO']}.tar.gz/download",
Expand Down Expand Up @@ -538,7 +537,7 @@ def write_script(
print(" " + line)


def get_footer(dep: dict) -> list[str]:
def get_footer(dep: dict[str, Any]) -> list[str]:
lines = []
for out in dep.get("headers", []):
lines.append(cmd_copy(out, "{inc_dir}"))
Expand Down Expand Up @@ -583,6 +582,7 @@ def build_dep(name: str, prefs: dict[str, str], verbose: bool) -> str:
license_text += f.read()
if "license_pattern" in dep:
match = re.search(dep["license_pattern"], license_text, re.DOTALL)
assert match is not None
license_text = "\n".join(match.groups())
assert len(license_text) > 50
with open(os.path.join(license_dir, f"{directory}.txt"), "w") as f:
Expand Down

0 comments on commit 8afb7dd

Please sign in to comment.