diff --git a/Tests/helper.py b/Tests/helper.py index 7abc495b6..d6a93a803 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -60,9 +60,7 @@ def convert_to_comparable( return new_a, new_b -def assert_deep_equal( - a: Sequence[Any], b: Sequence[Any], msg: str | None = None -) -> None: +def assert_deep_equal(a: Any, b: Any, msg: str | None = None) -> None: try: assert len(a) == len(b), msg or f"got length {len(a)}, expected {len(b)}" except Exception: diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index f491f9875..8707311dc 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1048,7 +1048,11 @@ class TestFileLibTiff(LibTiffTestCase): ], ) def test_wrong_bits_per_sample( - self, file_name: str, mode: str, size: tuple[int, int], tile + self, + file_name: str, + mode: str, + size: tuple[int, int], + tile: list[tuple[str, tuple[int, int, int, int], int, tuple[Any, ...]]], ) -> None: with Image.open("Tests/images/" + file_name) as im: assert im.mode == mode diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 882dccb32..e0d7999e3 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Generator from pathlib import Path import pytest @@ -96,7 +97,9 @@ def test_write_animation_RGB(tmp_path: Path) -> None: check(temp_file1) # Tests appending using a generator - def im_generator(ims): + def im_generator( + ims: list[Image.Image], + ) -> Generator[Image.Image, None, None]: yield from ims temp_file2 = str(tmp_path / "temp_generator.webp") diff --git a/Tests/test_image.py b/Tests/test_image.py index 07161fcfa..8e6f63fac 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -372,8 +372,9 @@ class TestImage: img = Image.alpha_composite(dst, src) # Assert - img_colors = sorted(img.getcolors()) - assert img_colors == expected_colors + img_colors = img.getcolors() + assert img_colors is not None + assert sorted(img_colors) == expected_colors def test_alpha_inplace(self) -> None: src = Image.new("RGBA", (128, 128), "blue") @@ -670,7 +671,9 @@ class TestImage: im_remapped = im.remap_palette([1, 0]) assert im_remapped.info["transparency"] == 1 - assert len(im_remapped.getpalette()) == 6 + palette = im_remapped.getpalette() + assert palette is not None + assert len(palette) == 6 # Test unused transparency im.info["transparency"] = 2 diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index c860771dc..854c79dae 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -27,7 +27,9 @@ class TestImagePutPixel: for y in range(im1.size[1]): for x in range(im1.size[0]): pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) + value = im1.getpixel(pos) + assert value is not None + im2.putpixel(pos, value) assert_image_equal(im1, im2) @@ -37,7 +39,9 @@ class TestImagePutPixel: for y in range(im1.size[1]): for x in range(im1.size[0]): pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) + value = im1.getpixel(pos) + assert value is not None + im2.putpixel(pos, value) assert not im2.readonly assert_image_equal(im1, im2) @@ -50,9 +54,9 @@ class TestImagePutPixel: assert pix1 is not None assert pix2 is not None with pytest.raises(TypeError): - pix1[0, "0"] + pix1[0, "0"] # type: ignore[index] with pytest.raises(TypeError): - pix1["0", 0] + pix1["0", 0] # type: ignore[index] for y in range(im1.size[1]): for x in range(im1.size[0]): @@ -71,7 +75,9 @@ class TestImagePutPixel: for y in range(-1, -im1.size[1] - 1, -1): for x in range(-1, -im1.size[0] - 1, -1): pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) + value = im1.getpixel(pos) + assert value is not None + im2.putpixel(pos, value) assert_image_equal(im1, im2) @@ -81,7 +87,9 @@ class TestImagePutPixel: for y in range(-1, -im1.size[1] - 1, -1): for x in range(-1, -im1.size[0] - 1, -1): pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) + value = im1.getpixel(pos) + assert value is not None + im2.putpixel(pos, value) assert not im2.readonly assert_image_equal(im1, im2) @@ -219,7 +227,7 @@ class TestImagePutPixelError: im = hopper(mode) for v in self.INVALID_TYPES: with pytest.raises(TypeError, match="color must be int or tuple"): - im.putpixel((0, 0), v) + im.putpixel((0, 0), v) # type: ignore[arg-type] @pytest.mark.parametrize( ("mode", "band_numbers", "match"), @@ -253,7 +261,7 @@ class TestImagePutPixelError: with pytest.raises( TypeError, match="color must be int or single-element tuple" ): - im.putpixel((0, 0), v) + im.putpixel((0, 0), v) # type: ignore[arg-type] @pytest.mark.parametrize("mode", IMAGE_MODES1 + IMAGE_MODES2) def test_putpixel_overflow_error(self, mode: str) -> None: diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 5b63ceec3..ebb7f2822 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -225,7 +225,7 @@ def test_l_macro_rounding(convert_mode: str) -> None: assert px is not None converted_color = px[0, 0] if convert_mode == "LA": - assert converted_color is not None + assert isinstance(converted_color, tuple) converted_color = converted_color[0] assert converted_color == 1 diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py index 8f8870f4f..8dbe82b29 100644 --- a/Tests/test_image_getcolors.py +++ b/Tests/test_image_getcolors.py @@ -54,17 +54,21 @@ def test_pack() -> None: assert A is None A = im.getcolors(maxcolors=3) + assert A is not None A.sort() assert A == expected A = im.getcolors(maxcolors=4) + assert A is not None A.sort() assert A == expected A = im.getcolors(maxcolors=8) + assert A is not None A.sort() assert A == expected A = im.getcolors(maxcolors=16) + assert A is not None A.sort() assert A == expected diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index dad26ef14..27cb7c59d 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -31,7 +31,7 @@ def test_sanity() -> None: def test_long_integers() -> None: # see bug-200802-systemerror - def put(value: int) -> tuple[int, int, int, int]: + def put(value: int) -> float | tuple[int, ...] | None: im = Image.new("RGBA", (1, 1)) im.putdata([value]) return im.getpixel((0, 0)) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 9f8aa601c..903cd8550 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -31,7 +31,9 @@ def test_libimagequant_quantize() -> None: converted = image.quantize(100, Image.Quantize.LIBIMAGEQUANT) assert converted.mode == "P" assert_image_similar(converted.convert("RGB"), image, 15) - assert len(converted.getcolors()) == 100 + colors = converted.getcolors() + assert colors is not None + assert len(colors) == 100 def test_octree_quantize() -> None: @@ -39,7 +41,9 @@ def test_octree_quantize() -> None: converted = image.quantize(100, Image.Quantize.FASTOCTREE) assert converted.mode == "P" assert_image_similar(converted.convert("RGB"), image, 20) - assert len(converted.getcolors()) == 100 + colors = converted.getcolors() + assert colors is not None + assert len(colors) == 100 def test_rgba_quantize() -> None: @@ -158,4 +162,6 @@ def test_small_palette() -> None: im = im.quantize(palette=p) # Assert - assert len(im.getcolors()) == 2 + quantized_colors = im.getcolors() + assert quantized_colors is not None + assert len(quantized_colors) == 2 diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 92ba1553c..ce6209c0d 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -237,13 +237,13 @@ class TestImagingCoreResampleAccuracy: class TestCoreResampleConsistency: def make_case( self, mode: str, fill: tuple[int, int, int] | float - ) -> tuple[Image.Image, tuple[int, ...]]: + ) -> tuple[Image.Image, float | tuple[int, ...]]: im = Image.new(mode, (512, 9), fill) px = im.load() assert px is not None return im.resize((9, 512), Image.Resampling.LANCZOS), px[0, 0] - def run_case(self, case: tuple[Image.Image, int | tuple[int, ...]]) -> None: + def run_case(self, case: tuple[Image.Image, float | tuple[int, ...]]) -> None: channel, color = case px = channel.load() assert px is not None @@ -256,6 +256,7 @@ class TestCoreResampleConsistency: def test_8u(self) -> None: im, color = self.make_case("RGB", (0, 64, 255)) r, g, b = im.split() + assert isinstance(color, tuple) self.run_case((r, color[0])) self.run_case((g, color[1])) self.run_case((b, color[2])) @@ -290,7 +291,11 @@ class TestCoreResampleAlphaCorrect: px = i.load() assert px is not None for y in range(i.size[1]): - used_colors = {px[x, y][0] for x in range(i.size[0])} + used_colors = set() + for x in range(i.size[0]): + value = px[x, y] + assert isinstance(value, tuple) + used_colors.add(value[0]) assert 256 == len(used_colors), ( "All colors should be present in resized image. " f"Only {len(used_colors)} on line {y}." @@ -332,12 +337,13 @@ class TestCoreResampleAlphaCorrect: assert px is not None for y in range(i.size[1]): for x in range(i.size[0]): - if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel: + value = px[x, y] + assert isinstance(value, tuple) + if value[-1] != 0 and value[:-1] != clean_pixel: message = ( - f"pixel at ({x}, {y}) is different:\n" - f"{px[x, y]}\n{clean_pixel}" + f"pixel at ({x}, {y}) is different:\n{value}\n{clean_pixel}" ) - assert px[x, y][:3] == clean_pixel, message + assert value[:3] == clean_pixel, message def test_dirty_pixels_rgba(self) -> None: case = self.make_dirty_case("RGBA", (255, 255, 0, 128), (0, 0, 255, 0)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 638d12247..7e83396de 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -192,8 +192,9 @@ class TestImageTransform: im = op(im, (40, 10)) - colors = sorted(im.getcolors()) - assert colors == sorted( + colors = im.getcolors() + assert colors is not None + assert sorted(colors) == sorted( ( (20 * 10, opaque), (20 * 10, transparent), diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 7e2290c15..4fc28cdb9 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -391,23 +391,25 @@ def test_overlay() -> None: def test_logical() -> None: def table( op: Callable[[Image.Image, Image.Image], Image.Image], a: int, b: int - ) -> tuple[int, int, int, int]: + ) -> list[float]: out = [] for x in (a, b): imx = Image.new("1", (1, 1), x) for y in (a, b): imy = Image.new("1", (1, 1), y) - out.append(op(imx, imy).getpixel((0, 0))) - return tuple(out) + value = op(imx, imy).getpixel((0, 0)) + assert not isinstance(value, tuple) and value is not None + out.append(value) + return out - assert table(ImageChops.logical_and, 0, 1) == (0, 0, 0, 255) - assert table(ImageChops.logical_or, 0, 1) == (0, 255, 255, 255) - assert table(ImageChops.logical_xor, 0, 1) == (0, 255, 255, 0) + assert table(ImageChops.logical_and, 0, 1) == [0, 0, 0, 255] + assert table(ImageChops.logical_or, 0, 1) == [0, 255, 255, 255] + assert table(ImageChops.logical_xor, 0, 1) == [0, 255, 255, 0] - assert table(ImageChops.logical_and, 0, 128) == (0, 0, 0, 255) - assert table(ImageChops.logical_or, 0, 128) == (0, 255, 255, 255) - assert table(ImageChops.logical_xor, 0, 128) == (0, 255, 255, 0) + assert table(ImageChops.logical_and, 0, 128) == [0, 0, 0, 255] + assert table(ImageChops.logical_or, 0, 128) == [0, 255, 255, 255] + assert table(ImageChops.logical_xor, 0, 128) == [0, 255, 255, 0] - assert table(ImageChops.logical_and, 0, 255) == (0, 0, 0, 255) - assert table(ImageChops.logical_or, 0, 255) == (0, 255, 255, 255) - assert table(ImageChops.logical_xor, 0, 255) == (0, 255, 255, 0) + assert table(ImageChops.logical_and, 0, 255) == [0, 0, 0, 255] + assert table(ImageChops.logical_or, 0, 255) == [0, 255, 255, 255] + assert table(ImageChops.logical_xor, 0, 255) == [0, 255, 255, 0] diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index c51f1155d..5ee5fcedf 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -691,7 +691,9 @@ def test_rgb_lab(mode: str) -> None: im = Image.new("LAB", (1, 1), (255, 0, 0)) converted_im = im.convert(mode) - assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255) + value = converted_im.getpixel((0, 0)) + assert isinstance(value, tuple) + assert value[:3] == (0, 255, 255) def test_deprecation() -> None: diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 1ff3fea2a..e397978cb 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,8 +1,8 @@ from __future__ import annotations -import contextlib import os.path from collections.abc import Sequence +from typing import Callable import pytest @@ -1422,25 +1422,44 @@ def test_default_font_size() -> None: im = Image.new("RGB", (220, 25)) draw = ImageDraw.Draw(im) - with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError): + + def check(func: Callable[[], None]) -> None: + if freetype_support: + func() + else: + with pytest.raises(ImportError): + func() + + def draw_text() -> None: draw.text((0, 0), text, font_size=16) assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png") - with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError): + check(draw_text) + + def draw_textlength() -> None: assert draw.textlength(text, font_size=16) == 216 - with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError): + check(draw_textlength) + + def draw_textbbox() -> None: assert draw.textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19) + check(draw_textbbox) + im = Image.new("RGB", (220, 25)) draw = ImageDraw.Draw(im) - with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError): + + def draw_multiline_text() -> None: draw.multiline_text((0, 0), text, font_size=16) assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png") - with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError): + check(draw_multiline_text) + + def draw_multiline_textbbox() -> None: assert draw.multiline_textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19) + check(draw_multiline_textbbox) + @pytest.mark.parametrize("bbox", BBOX) def test_same_color_outline(bbox: Coords) -> None: diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 68b28ef07..bb686bb3b 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -305,7 +305,7 @@ class TestPyDecoder(CodecsTest): def test_decode(self) -> None: decoder = ImageFile.PyDecoder(None) with pytest.raises(NotImplementedError): - decoder.decode(None) + decoder.decode(b"") class TestPyEncoder(CodecsTest): diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 32615cf0e..4363f456e 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -41,11 +41,15 @@ A = string_to_img( def img_to_string(im: Image.Image) -> str: """Turn a (small) binary image into a string representation""" chars = ".1" - width, height = im.size - return "\n".join( - "".join(chars[im.getpixel((c, r)) > 0] for c in range(width)) - for r in range(height) - ) + result = [] + for r in range(im.height): + line = "" + for c in range(im.width): + value = im.getpixel((c, r)) + assert not isinstance(value, tuple) and value is not None + line += chars[value > 0] + result.append(line) + return "\n".join(result) def img_string_normalize(im: str) -> str: diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 3598df830..e33e6d4c8 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -259,20 +259,26 @@ def test_colorize_2color() -> None: left = (0, 1) middle = (127, 1) right = (255, 1) + value = im_test.getpixel(left) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(left), + value, (255, 0, 0), threshold=1, msg="black test pixel incorrect", ) + value = im_test.getpixel(middle) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(middle), + value, (127, 63, 0), threshold=1, msg="mid test pixel incorrect", ) + value = im_test.getpixel(right) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(right), + value, (0, 127, 0), threshold=1, msg="white test pixel incorrect", @@ -295,20 +301,26 @@ def test_colorize_2color_offset() -> None: left = (25, 1) middle = (75, 1) right = (125, 1) + value = im_test.getpixel(left) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(left), + value, (255, 0, 0), threshold=1, msg="black test pixel incorrect", ) + value = im_test.getpixel(middle) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(middle), + value, (127, 63, 0), threshold=1, msg="mid test pixel incorrect", ) + value = im_test.getpixel(right) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(right), + value, (0, 127, 0), threshold=1, msg="white test pixel incorrect", @@ -339,29 +351,37 @@ def test_colorize_3color_offset() -> None: middle = (100, 1) right_middle = (150, 1) right = (225, 1) + value = im_test.getpixel(left) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(left), + value, (255, 0, 0), threshold=1, msg="black test pixel incorrect", ) + value = im_test.getpixel(left_middle) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(left_middle), + value, (127, 0, 127), threshold=1, msg="low-mid test pixel incorrect", ) + value = im_test.getpixel(middle) + assert isinstance(value, tuple) + assert_tuple_approx_equal(value, (0, 0, 255), threshold=1, msg="mid incorrect") + value = im_test.getpixel(right_middle) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(middle), (0, 0, 255), threshold=1, msg="mid incorrect" - ) - assert_tuple_approx_equal( - im_test.getpixel(right_middle), + value, (0, 63, 127), threshold=1, msg="high-mid test pixel incorrect", ) + value = im_test.getpixel(right) + assert isinstance(value, tuple) assert_tuple_approx_equal( - im_test.getpixel(right), + value, (0, 127, 0), threshold=1, msg="white test pixel incorrect", @@ -444,6 +464,7 @@ def test_exif_transpose_xml_without_xmp() -> None: del im.info["xmp"] transposed_im = ImageOps.exif_transpose(im) + assert transposed_im is not None assert 0x0112 not in transposed_im.getexif() diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index e5869892e..f84c6c03a 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -45,10 +45,12 @@ def test_kw() -> None: # Test "file" im = ImageTk._get_image_from_kw(kw) + assert im is not None assert_image_equal(im, im1) # Test "data" im = ImageTk._get_image_from_kw(kw) + assert im is not None assert_image_equal(im, im2) # Test no relevant entry diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index ae80b98b8..9d06a9332 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -11,7 +11,11 @@ from PIL.TiffImagePlugin import IFDRational from .helper import hopper, skip_unless_feature -def _test_equal(num, denom, target) -> None: +def _test_equal( + num: float | Fraction | IFDRational, + denom: int, + target: float | Fraction | IFDRational, +) -> None: t = IFDRational(num, denom) assert target == t