Skip to content

Commit 835ca1b

Browse files
authored
Merge pull request #7924 from hugovk/image-type-hints
Add type hints to `Image.py`
2 parents 2237677 + 7c5d0b9 commit 835ca1b

File tree

1 file changed

+54
-46
lines changed

1 file changed

+54
-46
lines changed

src/PIL/Image.py

+54-46
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
_plugins,
5656
)
5757
from ._binary import i32le, o32be, o32le
58+
from ._typing import TypeGuard
5859
from ._util import DeferredError, is_path
5960

6061
ElementTree: ModuleType | None
@@ -120,7 +121,7 @@ class DecompressionBombError(Exception):
120121
cffi = None
121122

122123

123-
def isImageType(t):
124+
def isImageType(t: Any) -> TypeGuard[Image]:
124125
"""
125126
Checks if an object is an image object.
126127
@@ -267,7 +268,7 @@ def getmodebase(mode: str) -> str:
267268
return ImageMode.getmode(mode).basemode
268269

269270

270-
def getmodetype(mode):
271+
def getmodetype(mode: str) -> str:
271272
"""
272273
Gets the storage type mode. Given a mode, this function returns a
273274
single-layer mode suitable for storing individual bands.
@@ -279,7 +280,7 @@ def getmodetype(mode):
279280
return ImageMode.getmode(mode).basetype
280281

281282

282-
def getmodebandnames(mode):
283+
def getmodebandnames(mode: str) -> tuple[str, ...]:
283284
"""
284285
Gets a list of individual band names. Given a mode, this function returns
285286
a tuple containing the names of individual bands (use
@@ -311,7 +312,7 @@ def getmodebands(mode: str) -> int:
311312
_initialized = 0
312313

313314

314-
def preinit():
315+
def preinit() -> None:
315316
"""
316317
Explicitly loads BMP, GIF, JPEG, PPM and PPM file format drivers.
317318
@@ -437,7 +438,7 @@ def _getencoder(mode, encoder_name, args, extra=()):
437438

438439

439440
class _E:
440-
def __init__(self, scale, offset):
441+
def __init__(self, scale, offset) -> None:
441442
self.scale = scale
442443
self.offset = offset
443444

@@ -508,22 +509,22 @@ def __init__(self):
508509
self._exif = None
509510

510511
@property
511-
def width(self):
512+
def width(self) -> int:
512513
return self.size[0]
513514

514515
@property
515-
def height(self):
516+
def height(self) -> int:
516517
return self.size[1]
517518

518519
@property
519-
def size(self):
520+
def size(self) -> tuple[int, int]:
520521
return self._size
521522

522523
@property
523524
def mode(self):
524525
return self._mode
525526

526-
def _new(self, im):
527+
def _new(self, im) -> Image:
527528
new = Image()
528529
new.im = im
529530
new._mode = im.mode
@@ -556,7 +557,7 @@ def __exit__(self, *args):
556557
self._close_fp()
557558
self.fp = None
558559

559-
def close(self):
560+
def close(self) -> None:
560561
"""
561562
Closes the file pointer, if possible.
562563
@@ -589,7 +590,7 @@ def _copy(self) -> None:
589590
self.pyaccess = None
590591
self.readonly = 0
591592

592-
def _ensure_mutable(self):
593+
def _ensure_mutable(self) -> None:
593594
if self.readonly:
594595
self._copy()
595596
else:
@@ -629,7 +630,7 @@ def __eq__(self, other):
629630
and self.tobytes() == other.tobytes()
630631
)
631632

632-
def __repr__(self):
633+
def __repr__(self) -> str:
633634
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
634635
self.__class__.__module__,
635636
self.__class__.__name__,
@@ -639,7 +640,7 @@ def __repr__(self):
639640
id(self),
640641
)
641642

642-
def _repr_pretty_(self, p, cycle):
643+
def _repr_pretty_(self, p, cycle) -> None:
643644
"""IPython plain text display support"""
644645

645646
# Same as __repr__ but without unpredictable id(self),
@@ -711,7 +712,7 @@ def __getstate__(self):
711712
im_data = self.tobytes() # load image first
712713
return [self.info, self.mode, self.size, self.getpalette(), im_data]
713714

714-
def __setstate__(self, state):
715+
def __setstate__(self, state) -> None:
715716
Image.__init__(self)
716717
info, mode, size, palette, data = state
717718
self.info = info
@@ -774,7 +775,7 @@ def tobytes(self, encoder_name: str = "raw", *args) -> bytes:
774775

775776
return b"".join(output)
776777

777-
def tobitmap(self, name="image"):
778+
def tobitmap(self, name: str = "image") -> bytes:
778779
"""
779780
Returns the image converted to an X11 bitmap.
780781
@@ -886,7 +887,12 @@ def verify(self):
886887
pass
887888

