Skip to content

Commit

Permalink
Merge pull request #7784 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 7, 2024
2 parents 5bd8fd1 + 463c368 commit 6782a07
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 134 deletions.
4 changes: 3 additions & 1 deletion Tests/test_color_lut.py
Expand Up @@ -15,7 +15,9 @@


class TestColorLut3DCoreAPI:
def generate_identity_table(self, channels, size):
def generate_identity_table(
self, channels: int, size: int | tuple[int, int, int]
) -> tuple[int, int, int, int, list[float]]:
if isinstance(size, tuple):
size_1d, size_2d, size_3d = size
else:
Expand Down
32 changes: 16 additions & 16 deletions Tests/test_file_eps.py
Expand Up @@ -84,7 +84,7 @@
("filename", "size"), ((FILE1, (460, 352)), (FILE2, (360, 252)))
)
@pytest.mark.parametrize("scale", (1, 2))
def test_sanity(filename, size, scale) -> None:
def test_sanity(filename: str, size: tuple[int, int], scale: int) -> None:
expected_size = tuple(s * scale for s in size)
with Image.open(filename) as image:
image.load(scale=scale)
Expand Down Expand Up @@ -129,28 +129,28 @@ def test_binary_header_only() -> None:


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_missing_version_comment(prefix) -> None:
def test_missing_version_comment(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_version))
with pytest.raises(SyntaxError):
EpsImagePlugin.EpsImageFile(data)


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_missing_boundingbox_comment(prefix) -> None:
def test_missing_boundingbox_comment(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_without_boundingbox))
with pytest.raises(SyntaxError, match='EPS header missing "%%BoundingBox" comment'):
EpsImagePlugin.EpsImageFile(data)


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_invalid_boundingbox_comment(prefix) -> None:
def test_invalid_boundingbox_comment(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox))
with pytest.raises(OSError, match="cannot determine EPS bounding box"):
EpsImagePlugin.EpsImageFile(data)


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix) -> None:
def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix: bytes) -> None:
data = io.BytesIO(
prefix + b"\n".join(simple_eps_file_with_invalid_boundingbox_valid_imagedata)
)
Expand All @@ -161,21 +161,21 @@ def test_invalid_boundingbox_comment_valid_imagedata_comment(prefix) -> None:


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_ascii_comment_too_long(prefix) -> None:
def test_ascii_comment_too_long(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_ascii_comment))
with pytest.raises(SyntaxError, match="not an EPS file"):
EpsImagePlugin.EpsImageFile(data)


