Skip to content

Commit 90babc8

Browse files
committed
Add type hints to Tests/helper.py
1 parent 6dc6d6d commit 90babc8

File tree

1 file changed

+71
-38
lines changed

1 file changed

+71
-38
lines changed

Tests/helper.py

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import sysconfig
1212
import tempfile
1313
from io import BytesIO
14+
from typing import Sequence, Any, Callable
1415

1516
import pytest
1617
from packaging.version import parse as parse_version
@@ -28,16 +29,16 @@
2829

2930
class test_image_results:
3031
@staticmethod
31-
def upload(a, b):
32+
def upload(a: Image.Image, b: Image.Image) -> None:
3233
a.show()
3334
b.show()
3435

3536
elif "GITHUB_ACTIONS" in os.environ:
3637
HAS_UPLOADER = True
3738

38-
class test_image_results:
39+
class test_image_results: # type: ignore[no-redef]
3940
@staticmethod
40-
def upload(a, b):
41+
def upload(a: Image.Image, b: Image.Image) -> str:
4142
dir_errors = os.path.join(os.path.dirname(__file__), "errors")
4243
os.makedirs(dir_errors, exist_ok=True)
4344
tmpdir = tempfile.mkdtemp(dir=dir_errors)
@@ -47,14 +48,16 @@ def upload(a, b):
4748

4849
else:
4950
try:
50-
import test_image_results
51+
import test_image_results # type: ignore[import-untyped, no-redef]
5152

5253
HAS_UPLOADER = True
5354
except ImportError:
5455
pass
5556

5657

57-
def convert_to_comparable(a, b):
58+
def convert_to_comparable(
59+
a: Image.Image, b: Image.Image
60+
) -> tuple[Image.Image, Image.Image]:
5861
new_a, new_b = a, b
5962
if a.mode == "P":
6063
new_a = Image.new("L", a.size)
@@ -67,14 +70,16 @@ def convert_to_comparable(a, b):
6770
return new_a, new_b
6871

6972

70-
def assert_deep_equal(a, b, msg=None):
73+
def assert_deep_equal(a: Sequence, b: Sequence, msg: str | None = None) -> None:
7174
try:
7275
assert len(a) == len(b), msg or f"got length {len(a)}, expected {len(b)}"
7376
except Exception:
7477
assert a == b, msg
7578

7679

77-
def assert_image(im, mode, size, msg=None):
80+
def assert_image(
81+
im: Image.Image, mode: str, size: tuple[int, int], msg: str | None = None
82+
) -> None:
7883
if mode is not None:
7984
assert im.mode == mode, (
8085
msg or f"got mode {repr(im.mode)}, expected {repr(mode)}"
@@ -86,7 +91,7 @@ def assert_image(im, mode, size, msg=None):
8691
)
8792

8893

89-
def assert_image_equal(a, b, msg=None):
94+
def assert_image_equal(a: Image.Image, b: Image.Image, msg: str | None = None) -> None:
9095
assert a.mode == b.mode, msg or f"got mode {repr(a.mode)}, expected {repr(b.mode)}"
9196
assert a.size == b.size, msg or f"got size {repr(a.size)}, expected {repr(b.size)}"
9297
if a.tobytes() != b.tobytes():
@@ -100,14 +105,18 @@ def assert_image_equal(a, b, msg=None):
100105
pytest.fail(msg or "got different content")
101106

102107

103-
def assert_image_equal_tofile(a, filename, msg=None, mode=None):
108+
def assert_image_equal_tofile(
109+
a: Image.Image, filename: str, msg: str | None = None, mode: str | None = None
110+
) -> None:
104111
with Image.open(filename) as img:
105112
if mode:
106113
img = img.convert(mode)
107114
assert_image_equal(a, img, msg)
108115

109116

110-
def assert_image_similar(a, b, epsilon, msg=None):
117+
def assert_image_similar(
118+
a: Image.Image, b: Image.Image, epsilon: float, msg: str | None = None
119+
) -> None:
111120
assert a.mode == b.mode, msg or f"got mode {repr(a.mode)}, expected {repr(b.mode)}"
112121
assert a.size == b.size, msg or f"got size {repr(a.size)}, expected {repr(b.size)}"
113122

@@ -134,24 +143,32 @@ def assert_image_similar(a, b, epsilon, msg=None):
134143
raise e
135144

136145

137-
def assert_image_similar_tofile(a, filename, epsilon, msg=None, mode=None):
146+
def assert_image_similar_tofile(
147+
a: Image.Image,
148+
filename: str,
149+
epsilon: float,
150+
msg: str | None = None,
151+
mode: str | None = None,
152+
) -> None:
138153
with Image.open(filename) as img:
139154
if mode:
140155
img = img.convert(mode)
141156
assert_image_similar(a, img, epsilon, msg)
142157

143158

144-
def assert_all_same(items, msg=None):
159+
def assert_all_same(items: Sequence[Any], msg: str | None = None) -> None:
145160
assert items.count(items[0]) == len(items), msg
146161

147162

148-
def assert_not_all_same(items, msg=None):
163+
def assert_not_all_same(items: Sequence[Any], msg: str | None = None) -> None:
149164
assert items.count(items[0]) != len(items), msg
150165

151166

152-
def assert_tuple_approx_equal(actuals, targets, threshold, msg):
167+
def assert_tuple_approx_equal(
168+
actuals: tuple[int, ...], targets: tuple[int, ...], threshold: int, msg: str
169+
) -> None:
153170
"""Tests if actuals has values within threshold from targets"""
154-
value = True
171+
value = 1
155172
for i, target in enumerate(targets):
156173
value *= target - threshold <= actuals[i] <= target + threshold
157174

@@ -163,17 +180,24 @@ def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
163180
return pytest.mark.skipif(not features.check(feature), reason=reason)
164181