888889
def convert(
889-
self, mode=None, matrix=None, dither=None, palette=Palette.WEB, colors=256
890+
self,
891+
mode: str | None = None,
892+
matrix: tuple[float, ...] | None = None,
893+
dither: Dither | None = None,
894+
palette: Palette = Palette.WEB,
895+
colors: int = 256,
890896
) -> Image:
891897
"""
892898
Returns a converted copy of this image. For the "P" mode, this
@@ -1117,12 +1123,12 @@ def convert_transparency(m, v):
11171123

11181124
def quantize(
11191125
self,
1120-
colors=256,
1121-
method=None,
1122-
kmeans=0,
1126+
colors: int = 256,
1127+
method: Quantize | None = None,
1128+
kmeans: int = 0,
11231129
palette=None,
1124-
dither=Dither.FLOYDSTEINBERG,
1125-
):
1130+
dither: Dither = Dither.FLOYDSTEINBERG,
1131+
) -> Image:
11261132
"""
11271133
Convert the image to 'P' mode with the specified number
11281134
of colors.
@@ -1210,7 +1216,7 @@ def copy(self) -> Image:
12101216

12111217
__copy__ = copy
12121218

1213-
def crop(self, box=None) -> Image:
1219+
def crop(self, box: tuple[int, int, int, int] | None = None) -> Image:
12141220
"""
12151221
Returns a rectangular region from this image. The box is a
12161222
4-tuple defining the left, upper, right, and lower pixel
@@ -1341,7 +1347,7 @@ def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int]:
13411347
self.load()
13421348
return self.im.getbbox(alpha_only)
13431349

1344-
def getcolors(self, maxcolors=256):
1350+
def getcolors(self, maxcolors: int = 256):
13451351
"""
13461352
Returns a list of colors used in this image.
13471353
@@ -1364,7 +1370,7 @@ def getcolors(self, maxcolors=256):
13641370
return out
13651371
return self.im.getcolors(maxcolors)
13661372

1367-
def getdata(self, band=None):
1373+
def getdata(self, band: int | None = None):
13681374
"""
13691375
Returns the contents of this image as a sequence object
13701376
containing pixel values. The sequence object is flattened, so
@@ -1387,7 +1393,7 @@ def getdata(self, band=None):
13871393
return self.im.getband(band)
13881394
return self.im # could be abused
13891395

1390-
def getextrema(self):
1396+
def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]:
13911397
"""
13921398
Gets the minimum and maximum pixel values for each band in
13931399
the image.
@@ -1468,7 +1474,7 @@ def getexif(self) -> Exif:
14681474

14691475
return self._exif
14701476

1471-
def _reload_exif(self):
1477+
def _reload_exif(self) -> None:
14721478
if self._exif is None or not self._exif._loaded:
14731479
return
14741480
self._exif._loaded = False
@@ -1605,7 +1611,7 @@ def getpixel(self, xy):
16051611
return self.pyaccess.getpixel(xy)
16061612
return self.im.getpixel(tuple(xy))
16071613

1608-
def getprojection(self):
1614+
def getprojection(self) -> tuple[list[int], list[int]]:
16091615
"""
16101616
Get projection to x and y axes
16111617
@@ -1617,7 +1623,7 @@ def getprojection(self):
16171623
x, y = self.im.getprojection()
16181624
return list(x), list(y)
16191625

1620-
def histogram(self, mask=None, extrema=None) -> list[int]:
1626+
def histogram(self, mask: Image | None = None, extrema=None) -> list[int]:
16211627
"""
16221628
Returns a histogram for the image. The histogram is returned as a
16231629
list of pixel counts, one for each pixel value in the source
@@ -2463,7 +2469,7 @@ def save(self, fp, format=None, **params) -> None:
24632469
if open_fp:
24642470
fp.close()
24652471

2466-
def seek(self, frame) -> None:
2472+
def seek(self, frame: int) -> None:
24672473
"""
24682474
Seeks to the given frame in this sequence file. If you seek
24692475
beyond the end of the sequence, the method raises an
@@ -2485,7 +2491,7 @@ def seek(self, frame) -> None:
24852491
msg = "no more images in file"
24862492
raise EOFError(msg)
24872493

2488-
def show(self, title=None):
2494+
def show(self, title: str | None = None) -> None:
24892495
"""
24902496
Displays this image. This method is mainly intended for debugging purposes.
24912497
@@ -2526,7 +2532,7 @@ def split(self) -> tuple[Image, ...]:
25262532
return (self.copy(),)
25272533
return tuple(map(self._new, self.im.split()))
25282534

