Skip to content

Commit

Permalink
Merge pull request #7816 from radarhere/type_hints
Browse files Browse the repository at this point in the history
Added type hints to additional tests
  • Loading branch information
radarhere committed Feb 20, 2024
2 parents 380bc17 + 7200f47 commit b9d3c21
Show file tree
Hide file tree
Showing 19 changed files with 63 additions and 46 deletions.
6 changes: 3 additions & 3 deletions Tests/test_deprecate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
),
],
)
def test_version(version, expected) -> None:
def test_version(version: int | None, expected: str) -> None:
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", version, "new thing")

Expand All @@ -46,7 +46,7 @@ def test_unknown_version() -> None:
),
],
)
def test_old_version(deprecated, plural, expected) -> None:
def test_old_version(deprecated: str, plural: bool, expected: str) -> None:
expected = r""
with pytest.raises(RuntimeError, match=expected):
_deprecate.deprecate(deprecated, 1, plural=plural)
Expand Down Expand Up @@ -76,7 +76,7 @@ def test_replacement_and_action() -> None:
"Upgrade to new thing.",
],
)
def test_action(action) -> None:
def test_action(action: str) -> None:
expected = (
r"Old thing is deprecated and will be removed in Pillow 11 \(2024-10-15\)\. "
r"Upgrade to new thing\."
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_ico.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_different_bit_depths(tmp_path: Path) -> None:


@pytest.mark.parametrize("mode", ("1", "L", "P", "RGB", "RGBA"))
def test_save_to_bytes_bmp(mode) -> None:
def test_save_to_bytes_bmp(mode: str) -> None:
output = io.BytesIO()
im = hopper(mode)
im.save(output, "ico", bitmap_format="bmp", sizes=[(32, 32), (64, 64)])
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_iptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def test_i() -> None:
assert ret == 97


def test_dump(monkeypatch) -> None:
def test_dump(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange
c = b"abc"
# Temporarily redirect stdout
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_msp.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_open_windows_v1() -> None:
assert isinstance(im, MspImagePlugin.MspImageFile)


def _assert_file_image_equal(source_path, target_path) -> None:
def _assert_file_image_equal(source_path: str, target_path: str) -> None:
with Image.open(source_path) as im:
assert_image_equal_tofile(im, target_path)

Expand Down
2 changes: 2 additions & 0 deletions Tests/test_file_png.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import zlib
from io import BytesIO
from pathlib import Path
from types import ModuleType
from typing import Any

import pytest
Expand All @@ -23,6 +24,7 @@
skip_unless_feature,
)

ElementTree: ModuleType | None
try:
from defusedxml import ElementTree
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_psd.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def test_combined_larger_than_size() -> None:
("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError),
],
)
def test_crashes(test_file, raises) -> None:
def test_crashes(test_file: str, raises) -> None:
with open(test_file, "rb") as f:
with pytest.raises(raises):
with Image.open(f):
Expand Down
4 changes: 2 additions & 2 deletions Tests/test_file_tga.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@


@pytest.mark.parametrize("mode", _MODES)
def test_sanity(mode, tmp_path: Path) -> None:
def roundtrip(original_im) -> None:
def test_sanity(mode: str, tmp_path: Path) -> None:
def roundtrip(original_im: Image.Image) -> None:
out = str(tmp_path / "temp.tga")

original_im.save(out, rle=rle)
Expand Down
6 changes: 4 additions & 2 deletions Tests/test_file_tiff_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ def test_iptc(tmp_path: Path) -> None:


@pytest.mark.parametrize("value, expected", ((b"test", "test"), (1, "1")))
def test_writing_other_types_to_ascii(value, expected, tmp_path: Path) -> None:
def test_writing_other_types_to_ascii(
value: bytes | int, expected: str, tmp_path: Path
) -> None:
info = TiffImagePlugin.ImageFileDirectory_v2()

tag = TiffTags.TAGS_V2[271]
Expand All @@ -206,7 +208,7 @@ def test_writing_other_types_to_ascii(value, expected, tmp_path: Path) -> None:


@pytest.mark.parametrize("value", (1, IFDRational(1)))
def test_writing_other_types_to_bytes(value, tmp_path: Path) -> None:
def test_writing_other_types_to_bytes(value: int | IFDRational, tmp_path: Path) -> None:
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()

Expand Down
19 changes: 12 additions & 7 deletions Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import shutil
from io import BytesIO
from pathlib import Path
from typing import Any

import pytest

Expand Down Expand Up @@ -237,7 +238,7 @@ def test_invalid_color_temperature() -> None:


@pytest.mark.parametrize("flag", ("my string", -1))
def test_invalid_flag(flag) -> None:
def test_invalid_flag(flag: str | int) -> None:
with hopper() as im:
with pytest.raises(
ImageCms.PyCMSError, match="flags must be an integer between 0 and "
Expand Down Expand Up @@ -335,19 +336,21 @@ def test_extended_information() -> None:
o = ImageCms.getOpenProfile(SRGB)
p = o.profile

def assert_truncated_tuple_equal(tup1, tup2, digits: int = 10) -> None:
def assert_truncated_tuple_equal(
tup1: tuple[Any, ...], tup2: tuple[Any, ...], digits: int = 10
) -> None:
# Helper function to reduce precision of tuples of floats
# recursively and then check equality.
power = 10**digits

def truncate_tuple(tuple_or_float):
def truncate_tuple(tuple_value: tuple[Any, ...]) -> tuple[Any, ...]:
return tuple(
(
truncate_tuple(val)
if isinstance(val, tuple)
else int(val * power) / power
)
for val in tuple_or_float
for val in tuple_value
)

assert truncate_tuple(tup1) == truncate_tuple(tup2)
Expand Down Expand Up @@ -504,8 +507,10 @@ def test_profile_typesafety() -> None:
ImageCms.ImageCmsProfile(1).tobytes()


def assert_aux_channel_preserved(mode, transform_in_place, preserved_channel) -> None:
def create_test_image():
def assert_aux_channel_preserved(
mode: str, transform_in_place: bool, preserved_channel: str
) -> None:
def create_test_image() -> Image.Image:
# set up test image with something interesting in the tested aux channel.
# fmt: off
nine_grid_deltas = [
Expand Down Expand Up @@ -633,7 +638,7 @@ def test_auxiliary_channels_isolated() -> None:


@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode) -> None:
def test_rgb_lab(mode: str) -> None:
im = Image.new(mode, (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (0, 128, 128)
Expand Down
22 changes: 12 additions & 10 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
from io import BytesIO
from pathlib import Path
from typing import BinaryIO
from typing import Any, BinaryIO

import pytest
from packaging.version import parse as parse_version
Expand Down Expand Up @@ -44,7 +44,7 @@ def test_sanity() -> None:
pytest.param(ImageFont.Layout.RAQM, marks=skip_unless_feature("raqm")),
],
)
def layout_engine(request):
def layout_engine(request: pytest.FixtureRequest) -> ImageFont.Layout:
return request.param


Expand Down Expand Up @@ -535,21 +535,23 @@ def test_unicode_extended(layout_engine: ImageFont.Layout) -> None:
(("linux", "/usr/local/share/fonts"), ("darwin", "/System/Library/Fonts")),
)
@pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
def test_find_font(monkeypatch, platform, font_directory) -> None:
def test_find_font(
monkeypatch: pytest.MonkeyPatch, platform: str, font_directory: str
) -> None:
def _test_fake_loading_font(path_to_fake: str, fontname: str) -> None:
# Make a copy of FreeTypeFont so we can patch the original
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
with monkeypatch.context() as m:
m.setattr(ImageFont, "_FreeTypeFont", free_type_font, raising=False)

def loadable_font(filepath, size, index, encoding, *args, **kwargs):
def loadable_font(
filepath: str, size: int, index: int, encoding: str, *args: Any
):
if filepath == path_to_fake:
return ImageFont._FreeTypeFont(
FONT_PATH, size, index, encoding, *args, **kwargs
FONT_PATH, size, index, encoding, *args
)
return ImageFont._FreeTypeFont(
filepath, size, index, encoding, *args, **kwargs
)
return ImageFont._FreeTypeFont(filepath, size, index, encoding, *args)

m.setattr(ImageFont, "FreeTypeFont", loadable_font)
font = ImageFont.truetype(fontname)
Expand All @@ -563,7 +565,7 @@ def loadable_font(filepath, size, index, encoding, *args, **kwargs):
if platform == "linux":
monkeypatch.setenv("XDG_DATA_DIRS", "/usr/share/:/usr/local/share/")

