mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-01 00:17:27 +03:00 
			
		
		
		
	Merge pull request #8270 from radarhere/type_hint
This commit is contained in:
		
						commit
						4df4df2a18
					
				|  | @ -154,7 +154,7 @@ class TestFileJpeg: | ||||||
|             assert k > 0.9 |             assert k > 0.9 | ||||||
| 
 | 
 | ||||||
|     def test_rgb(self) -> None: |     def test_rgb(self) -> None: | ||||||
|         def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, int, int]: |         def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, ...]: | ||||||
|             return tuple(v[0] for v in im.layer) |             return tuple(v[0] for v in im.layer) | ||||||
| 
 | 
 | ||||||
|         im = hopper() |         im = hopper() | ||||||
|  |  | ||||||
|  | @ -99,7 +99,7 @@ class TestImage: | ||||||
|         im = Image.new("L", (100, 100)) |         im = Image.new("L", (100, 100)) | ||||||
| 
 | 
 | ||||||
|         p = Pretty() |         p = Pretty() | ||||||
|         im._repr_pretty_(p, None) |         im._repr_pretty_(p, False) | ||||||
|         assert p.pretty_output == "<PIL.Image.Image image mode=L size=100x100>" |         assert p.pretty_output == "<PIL.Image.Image image mode=L size=100x100>" | ||||||
| 
 | 
 | ||||||
|     def test_open_formats(self) -> None: |     def test_open_formats(self) -> None: | ||||||
|  |  | ||||||
|  | @ -185,6 +185,14 @@ Plugin reference | ||||||
|     :undoc-members: |     :undoc-members: | ||||||
|     :show-inheritance: |     :show-inheritance: | ||||||
| 
 | 
 | ||||||
|  | :mod:`~PIL.MpoImagePlugin` Module | ||||||
|  | ---------------------------------- | ||||||
|  | 
 | ||||||
|  | .. automodule:: PIL.MpoImagePlugin | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  | 
 | ||||||
| :mod:`~PIL.MspImagePlugin` Module | :mod:`~PIL.MspImagePlugin` Module | ||||||
| --------------------------------- | --------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -65,16 +65,24 @@ def has_ghostscript() -> bool: | ||||||
|     return gs_binary is not False |     return gs_binary is not False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def Ghostscript(tile, size, fp, scale=1, transparency: bool = False) -> Image.Image: | def Ghostscript( | ||||||
|  |     tile: list[ImageFile._Tile], | ||||||
|  |     size: tuple[int, int], | ||||||
|  |     fp: IO[bytes], | ||||||
|  |     scale: int = 1, | ||||||
|  |     transparency: bool = False, | ||||||
|  | ) -> Image.Image: | ||||||
|     """Render an image using Ghostscript""" |     """Render an image using Ghostscript""" | ||||||
|     global gs_binary |     global gs_binary | ||||||
|     if not has_ghostscript(): |     if not has_ghostscript(): | ||||||
|         msg = "Unable to locate Ghostscript on paths" |         msg = "Unable to locate Ghostscript on paths" | ||||||
|         raise OSError(msg) |         raise OSError(msg) | ||||||
|  |     assert isinstance(gs_binary, str) | ||||||
| 
 | 
 | ||||||
|     # Unpack decoder tile |     # Unpack decoder tile | ||||||
|     decoder, tile, offset, data = tile[0] |     args = tile[0].args | ||||||
|     length, bbox = data |     assert isinstance(args, tuple) | ||||||
|  |     length, bbox = args | ||||||
| 
 | 
 | ||||||
