Merge pull request #7750 from hugovk/type-hints-replace-io.BytesIO

Replace `io.BytesIO` in type hints
This commit is contained in:
Andrew Murray 2024-02-13 21:29:52 +11:00 committed by GitHub
commit 3374e91d5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 48 additions and 47 deletions

View File

@ -12,6 +12,9 @@ exclude_also =
except ImportError except ImportError
if TYPE_CHECKING: if TYPE_CHECKING:
@abc.abstractmethod @abc.abstractmethod
# Empty bodies in protocols or abstract methods
^\s*def [a-zA-Z0-9_]+\(.*\)(\s*->.*)?:\s*\.\.\.(\s*#.*)?$
^\s*\.\.\.(\s*#.*)?$
[run] [run]
omit = omit =

View File

@ -162,8 +162,6 @@ class TestImage:
pass pass
def test_pathlib(self, tmp_path: Path) -> None: def test_pathlib(self, tmp_path: Path) -> None:
from PIL.Image import Path
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im: with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
assert im.mode == "P" assert im.mode == "P"
assert im.size == (10, 10) assert im.size == (10, 10)

View File

@ -1,29 +1,16 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path, PurePath
import pytest import pytest
from PIL import _util from PIL import _util
def test_is_path() -> None: @pytest.mark.parametrize(
# Arrange "test_path", ["filename.ext", Path("filename.ext"), PurePath("filename.ext")]
fp = "filename.ext" )
def test_is_path(test_path) -> None:
# Act
it_is = _util.is_path(fp)
# Assert
assert it_is
def test_path_obj_is_path() -> None:
# Arrange
from pathlib import Path
test_path = Path("filename.ext")
# Act # Act
it_is = _util.is_path(test_path) it_is = _util.is_path(test_path)

View File

@ -1,5 +1,5 @@
Internal Reference Docs Internal Reference
======================= ==================
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -33,6 +33,14 @@ 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:: StrOrBytesPath
Typing alias.
.. py:class:: SupportsRead
An object that supports the read method.
.. py:data:: TypeGuard .. py:data:: TypeGuard
:value: typing.TypeGuard :value: typing.TypeGuard

View File

@ -3,7 +3,7 @@
File Handling in Pillow File Handling in Pillow
======================= =======================
When opening a file as an image, Pillow requires a filename, ``pathlib.Path`` When opening a file as an image, Pillow requires a filename, ``os.PathLike``
object, or a file-like object. Pillow uses the filename or ``Path`` to open a object, or a file-like object. Pillow uses the filename or ``Path`` to open a
file, so for the rest of this article, they will all be treated as a file-like file, so for the rest of this article, they will all be treated as a file-like
object. object.

View File

@ -27,11 +27,12 @@
""" """
from __future__ import annotations from __future__ import annotations
from io import BytesIO from typing import IO
from . import ImageFile, ImagePalette, UnidentifiedImageError from . import ImageFile, ImagePalette, UnidentifiedImageError
from ._binary import i16be as i16 from ._binary import i16be as i16
from ._binary import i32be as i32 from ._binary import i32be as i32
from ._typing import StrOrBytesPath
class GdImageFile(ImageFile.ImageFile): class GdImageFile(ImageFile.ImageFile):
@ -80,7 +81,7 @@ class GdImageFile(ImageFile.ImageFile):
] ]
def open(fp: BytesIO, mode: str = "r") -> GdImageFile: def open(fp: StrOrBytesPath | IO[bytes], mode: str = "r") -> GdImageFile:
""" """
Load texture from a GD image file. Load texture from a GD image file.

View File