165182

166-
def skip_unless_feature_version(feature, version_required, reason=None):
183+
def skip_unless_feature_version(
184+
feature: str, version_required: str, reason: str | None = None
185+
) -> pytest.MarkDecorator:
167186
if not features.check(feature):
168187
return pytest.mark.skip(f"{feature} not available")
169188
if reason is None:
170189
reason = f"{feature} is older than {version_required}"
171-
version_required = parse_version(version_required)
190+
version_required_ = parse_version(version_required)
172191
version_available = parse_version(features.version(feature))
173-
return pytest.mark.skipif(version_available < version_required, reason=reason)
192+
return pytest.mark.skipif(version_available < version_required_, reason=reason)
174193

175194

176-
def mark_if_feature_version(mark, feature, version_blacklist, reason=None):
195+
def mark_if_feature_version(
196+
mark: pytest.MarkDecorator,
197+
feature: str,
198+
version_blacklist: str,
199+
reason: str | None = None,
200+
) -> pytest.MarkDecorator:
177201
if not features.check(feature):
178202
return pytest.mark.pil_noop_mark()
179203
if reason is None:
@@ -194,7 +218,7 @@ class PillowLeakTestCase:
194218
iterations = 100 # count
195219
mem_limit = 512 # k
196220

197-
def _get_mem_usage(self):
221+
def _get_mem_usage(self) -> float:
198222
"""
199223
Gets the RUSAGE memory usage, returns in K. Encapsulates the difference
200224
between macOS and Linux rss reporting
@@ -216,7 +240,7 @@ def _get_mem_usage(self):
216240
# This is the maximum resident set size used (in kilobytes).
217241
return mem # Kb
218242

219-
def _test_leak(self, core):
243+
def _test_leak(self, core: Callable[[], None]) -> None:
220244
start_mem = self._get_mem_usage()
221245
for cycle in range(self.iterations):
222246
core()
@@ -228,17 +252,22 @@ def _test_leak(self, core):
228252
# helpers
229253

230254

231-
def fromstring(data):
255+
def fromstring(data: bytes) -> Image.Image:
232256
return Image.open(BytesIO(data))
233257

234258

235-
def tostring(im, string_format, **options):
259+
def tostring(im: Image.Image, string_format: str, **options: Any) -> bytes:
236260
out = BytesIO()
237261
im.save(out, string_format, **options)
238262
return out.getvalue()
239263

240264

241-
def hopper(mode=None, cache={}):
265+
def hopper(
266+
mode: str | None = None, cache: dict[str, Image.Image] | None = None
267+
) -> Image.Image:
268+
if cache is None:
269+
cache = {}
270+
242271
if mode is None:
243272
# Always return fresh not-yet-loaded version of image.
244273
# Operations on not-yet-loaded images is separate class of errors
@@ -259,29 +288,31 @@ def hopper(mode=None, cache={}):
259288
return im.copy()
260289

261290

262-
def djpeg_available():
291+
def djpeg_available() -> bool:
263292
if shutil.which("djpeg"):
264293
try:
265294
subprocess.check_call(["djpeg", "-version"])
266295
return True
267296
except subprocess.CalledProcessError: # pragma: no cover
268297
return False
298+
return False
269299

270300

271-
def cjpeg_available():
301+
def cjpeg_available() -> bool:
272302
if shutil.which("cjpeg"):
273303
try:
274304
subprocess.check_call(["cjpeg", "-version"])
275305
return True
276306
except subprocess.CalledProcessError: # pragma: no cover
277307
return False
308+
return False
278309

279310

280-
def netpbm_available():
311+
def netpbm_available() -> bool:
281312
return bool(shutil.which("ppmquant") and shutil.which("ppmtogif"))
282313

283314

284-
def magick_command():
315+
def magick_command() -> list[str] | None:
285316
if sys.platform == "win32":
286317
magickhome = os.environ.get("MAGICK_HOME")
287318
if magickhome:
@@ -299,46 +330,48 @@ def magick_command():
299330
if graphicsmagick and shutil.which(graphicsmagick[0]):
300331
return graphicsmagick
301332

333+
return None
334+
302335

303-
def on_appveyor():
336+
def on_appveyor() -> bool:
304337
return "APPVEYOR" in os.environ
305338

306339

307-
def on_github_actions():
340+
def on_github_actions() -> bool:
308341
return "GITHUB_ACTIONS" in os.environ
309342

310343

311-
def on_ci():
344+
def on_ci() -> bool:
312345
# GitHub Actions and AppVeyor have "CI"
313346
return "CI" in os.environ
314347

315348

316-
def is_big_endian():
349+
def is_big_endian() -> bool:
317350
return sys.byteorder == "big"
318351

319352

320-
def is_ppc64le():
353+
def is_ppc64le() -> bool:
321354
import platform
322355

323356
return platform.machine() == "ppc64le"
324357

325358

326-
def is_win32():
359+
def is_win32() -> bool:
327360
return sys.platform.startswith("win32")
328361

329362

330-
def is_pypy():
363+
def is_pypy() -> bool:
331364
return hasattr(sys, "pypy_translation_info")
332365

333366

334-
def is_mingw():
367+
def is_mingw() -> bool:
335368
return sysconfig.get_platform() == "mingw"
336369

337370

338371
class CachedProperty:
339-
def __init__(self, func):
372+
def __init__(self, func: Callable[[Any], Any]) -> None:
340373
self.func = func
341374

342-
def __get__(self, instance, cls=None):
375+
def __get__(self, instance: dict[Any, Callable[[], Any]], cls: Any = None) -> Any:
343376
result = instance.__dict__[self.func.__name__] = self.func(instance)
344377
return result

0 commit comments

Comments
 (0)