From c422cb5ba8da86672e6496d73798d0ae484b73c6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:32:19 +0200 Subject: [PATCH] Replace io.BytesIO in type hints --- src/PIL/GdImageFile.py | 7 +++++-- src/PIL/MpegImagePlugin.py | 5 ++--- src/PIL/PpmImagePlugin.py | 7 +++++-- src/PIL/SgiImagePlugin.py | 7 +++++-- src/PIL/TgaImagePlugin.py | 7 +++++-- src/PIL/XbmImagePlugin.py | 7 +++++-- src/PIL/_typing.py | 16 +++++++++++++++- src/PIL/_util.py | 4 ++-- 8 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 7bb4736af13..315ac6d6c1a 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -27,11 +27,12 @@ class is not registered for use with :py:func:`PIL.Image.open()`. To open a """ from __future__ import annotations -from io import BytesIO +from typing import IO from . import ImageFile, ImagePalette, UnidentifiedImageError from ._binary import i16be as i16 from ._binary import i32be as i32 +from ._typing import FileDescriptor, StrOrBytesPath class GdImageFile(ImageFile.ImageFile): @@ -80,7 +81,9 @@ def _open(self) -> None: ] -def open(fp: BytesIO, mode: str = "r") -> GdImageFile: +def open( + fp: StrOrBytesPath | FileDescriptor | IO[bytes], mode: str = "r" +) -> GdImageFile: """ Load texture from a GD image file. diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py index b9e9243e59f..2dab9e86b68 100644 --- a/src/PIL/MpegImagePlugin.py +++ b/src/PIL/MpegImagePlugin.py @@ -14,17 +14,16 @@ # from __future__ import annotations -from io import BytesIO - from . import Image, ImageFile from ._binary import i8 +from ._typing import SupportsRead # # Bitstream parser class BitStream: - def __init__(self, fp: BytesIO) -> None: + def __init__(self, fp: SupportsRead) -> None: self.fp = fp self.bits = 0 self.bitbuffer = 0 diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 3e45ba95c84..f1d45fca990 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -16,12 +16,13 @@ from __future__ import annotations import math -from io import BytesIO +from typing import IO from . import Image, ImageFile from ._binary import i16be as i16 from ._binary import o8 from ._binary import o32le as o32 +from ._typing import FileDescriptor, StrOrBytesPath # # -------------------------------------------------------------------- @@ -324,7 +325,9 @@ def decode(self, buffer: bytes) -> tuple[int, int]: # -------------------------------------------------------------------- -def _save(im: Image.Image, fp: BytesIO, filename: str) -> None: +def _save( + im: Image.Image, fp: StrOrBytesPath | FileDescriptor | IO[bytes], filename: str +) -> None: if im.mode == "1": rawmode, head = "1;I", b"P4" elif im.mode == "L": diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index ccf661ff1f3..ba3570eaaf4 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -24,11 +24,12 @@ import os import struct -from io import BytesIO +from typing import IO from . import Image, ImageFile from ._binary import i16be as i16 from ._binary import o8 +from ._typing import FileDescriptor, StrOrBytesPath def _accept(prefix: bytes) -> bool: @@ -125,7 +126,9 @@ def _open(self) -> None: ] -def _save(im: Image.Image, fp: BytesIO, filename: str) -> None: +def _save( + im: Image.Image, fp: StrOrBytesPath | FileDescriptor | IO[bytes], filename: str +) -> None: if im.mode not in {"RGB", "RGBA", "L"}: msg = "Unsupported SGI image mode" raise ValueError(msg) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 584932d2c7d..9bf20d14bba 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -18,12 +18,13 @@ from __future__ import annotations import warnings -from io import BytesIO +from typing import IO from . import Image, ImageFile, ImagePalette from ._binary import i16le as i16 from ._binary import o8 from ._binary import o16le as o16 +from ._typing import FileDescriptor, StrOrBytesPath # # -------------------------------------------------------------------- @@ -175,7 +176,9 @@ def load_end(self) -> None: } -def _save(im: Image.Image, fp: BytesIO, filename: str) -> None: +def _save( + im: Image.Image, fp: StrOrBytesPath | FileDescriptor | IO[bytes], filename: str +) -> None: try: rawmode, bits, colormaptype, imagetype = SAVE[im.mode] except KeyError as e: diff --git a/src/PIL/XbmImagePlugin.py b/src/PIL/XbmImagePlugin.py index 0291e2858ac..98da0d0bcf3 100644 --- a/src/PIL/XbmImagePlugin.py +++ b/src/PIL/XbmImagePlugin.py @@ -21,9 +21,10 @@ from __future__ import annotations import re -from io import BytesIO +from typing import IO from . import Image, ImageFile +from ._typing import FileDescriptor, StrOrBytesPath # XBM header xbm_head = re.compile( @@ -70,7 +71,9 @@ def _open(self) -> None: self.tile = [("xbm", (0, 0) + self.size, m.end(), None)] -def _save(im: Image.Image, fp: BytesIO, filename: str) -> None: +def _save( + im: Image.Image, fp: StrOrBytesPath | FileDescriptor | IO[bytes], filename: str +) -> None: if im.mode != "1": msg = f"cannot write mode {im.mode} as XBM" raise OSError(msg) diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index 608b2b41fa8..144e9dcf1d3 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -1,6 +1,8 @@ from __future__ import annotations +import os import sys +from typing import Protocol, TypeVar if sys.version_info >= (3, 10): from typing import TypeGuard @@ -15,4 +17,16 @@ def __class_getitem__(cls, item: Any) -> type[bool]: return bool -__all__ = ["TypeGuard"] +_T_co = TypeVar("_T_co", covariant=True) + + +class SupportsRead(Protocol[_T_co]): + def read(self, __length: int = ...) -> _T_co: + ... + + +FileDescriptor = int +StrOrBytesPath = str | bytes | os.PathLike[str] | os.PathLike[bytes] + + +__all__ = ["FileDescriptor", "TypeGuard", "StrOrBytesPath", "SupportsRead"] diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 13f369cca1d..4ecdc4bd307 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -4,10 +4,10 @@ from pathlib import Path from typing import Any, NoReturn -from ._typing import TypeGuard +from ._typing import StrOrBytesPath, TypeGuard -def is_path(f: Any) -> TypeGuard[bytes | str | Path]: +def is_path(f: Any) -> TypeGuard[StrOrBytesPath]: return isinstance(f, (bytes, str, Path))