|     # Hack to support hi-res rendering |     # Hack to support hi-res rendering | ||||||
|     scale = int(scale) or 1 |     scale = int(scale) or 1 | ||||||
|  | @ -227,7 +235,11 @@ class EpsImageFile(ImageFile.ImageFile): | ||||||
|                         # put floating point values there anyway. |                         # put floating point values there anyway. | ||||||
|                         box = [int(float(i)) for i in v.split()] |                         box = [int(float(i)) for i in v.split()] | ||||||
|                         self._size = box[2] - box[0], box[3] - box[1] |                         self._size = box[2] - box[0], box[3] - box[1] | ||||||
|                         self.tile = [("eps", (0, 0) + self.size, offset, (length, box))] |                         self.tile = [ | ||||||
|  |                             ImageFile._Tile( | ||||||
|  |                                 "eps", (0, 0) + self.size, offset, (length, box) | ||||||
|  |                             ) | ||||||
|  |                         ] | ||||||
|                     except Exception: |                     except Exception: | ||||||
|                         pass |                         pass | ||||||
|             return True |             return True | ||||||
|  |  | ||||||
|  | @ -220,7 +220,7 @@ if hasattr(core, "DEFAULT_STRATEGY"): | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from xml.etree.ElementTree import Element |     from xml.etree.ElementTree import Element | ||||||
| 
 | 
 | ||||||
|     from . import ImageFile, ImagePalette |     from . import ImageFile, ImagePalette, TiffImagePlugin | ||||||
|     from ._typing import NumpyArray, StrOrBytesPath, TypeGuard |     from ._typing import NumpyArray, StrOrBytesPath, TypeGuard | ||||||
| ID: list[str] = [] | ID: list[str] = [] | ||||||
| OPEN: dict[ | OPEN: dict[ | ||||||
|  | @ -676,7 +676,7 @@ class Image: | ||||||
|             id(self), |             id(self), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def _repr_pretty_(self, p, cycle) -> None: |     def _repr_pretty_(self, p, cycle: bool) -> None: | ||||||
|         """IPython plain text display support""" |         """IPython plain text display support""" | ||||||
| 
 | 
 | ||||||
|         # Same as __repr__ but without unpredictable id(self), |         # Same as __repr__ but without unpredictable id(self), | ||||||
|  | @ -1551,6 +1551,7 @@ class Image: | ||||||
|                     ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) |                     ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) | ||||||
|         ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) |         ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) | ||||||
|         if ifd1 and ifd1.get(513): |         if ifd1 and ifd1.get(513): | ||||||
|  |             assert exif._info is not None | ||||||
|             ifds.append((ifd1, exif._info.next)) |             ifds.append((ifd1, exif._info.next)) | ||||||
| 
 | 
 | ||||||
|         offset = None |         offset = None | ||||||
|  | @ -1560,6 +1561,7 @@ class Image: | ||||||
|                 offset = current_offset |                 offset = current_offset | ||||||
| 
 | 
 | ||||||
|             fp = self.fp |             fp = self.fp | ||||||
|  |             if ifd is not None: | ||||||
|                 thumbnail_offset = ifd.get(513) |                 thumbnail_offset = ifd.get(513) | ||||||
|                 if thumbnail_offset is not None: |                 if thumbnail_offset is not None: | ||||||
|                     thumbnail_offset += getattr(self, "_exif_offset", 0) |                     thumbnail_offset += getattr(self, "_exif_offset", 0) | ||||||
|  | @ -3869,14 +3871,14 @@ class Exif(_ExifBase): | ||||||
|     bigtiff = False |     bigtiff = False | ||||||
|     _loaded = False |     _loaded = False | ||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self) -> None: | ||||||
|         self._data = {} |         self._data: dict[int, Any] = {} | ||||||
|         self._hidden_data = {} |         self._hidden_data: dict[int, Any] = {} | ||||||
|         self._ifds = {} |         self._ifds: dict[int, dict[int, Any]] = {} | ||||||
|         self._info = None |         self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None | ||||||
|         self._loaded_exif = None |         self._loaded_exif: bytes | None = None | ||||||
| 
 | 
 | ||||||
|     def _fixup(self, value): |     def _fixup(self, value: Any) -> Any: | ||||||
|         try: |         try: | ||||||
|             if len(value) == 1 and isinstance(value, tuple): |             if len(value) == 1 and isinstance(value, tuple): | ||||||
|                 return value[0] |                 return value[0] | ||||||
|  | @ -3884,24 +3886,26 @@ class Exif(_ExifBase): | ||||||
|             pass |             pass | ||||||
|         return value |         return value | ||||||
| 
 | 
 | ||||||