2529-
def getchannel(self, channel):
2535+
def getchannel(self, channel: int | str) -> Image:
25302536
"""
25312537
Returns an image containing a single channel of the source image.
25322538
@@ -2601,13 +2607,13 @@ def thumbnail(self, size, resample=Resampling.BICUBIC, reducing_gap=2.0):
26012607

26022608
provided_size = tuple(map(math.floor, size))
26032609

2604-
def preserve_aspect_ratio():
2610+
def preserve_aspect_ratio() -> tuple[int, int] | None:
26052611
def round_aspect(number, key):
26062612
return max(min(math.floor(number), math.ceil(number), key=key), 1)
26072613

26082614
x, y = provided_size
26092615
if x >= self.width and y >= self.height:
2610-
return
2616+
return None
26112617

26122618
aspect = self.width / self.height
26132619
if x / y >= aspect:
@@ -2927,7 +2933,9 @@ def _check_size(size):
29272933
return True
29282934

29292935

2930-
def new(mode, size, color=0) -> Image:
2936+
def new(
2937+
mode: str, size: tuple[int, int], color: float | tuple[float, ...] | str | None = 0
2938+
) -> Image:
29312939
"""
29322940
Creates a new image with the given mode and size.
29332941
@@ -3193,7 +3201,7 @@ def fromqpixmap(im):
31933201
}
31943202

31953203

3196-
def _decompression_bomb_check(size):
3204+
def _decompression_bomb_check(size: tuple[int, int]) -> None:
31973205
if MAX_IMAGE_PIXELS is None:
31983206
return
31993207

@@ -3335,7 +3343,7 @@ def _open_core(fp, filename, prefix, formats):
33353343
# Image processing.
33363344

33373345

3338-
def alpha_composite(im1, im2):
3346+
def alpha_composite(im1: Image, im2: Image) -> Image:
33393347
"""
33403348
Alpha composite im2 over im1.
33413349
@@ -3350,7 +3358,7 @@ def alpha_composite(im1, im2):
33503358
return im1._new(core.alpha_composite(im1.im, im2.im))
33513359

33523360

3353-
def blend(im1, im2, alpha):
3361+
def blend(im1: Image, im2: Image, alpha: float) -> Image:
33543362
"""
33553363
Creates a new image by interpolating between two input images, using
33563364
a constant alpha::
@@ -3373,7 +3381,7 @@ def blend(im1, im2, alpha):
33733381
return im1._new(core.blend(im1.im, im2.im, alpha))
33743382

33753383

3376-
def composite(image1, image2, mask):
3384+
def composite(image1: Image, image2: Image, mask: Image) -> Image:
33773385
"""
33783386
Create composite image by blending images using a transparency mask.
33793387
@@ -3483,7 +3491,7 @@ def register_save(id: str, driver) -> None:
34833491
SAVE[id.upper()] = driver
34843492

34853493

3486-
def register_save_all(id, driver):
3494+
def register_save_all(id, driver) -> None:
34873495
"""
34883496
Registers an image function to save all the frames
34893497
of a multiframe format. This function should not be
@@ -3557,7 +3565,7 @@ def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None:
35573565
# Simple display support.
35583566

35593567

3560-
def _show(image, **options):
3568+
def _show(image, **options) -> None:
35613569
from . import ImageShow
35623570

35633571
ImageShow.show(image, **options)
@@ -3613,7 +3621,7 @@ def radial_gradient(mode):
36133621
# Resources
36143622

36153623

3616-
def _apply_env_variables(env=None):
3624+
def _apply_env_variables(env=None) -> None:
36173625
if env is None:
36183626
env = os.environ
36193627

@@ -3928,21 +3936,21 @@ def get_ifd(self, tag):
39283936
}
39293937
return ifd
39303938

3931-
def hide_offsets(self):
3939+
def hide_offsets(self) -> None:
39323940
for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo):
39333941
if tag in self:
39343942
self._hidden_data[tag] = self[tag]
39353943
del self[tag]
39363944

3937-
def __str__(self):
3945+
def __str__(self) -> str:
39383946
if self._info is not None:
39393947
# Load all keys into self._data
39403948
for tag in self._info:
39413949
self[tag]
39423950

39433951
return str(self._data)
39443952

3945-
def __len__(self):
3953+
def __len__(self) -> int:
39463954
keys = set(self._data)
39473955
if self._info is not None:
39483956
keys.update(self._info)
@@ -3954,10 +3962,10 @@ def __getitem__(self, tag):
39543962
del self._info[tag]
39553963
return self._data[tag]
39563964

3957-
def __contains__(self, tag):
3965+
def __contains__(self, tag) -> bool:
39583966
return tag in self._data or (self._info is not None and tag in self._info)
39593967

3960-
def __setitem__(self, tag, value):
3968+
def __setitem__(self, tag, value) -> None:
39613969
if self._info is not None and tag in self._info:
39623970
del self._info[tag]
39633971
self._data[tag] = value

0 commit comments

Comments
 (0)