@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_long_binary_data(prefix) -> None:
def test_long_binary_data(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
EpsImagePlugin.EpsImageFile(data)


@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
@pytest.mark.parametrize("prefix", (b"", simple_binary_header))
def test_load_long_binary_data(prefix) -> None:
def test_load_long_binary_data(prefix: bytes) -> None:
data = io.BytesIO(prefix + b"\n".join(simple_eps_file_with_long_binary_data))
with Image.open(data) as img:
img.load()
Expand Down Expand Up @@ -305,7 +305,7 @@ def test_render_scale2() -> None:

@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
@pytest.mark.parametrize("filename", (FILE1, FILE2, "Tests/images/illu10_preview.eps"))
def test_resize(filename) -> None:
def test_resize(filename: str) -> None:
with Image.open(filename) as im:
new_size = (100, 100)
im = im.resize(new_size)
Expand All @@ -314,7 +314,7 @@ def test_resize(filename) -> None:

@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
@pytest.mark.parametrize("filename", (FILE1, FILE2))
def test_thumbnail(filename) -> None:
def test_thumbnail(filename: str) -> None:
# Issue #619
with Image.open(filename) as im:
new_size = (100, 100)
Expand All @@ -335,7 +335,7 @@ def test_readline_psfile(tmp_path: Path) -> None:
line_endings = ["\r\n", "\n", "\n\r", "\r"]
strings = ["something", "else", "baz", "bif"]

def _test_readline(t, ending) -> None:
def _test_readline(t: EpsImagePlugin.PSFile, ending: str) -> None:
ending = "Failure with line ending: %s" % (
"".join("%s" % ord(s) for s in ending)
)
Expand All @@ -344,13 +344,13 @@ def _test_readline(t, ending) -> None:
assert t.readline().strip("\r\n") == "baz", ending
assert t.readline().strip("\r\n") == "bif", ending

def _test_readline_io_psfile(test_string, ending) -> None:
def _test_readline_io_psfile(test_string: str, ending: str) -> None:
f = io.BytesIO(test_string.encode("latin-1"))
with pytest.warns(DeprecationWarning):
t = EpsImagePlugin.PSFile(f)
_test_readline(t, ending)

def _test_readline_file_psfile(test_string, ending) -> None:
def _test_readline_file_psfile(test_string: str, ending: str) -> None:
f = str(tmp_path / "temp.txt")
with open(f, "wb") as w:
w.write(test_string.encode("latin-1"))
Expand All @@ -376,7 +376,7 @@ def test_psfile_deprecation() -> None:
"line_ending",
(b"\r\n", b"\n", b"\n\r", b"\r"),
)
def test_readline(prefix, line_ending) -> None:
def test_readline(prefix: bytes, line_ending: bytes) -> None:
simple_file = prefix + line_ending.join(simple_eps_file_with_comments)
data = io.BytesIO(simple_file)
test_file = EpsImagePlugin.EpsImageFile(data)
Expand All @@ -394,7 +394,7 @@ def test_readline(prefix, line_ending) -> None:
"Tests/images/illuCS6_preview.eps",
),
)
def test_open_eps(filename) -> None:
def test_open_eps(filename: str) -> None:
# https://github.com/python-pillow/Pillow/issues/1104
with Image.open(filename) as img:
assert img.mode == "RGB"
Expand All @@ -417,7 +417,7 @@ def test_emptyline() -> None:
"test_file",
["Tests/images/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],
)
def test_timeout(test_file) -> None:
def test_timeout(test_file: str) -> None:
with open(test_file, "rb") as f:
with pytest.raises(Image.UnidentifiedImageError):
with Image.open(f):
Expand Down
35 changes: 17 additions & 18 deletions Tests/test_file_jpeg.py
Expand Up @@ -5,6 +5,7 @@
import warnings
from io import BytesIO
from pathlib import Path
from typing import Any

import pytest

Expand Down Expand Up @@ -42,7 +43,7 @@

@skip_unless_feature("jpg")
class TestFileJpeg:
def roundtrip(self, im, **options):
def roundtrip(self, im: Image.Image, **options: Any) -> Image.Image:
out = BytesIO()
im.save(out, "JPEG", **options)
test_bytes = out.tell()
Expand All @@ -51,7 +52,7 @@ def roundtrip(self, im, **options):
im.bytes = test_bytes # for testing only
return im