|     def _fixup_dict(self, src_dict): |     def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]: | ||||||
|         # Helper function |         # Helper function | ||||||
|         # returns a dict with any single item tuples/lists as individual values |         # returns a dict with any single item tuples/lists as individual values | ||||||
|         return {k: self._fixup(v) for k, v in src_dict.items()} |         return {k: self._fixup(v) for k, v in src_dict.items()} | ||||||
| 
 | 
 | ||||||
|     def _get_ifd_dict(self, offset: int, group: int | None = None): |     def _get_ifd_dict( | ||||||
|  |         self, offset: int, group: int | None = None | ||||||
|  |     ) -> dict[int, Any] | None: | ||||||
|         try: |         try: | ||||||
|             # an offset pointer to the location of the nested embedded IFD. |             # an offset pointer to the location of the nested embedded IFD. | ||||||
|             # It should be a long, but may be corrupted. |             # It should be a long, but may be corrupted. | ||||||
|             self.fp.seek(offset) |             self.fp.seek(offset) | ||||||
|         except (KeyError, TypeError): |         except (KeyError, TypeError): | ||||||
|             pass |             return None | ||||||
|         else: |         else: | ||||||
|             from . import TiffImagePlugin |             from . import TiffImagePlugin | ||||||
| 
 | 
 | ||||||
|             info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) |             info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) | ||||||
|             info.load(self.fp) |             info.load(self.fp) | ||||||
|             return self._fixup_dict(info) |             return self._fixup_dict(dict(info)) | ||||||
| 
 | 
 | ||||||
|     def _get_head(self) -> bytes: |     def _get_head(self) -> bytes: | ||||||
|         version = b"\x2B" if self.bigtiff else b"\x2A" |         version = b"\x2B" if self.bigtiff else b"\x2A" | ||||||
|  | @ -3966,7 +3970,7 @@ class Exif(_ExifBase): | ||||||
|         self.fp.seek(offset) |         self.fp.seek(offset) | ||||||
|         self._info.load(self.fp) |         self._info.load(self.fp) | ||||||
| 
 | 
 | ||||||
|     def _get_merged_dict(self): |     def _get_merged_dict(self) -> dict[int, Any]: | ||||||
|         merged_dict = dict(self) |         merged_dict = dict(self) | ||||||
| 
 | 
 | ||||||
|         # get EXIF extension |         # get EXIF extension | ||||||
|  | @ -4124,7 +4128,7 @@ class Exif(_ExifBase): | ||||||
|             keys.update(self._info) |             keys.update(self._info) | ||||||
|         return len(keys) |         return len(keys) | ||||||
| 
 | 
 | ||||||
|     def __getitem__(self, tag: int): |     def __getitem__(self, tag: int) -> Any: | ||||||
|         if self._info is not None and tag not in self._data and tag in self._info: |         if self._info is not None and tag not in self._data and tag in self._info: | ||||||
|             self._data[tag] = self._fixup(self._info[tag]) |             self._data[tag] = self._fixup(self._info[tag]) | ||||||
|             del self._info[tag] |             del self._info[tag] | ||||||
|  | @ -4133,7 +4137,7 @@ class Exif(_ExifBase): | ||||||
|     def __contains__(self, tag: object) -> bool: |     def __contains__(self, tag: object) -> bool: | ||||||
|         return tag in self._data or (self._info is not None and tag in self._info) |         return tag in self._data or (self._info is not None and tag in self._info) | ||||||
| 
 | 
 | ||||||