def fake_walker(path):
def fake_walker(path: str) -> list[tuple[str, list[str], list[str]]]:
if path == font_directory:
return [
(
Expand Down Expand Up @@ -1101,7 +1103,7 @@ def test_oom(test_file: str) -> None:
font.getmask("Test Text")


def test_raqm_missing_warning(monkeypatch) -> None:
def test_raqm_missing_warning(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(ImageFont.core, "HAVE_RAQM", False)
with pytest.warns(UserWarning) as record:
font = ImageFont.truetype(
Expand Down
6 changes: 4 additions & 2 deletions Tests/test_imagegrab.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def test_grabclipboard(self) -> None:
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_grabclipboard_file(self) -> None:
p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE)
assert p.stdin is not None
p.stdin.write(rb'Set-Clipboard -Path "Tests\images\hopper.gif"')
p.communicate()

Expand All @@ -94,6 +95,7 @@ def test_grabclipboard_file(self) -> None:
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_grabclipboard_png(self) -> None:
p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE)
assert p.stdin is not None
p.stdin.write(
rb"""$bytes = [System.IO.File]::ReadAllBytes("Tests\images\hopper.png")
$ms = new-object System.IO.MemoryStream(, $bytes)
Expand All @@ -113,7 +115,7 @@ def test_grabclipboard_png(self) -> None:
reason="Linux with wl-clipboard only",
)
@pytest.mark.parametrize("ext", ("gif", "png", "ico"))
def test_grabclipboard_wl_clipboard(self, ext) -> None:
def test_grabclipboard_wl_clipboard(self, ext: str) -> None:
image_path = "Tests/images/hopper." + ext
with open(image_path, "rb") as fp:
subprocess.call(["wl-copy"], stdin=fp)
Expand All @@ -128,6 +130,6 @@ def test_grabclipboard_wl_clipboard(self, ext) -> None:
reason="Linux with wl-clipboard only",
)
@pytest.mark.parametrize("arg", ("text", "--clear"))
def test_grabclipboard_wl_clipboard_errors(self, arg):
def test_grabclipboard_wl_clipboard_errors(self, arg: str) -> None:
subprocess.call(["wl-copy", arg])
assert ImageGrab.grabclipboard() is None
8 changes: 5 additions & 3 deletions Tests/test_imagepath.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ def test_path() -> None:
ImagePath.Path((0, 1)),
),
)
def test_path_constructors(coords) -> None:
def test_path_constructors(
coords: Sequence[float] | array.array[float] | ImagePath.Path,
) -> None:
# Arrange / Act
p = ImagePath.Path(coords)

Expand Down Expand Up @@ -206,9 +208,9 @@ class Evil:
def __init__(self) -> None:
self.corrupt = Image.core.path(0x4000000000000000)

def __getitem__(self, i):
def __getitem__(self, i: int) -> bytes:
x = self.corrupt[i]
return struct.pack("dd", x[0], x[1])

def __setitem__(self, i, x) -> None:
def __setitem__(self, i: int, x: bytes) -> None:
self.corrupt[i] = struct.unpack("dd", x)
2 changes: 1 addition & 1 deletion Tests/test_imageqt.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_rgb() -> None:

assert qRgb(0, 0, 0) == qRgba(0, 0, 0, 255)

def checkrgb(r, g, b) -> None:
def checkrgb(r: int, g: int, b: int) -> None:
val = ImageQt.rgb(r, g, b)
val = val % 2**24 # drop the alpha
assert val >> 16 == r
Expand Down
10 changes: 6 additions & 4 deletions Tests/test_imageshow.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from typing import Any

import pytest

from PIL import Image, ImageShow
Expand All @@ -24,9 +26,9 @@ def test_register() -> None:
"order",
[-1, 0],
)
def test_viewer_show(order) -> None:
def test_viewer_show(order: int) -> None:
class TestViewer(ImageShow.Viewer):
def show_image(self, image, **options) -> bool:
def show_image(self, image: Image.Image, **options: Any) -> bool:
self.methodCalled = True
return True

Expand All @@ -48,7 +50,7 @@ def show_image(self, image, **options) -> bool:
reason="Only run on CIs; hangs on Windows CIs",
)
@pytest.mark.parametrize("mode", ("1", "I;16", "LA", "RGB", "RGBA"))
def test_show(mode) -> None:
def test_show(mode: str) -> None:
im = hopper(mode)
assert ImageShow.show(im)

Expand All @@ -73,7 +75,7 @@ def test_viewer() -> None:


@pytest.mark.parametrize("viewer", ImageShow._viewers)
def test_viewers(viewer) -> None:
def test_viewers(viewer: ImageShow.Viewer) -> None:
try:
viewer.get_command("test.jpg")
except NotImplementedError:
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_imagewin_pointers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class BITMAPINFOHEADER(ctypes.Structure):
]
CreateDIBSection.restype = ctypes.wintypes.HBITMAP

def serialize_dib(bi, pixels):
def serialize_dib(bi, pixels) -> bytearray:
bf = BITMAPFILEHEADER()
bf.bfType = 0x4D42
bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize
Expand Down
6 changes: 3 additions & 3 deletions Tests/test_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


def test_numpy_to_image() -> None:
def to_image(dtype, bands: int = 1, boolean: int = 0):
def to_image(dtype, bands: int = 1, boolean: int = 0) -> Image.Image:
if bands == 1:
if boolean:
data = [0, 255] * 50
Expand Down Expand Up @@ -99,7 +99,7 @@ def test_1d_array() -> None:
assert_image(Image.fromarray(a), "L", (1, 5))


def _test_img_equals_nparray(img, np) -> None:
def _test_img_equals_nparray(img: Image.Image, np) -> None:
assert len(np.shape) >= 2
np_size = np.shape[1], np.shape[0]
assert img.size == np_size
Expand Down Expand Up @@ -157,7 +157,7 @@ def test_save_tiff_uint16() -> None:
("HSV", numpy.uint8),
),
)
def test_to_array(mode, dtype) -> None:
def test_to_array(mode: str, dtype) -> None:
img = hopper(mode)

# Resize to non-square
Expand Down
4 changes: 2 additions & 2 deletions Tests/test_qt_image_qapplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from PIL import ImageQt
from PIL import Image, ImageQt

from .helper import assert_image_equal_tofile, assert_image_similar, hopper

Expand Down Expand Up @@ -37,7 +37,7 @@ def __init__(self) -> None:
lbl.setPixmap(pixmap1.copy())


def roundtrip(expected) -> None:
def roundtrip(expected: Image.Image) -> None:
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb
assert_image_similar(result, expected.convert("RGB"), 1)
Expand Down
Loading

0 comments on commit b9d3c21

Please sign in to comment.