mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge pull request #8339 from radarhere/type_hint
This commit is contained in:
commit
eaeda4a6be
|
@ -6,6 +6,7 @@ numpy
|
|||
packaging
|
||||
pytest
|
||||
sphinx
|
||||
types-atheris
|
||||
types-defusedxml
|
||||
types-olefile
|
||||
types-setuptools
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
|
||||
import atheris
|
||||
from atheris.import_hook import instrument_imports
|
||||
|
||||
with atheris.instrument_imports():
|
||||
with instrument_imports():
|
||||
import sys
|
||||
|
||||
import fuzzers
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
|
||||
import atheris
|
||||
from atheris.import_hook import instrument_imports
|
||||
|
||||
with atheris.instrument_imports():
|
||||
with instrument_imports():
|
||||
import sys
|
||||
|
||||
import fuzzers
|
||||
|
|
|
@ -33,6 +33,10 @@ Internal Modules
|
|||
Provides a convenient way to import type hints that are not available
|
||||
on some Python versions.
|
||||
|
||||
.. py:class:: IntegralLike
|
||||
|
||||
Typing alias.
|
||||
|
||||
.. py:class:: NumpyArray
|
||||
|
||||
Typing alias.
|
||||
|
|
|
@ -163,7 +163,3 @@ follow_imports = "silent"
|
|||
warn_redundant_casts = true
|
||||
warn_unreachable = 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:
|
||||
self.fd.seek(1, os.SEEK_CUR)
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -225,6 +225,11 @@ if TYPE_CHECKING:
|
|||
|
||||
from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin
|
||||
from ._typing import NumpyArray, StrOrBytesPath, TypeGuard
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from types import CapsuleType
|
||||
else:
|
||||
CapsuleType = object
|
||||
ID: list[str] = []
|
||||
OPEN: dict[
|
||||
str,
|
||||
|
@ -1598,7 +1603,7 @@ class Image:
|
|||
self.fp.seek(offset)
|
||||
return child_images
|
||||
|
||||
def getim(self):
|
||||
def getim(self) -> CapsuleType:
|
||||
"""
|
||||
Returns a capsule that points to the internal image memory.
|
||||
|
||||
|
|
|
@ -745,19 +745,22 @@ class PyDecoder(PyCodec):
|
|||
msg = "unavailable in base decoder"
|
||||
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
|
||||
|
||||
:param data: Bytes to be set
|
||||
:param rawmode: The rawmode to be used for the decoder.
|
||||
If not specified, it will default to the mode of the image
|
||||
:param extra: Extra arguments for the decoder.
|
||||
:returns: None
|
||||
"""
|
||||
|
||||
if not rawmode:
|
||||
rawmode = self.mode
|
||||
d = Image._getdecoder(self.mode, "raw", rawmode)
|
||||
d = Image._getdecoder(self.mode, "raw", rawmode, extra)
|
||||
assert self.im is not None
|
||||
d.setimage(self.im, self.state.extents())
|
||||
s = d.decode(data)
|
||||
|
|
|
@ -34,7 +34,7 @@ import warnings
|
|||
from enum import IntEnum
|
||||
from io import BytesIO
|
||||
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 ._typing import StrOrBytesPath
|
||||
|
@ -245,7 +245,7 @@ class FreeTypeFont:
|
|||
|
||||
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 = core.getfont(
|
||||
"", size, index, encoding, self.font_bytes, layout_engine
|
||||
|
@ -267,7 +267,7 @@ class FreeTypeFont:
|
|||
font, size, index, encoding, layout_engine=layout_engine
|
||||
)
|
||||
else:
|
||||
load_from_bytes(font)
|
||||
load_from_bytes(cast(IO[bytes], font))
|
||||
|
||||
def __getstate__(self) -> list[Any]:
|
||||
return [self.path, self.size, self.index, self.encoding, self.layout_engine]
|
||||
|
|
|
@ -18,6 +18,7 @@ from __future__ import annotations
|
|||
import io
|
||||
import os
|
||||
import struct
|
||||
from collections.abc import Callable
|
||||
from typing import IO, cast
|
||||
|
||||
from . import Image, ImageFile, ImagePalette, _binary
|
||||
|
@ -316,8 +317,13 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
else:
|
||||
self.fp.seek(length - 2, os.SEEK_CUR)
|
||||
|
||||
@property
|
||||
def reduce(self):
|
||||
@property # type: ignore[override]
|
||||
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
|
||||
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
|
||||
# property. This attempts to allow for both scenarios
|
||||
|
|
|
@ -152,7 +152,7 @@ class MspDecoder(ImageFile.PyDecoder):
|
|||
msg = f"Corrupted MSP file in row {x}"
|
||||
raise OSError(msg) from e
|
||||
|
||||
self.set_as_raw(img.getvalue(), ("1", 0, 1))
|
||||
self.set_as_raw(img.getvalue(), "1")
|
||||
|
||||
return -1, 0
|
||||
|
||||
|
|
|
@ -258,7 +258,9 @@ class iTXt(str):
|
|||
tkey: str | bytes | None
|
||||
|
||||
@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 text: value for this key
|
||||
|
|
|
@ -61,6 +61,9 @@ from ._typing import StrOrBytesPath
|
|||
from ._util import is_path
|
||||
from .TiffTags import TYPES
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._typing import IntegralLike
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 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(
|
||||
val: float | Fraction | IFDRational, max_val: int
|
||||
) -> tuple[float, float]:
|
||||
) -> tuple[IntegralLike, IntegralLike]:
|
||||
inv = abs(float(val)) > 1
|
||||
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
||||
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)
|
||||
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))
|
||||
|
||||
if max(n_d) > max_val:
|
||||
val = Fraction(*n_d)
|
||||
n_d = _limit_rational(val, max_val)
|
||||
n_d_float = tuple(float(i) for i in n_d)
|
||||
if max(n_d_float) > max_val:
|
||||
n_d = _limit_rational(n_d_float[0] / n_d_float[1], max_val)
|
||||
|
||||
return n_d
|
||||
|
||||
|
@ -318,8 +323,10 @@ _load_dispatch = {}
|
|||
_write_dispatch = {}
|
||||
|
||||
|
||||
def _delegate(op: str):
|
||||
def delegate(self, *args):
|
||||
def _delegate(op: str) -> Any:
|
||||
def delegate(
|
||||
self: IFDRational, *args: tuple[float, ...]
|
||||
) -> bool | float | Fraction:
|
||||
return getattr(self._val, op)(*args)
|
||||
|
||||
return delegate
|
||||
|
@ -358,7 +365,10 @@ class IFDRational(Rational):
|
|||
self._numerator = value.numerator
|
||||
self._denominator = value.denominator
|
||||
else:
|
||||
self._numerator = value
|
||||
if TYPE_CHECKING:
|
||||
self._numerator = cast(IntegralLike, value)
|
||||
else:
|
||||
self._numerator = value
|
||||
self._denominator = denominator
|
||||
|
||||
if denominator == 0:
|
||||
|
@ -371,14 +381,14 @@ class IFDRational(Rational):
|
|||
self._val = Fraction(value / denominator)
|
||||
|
||||
@property
|
||||
def numerator(self):
|
||||
def numerator(self) -> IntegralLike:
|
||||
return self._numerator
|
||||
|
||||
@property
|
||||
def denominator(self) -> int:
|
||||
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
|
||||
|
@ -406,14 +416,18 @@ class IFDRational(Rational):
|
|||
val = float(val)
|
||||
return val == other
|
||||
|
||||
def __getstate__(self) -> list[float | Fraction]:
|
||||
def __getstate__(self) -> list[float | Fraction | IntegralLike]:
|
||||
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)
|
||||
_val, _numerator, _denominator = state
|
||||
assert isinstance(_val, (float, Fraction))
|
||||
self._val = _val
|
||||
self._numerator = _numerator
|
||||
if TYPE_CHECKING:
|
||||
self._numerator = cast(IntegralLike, _numerator)
|
||||
else:
|
||||
self._numerator = _numerator
|
||||
assert isinstance(_denominator, int)
|
||||
self._denominator = _denominator
|
||||
|
||||
|
@ -471,8 +485,8 @@ def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc
|
|||
return decorator
|
||||
|
||||
|
||||
def _register_writer(idx: int):
|
||||
def decorator(func):
|
||||
def _register_writer(idx: int) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
||||
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||
_write_dispatch[idx] = func # noqa: F821
|
||||
return func
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ from collections.abc import Sequence
|
|||
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from numbers import _IntegralLike as IntegralLike
|
||||
|
||||
try:
|
||||
import numpy.typing as npt
|
||||
|
||||
|
@ -38,4 +40,4 @@ class SupportsRead(Protocol[_T_co]):
|
|||
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