|     def __setitem__(self, tag: int, value) -> None: |     def __setitem__(self, tag: int, value: Any) -> None: | ||||||
|         if self._info is not None and tag in self._info: |         if self._info is not None and tag in self._info: | ||||||
|             del self._info[tag] |             del self._info[tag] | ||||||
|         self._data[tag] = value |         self._data[tag] = value | ||||||
|  |  | ||||||
|  | @ -86,7 +86,7 @@ def raise_oserror(error: int) -> OSError: | ||||||
|     raise _get_oserror(error, encoder=False) |     raise _get_oserror(error, encoder=False) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _tilesort(t) -> int: | def _tilesort(t: _Tile) -> int: | ||||||
|     # sort on offset |     # sort on offset | ||||||
|     return t[2] |     return t[2] | ||||||
| 
 | 
 | ||||||
|  | @ -161,7 +161,7 @@ class ImageFile(Image.Image): | ||||||
|             return Image.MIME.get(self.format.upper()) |             return Image.MIME.get(self.format.upper()) | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     def __setstate__(self, state) -> None: |     def __setstate__(self, state: list[Any]) -> None: | ||||||
|         self.tile = [] |         self.tile = [] | ||||||
|         super().__setstate__(state) |         super().__setstate__(state) | ||||||
| 
 | 
 | ||||||
|  | @ -525,7 +525,7 @@ class Parser: | ||||||
| # -------------------------------------------------------------------- | # -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _save(im, fp, tile, bufsize: int = 0) -> None: | def _save(im: Image.Image, fp: IO[bytes], tile, bufsize: int = 0) -> None: | ||||||
|     """Helper to save image based on tile list |     """Helper to save image based on tile list | ||||||
| 
 | 
 | ||||||
|     :param im: Image object. |     :param im: Image object. | ||||||
|  | @ -554,7 +554,12 @@ def _save(im, fp, tile, bufsize: int = 0) -> None: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _encode_tile( | def _encode_tile( | ||||||
|     im, fp: IO[bytes], tile: list[_Tile], bufsize: int, fh, exc=None |     im: Image.Image, | ||||||
|  |     fp: IO[bytes], | ||||||
|  |     tile: list[_Tile], | ||||||
|  |     bufsize: int, | ||||||
|  |     fh, | ||||||
|  |     exc: BaseException | None = None, | ||||||
| ) -> None: | ) -> None: | ||||||
|     for encoder_name, extents, offset, args in tile: |     for encoder_name, extents, offset, args in tile: | ||||||
|         if offset > 0: |         if offset > 0: | ||||||
|  | @ -664,7 +669,11 @@ class PyCodec: | ||||||
|         """ |         """ | ||||||
|         self.fd = fd |         self.fd = fd | ||||||
| 
 | 
 | ||||||
|     def setimage(self, im, extents=None): |     def setimage( | ||||||
|  |         self, | ||||||
|  |         im: Image.core.ImagingCore, | ||||||
|  |         extents: tuple[int, int, int, int] | None = None, | ||||||
|  |     ) -> None: | ||||||
|         """ |         """ | ||||||
|         Called from ImageFile to set the core output image for the codec |         Called from ImageFile to set the core output image for the codec | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ import subprocess | ||||||
| import sys | import sys | ||||||
| import tempfile | import tempfile | ||||||
| import warnings | import warnings | ||||||
| from typing import IO, Any | from typing import IO, TYPE_CHECKING, Any | ||||||
| 
 | 
 | ||||||
| from . import Image, ImageFile | from . import Image, ImageFile | ||||||
| from ._binary import i16be as i16 | from ._binary import i16be as i16 | ||||||
|  | @ -51,6 +51,9 @@ from ._binary import o8 | ||||||
| from ._binary import o16be as o16 | from ._binary import o16be as o16 | ||||||
| from .JpegPresets import presets | from .JpegPresets import presets | ||||||
| 
 | 
 | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from .MpoImagePlugin import MpoImageFile | ||||||
|  | 
 | ||||||
| # | # | ||||||
| # Parser | # Parser | ||||||
| 
 | 
 | ||||||
|  | @ -329,7 +332,7 @@ class JpegImageFile(ImageFile.ImageFile): | ||||||
|     format = "JPEG" |     format = "JPEG" | ||||||
|     format_description = "JPEG (ISO 10918)" |     format_description = "JPEG (ISO 10918)" | ||||||
| 
 | 
 | ||||||
