Skip to content

Commit d6d6c7c

Browse files
authored
Merge pull request #7707 from radarhere/type_hints_imagemath
2 parents fc67ee2 + 993bc6c commit d6d6c7c

4 files changed

Lines changed: 81 additions & 66 deletions

File tree

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ exclude = [
146146
'^src/PIL/DdsImagePlugin.py$',
147147
'^src/PIL/FpxImagePlugin.py$',
148148
'^src/PIL/Image.py$',
149-
'^src/PIL/ImageMath.py$',
150149
'^src/PIL/ImageMorph.py$',
151150
'^src/PIL/ImageQt.py$',
152151
'^src/PIL/ImageShow.py$',

src/PIL/Image.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ def verify(self):
873873

874874
def convert(
875875
self, mode=None, matrix=None, dither=None, palette=Palette.WEB, colors=256
876-
):
876+
) -> Image:
877877
"""
878878
Returns a converted copy of this image. For the "P" mode, this
879879
method translates pixels through the palette. If mode is
@@ -1305,7 +1305,7 @@ def getbands(self):
13051305
"""
13061306
return ImageMode.getmode(self.mode).bands
13071307

1308-
def getbbox(self, *, alpha_only=True):
1308+
def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int]:
13091309
"""
13101310
Calculates the bounding box of the non-zero regions in the
13111311
image.

src/PIL/ImageMath.py

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@
1717
from __future__ import annotations
1818

1919
import builtins
20+
from types import CodeType
21+
from typing import Any
2022

2123
from . import Image, _imagingmath
2224

2325

2426
class _Operand:
2527
"""Wraps an image operand, providing standard operators"""
2628

27-
def __init__(self, im):
29+
def __init__(self, im: Image.Image):
2830
self.im = im
2931

30-
def __fixup(self, im1):
32+
def __fixup(self, im1: _Operand | float) -> Image.Image:
3133
# convert image to suitable mode
3234
if isinstance(im1, _Operand):
3335
# argument was an image.
@@ -45,122 +47,131 @@ def __fixup(self, im1):
4547
else:
4648
return Image.new("F", self.im.size, im1)
4749

48-
def apply(self, op, im1, im2=None, mode=None):
49-
im1 = self.__fixup(im1)
50+
def apply(
51+
self,
52+
op: str,
53+
im1: _Operand | float,
54+
im2: _Operand | float | None = None,
55+
mode: str | None = None,
56+
) -> _Operand:
57+
im_1 = self.__fixup(im1)
5058
if im2 is None:
5159
# unary operation
52-
out = Image.new(mode or im1.mode, im1.size, None)
53-
im1.load()
60+
out = Image.new(mode or im_1.mode, im_1.size, None)
61+
im_1.load()
5462
try:
55-
op = getattr(_imagingmath, op + "_" + im1.mode)
63+
op = getattr(_imagingmath, op + "_" + im_1.mode)
5664
except AttributeError as e:
5765
msg = f"bad operand type for '{op}'"
5866
raise TypeError(msg) from e
59-
_imagingmath.unop(op, out.im.id, im1.im.id)
67+
_imagingmath.unop(op, out.im.id, im_1.im.id)
6068
else:
6169
# binary operation
62-
im2 = self.__fixup(im2)
63-
if im1.mode != im2.mode:
70+
im_2 = self.__fixup(im2)
71+
if im_1.mode != im_2.mode:
6472
# convert both arguments to floating point
65-
if im1.mode != "F":
66-
im1 = im1.convert("F")
67-
if im2.mode != "F":
68-
im2 = im2.convert("F")
69-
if im1.size != im2.size:
73+
if im_1.mode != "F":
74+
im_1 = im_1.convert("F")
75+
if im_2.mode != "F":
76+
im_2 = im_2.convert("F")
77+
if im_1.size != im_2.size:
7078
# crop both arguments to a common size
71-
size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))
72-
if im1.size != size:
73-
im1 = im1.crop((0, 0) + size)
74-
if im2.size != size:
75-
im2 = im2.crop((0, 0) + size)
76-
out = Image.new(mode or im1.mode, im1.size, None)
77-
im1.load()
78-
im2.load()
79+
size = (
80+
min(im_1.size[0], im_2.size[0]),
81+
min(im_1.size[1], im_2.size[1]),
82+
)
83+
if im_1.size != size:
84+
im_1 = im_1.crop((0, 0) + size)
85+
if im_2.size != size:
86+
im_2 = im_2.crop((0, 0) + size)
87+
out = Image.new(mode or im_1.mode, im_1.size, None)
88+
im_1.load()
89+
im_2.load()
7990
try:
80-
op = getattr(_imagingmath, op + "_" + im1.mode)
91+
op = getattr(_imagingmath, op + "_" + im_1.mode)
8192
except AttributeError as e:
8293
msg = f"bad operand type for '{op}'"
8394
raise TypeError(msg) from e
84-
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
95+
_imagingmath.binop(op, out.im.id, im_1.im.id, im_2.im.id)
8596
return _Operand(out)
8697

8798
# unary operators
88-
def __bool__(self):
99+
def __bool__(self) -> bool:
89100
# an image is "true" if it contains at least one non-zero pixel
90101
return self.im.getbbox() is not None
91102

92-
def __abs__(self):
103+
def __abs__(self) -> _Operand:
93104
return self.apply("abs", self)
94105

95-
def __pos__(self):
106+
def __pos__(self) -> _Operand:
96107
return self
97108

98-
def __neg__(self):
109+
def __neg__(self) -> _Operand:
99110
return self.apply("neg", self)
100111

101112
# binary operators
102-
def __add__(self, other):
113+
def __add__(self, other: _Operand | float) -> _Operand:
103114
return self.apply("add", self, other)
104115

105-
def __radd__(self, other):
116+
def __radd__(self, other: _Operand | float) -> _Operand:
106117
return self.apply("add", other, self)
107118

108-
def __sub__(self, other):
119+
def __sub__(self, other: _Operand | float) -> _Operand:
109120
return self.apply("sub", self, other)
110121

111-
def __rsub__(self, other):
122+
def __rsub__(self, other: _Operand | float) -> _Operand:
112123
return self.apply("sub", other, self)
113124

114-
def __mul__(self, other):
125+
def __mul__(self, other: _Operand | float) -> _Operand:
115126
return self.apply("mul", self, other)
116127

117-
def __rmul__(self, other):
128+
def __rmul__(self, other: _Operand | float) -> _Operand:
118129
return self.apply("mul", other, self)
119130

120-
def __truediv__(self, other):
131+
def __truediv__(self, other: _Operand | float) -> _Operand:
121132
return self.apply("div", self, other)
122133

123-
def __rtruediv__(self, other):
134+
def __rtruediv__(self, other: _Operand | float) -> _Operand:
124135
return self.apply("div", other, self)
125136

126-
def __mod__(self, other):
137+
def __mod__(self, other: _Operand | float) -> _Operand:
127138
return self.apply("mod", self, other)
128139

129-
def __rmod__(self, other):
140+
def __rmod__(self, other: _Operand | float) -> _Operand:
130141
return self.apply("mod", other, self)
131142

132-
def __pow__(self, other):
143+
def __pow__(self, other: _Operand | float) -> _Operand:
133144
return self.apply("pow", self, other)
134145

135-
def __rpow__(self, other):
146+
def __rpow__(self, other: _Operand | float) -> _Operand:
136147
return self.apply("pow", other, self)
137148

138149
# bitwise
139-
def __invert__(self):
150+
def __invert__(self) -> _Operand:
140151
return self.apply("invert", self)
141152

142-
def __and__(self, other):
153+
def __and__(self, other: _Operand | float) -> _Operand:
143154
return self.apply("and", self, other)
144155

145-
def __rand__(self, other):
156+
def __rand__(self, other: _Operand | float) -> _Operand:
146157
return self.apply("and", other, self)
147158

148-
def __or__(self, other):
159+
def __or__(self, other: _Operand | float) -> _Operand:
149160
return self.apply("or", self, other)
150161

151-
def __ror__(self, other):
162+
def __ror__(self, other: _Operand | float) -> _Operand:
152163
return self.apply("or", other, self)
153164

154-
def __xor__(self, other):
165+
def __xor__(self, other: _Operand | float) -> _Operand:
155166
return self.apply("xor", self, other)
156167

157-
def __rxor__(self, other):
168+
def __rxor__(self, other: _Operand | float) -> _Operand:
158169
return self.apply("xor", other, self)
159170

160-
def __lshift__(self, other):
171+
def __lshift__(self, other: _Operand | float) -> _Operand:
161172
return self.apply("lshift", self, other)
162173

163-
def __rshift__(self, other):
174+
def __rshift__(self, other: _Operand | float) -> _Operand:
164175
return self.apply("rshift", self, other)
165176

166177
# logical
@@ -170,46 +181,46 @@ def __eq__(self, other):
170181
def __ne__(self, other):
171182
return self.apply("ne", self, other)
172183

173-
def __lt__(self, other):
184+
def __lt__(self, other: _Operand | float) -> _Operand:
174185
return self.apply("lt", self, other)
175186

176-
def __le__(self, other):
187+
def __le__(self, other: _Operand | float) -> _Operand:
177188
return self.apply("le", self, other)
178189

179-
def __gt__(self, other):
190+
def __gt__(self, other: _Operand | float) -> _Operand:
180191
return self.apply("gt", self, other)
181192

182-
def __ge__(self, other):
193+
def __ge__(self, other: _Operand | float) -> _Operand:
183194
return self.apply("ge", self, other)
184195

185196

186197
# conversions
187-
def imagemath_int(self):
198+
def imagemath_int(self: _Operand) -> _Operand:
188199
return _Operand(self.im.convert("I"))
189200

190201

191-
def imagemath_float(self):
202+
def imagemath_float(self: _Operand) -> _Operand:
192203
return _Operand(self.im.convert("F"))
193204

194205

195206
# logical
196-
def imagemath_equal(self, other):
207+
def imagemath_equal(self: _Operand, other: _Operand | float | None) -> _Operand:
197208
return self.apply("eq", self, other, mode="I")
198209

199210

200-
def imagemath_notequal(self, other):
211+
def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> _Operand:
201212
return self.apply("ne", self, other, mode="I")
202213

203214

204-
def imagemath_min(self, other):
215+
def imagemath_min(self: _Operand, other: _Operand | float | None) -> _Operand:
205216
return self.apply("min", self, other)
206217

207218

208-
def imagemath_max(self, other):
219+
def imagemath_max(self: _Operand, other: _Operand | float | None) -> _Operand:
209220
return self.apply("max", self, other)
210221

211222

212-
def imagemath_convert(self, mode):
223+
def imagemath_convert(self: _Operand, mode: str) -> _Operand:
213224
return _Operand(self.im.convert(mode))
214225

215226

@@ -219,7 +230,7 @@ def imagemath_convert(self, mode):
219230
ops[k[10:]] = v
220231

221232

222-
def eval(expression, _dict={}, **kw):
233+
def eval(expression: str, _dict: dict[str, Any] = {}, **kw: Any) -> Any:
223234
"""
224235
Evaluates an image expression.
225236
@@ -247,7 +258,7 @@ def eval(expression, _dict={}, **kw):
247258

248259
compiled_code = compile(expression, "<string>", "eval")
249260

250-
def scan(code):
261+
def scan(code: CodeType) -> None:
251262
for const in code.co_consts:
252263
if type(const) is type(compiled_code):
253264
scan(const)

src/PIL/_imagingmath.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
def __getattr__(name: str) -> Any: ...

0 commit comments

Comments
 (0)