Merge pull request #7642 from nulano/types-util

Add type hints to _util
This commit is contained in:
Andrew Murray 2024-01-01 21:27:57 +11:00 committed by GitHub
commit 57096f55cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 70 additions and 16 deletions

View File

@ -66,7 +66,7 @@ def test_deferred_error():
# Arrange # Arrange
# Act # Act
thing = _util.DeferredError(ValueError("Some error text")) thing = _util.DeferredError.new(ValueError("Some error text"))
# Assert # Assert
with pytest.raises(ValueError): with pytest.raises(ValueError):

View File

@ -25,6 +25,19 @@ Internal Modules
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
:mod:`~PIL._typing` Module
--------------------------
.. module:: PIL._typing
Provides a convenient way to import type hints that are not available
on some Python versions.
.. py:data:: TypeGuard
:value: typing.TypeGuard
See :py:obj:`typing.TypeGuard`.
:mod:`~PIL._util` Module :mod:`~PIL._util` Module
------------------------ ------------------------

View File

@ -65,6 +65,9 @@ tests = [
"pytest-cov", "pytest-cov",
"pytest-timeout", "pytest-timeout",
] ]
typing = [
'typing-extensions; python_version < "3.10"',
]
xmp = [ xmp = [
"defusedxml", "defusedxml",
] ]
@ -143,9 +146,6 @@ exclude = [
'^src/PIL/DdsImagePlugin.py$', '^src/PIL/DdsImagePlugin.py$',
'^src/PIL/FpxImagePlugin.py$', '^src/PIL/FpxImagePlugin.py$',
'^src/PIL/Image.py$', '^src/PIL/Image.py$',
'^src/PIL/ImageCms.py$',
'^src/PIL/ImageFile.py$',
'^src/PIL/ImageFont.py$',
'^src/PIL/ImageMath.py$', '^src/PIL/ImageMath.py$',
'^src/PIL/ImageMorph.py$', '^src/PIL/ImageMorph.py$',
'^src/PIL/ImageQt.py$', '^src/PIL/ImageQt.py$',

View File

@ -92,7 +92,7 @@ try:
raise ImportError(msg) raise ImportError(msg)
except ImportError as v: except ImportError as v:
core = DeferredError(ImportError("The _imaging C module is not installed.")) core = DeferredError.new(ImportError("The _imaging C module is not installed."))
# Explanations for ways that we know we might have an import error # Explanations for ways that we know we might have an import error
if str(v).startswith("Module use of python"): if str(v).startswith("Module use of python"):
# The _imaging C module is present, but not compiled for # The _imaging C module is present, but not compiled for

View File

@ -28,7 +28,7 @@ except ImportError as ex:
# anything in core. # anything in core.
from ._util import DeferredError from ._util import DeferredError
_imagingcms = DeferredError(ex) _imagingcms = DeferredError.new(ex)
DESCRIPTION = """ DESCRIPTION = """
pyCMS pyCMS

View File

@ -32,7 +32,7 @@ import io
import itertools import itertools
import struct import struct
import sys import sys
from typing import NamedTuple from typing import Any, NamedTuple
from . import Image from . import Image
from ._deprecate import deprecate from ._deprecate import deprecate
@ -94,7 +94,7 @@ class _Tile(NamedTuple):
encoder_name: str encoder_name: str
extents: tuple[int, int, int, int] extents: tuple[int, int, int, int]
offset: int offset: int
args: tuple | str | None args: tuple[Any, ...] | str | None
# #

View File

@ -34,7 +34,7 @@ import warnings
from enum import IntEnum from enum import IntEnum
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import IO from typing import BinaryIO
from . import Image from . import Image
from ._util import is_directory, is_path from ._util import is_directory, is_path
@ -53,7 +53,7 @@ try:
except ImportError as ex: except ImportError as ex:
from ._util import DeferredError from ._util import DeferredError
core = DeferredError(ex) core = DeferredError.new(ex)
def _string_length_check(text): def _string_length_check(text):
@ -193,7 +193,7 @@ class FreeTypeFont:
def __init__( def __init__(
self, self,
font: bytes | str | Path | IO | None = None, font: bytes | str | Path | BinaryIO | None = None,
size: float = 10, size: float = 10,
index: int = 0, index: int = 0,
encoding: str = "", encoding: str = "",

View File

@ -43,7 +43,7 @@ except ImportError as ex:
# anything in core. # anything in core.
from ._util import DeferredError from ._util import DeferredError
FFI = ffi = DeferredError(ex) FFI = ffi = DeferredError.new(ex)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

5
src/PIL/_imagingcms.pyi Normal file
View File

@ -0,0 +1,5 @@
from __future__ import annotations
from typing import Any
def __getattr__(name: str) -> Any: ...

5
src/PIL/_imagingft.pyi Normal file
View File

@ -0,0 +1,5 @@
from __future__ import annotations
from typing import Any
def __getattr__(name: str) -> Any: ...

18
src/PIL/_typing.py Normal file
View File

@ -0,0 +1,18 @@
from __future__ import annotations
import sys
if sys.version_info >= (3, 10):
from typing import TypeGuard
else:
try:
from typing_extensions import TypeGuard
except ImportError:
from typing import Any
class TypeGuard: # type: ignore[no-redef]
def __class_getitem__(cls, item: Any) -> type[bool]:
return bool
__all__ = ["TypeGuard"]

View File

@ -2,20 +2,31 @@ from __future__ import annotations
import os import os
from pathlib import Path from pathlib import Path
from typing import Any, NoReturn
from ._typing import TypeGuard
def is_path(f): def is_path(f: Any) -> TypeGuard[bytes | str | Path]:
return isinstance(f, (bytes, str, Path)) return isinstance(f, (bytes, str, Path))
def is_directory(f): def is_directory(f: Any) -> TypeGuard[bytes | str | Path]:
"""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)
class DeferredError: class DeferredError:
def __init__(self, ex): def __init__(self, ex: BaseException):
self.ex = ex self.ex = ex
def __getattr__(self, elt): def __getattr__(self, elt: str) -> NoReturn:
raise self.ex raise self.ex
@staticmethod
def new(ex: BaseException) -> Any:
"""
Creates an object that raises the wrapped exception ``ex`` when used,
and casts it to :py:obj:`~typing.Any` type.
"""
return DeferredError(ex)

View File

@ -35,5 +35,7 @@ skip_install = true
deps = deps =
mypy==1.7.1 mypy==1.7.1
numpy numpy
extras =
typing
commands = commands =
mypy src {posargs} mypy src {posargs}