|     def _open(self): |     def _open(self) -> None: | ||||||
|         s = self.fp.read(3) |         s = self.fp.read(3) | ||||||
| 
 | 
 | ||||||
|         if not _accept(s): |         if not _accept(s): | ||||||
|  | @ -342,13 +345,13 @@ class JpegImageFile(ImageFile.ImageFile): | ||||||
|         self._exif_offset = 0 |         self._exif_offset = 0 | ||||||
| 
 | 
 | ||||||
|         # JPEG specifics (internal) |         # JPEG specifics (internal) | ||||||
|         self.layer = [] |         self.layer: list[tuple[int, int, int, int]] = [] | ||||||
|         self.huffman_dc = {} |         self.huffman_dc: dict[Any, Any] = {} | ||||||
|         self.huffman_ac = {} |         self.huffman_ac: dict[Any, Any] = {} | ||||||
|         self.quantization = {} |         self.quantization: dict[int, list[int]] = {} | ||||||
|         self.app = {}  # compatibility |         self.app: dict[str, bytes] = {}  # compatibility | ||||||
|         self.applist = [] |         self.applist: list[tuple[str, bytes]] = [] | ||||||
|         self.icclist = [] |         self.icclist: list[bytes] = [] | ||||||
| 
 | 
 | ||||||