@ -40,7 +40,6 @@ import tempfile
import warnings import warnings
from collections.abc import Callable, MutableMapping from collections.abc import Callable, MutableMapping
from enum import IntEnum from enum import IntEnum
from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import IO, TYPE_CHECKING, Any from typing import IO, TYPE_CHECKING, Any
@ -2383,7 +2382,7 @@ class Image:
implement the ``seek``, ``tell``, and ``write`` implement the ``seek``, ``tell``, and ``write``
methods, and be opened in binary mode. methods, and be opened in binary mode.
:param fp: A filename (string), pathlib.Path object or file object. :param fp: A filename (string), os.PathLike object or file object.
:param format: Optional format override. If omitted, the :param format: Optional format override. If omitted, the
format to use is determined from the filename extension. format to use is determined from the filename extension.
If a file object was used instead of a filename, this If a file object was used instead of a filename, this
@ -2398,11 +2397,8 @@ class Image:
filename: str | bytes = "" filename: str | bytes = ""
open_fp = False open_fp = False
if isinstance(fp, Path): if is_path(fp):
filename = str(fp) filename = os.path.realpath(os.fspath(fp))
open_fp = True
elif isinstance(fp, (str, bytes)):
filename = fp
open_fp = True open_fp = True
elif fp == sys.stdout: elif fp == sys.stdout:
try: try:
@ -3225,7 +3221,7 @@ def open(fp, mode="r", formats=None) -> Image:
:py:meth:`~PIL.Image.Image.load` method). See :py:meth:`~PIL.Image.Image.load` method). See
:py:func:`~PIL.Image.new`. See :ref:`file-handling`. :py:func:`~PIL.Image.new`. See :ref:`file-handling`.
:param fp: A filename (string), pathlib.Path object or a file object. :param fp: A filename (string), os.PathLike object or a file object.
The file object must implement ``file.read``, The file object must implement ``file.read``,
``file.seek``, and ``file.tell`` methods, ``file.seek``, and ``file.tell`` methods,
and be opened in binary mode. The file object will also seek to zero and be opened in binary mode. The file object will also seek to zero

View File

@ -33,10 +33,10 @@ import sys
import warnings import warnings
from enum import IntEnum from enum import IntEnum
from io import BytesIO from io import BytesIO
from pathlib import Path
from typing import BinaryIO from typing import BinaryIO
from . import Image from . import Image
from ._typing import StrOrBytesPath
from ._util import is_directory, is_path from ._util import is_directory, is_path
@ -193,7 +193,7 @@ class FreeTypeFont:
def __init__( def __init__(
self, self,
font: bytes | str | Path | BinaryIO | None = None, font: StrOrBytesPath | BinaryIO | None = None,
size: float = 10, size: float = 10,
index: int = 0, index: int = 0,
encoding: str = "", encoding: str = "",
@ -230,8 +230,7 @@ class FreeTypeFont:
) )
if is_path(font): if is_path(font):
if isinstance(font, Path): font = os.path.realpath(os.fspath(font))
font = str(font)
if sys.platform == "win32": if sys.platform == "win32":
font_bytes_path = font if isinstance(font, bytes) else font.encode() font_bytes_path = font if isinstance(font, bytes) else font.encode()
try: try:

View File

@ -14,17 +14,16 @@
# #
from __future__ import annotations from __future__ import annotations
from io import BytesIO
from . import Image, ImageFile from . import Image, ImageFile
from ._binary import i8 from ._binary import i8
from ._typing import SupportsRead
# #
# Bitstream parser # Bitstream parser
class BitStream: class BitStream:
def __init__(self, fp: BytesIO) -> None: def __init__(self, fp: SupportsRead[bytes]) -> None:
self.fp = fp self.fp = fp
self.bits = 0 self.bits = 0
self.bitbuffer = 0 self.bitbuffer = 0

View File

@ -1,7 +1,8 @@
from __future__ import annotations from __future__ import annotations
import os
import sys import sys
from typing import Sequence, Union from typing import Protocol, Sequence, TypeVar, Union
if sys.version_info >= (3, 10): if sys.version_info >= (3, 10):
from typing import TypeGuard from typing import TypeGuard
@ -19,4 +20,14 @@ else:
Coords = Union[Sequence[float], Sequence[Sequence[float]]] Coords = Union[Sequence[float], Sequence[Sequence[float]]]
__all__ = ["TypeGuard"] _T_co = TypeVar("_T_co", covariant=True)
class SupportsRead(Protocol[_T_co]):
def read(self, __length: int = ...) -> _T_co: ...
StrOrBytesPath = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]
__all__ = ["TypeGuard", "StrOrBytesPath", "SupportsRead"]

View File

@ -1,17 +1,16 @@
from __future__ import annotations from __future__ import annotations
import os import os
from pathlib import Path
from typing import Any, NoReturn 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)) return isinstance(f, (bytes, str, os.PathLike))
def is_directory(f: Any) -> TypeGuard[bytes | str | Path]: def is_directory(f: Any) -> TypeGuard[StrOrBytesPath]:
"""Checks if an object is a string, and that it points to a directory.""" """Checks if an object is a string, and that it points to a directory."""
return is_path(f) and os.path.isdir(f) return is_path(f) and os.path.isdir(f)