mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Added type hints
This commit is contained in:
parent
44c2ff3f0b
commit
e47b181084
|
@ -6,6 +6,7 @@ numpy
|
||||||
packaging
|
packaging
|
||||||
pytest
|
pytest
|
||||||
sphinx
|
sphinx
|
||||||
|
types-atheris
|
||||||
types-defusedxml
|
types-defusedxml
|
||||||
types-olefile
|
types-olefile
|
||||||
types-setuptools
|
types-setuptools
|
||||||
|
|
|
@ -16,8 +16,9 @@
|
||||||
|
|
||||||
|
|
||||||
import atheris
|
import atheris
|
||||||
|
from atheris.import_hook import instrument_imports
|
||||||
|
|
||||||
with atheris.instrument_imports():
|
with instrument_imports():
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import fuzzers
|
import fuzzers
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
|
|
||||||
import atheris
|
import atheris
|
||||||
|
from atheris.import_hook import instrument_imports
|
||||||
|
|
||||||
with atheris.instrument_imports():
|
with instrument_imports():
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import fuzzers
|
import fuzzers
|
||||||
|
|
|
@ -33,6 +33,10 @@ Internal Modules
|
||||||
Provides a convenient way to import type hints that are not available
|
Provides a convenient way to import type hints that are not available
|
||||||
on some Python versions.
|
on some Python versions.
|
||||||
|
|
||||||
|
.. py:class:: IntegralLike
|
||||||
|
|
||||||
|
Typing alias.
|
||||||
|
|
||||||
.. py:class:: NumpyArray
|
.. py:class:: NumpyArray
|
||||||
|
|
||||||
Typing alias.
|
Typing alias.
|
||||||
|
|
|
@ -163,7 +163,3 @@ follow_imports = "silent"
|
||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
warn_unreachable = true
|
warn_unreachable = true
|
||||||
warn_unused_ignores = true
|
warn_unused_ignores = true
|
||||||
exclude = [
|
|
||||||
'^Tests/oss-fuzz/fuzz_font.py$',
|
|
||||||
'^Tests/oss-fuzz/fuzz_pillow.py$',
|
|
||||||
]
|
|
||||||
|
|
|
@ -387,7 +387,7 @@ class BmpRleDecoder(ImageFile.PyDecoder):
|
||||||
if self.fd.tell() % 2 != 0:
|
if self.fd.tell() % 2 != 0:
|
||||||
self.fd.seek(1, os.SEEK_CUR)
|
self.fd.seek(1, os.SEEK_CUR)
|
||||||
rawmode = "L" if self.mode == "L" else "P"
|
rawmode = "L" if self.mode == "L" else "P"
|
||||||
self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))
|
self.set_as_raw(bytes(data), rawmode, (0, self.args[-1]))
|
||||||
return -1, 0
|
return -1, 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,11 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin
|
from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin
|
||||||
from ._typing import NumpyArray, StrOrBytesPath, TypeGuard
|
from ._typing import NumpyArray, StrOrBytesPath, TypeGuard
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 13):
|
||||||
|
from types import CapsuleType
|
||||||
|
else:
|
||||||
|
CapsuleType = object
|
||||||
ID: list[str] = []
|
ID: list[str] = []
|
||||||
OPEN: dict[
|
OPEN: dict[
|
||||||
str,
|
str,
|
||||||
|
@ -1598,7 +1603,7 @@ class Image:
|
||||||
self.fp.seek(offset)
|
self.fp.seek(offset)
|
||||||
return child_images
|
return child_images
|
||||||
|
|
||||||
def getim(self):
|
def getim(self) -> CapsuleType:
|
||||||
"""
|
"""
|
||||||
Returns a capsule that points to the internal image memory.
|
Returns a capsule that points to the internal image memory.
|
||||||
|
|
||||||
|
|
|
@ -736,19 +736,22 @@ class PyDecoder(PyCodec):
|
||||||
msg = "unavailable in base decoder"
|
msg = "unavailable in base decoder"
|
||||||
raise NotImplementedError(msg)
|
raise NotImplementedError(msg)
|
||||||
|
|
||||||
def set_as_raw(self, data: bytes, rawmode=None) -> None:
|
def set_as_raw(
|
||||||
|
self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = ()
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Convenience method to set the internal image from a stream of raw data
|
Convenience method to set the internal image from a stream of raw data
|
||||||
|
|
||||||
:param data: Bytes to be set
|
:param data: Bytes to be set
|
||||||
:param rawmode: The rawmode to be used for the decoder.
|
:param rawmode: The rawmode to be used for the decoder.
|
||||||
If not specified, it will default to the mode of the image
|
If not specified, it will default to the mode of the image
|
||||||
|
:param extra: Extra arguments for the decoder.
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not rawmode:
|
if not rawmode:
|
||||||
rawmode = self.mode
|
rawmode = self.mode
|
||||||
d = Image._getdecoder(self.mode, "raw", rawmode)
|
d = Image._getdecoder(self.mode, "raw", rawmode, extra)
|
||||||
assert self.im is not None
|
assert self.im is not None
|
||||||
d.setimage(self.im, self.state.extents())
|
d.setimage(self.im, self.state.extents())
|
||||||
s = d.decode(data)
|
s = d.decode(data)
|
||||||
|
|
|
@ -34,7 +34,7 @@ import warnings
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import IO, TYPE_CHECKING, Any, BinaryIO, TypedDict
|
from typing import IO, TYPE_CHECKING, Any, BinaryIO, TypedDict, cast
|
||||||
|
|
||||||
from . import Image
|
from . import Image
|
||||||
from ._typing import StrOrBytesPath
|
from ._typing import StrOrBytesPath
|
||||||
|
@ -245,7 +245,7 @@ class FreeTypeFont:
|
||||||
|
|
||||||
self.layout_engine = layout_engine
|
self.layout_engine = layout_engine
|
||||||
|
|
||||||
def load_from_bytes(f) -> None:
|
def load_from_bytes(f: IO[bytes]) -> None:
|
||||||
self.font_bytes = f.read()
|
self.font_bytes = f.read()
|
||||||
self.font = core.getfont(
|
self.font = core.getfont(
|
||||||
"", size, index, encoding, self.font_bytes, layout_engine
|
"", size, index, encoding, self.font_bytes, layout_engine
|
||||||
|
@ -267,7 +267,7 @@ class FreeTypeFont:
|
||||||
font, size, index, encoding, layout_engine=layout_engine
|
font, size, index, encoding, layout_engine=layout_engine
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
load_from_bytes(font)
|
load_from_bytes(cast(IO[bytes], font))
|
||||||
|
|
||||||
def __getstate__(self) -> list[Any]:
|
def __getstate__(self) -> list[Any]:
|
||||||
return [self.path, self.size, self.index, self.encoding, self.layout_engine]
|
return [self.path, self.size, self.index, self.encoding, self.layout_engine]
|
||||||
|
|
|
@ -18,6 +18,7 @@ from __future__ import annotations
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
from collections.abc import Callable
|
||||||
from typing import IO, cast
|
from typing import IO, cast
|
||||||
|
|
||||||
from . import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette, _binary
|
||||||
|
@ -316,8 +317,13 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
else:
|
else:
|
||||||
self.fp.seek(length - 2, os.SEEK_CUR)
|
self.fp.seek(length - 2, os.SEEK_CUR)
|
||||||
|
|
||||||
@property
|
@property # type: ignore[override]
|
||||||
def reduce(self):
|
def reduce(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Callable[[int | tuple[int, int], tuple[int, int, int, int] | None], Image.Image]
|
||||||
|
| int
|
||||||
|
):
|
||||||
# https://github.com/python-pillow/Pillow/issues/4343 found that the
|
# https://github.com/python-pillow/Pillow/issues/4343 found that the
|
||||||
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
|
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
|
||||||
# property. This attempts to allow for both scenarios
|
# property. This attempts to allow for both scenarios
|
||||||
|
|
|
@ -152,7 +152,7 @@ class MspDecoder(ImageFile.PyDecoder):
|
||||||
msg = f"Corrupted MSP file in row {x}"
|
msg = f"Corrupted MSP file in row {x}"
|
||||||
raise OSError(msg) from e
|
raise OSError(msg) from e
|
||||||
|
|
||||||
self.set_as_raw(img.getvalue(), ("1", 0, 1))
|
self.set_as_raw(img.getvalue(), "1")
|
||||||
|
|
||||||
return -1, 0
|
return -1, 0
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,9 @@ class iTXt(str):
|
||||||
tkey: str | bytes | None
|
tkey: str | bytes | None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __new__(cls, text, lang=None, tkey=None):
|
def __new__(
|
||||||
|
cls, text: str, lang: str | None = None, tkey: str | None = None
|
||||||
|
) -> iTXt:
|
||||||
"""
|
"""
|
||||||
:param cls: the class to use when creating the instance
|
:param cls: the class to use when creating the instance
|
||||||
:param text: value for this key
|
:param text: value for this key
|
||||||
|
|
|
@ -61,6 +61,9 @@ from ._typing import StrOrBytesPath
|
||||||
from ._util import is_path
|
from ._util import is_path
|
||||||
from .TiffTags import TYPES
|
from .TiffTags import TYPES
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ._typing import IntegralLike
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Set these to true to force use of libtiff for reading or writing.
|
# Set these to true to force use of libtiff for reading or writing.
|
||||||
|
@ -291,22 +294,24 @@ def _accept(prefix: bytes) -> bool:
|
||||||
|
|
||||||
def _limit_rational(
|
def _limit_rational(
|
||||||
val: float | Fraction | IFDRational, max_val: int
|
val: float | Fraction | IFDRational, max_val: int
|
||||||
) -> tuple[float, float]:
|
) -> tuple[IntegralLike, IntegralLike]:
|
||||||
inv = abs(float(val)) > 1
|
inv = abs(float(val)) > 1
|
||||||
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
||||||
return n_d[::-1] if inv else n_d
|
return n_d[::-1] if inv else n_d
|
||||||
|
|
||||||
|
|
||||||
def _limit_signed_rational(val, max_val, min_val):
|
def _limit_signed_rational(
|
||||||
|
val: IFDRational, max_val: int, min_val: int
|
||||||
|
) -> tuple[IntegralLike, IntegralLike]:
|
||||||
frac = Fraction(val)
|
frac = Fraction(val)
|
||||||
n_d = frac.numerator, frac.denominator
|
n_d: tuple[IntegralLike, IntegralLike] = frac.numerator, frac.denominator
|
||||||
|
|
||||||
if min(n_d) < min_val:
|
if min(float(i) for i in n_d) < min_val:
|
||||||
n_d = _limit_rational(val, abs(min_val))
|
n_d = _limit_rational(val, abs(min_val))
|
||||||
|
|
||||||
if max(n_d) > max_val:
|
n_d_float = tuple(float(i) for i in n_d)
|
||||||
val = Fraction(*n_d)
|
if max(n_d_float) > max_val:
|
||||||
n_d = _limit_rational(val, max_val)
|
n_d = _limit_rational(n_d_float[0] / n_d_float[1], max_val)
|
||||||
|
|
||||||
return n_d
|
return n_d
|
||||||
|
|
||||||
|
@ -318,8 +323,10 @@ _load_dispatch = {}
|
||||||
_write_dispatch = {}
|
_write_dispatch = {}
|
||||||
|
|
||||||
|
|
||||||
def _delegate(op: str):
|
def _delegate(op: str) -> Any:
|
||||||
def delegate(self, *args):
|
def delegate(
|
||||||
|
self: IFDRational, *args: tuple[float, ...]
|
||||||
|
) -> bool | float | Fraction:
|
||||||
return getattr(self._val, op)(*args)
|
return getattr(self._val, op)(*args)
|
||||||
|
|
||||||
return delegate
|
return delegate
|
||||||
|
@ -358,7 +365,10 @@ class IFDRational(Rational):
|
||||||
self._numerator = value.numerator
|
self._numerator = value.numerator
|
||||||
self._denominator = value.denominator
|
self._denominator = value.denominator
|
||||||
else:
|
else:
|
||||||
self._numerator = value
|
if TYPE_CHECKING:
|
||||||
|
self._numerator = cast(IntegralLike, value)
|
||||||
|
else:
|
||||||
|
self._numerator = value
|
||||||
self._denominator = denominator
|
self._denominator = denominator
|
||||||
|
|
||||||
if denominator == 0:
|
if denominator == 0:
|
||||||
|
@ -371,14 +381,14 @@ class IFDRational(Rational):
|
||||||
self._val = Fraction(value / denominator)
|
self._val = Fraction(value / denominator)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def numerator(self):
|
def numerator(self) -> IntegralLike:
|
||||||
return self._numerator
|
return self._numerator
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def denominator(self) -> int:
|
def denominator(self) -> int:
|
||||||
return self._denominator
|
return self._denominator
|
||||||
|
|
||||||
def limit_rational(self, max_denominator: int) -> tuple[float, int]:
|
def limit_rational(self, max_denominator: int) -> tuple[IntegralLike, int]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param max_denominator: Integer, the maximum denominator value
|
:param max_denominator: Integer, the maximum denominator value
|
||||||
|
@ -406,14 +416,18 @@ class IFDRational(Rational):
|
||||||
val = float(val)
|
val = float(val)
|
||||||
return val == other
|
return val == other
|
||||||
|
|
||||||
def __getstate__(self) -> list[float | Fraction]:
|
def __getstate__(self) -> list[float | Fraction | IntegralLike]:
|
||||||
return [self._val, self._numerator, self._denominator]
|
return [self._val, self._numerator, self._denominator]
|
||||||
|
|
||||||
def __setstate__(self, state: list[float | Fraction]) -> None:
|
def __setstate__(self, state: list[float | Fraction | IntegralLike]) -> None:
|
||||||
IFDRational.__init__(self, 0)
|
IFDRational.__init__(self, 0)
|
||||||
_val, _numerator, _denominator = state
|
_val, _numerator, _denominator = state
|
||||||
|
assert isinstance(_val, (float, Fraction))
|
||||||
self._val = _val
|
self._val = _val
|
||||||
self._numerator = _numerator
|
if TYPE_CHECKING:
|
||||||
|
self._numerator = cast(IntegralLike, _numerator)
|
||||||
|
else:
|
||||||
|
self._numerator = _numerator
|
||||||
assert isinstance(_denominator, int)
|
assert isinstance(_denominator, int)
|
||||||
self._denominator = _denominator
|
self._denominator = _denominator
|
||||||
|
|
||||||
|
@ -471,8 +485,8 @@ def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def _register_writer(idx: int):
|
def _register_writer(idx: int) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
||||||
def decorator(func):
|
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||||
_write_dispatch[idx] = func # noqa: F821
|
_write_dispatch[idx] = func # noqa: F821
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ from collections.abc import Sequence
|
||||||
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union
|
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from numbers import _IntegralLike as IntegralLike
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
@ -38,4 +40,4 @@ class SupportsRead(Protocol[_T_co]):
|
||||||
StrOrBytesPath = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]
|
StrOrBytesPath = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["TypeGuard", "StrOrBytesPath", "SupportsRead"]
|
__all__ = ["IntegralLike", "StrOrBytesPath", "SupportsRead", "TypeGuard"]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user