|         while True: |         while True: | ||||||
|             i = s[0] |             i = s[0] | ||||||
|  | @ -831,7 +834,9 @@ def _save_cjpeg(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: | ||||||
| 
 | 
 | ||||||
| ## | ## | ||||||
| # Factory for making JPEG and MPO instances | # Factory for making JPEG and MPO instances | ||||||
| def jpeg_factory(fp: IO[bytes] | None = None, filename: str | bytes | None = None): | def jpeg_factory( | ||||||
|  |     fp: IO[bytes] | None = None, filename: str | bytes | None = None | ||||||
|  | ) -> JpegImageFile | MpoImageFile: | ||||||
|     im = JpegImageFile(fp, filename) |     im = JpegImageFile(fp, filename) | ||||||
|     try: |     try: | ||||||
|         mpheader = im._getmp() |         mpheader = im._getmp() | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ import warnings | ||||||
| import zlib | import zlib | ||||||
| from collections.abc import Callable | from collections.abc import Callable | ||||||
| from enum import IntEnum | from enum import IntEnum | ||||||
| from typing import IO, TYPE_CHECKING, Any, NamedTuple, NoReturn | from typing import IO, TYPE_CHECKING, Any, NamedTuple, NoReturn, cast | ||||||
| 
 | 
 | ||||||
| from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence | from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence | ||||||
| from ._binary import i16be as i16 | from ._binary import i16be as i16 | ||||||
|  | @ -1223,7 +1223,11 @@ def _write_multiple_frames( | ||||||
|     if default_image: |     if default_image: | ||||||
|         if im.mode != mode: |         if im.mode != mode: | ||||||
|             im = im.convert(mode) |             im = im.convert(mode) | ||||||
|         ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) |         ImageFile._save( | ||||||
|  |             im, | ||||||
|  |             cast(IO[bytes], _idat(fp, chunk)), | ||||||
|  |             [("zip", (0, 0) + im.size, 0, rawmode)], | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     seq_num = 0 |     seq_num = 0 | ||||||
|     for frame, frame_data in enumerate(im_frames): |     for frame, frame_data in enumerate(im_frames): | ||||||
|  | @ -1258,14 +1262,14 @@ def _write_multiple_frames( | ||||||
|             # first frame must be in IDAT chunks for backwards compatibility |             # first frame must be in IDAT chunks for backwards compatibility | ||||||
|             ImageFile._save( |             ImageFile._save( | ||||||
|                 im_frame, |                 im_frame, | ||||||
|                 _idat(fp, chunk), |                 cast(IO[bytes], _idat(fp, chunk)), | ||||||
|                 [("zip", (0, 0) + im_frame.size, 0, rawmode)], |                 [("zip", (0, 0) + im_frame.size, 0, rawmode)], | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             fdat_chunks = _fdat(fp, chunk, seq_num) |             fdat_chunks = _fdat(fp, chunk, seq_num) | ||||||
|             ImageFile._save( |             ImageFile._save( | ||||||
|                 im_frame, |                 im_frame, | ||||||
|                 fdat_chunks, |                 cast(IO[bytes], fdat_chunks), | ||||||
|                 [("zip", (0, 0) + im_frame.size, 0, rawmode)], |                 [("zip", (0, 0) + im_frame.size, 0, rawmode)], | ||||||
|             ) |             ) | ||||||
|             seq_num = fdat_chunks.seq_num |             seq_num = fdat_chunks.seq_num | ||||||
|  | @ -1465,7 +1469,9 @@ def _save( | ||||||
|         ) |         ) | ||||||
|     if single_im: |     if single_im: | ||||||
|         ImageFile._save( |         ImageFile._save( | ||||||
|             single_im, _idat(fp, chunk), [("zip", (0, 0) + single_im.size, 0, rawmode)] |             single_im, | ||||||
|  |             cast(IO[bytes], _idat(fp, chunk)), | ||||||
|  |             [("zip", (0, 0) + single_im.size, 0, rawmode)], | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     if info: |     if info: | ||||||
|  |  | ||||||
|  | @ -288,8 +288,10 @@ def _accept(prefix: bytes) -> bool: | ||||||
|     return prefix[:4] in PREFIXES |     return prefix[:4] in PREFIXES | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _limit_rational(val, max_val): | def _limit_rational( | ||||||
|     inv = abs(val) > 1 |     val: float | Fraction | IFDRational, max_val: int | ||||||
|  | ) -> tuple[float, float]: | ||||||
|  |     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 | ||||||
| 
 | 
 | ||||||
|  | @ -792,7 +794,9 @@ class ImageFileDirectory_v2(_IFDv2Base): | ||||||
|         return value + b"\0" |         return value + b"\0" | ||||||
| 
 | 
 | ||||||
|     @_register_loader(5, 8) |     @_register_loader(5, 8) | ||||||
|     def load_rational(self, data, legacy_api: bool = True): |     def load_rational( | ||||||
|  |         self, data: bytes, legacy_api: bool = True | ||||||
|  |     ) -> tuple[tuple[int, int] | IFDRational, ...]: | ||||||
|         vals = self._unpack(f"{len(data) // 4}L", data) |         vals = self._unpack(f"{len(data) // 4}L", data) | ||||||
| 
 | 
 | ||||||
|         def combine(a: int, b: int) -> tuple[int, int] | IFDRational: |         def combine(a: int, b: int) -> tuple[int, int] | IFDRational: | ||||||
|  | @ -801,7 +805,7 @@ class ImageFileDirectory_v2(_IFDv2Base): | ||||||
|         return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) |         return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) | ||||||
| 
 | 
 | ||||||
|     @_register_writer(5) |     @_register_writer(5) | ||||||
|     def write_rational(self, *values) -> bytes: |     def write_rational(self, *values: IFDRational) -> bytes: | ||||||
|         return b"".join( |         return b"".join( | ||||||
|             self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values |             self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values | ||||||
|         ) |         ) | ||||||
|  | @ -828,7 +832,7 @@ class ImageFileDirectory_v2(_IFDv2Base): | ||||||
|         return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) |         return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) | ||||||
| 
 | 
 | ||||||
|     @_register_writer(10) |     @_register_writer(10) | ||||||
|     def write_signed_rational(self, *values) -> bytes: |     def write_signed_rational(self, *values: IFDRational) -> bytes: | ||||||
|         return b"".join( |         return b"".join( | ||||||
|             self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) |             self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) | ||||||
|             for frac in values |             for frac in values | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user