def gen_random_image(self, size, mode: str = "RGB"):
def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Image:
"""Generates a very hard to compress file
:param size: tuple
:param mode: optional image mode
Expand All @@ -71,7 +72,7 @@ def test_sanity(self) -> None:
assert im.get_format_mimetype() == "image/jpeg"

@pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0)))
def test_zero(self, size, tmp_path: Path) -> None:
def test_zero(self, size: tuple[int, int], tmp_path: Path) -> None:
f = str(tmp_path / "temp.jpg")
im = Image.new("RGB", size)
with pytest.raises(ValueError):
Expand Down Expand Up @@ -108,13 +109,11 @@ def test_comment_write(self) -> None:
assert "comment" not in reloaded.info

# Test that a comment argument overrides the default comment
for comment in ("Test comment text", b"Text comment text"):
for comment in ("Test comment text", b"Test comment text"):
out = BytesIO()
im.save(out, format="JPEG", comment=comment)
with Image.open(out) as reloaded:
if not isinstance(comment, bytes):
comment = comment.encode()
assert reloaded.info["comment"] == comment
assert reloaded.info["comment"] == b"Test comment text"

def test_cmyk(self) -> None:
# Test CMYK handling. Thanks to Tim and Charlie for test data,
Expand Down Expand Up @@ -145,7 +144,7 @@ def test_cmyk(self) -> None:
assert k > 0.9

def test_rgb(self) -> None:
def getchannels(im):
def getchannels(im: Image.Image) -> tuple[int, int, int]:
return tuple(v[0] for v in im.layer)

im = hopper()
Expand All @@ -161,8 +160,8 @@ def getchannels(im):
"test_image_path",
[TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"],
)
def test_dpi(self, test_image_path) -> None:
def test(xdpi, ydpi=None):
def test_dpi(self, test_image_path: str) -> None:
def test(xdpi: int, ydpi: int | None = None):
with Image.open(test_image_path) as im:
im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))
return im.info.get("dpi")
Expand Down Expand Up @@ -207,7 +206,7 @@ def test_icc(self, tmp_path: Path) -> None:
ImageFile.MAXBLOCK * 4 + 3, # large block
),
)
def test_icc_big(self, n) -> None:
def test_icc_big(self, n: int) -> None:
# Make sure that the "extra" support handles large blocks
# The ICC APP marker can store 65519 bytes per marker, so
# using a 4-byte test code should allow us to detect out of
Expand Down Expand Up @@ -433,7 +432,7 @@ def test_smooth(self) -> None:
assert_image(im1, im2.mode, im2.size)

def test_subsampling(self) -> None:
def getsampling(im):
def getsampling(im: Image.Image):
layer = im.layer
return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]

Expand Down Expand Up @@ -530,7 +529,7 @@ def test_truncated_jpeg_throws_oserror(self) -> None:
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
)
def test_qtables(self, tmp_path: Path) -> None:
def _n_qtables_helper(n, test_file) -> None:
def _n_qtables_helper(n: int, test_file: str) -> None:
with Image.open(test_file) as im:
f = str(tmp_path / "temp.jpg")
im.save(f, qtables=[[n] * 64] * n)
Expand Down Expand Up @@ -666,7 +665,7 @@ def test_save_low_quality_baseline_qtables(self) -> None:
"blocks, rows, markers",
((0, 0, 0), (1, 0, 15), (3, 0, 5), (8, 0, 1), (0, 1, 3), (0, 2, 1)),
)
def test_restart_markers(self, blocks, rows, markers) -> None:
def test_restart_markers(self, blocks: int, rows: int, markers: int) -> None:
im = Image.new("RGB", (32, 32)) # 16 MCUs
out = BytesIO()
im.save(
Expand Down Expand Up @@ -724,13 +723,13 @@ def test_bad_mpo_header(self) -> None:
assert im.format == "JPEG"

@pytest.mark.parametrize("mode", ("1", "L", "RGB", "RGBX", "CMYK", "YCbCr"))
def test_save_correct_modes(self, mode) -> None:
def test_save_correct_modes(self, mode: str) -> None:
out = BytesIO()
img = Image.new(mode, (20, 20))
img.save(out, "JPEG")

@pytest.mark.parametrize("mode", ("LA", "La", "RGBA", "RGBa", "P"))
def test_save_wrong_modes(self, mode) -> None:
def test_save_wrong_modes(self, mode: str) -> None:
# ref https://github.com/python-pillow/Pillow/issues/2005
out = BytesIO()
img = Image.new(mode, (20, 20))
Expand Down Expand Up @@ -982,12 +981,12 @@ def test_eof(self) -> None:
# Even though this decoder never says that it is finished
# the image should still end when there is no new data
class InfiniteMockPyDecoder(ImageFile.PyDecoder):
def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
return 0, 0

decoder = InfiniteMockPyDecoder(None)

def closure(mode, *args):
def closure(mode: str, *args) -> InfiniteMockPyDecoder:
decoder.__init__(mode, *args)
return decoder

Expand Down
21 changes: 11 additions & 10 deletions Tests/test_file_jpeg2k.py
Expand Up @@ -4,6 +4,7 @@
import re
from io import BytesIO
from pathlib import Path
from typing import Any

import pytest

Expand Down Expand Up @@ -36,7 +37,7 @@
# 'Not enough memory to handle tile data'


def roundtrip(im, **options):
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
out = BytesIO()
im.save(out, "JPEG2000", **options)
test_bytes = out.tell()
Expand Down Expand Up @@ -138,7 +139,7 @@ def test_prog_res_rt() -> None:


@pytest.mark.parametrize("num_resolutions", range(2, 6))
def test_default_num_resolutions(num_resolutions) -> None:
def test_default_num_resolutions(num_resolutions: int) -> None:
d = 1 << (num_resolutions - 1)
im = test_card.resize((d - 1, d - 1))
with pytest.raises(OSError):
Expand Down Expand Up @@ -198,9 +199,9 @@ def test_layers_type(tmp_path: Path) -> None:
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers)

for quality_layers in ["quality_layers", ("100", "50", "10")]:
for quality_layers_str in ["quality_layers", ("100", "50", "10")]:
with pytest.raises(ValueError):
test_card.save(outfile, quality_layers=quality_layers)
test_card.save(outfile, quality_layers=quality_layers_str)


def test_layers() -> None:
Expand Down Expand Up @@ -233,7 +234,7 @@ def test_layers() -> None:
("foo.jp2", {"no_jp2": False}, 4, b"jP"),
),
)
def test_no_jp2(name, args, offset, data) -> None:
def test_no_jp2(name: str, args: dict[str, bool], offset: int, data: bytes) -> None:
out = BytesIO()
if name:
out.name = name
Expand Down Expand Up @@ -278,7 +279,7 @@ def test_sgnd(tmp_path: Path) -> None:


@pytest.mark.parametrize("ext", (".j2k", ".jp2"))
def test_rgba(ext) -> None:
def test_rgba(ext: str) -> None:
# Arrange
with Image.open("Tests/images/rgb_trns_ycbc" + ext) as im:
# Act
Expand All @@ -289,7 +290,7 @@ def test_rgba(ext) -> None:


@pytest.mark.parametrize("ext", (".j2k", ".jp2"))
def test_16bit_monochrome_has_correct_mode(ext) -> None:
def test_16bit_monochrome_has_correct_mode(ext: str) -> None:
with Image.open("Tests/images/16bit.cropped" + ext) as im:
im.load()
assert im.mode == "I;16"
Expand Down Expand Up @@ -346,12 +347,12 @@ def test_parser_feed() -> None:
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
)
@pytest.mark.parametrize("name", ("subsampling_1", "subsampling_2", "zoo1", "zoo2"))
def test_subsampling_decode(name) -> None:
def test_subsampling_decode(name: str) -> None:
test = f"{EXTRA_DIR}/{name}.jp2"
reference = f"{EXTRA_DIR}/{name}.ppm"

with Image.open(test) as im:
epsilon = 3 # for YCbCr images
epsilon = 3.0 # for YCbCr images
with Image.open(reference) as im2:
width, height = im2.size
if name[-1] == "2":
Expand Down Expand Up @@ -400,7 +401,7 @@ def test_save_comment() -> None:
"Tests/images/crash-d2c93af851d3ab9a19e34503626368b2ecde9c03.j2k",
],
)
def test_crashes(test_file) -> None:
def test_crashes(test_file: str) -> None:
with open(test_file, "rb") as f:
with Image.open(f) as im:
# Valgrind should not complain here
Expand Down

0 comments on commit 6782a07

Please sign in to comment.