Skip to content

Commit f7443a7

Browse files
Add a return type for PIL.Image.load (#9466)
Co-authored-by: Alex Waygood <[email protected]>
1 parent c4b6d63 commit f7443a7

16 files changed

+50
-21
lines changed

stubs/Pillow/PIL/EpsImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
split: Any
@@ -24,5 +25,5 @@ class EpsImageFile(ImageFile):
2425
im: Any
2526
mode: Any
2627
tile: Any
27-
def load(self, scale: int = 1, transparency: bool = False) -> None: ...
28+
def load(self, scale: int = 1, transparency: bool = False) -> _PixelAccessor: ...
2829
def load_seek(self, *args, **kwargs) -> None: ...

stubs/Pillow/PIL/FpxImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from _typeshed import Incomplete
22
from typing import Any, ClassVar
33
from typing_extensions import Literal, TypeAlias
44

5+
from ._imaging import _PixelAccessor
56
from .ImageFile import ImageFile
67

78
_OleFileIO: TypeAlias = Any # olefile.OleFileIO
@@ -19,4 +20,4 @@ class FpxImageFile(ImageFile):
1920
jpeg: dict[int, Incomplete]
2021
tile_prefix: Incomplete
2122
stream: list[str]
22-
def load(self): ...
23+
def load(self) -> _PixelAccessor: ...

stubs/Pillow/PIL/GbrImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
class GbrImageFile(ImageFile):
78
format: ClassVar[Literal["GBR"]]
89
format_description: ClassVar[str]
910
im: Any
10-
def load(self) -> None: ...
11+
def load(self) -> _PixelAccessor: ...

stubs/Pillow/PIL/IcnsImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from _typeshed import Incomplete
22
from typing import Any, ClassVar
33
from typing_extensions import Literal
44

5+
from ._imaging import _PixelAccessor
56
from .ImageFile import ImageFile
67

78
enable_jpeg2k: Any
@@ -33,4 +34,4 @@ class IcnsImageFile(ImageFile):
3334
best_size: Any
3435
im: Any
3536
mode: Any
36-
def load(self) -> None: ...
37+
def load(self) -> _PixelAccessor: ...

stubs/Pillow/PIL/IcoImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
class IcoFile:
@@ -22,5 +23,5 @@ class IcoImageFile(ImageFile):
2223
def size(self, value) -> None: ...
2324
im: Any
2425
mode: Any
25-
def load(self) -> None: ...
26+
def load(self) -> _PixelAccessor: ...
2627
def load_seek(self) -> None: ...

stubs/Pillow/PIL/Image.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ from ._imaging import (
1313
FIXED as FIXED,
1414
HUFFMAN_ONLY as HUFFMAN_ONLY,
1515
RLE as RLE,
16+
_PixelAccessor,
1617
)
1718
from .ImageFilter import Filter
1819
from .ImagePalette import ImagePalette
@@ -180,7 +181,7 @@ class Image:
180181
def tobytes(self, encoder_name: str = "raw", *args) -> bytes: ...
181182
def tobitmap(self, name: str = "image") -> bytes: ...
182183
def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None: ...
183-
def load(self) -> None: ...
184+
def load(self) -> _PixelAccessor: ...
184185
def verify(self) -> None: ...
185186
def convert(
186187
self,

stubs/Pillow/PIL/ImageFile.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from _typeshed import Incomplete, Unused
22
from typing import Any, NoReturn
33
from typing_extensions import Self
44

5+
from ._imaging import _PixelAccessor
56
from .Image import Image
67

78
MAXBLOCK: int
@@ -24,12 +25,12 @@ class ImageFile(Image):
2425
def verify(self) -> None: ...
2526
map: Any
2627
im: Any
27-
def load(self): ...
28+
def load(self) -> _PixelAccessor: ...
2829
def load_prepare(self) -> None: ...
2930
def load_end(self) -> None: ...
3031

3132
class StubImageFile(ImageFile):
32-
def load(self) -> None: ...
33+
def load(self) -> _PixelAccessor: ...
3334

3435
class Parser:
3536
incremental: Incomplete | None

stubs/Pillow/PIL/IptcImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
COMPRESSION: Any
@@ -15,6 +16,6 @@ class IptcImageFile(ImageFile):
1516
def getint(self, key): ...
1617
def field(self): ...
1718
im: Any
18-
def load(self): ...
19+
def load(self) -> _PixelAccessor: ...
1920

2021
def getiptcinfo(im): ...
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
class Jpeg2KImageFile(ImageFile):
78
format: ClassVar[Literal["JPEG2000"]]
89
format_description: ClassVar[str]
910
reduce: Any
1011
tile: Any
11-
def load(self): ...
12+
def load(self) -> _PixelAccessor: ...

stubs/Pillow/PIL/PngImagePlugin.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ class PngImageFile(ImageFile):
102102
def load_read(self, read_bytes): ...
103103
png: Any
104104
im: Any
105-
pyaccess: Any
106105
def load_end(self) -> None: ...
107106
def getexif(self): ...
108107

stubs/Pillow/PIL/PyAccess.pyi

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
from logging import Logger
22
from typing import Any
33

4+
from PIL._imaging import _PixelAccessor
5+
46
ffi: Any
57
logger: Logger
68

7-
class PyAccess:
9+
class PyAccess(_PixelAccessor):
810
readonly: Any
911
image8: Any
1012
image32: Any
1113
image: Any
1214
def __init__(self, img, readonly: bool = False) -> None: ...
13-
def __setitem__(self, xy, color) -> None: ...
14-
def __getitem__(self, xy): ...
15-
putpixel: Any
16-
getpixel: Any
17-
def check_xy(self, xy): ...
15+
def __setitem__(self, xy: tuple[int, int], color) -> None: ...
16+
def __getitem__(self, xy: tuple[int, int]) -> Any: ...
17+
def putpixel(self, xy: tuple[int, int], color) -> None: ...
18+
def getpixel(self, xy: tuple[int, int]) -> Any: ...
19+
def check_xy(self, xy: tuple[int, int]): ...
1820

1921
class _PyAccess32_2(PyAccess):
2022
def get_pixel(self, x, y): ...

stubs/Pillow/PIL/TiffImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ from types import TracebackType
55
from typing import Any, ClassVar
66
from typing_extensions import Literal
77

8+
from ._imaging import _PixelAccessor
89
from .ImageFile import ImageFile
910

1011
logger: Any
@@ -153,7 +154,7 @@ class TiffImageFile(ImageFile):
153154
im: Any
154155
def seek(self, frame) -> None: ...
155156
def tell(self): ...
156-
def load(self): ...
157+
def load(self) -> _PixelAccessor: ...
157158
def load_end(self) -> None: ...
158159

159160
SAVE_INFO: Any

stubs/Pillow/PIL/WalImageFile.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ from typing import ClassVar
22
from typing_extensions import Literal
33

44
from . import ImageFile
5+
from ._imaging import _PixelAccessor
56

67
class WalImageFile(ImageFile.ImageFile):
78
format: ClassVar[Literal["WAL"]]
89
format_description: ClassVar[str]
9-
def load(self) -> None: ...
10+
def load(self) -> _PixelAccessor: ...
1011

1112
def open(filename): ...
1213

stubs/Pillow/PIL/WebPImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any, ClassVar
22
from typing_extensions import Literal, TypeAlias
33

4+
from ._imaging import _PixelAccessor
45
from .ImageFile import ImageFile
56

67
SUPPORTED: bool
@@ -13,5 +14,5 @@ class WebPImageFile(ImageFile):
1314
def seek(self, frame) -> None: ...
1415
fp: Any
1516
tile: Any
16-
def load(self): ...
17+
def load(self) -> _PixelAccessor: ...
1718
def tell(self): ...

stubs/Pillow/PIL/WmfImagePlugin.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ from _typeshed import Incomplete
33
from typing import Any, ClassVar
44
from typing_extensions import Literal
55

6+
from ._imaging import _PixelAccessor
67
from .ImageFile import StubImageFile
78

89
def register_handler(handler) -> None: ...
@@ -16,4 +17,4 @@ if sys.platform == "win32":
1617
class WmfStubImageFile(StubImageFile):
1718
format: ClassVar[Literal["WMF"]]
1819
format_description: ClassVar[str]
19-
def load(self, dpi: Incomplete | None = None) -> None: ...
20+
def load(self, dpi: Incomplete | None = None) -> _PixelAccessor: ...

stubs/Pillow/PIL/_imaging.pyi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from _typeshed import Incomplete
22
from collections.abc import Sequence
3+
from typing import Protocol
34
from typing_extensions import Literal
45

56
DEFAULT_STRATEGY: Literal[0]
@@ -8,6 +9,20 @@ HUFFMAN_ONLY: Literal[2]
89
RLE: Literal[3]
910
FIXED: Literal[4]
1011

12+
class _PixelAccessor(Protocol): # noqa: Y046
13+
# PIL has two concrete types for accessing an image's pixels by coordinate lookup:
14+
# PixelAccess (written in C; not runtime-importable) and PyAccess (written in
15+
# Python + cffi; is runtime-importable). PixelAccess came first. PyAccess was added
16+
# in later to support PyPy, but otherwise is intended to expose the same interface
17+
# PixelAccess.
18+
#
19+
# This protocol describes that interface.
20+
# TODO: should the color args and getter return types be _Color?
21+
def __setitem__(self, xy: tuple[int, int], color: Incomplete) -> None: ...
22+
def __getitem__(self, xy: tuple[int, int]) -> Incomplete: ...
23+
def putpixel(self, xy: tuple[int, int], color: Incomplete) -> None: ...
24+
def getpixel(self, xy: tuple[int, int]) -> Incomplete: ...
25+
1126
class _Path:
1227
def __getattr__(self, item: str) -> Incomplete: ...
1328

0 commit comments

Comments
 (0)