mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-12 10:16:17 +03:00
Added type hints
This commit is contained in:
parent
6782a07b8e
commit
19a6edeecc
|
@ -141,16 +141,6 @@ warn_redundant_casts = true
|
|||
warn_unreachable = true
|
||||
warn_unused_ignores = true
|
||||
exclude = [
|
||||
'^src/PIL/_tkinter_finder.py$',
|
||||
'^src/PIL/DdsImagePlugin.py$',
|
||||
'^src/PIL/FpxImagePlugin.py$',
|
||||
'^src/PIL/Image.py$',
|
||||
'^src/PIL/ImageQt.py$',
|
||||
'^src/PIL/ImImagePlugin.py$',
|
||||
'^src/PIL/MicImagePlugin.py$',
|
||||
'^src/PIL/PdfParser.py$',
|
||||
'^src/PIL/PyAccess.py$',
|
||||
'^src/PIL/TiffImagePlugin.py$',
|
||||
'^src/PIL/TiffTags.py$',
|
||||
'^src/PIL/WebPImagePlugin.py$',
|
||||
]
|
||||
|
|
|
@ -270,13 +270,17 @@ class D3DFMT(IntEnum):
|
|||
# Backward compatibility layer
|
||||
module = sys.modules[__name__]
|
||||
for item in DDSD:
|
||||
assert item.name is not None
|
||||
setattr(module, "DDSD_" + item.name, item.value)
|
||||
for item in DDSCAPS:
|
||||
setattr(module, "DDSCAPS_" + item.name, item.value)
|
||||
for item in DDSCAPS2:
|
||||
setattr(module, "DDSCAPS2_" + item.name, item.value)
|
||||
for item in DDPF:
|
||||
setattr(module, "DDPF_" + item.name, item.value)
|
||||
for item1 in DDSCAPS:
|
||||
assert item1.name is not None
|
||||
setattr(module, "DDSCAPS_" + item1.name, item1.value)
|
||||
for item2 in DDSCAPS2:
|
||||
assert item2.name is not None
|
||||
setattr(module, "DDSCAPS2_" + item2.name, item2.value)
|
||||
for item3 in DDPF:
|
||||
assert item3.name is not None
|
||||
setattr(module, "DDPF_" + item3.name, item3.value)
|
||||
|
||||
DDS_FOURCC = DDPF.FOURCC
|
||||
DDS_RGB = DDPF.RGB
|
||||
|
|
|
@ -93,8 +93,8 @@ for i in ["16", "16L", "16B"]:
|
|||
for i in ["32S"]:
|
||||
OPEN[f"L {i} image"] = ("I", f"I;{i}")
|
||||
OPEN[f"L*{i} image"] = ("I", f"I;{i}")
|
||||
for i in range(2, 33):
|
||||
OPEN[f"L*{i} image"] = ("F", f"F;{i}")
|
||||
for j in range(2, 33):
|
||||
OPEN[f"L*{j} image"] = ("F", f"F;{j}")
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import atexit
|
||||
import builtins
|
||||
import io
|
||||
|
@ -40,11 +41,8 @@ import warnings
|
|||
from collections.abc import Callable, MutableMapping
|
||||
from enum import IntEnum
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from defusedxml import ElementTree
|
||||
except ImportError:
|
||||
ElementTree = None
|
||||
from types import ModuleType
|
||||
from typing import IO, TYPE_CHECKING, Any
|
||||
|
||||
# VERSION was removed in Pillow 6.0.0.
|
||||
# PILLOW_VERSION was removed in Pillow 9.0.0.
|
||||
|
@ -60,6 +58,12 @@ from . import (
|
|||
from ._binary import i32le, o32be, o32le
|
||||
from ._util import DeferredError, is_path
|
||||
|
||||
ElementTree: ModuleType | None
|
||||
try:
|
||||
from defusedxml import ElementTree
|
||||
except ImportError:
|
||||
ElementTree = None
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -110,6 +114,7 @@ except ImportError as v:
|
|||
|
||||
|
||||
USE_CFFI_ACCESS = False
|
||||
cffi: ModuleType | None
|
||||
try:
|
||||
import cffi
|
||||
except ImportError:
|
||||
|
@ -211,14 +216,22 @@ if hasattr(core, "DEFAULT_STRATEGY"):
|
|||
# --------------------------------------------------------------------
|
||||
# Registries
|
||||
|
||||
ID = []
|
||||
OPEN = {}
|
||||
MIME = {}
|
||||
SAVE = {}
|
||||
SAVE_ALL = {}
|
||||
EXTENSION = {}
|
||||
DECODERS = {}
|
||||
ENCODERS = {}
|
||||
if TYPE_CHECKING:
|
||||
from . import ImageFile # pragma: no cover
|
||||
ID: list[str] = []
|
||||
OPEN: dict[
|
||||
str,
|
||||
tuple[
|
||||
Callable[[IO[bytes], str | bytes], ImageFile.ImageFile],
|
||||
Callable[[bytes], bool] | None,
|
||||
],
|
||||
] = {}
|
||||
MIME: dict[str, str] = {}
|
||||
SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
||||
SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
||||
EXTENSION: dict[str, str] = {}
|
||||
DECODERS: dict[str, object] = {}
|
||||
ENCODERS: dict[str, object] = {}
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Modes
|
||||
|
@ -2383,12 +2396,12 @@ class Image:
|
|||
may have been created, and may contain partial data.
|
||||
"""
|
||||
|
||||
filename = ""
|
||||
filename: str | bytes = ""
|
||||
open_fp = False
|
||||
if isinstance(fp, Path):
|
||||
filename = str(fp)
|
||||
open_fp = True
|
||||
elif is_path(fp):
|
||||
elif isinstance(fp, (str, bytes)):
|
||||
filename = fp
|
||||
open_fp = True
|
||||
elif fp == sys.stdout:
|
||||
|
@ -2398,7 +2411,7 @@ class Image:
|
|||
pass
|
||||
if not filename and hasattr(fp, "name") and is_path(fp.name):
|
||||
# only set the name for metadata purposes
|
||||
filename = fp.name
|
||||
filename = os.path.realpath(os.fspath(fp.name))
|
||||
|
||||
# may mutate self!
|
||||
self._ensure_mutable()
|
||||
|
@ -2409,7 +2422,8 @@ class Image:
|
|||
|
||||
preinit()
|
||||
|
||||
ext = os.path.splitext(filename)[1].lower()
|
||||
filename_ext = os.path.splitext(filename)[1].lower()
|
||||
ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext
|
||||
|
||||
if not format:
|
||||
if ext not in EXTENSION:
|
||||
|
@ -2451,7 +2465,7 @@ class Image:
|
|||
if open_fp:
|
||||
fp.close()
|
||||
|
||||
def seek(self, frame) -> Image:
|
||||
def seek(self, frame) -> None:
|
||||
"""
|
||||
Seeks to the given frame in this sequence file. If you seek
|
||||
beyond the end of the sequence, the method raises an
|
||||
|
@ -2511,10 +2525,9 @@ class Image:
|
|||
|
||||
self.load()
|
||||
if self.im.bands == 1:
|
||||
ims = [self.copy()]
|
||||
return (self.copy(),)
|
||||
else:
|
||||
ims = map(self._new, self.im.split())
|
||||
return tuple(ims)
|
||||
return tuple(map(self._new, self.im.split()))
|
||||
|
||||
def getchannel(self, channel):
|
||||
"""
|
||||
|
@ -2871,7 +2884,14 @@ class ImageTransformHandler:
|
|||
(for use with :py:meth:`~PIL.Image.Image.transform`)
|
||||
"""
|
||||
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def transform(
|
||||
self,
|
||||
size: tuple[int, int],
|
||||
image: Image,
|
||||
**options: dict[str, str | int | tuple[int, ...] | list[int]],
|
||||
) -> Image:
|
||||
pass # pragma: no cover
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
@ -3243,11 +3263,9 @@ def open(fp, mode="r", formats=None) -> Image:
|
|||
raise TypeError(msg)
|
||||
|
||||
exclusive_fp = False
|
||||
filename = ""
|
||||
if isinstance(fp, Path):
|
||||
filename = str(fp.resolve())
|
||||
elif is_path(fp):
|
||||
filename = fp
|
||||
filename: str | bytes = ""
|
||||
if is_path(fp):
|
||||
filename = os.path.realpath(os.fspath(fp))
|
||||
|
||||
if filename:
|
||||
fp = builtins.open(filename, "rb")
|
||||
|
@ -3421,7 +3439,11 @@ def merge(mode, bands):
|
|||
# Plugin registry
|
||||
|
||||
|
||||
def register_open(id, factory, accept=None) -> None:
|
||||
def register_open(
|
||||
id,
|
||||
factory: Callable[[IO[bytes], str | bytes], ImageFile.ImageFile],
|
||||
accept: Callable[[bytes], bool] | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Register an image file plugin. This function should not be used
|
||||
in application code.
|
||||
|
@ -3631,7 +3653,13 @@ _apply_env_variables()
|
|||
atexit.register(core.clear_cache)
|
||||
|
||||
|
||||
class Exif(MutableMapping):
|
||||
if TYPE_CHECKING:
|
||||
_ExifBase = MutableMapping[int, Any] # pragma: no cover
|
||||
else:
|
||||
_ExifBase = MutableMapping
|
||||
|
||||
|
||||
class Exif(_ExifBase):
|
||||
"""
|
||||
This class provides read and write access to EXIF image data::
|
||||
|
||||
|
|
|
@ -19,19 +19,26 @@ from __future__ import annotations
|
|||
|
||||
import sys
|
||||
from io import BytesIO
|
||||
from typing import Callable
|
||||
|
||||
from . import Image
|
||||
from ._util import is_path
|
||||
|
||||
qt_version: str | None
|
||||
qt_versions = [
|
||||
["6", "PyQt6"],
|
||||
["side6", "PySide6"],
|
||||
]
|
||||
|
||||
# If a version has already been imported, attempt it first
|
||||
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
|
||||
for qt_version, qt_module in qt_versions:
|
||||
qt_versions.sort(key=lambda version: version[1] in sys.modules, reverse=True)
|
||||
for version, qt_module in qt_versions:
|
||||
try:
|
||||
QBuffer: type
|
||||
QIODevice: type
|
||||
QImage: type
|
||||
QPixmap: type
|
||||
qRgba: Callable[[int, int, int, int], int]
|
||||
if qt_module == "PyQt6":
|
||||
from PyQt6.QtCore import QBuffer, QIODevice
|
||||
from PyQt6.QtGui import QImage, QPixmap, qRgba
|
||||
|
@ -41,6 +48,7 @@ for qt_version, qt_module in qt_versions:
|
|||
except (ImportError, RuntimeError):
|
||||
continue
|
||||
qt_is_installed = True
|
||||
qt_version = version
|
||||
break
|
||||
else:
|
||||
qt_is_installed = False
|
||||
|
|
|
@ -8,6 +8,7 @@ import os
|
|||
import re
|
||||
import time
|
||||
import zlib
|
||||
from typing import TYPE_CHECKING, Any, List, Union
|
||||
|
||||
|
||||
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
|
||||
|
@ -239,12 +240,18 @@ class PdfName:
|
|||
return bytes(result)
|
||||
|
||||
|
||||
class PdfArray(list):
|
||||
class PdfArray(List[Any]):
|
||||
def __bytes__(self):
|
||||
return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]"
|
||||
|
||||
|
||||
class PdfDict(collections.UserDict):
|
||||
if TYPE_CHECKING:
|
||||
_DictBase = collections.UserDict[Union[str, bytes], Any] # pragma: no cover
|
||||
else:
|
||||
_DictBase = collections.UserDict
|
||||
|
||||
|
||||
class PdfDict(_DictBase):
|
||||
def __setattr__(self, key, value):
|
||||
if key == "data":
|
||||
collections.UserDict.__setattr__(self, key, value)
|
||||
|
|
|
@ -25,6 +25,7 @@ import sys
|
|||
|
||||
from ._deprecate import deprecate
|
||||
|
||||
FFI: type
|
||||
try:
|
||||
from cffi import FFI
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import warnings
|
|||
from collections.abc import MutableMapping
|
||||
from fractions import Fraction
|
||||
from numbers import Number, Rational
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
|
||||
from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags
|
||||
from ._binary import i16be as i16
|
||||
|
@ -306,6 +307,13 @@ _load_dispatch = {}
|
|||
_write_dispatch = {}
|
||||
|
||||
|
||||
def _delegate(op):
|
||||
def delegate(self, *args):
|
||||
return getattr(self._val, op)(*args)
|
||||
|
||||
return delegate
|
||||
|
||||
|
||||
class IFDRational(Rational):
|
||||
"""Implements a rational class where 0/0 is a legal value to match
|
||||
the in the wild use of exif rationals.
|
||||
|
@ -391,12 +399,6 @@ class IFDRational(Rational):
|
|||
self._numerator = _numerator
|
||||
self._denominator = _denominator
|
||||
|
||||
def _delegate(op):
|
||||
def delegate(self, *args):
|
||||
return getattr(self._val, op)(*args)
|
||||
|
||||
return delegate
|
||||
|
||||
""" a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul',
|
||||
'truediv', 'rtruediv', 'floordiv', 'rfloordiv',
|
||||
'mod','rmod', 'pow','rpow', 'pos', 'neg',
|
||||
|
@ -436,7 +438,50 @@ class IFDRational(Rational):
|
|||
__int__ = _delegate("__int__")
|
||||
|
||||
|
||||
class ImageFileDirectory_v2(MutableMapping):
|
||||
def _register_loader(idx, size):
|
||||
def decorator(func):
|
||||
from .TiffTags import TYPES
|
||||
|
||||
if func.__name__.startswith("load_"):
|
||||
TYPES[idx] = func.__name__[5:].replace("_", " ")
|
||||
_load_dispatch[idx] = size, func # noqa: F821
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def _register_writer(idx):
|
||||
def decorator(func):
|
||||
_write_dispatch[idx] = func # noqa: F821
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def _register_basic(idx_fmt_name):
|
||||
from .TiffTags import TYPES
|
||||
|
||||
idx, fmt, name = idx_fmt_name
|
||||
TYPES[idx] = name
|
||||
size = struct.calcsize("=" + fmt)
|
||||
_load_dispatch[idx] = ( # noqa: F821
|
||||
size,
|
||||
lambda self, data, legacy_api=True: (
|
||||
self._unpack(f"{len(data) // size}{fmt}", data)
|
||||
),
|
||||
)
|
||||
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
|
||||
b"".join(self._pack(fmt, value) for value in values)
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_IFDv2Base = MutableMapping[int, Any] # pragma: no cover
|
||||
else:
|
||||
_IFDv2Base = MutableMapping
|
||||
|
||||
|
||||
class ImageFileDirectory_v2(_IFDv2Base):
|
||||
"""This class represents a TIFF tag directory. To speed things up, we
|
||||
don't decode tags unless they're asked for.
|
||||
|
||||
|
@ -497,6 +542,9 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
|
||||
"""
|
||||
|
||||
_load_dispatch: dict[int, Callable[[ImageFileDirectory_v2, bytes, bool], Any]] = {}
|
||||
_write_dispatch: dict[int, Callable[..., Any]] = {}
|
||||
|
||||
def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
|
||||
"""Initialize an ImageFileDirectory.
|
||||
|
||||
|
@ -531,7 +579,10 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
|
||||
prefix = property(lambda self: self._prefix)
|
||||
offset = property(lambda self: self._offset)
|
||||
legacy_api = property(lambda self: self._legacy_api)
|
||||
|
||||
@property
|
||||
def legacy_api(self):
|
||||
return self._legacy_api
|
||||
|
||||
@legacy_api.setter
|
||||
def legacy_api(self, value):
|
||||
|
@ -674,40 +725,6 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
def _pack(self, fmt, *values):
|
||||
return struct.pack(self._endian + fmt, *values)
|
||||
|
||||
def _register_loader(idx, size):
|
||||
def decorator(func):
|
||||
from .TiffTags import TYPES
|
||||
|
||||
if func.__name__.startswith("load_"):
|
||||
TYPES[idx] = func.__name__[5:].replace("_", " ")
|
||||
_load_dispatch[idx] = size, func # noqa: F821
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def _register_writer(idx):
|
||||
def decorator(func):
|
||||
_write_dispatch[idx] = func # noqa: F821
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def _register_basic(idx_fmt_name):
|
||||
from .TiffTags import TYPES
|
||||
|
||||
idx, fmt, name = idx_fmt_name
|
||||
TYPES[idx] = name
|
||||
size = struct.calcsize("=" + fmt)
|
||||
_load_dispatch[idx] = ( # noqa: F821
|
||||
size,
|
||||
lambda self, data, legacy_api=True: (
|
||||
self._unpack(f"{len(data) // size}{fmt}", data)
|
||||
),
|
||||
)
|
||||
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
|
||||
b"".join(self._pack(fmt, value) for value in values)
|
||||
)
|
||||
|
||||
list(
|
||||
map(
|
||||
_register_basic,
|
||||
|
@ -995,7 +1012,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
|
|||
tagdata = property(lambda self: self._tagdata)
|
||||
|
||||
# defined in ImageFileDirectory_v2
|
||||
tagtype: dict
|
||||
tagtype: dict[int, int]
|
||||
"""Dictionary of tag types"""
|
||||
|
||||
@classmethod
|
||||
|
@ -1835,11 +1852,11 @@ def _save(im, fp, filename):
|
|||
tags = list(atts.items())
|
||||
tags.sort()
|
||||
a = (rawmode, compression, _fp, filename, tags, types)
|
||||
e = Image._getencoder(im.mode, "libtiff", a, encoderconfig)
|
||||
e.setimage(im.im, (0, 0) + im.size)
|
||||
encoder = Image._getencoder(im.mode, "libtiff", a, encoderconfig)
|
||||
encoder.setimage(im.im, (0, 0) + im.size)
|
||||
while True:
|
||||
# undone, change to self.decodermaxblock:
|
||||
errcode, data = e.encode(16 * 1024)[1:]
|
||||
errcode, data = encoder.encode(16 * 1024)[1:]
|
||||
if not _fp:
|
||||
fp.write(data)
|
||||
if errcode:
|
||||
|
|
|
@ -22,7 +22,7 @@ from collections import namedtuple
|
|||
|
||||
|
||||
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
|
||||
__slots__ = []
|
||||
__slots__: list[str] = []
|
||||
|
||||
def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None):
|
||||
return super().__new__(cls, value, name, type, length, enum or {})
|
||||
|
@ -437,7 +437,7 @@ _populate()
|
|||
##
|
||||
# Map type numbers to type names -- defined in ImageFileDirectory.
|
||||
|
||||
TYPES = {}
|
||||
TYPES: dict[int, str] = {}
|
||||
|
||||
#
|
||||
# These tags are handled by default in libtiff, without
|
||||
|
|
5
src/PIL/_imaging.pyi
Normal file
5
src/PIL/_imaging.pyi
Normal file
|
@ -0,0 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
def __getattr__(name: str) -> Any: ...
|
|
@ -5,7 +5,8 @@ from __future__ import annotations
|
|||
|
||||
import sys
|
||||
import tkinter
|
||||
from tkinter import _tkinter as tk
|
||||
|
||||
tk = getattr(tkinter, "_tkinter")
|
||||
|
||||
try:
|
||||
if hasattr(sys, "pypy_find_executable"):
|
||||
|
|
5
src/PIL/_webp.pyi
Normal file
5
src/PIL/_webp.pyi
Normal file
|
@ -0,0 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
def __getattr__(name: str) -> Any: ...
|
Loading…
Reference in New Issue
Block a user