From 486dac7efcc3fd937a00f4180ef32f4a75686f15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Jul 2024 19:17:23 +1000 Subject: [PATCH] Added type hints --- Tests/test_file_gif.py | 2 +- Tests/test_file_libtiff.py | 21 ++++++++++++++++----- Tests/test_file_pdf.py | 2 +- Tests/test_file_tiff.py | 22 +++++++++++++--------- Tests/test_image.py | 2 +- Tests/test_imagegrab.py | 7 ++++--- pyproject.toml | 8 ++++++++ src/PIL/Image.py | 4 ++-- tox.ini | 4 +++- 9 files changed, 49 insertions(+), 23 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 79a2ec0ab37..85b017d29af 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -353,7 +353,7 @@ def test_palette_434(tmp_path: Path) -> None: def roundtrip(im: Image.Image, **kwargs: bool) -> Image.Image: out = str(tmp_path / "temp.gif") - im.copy().save(out, **kwargs) + im.copy().save(out, "GIF", **kwargs) reloaded = Image.open(out) return reloaded diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 8707311dc8c..d5dbeeb6f74 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -92,11 +92,22 @@ def test_g4_tiff_bytesio(self, tmp_path: Path) -> None: def test_g4_non_disk_file_object(self, tmp_path: Path) -> None: """Testing loading from non-disk non-BytesIO file object""" test_file = "Tests/images/hopper_g4_500.tif" - s = io.BytesIO() with open(test_file, "rb") as f: - s.write(f.read()) - s.seek(0) - r = io.BufferedReader(s) + data = f.read() + + class NonBytesIO(io.RawIOBase): + def read(self, size: int = -1) -> bytes: + nonlocal data + if size == -1: + size = len(data) + result = data[:size] + data = data[size:] + return result + + def readable(self) -> bool: + return True + + r = io.BufferedReader(NonBytesIO()) with Image.open(r) as im: assert im.size == (500, 500) self._assert_noerr(tmp_path, im) @@ -1139,7 +1150,7 @@ def test_save_single_strip(self, argument: bool, tmp_path: Path) -> None: arguments: dict[str, str | int] = {"compression": "tiff_adobe_deflate"} if argument: arguments["strip_size"] = 2**18 - im.save(out, **arguments) + im.save(out, "TIFF", **arguments) with Image.open(out) as im: assert isinstance(im, TiffImagePlugin.TiffImageFile) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 02f07a565d7..3729ca58b34 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -118,7 +118,7 @@ def test_dpi(params: dict[str, int | tuple[int, int]], tmp_path: Path) -> None: im = hopper() outfile = str(tmp_path / "temp.pdf") - im.save(outfile, **params) + im.save(outfile, "PDF", **params) with open(outfile, "rb") as fp: contents = fp.read() diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 93a9f9e6a61..8cad252724c 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -78,6 +78,7 @@ def test_closed_file(self) -> None: def test_seek_after_close(self) -> None: im = Image.open("Tests/images/multipage.tiff") + assert isinstance(im, TiffImagePlugin.TiffImageFile) im.close() with pytest.raises(ValueError): @@ -424,13 +425,13 @@ def test_load_string(self) -> None: def test_load_float(self) -> None: ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdabcd" - ret = ifd.load_float(data, False) + ret = getattr(ifd, "load_float")(data, False) assert ret == (1.6777999408082104e22, 1.6777999408082104e22) def test_load_double(self) -> None: ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdefghabcdefgh" - ret = ifd.load_double(data, False) + ret = getattr(ifd, "load_double")(data, False) assert ret == (8.540883223036124e194, 8.540883223036124e194) def test_ifd_tag_type(self) -> None: @@ -599,7 +600,7 @@ def test_gray_semibyte_per_pixel(self) -> None: def test_with_underscores(self, tmp_path: Path) -> None: kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36} filename = str(tmp_path / "temp.tif") - hopper("RGB").save(filename, **kwargs) + hopper("RGB").save(filename, "TIFF", **kwargs) with Image.open(filename) as im: # legacy interface assert im.tag[X_RESOLUTION][0][0] == 72 @@ -624,14 +625,17 @@ def test_roundtrip_tiff_uint16(self, tmp_path: Path) -> None: def test_iptc(self, tmp_path: Path) -> None: # Do not preserve IPTC_NAA_CHUNK by default if type is LONG outfile = str(tmp_path / "temp.tif") - im = hopper() - ifd = TiffImagePlugin.ImageFileDirectory_v2() - ifd[33723] = 1 - ifd.tagtype[33723] = 4 - im.tag_v2 = ifd - im.save(outfile) + with Image.open("Tests/images/hopper.tif") as im: + im.load() + assert isinstance(im, TiffImagePlugin.TiffImageFile) + ifd = TiffImagePlugin.ImageFileDirectory_v2() + ifd[33723] = 1 + ifd.tagtype[33723] = 4 + im.tag_v2 = ifd + im.save(outfile) with Image.open(outfile) as im: + assert isinstance(im, TiffImagePlugin.TiffImageFile) assert 33723 not in im.tag_v2 def test_rowsperstrip(self, tmp_path: Path) -> None: diff --git a/Tests/test_image.py b/Tests/test_image.py index 8e6f63face6..4fdc4179170 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -704,7 +704,7 @@ def _make_new( else: assert new_image.palette is None - _make_new(im, im_p, ImagePalette.ImagePalette(list(range(256)) * 3)) + _make_new(im, im_p, ImagePalette.ImagePalette("RGB")) _make_new(im_p, im, None) _make_new(im, blank_p, ImagePalette.ImagePalette()) _make_new(im, blank_pa, ImagePalette.ImagePalette()) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 5dfa51697c2..5cd510751e2 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -60,6 +60,8 @@ def test_grab_invalid_xdisplay(self) -> None: def test_grabclipboard(self) -> None: if sys.platform == "darwin": subprocess.call(["screencapture", "-cx"]) + + ImageGrab.grabclipboard() elif sys.platform == "win32": p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE) p.stdin.write( @@ -69,6 +71,8 @@ def test_grabclipboard(self) -> None: [Windows.Forms.Clipboard]::SetImage($bmp)""" ) p.communicate() + + ImageGrab.grabclipboard() else: if not shutil.which("wl-paste") and not shutil.which("xclip"): with pytest.raises( @@ -77,9 +81,6 @@ def test_grabclipboard(self) -> None: r" ImageGrab.grabclipboard\(\) on Linux", ): ImageGrab.grabclipboard() - return - - ImageGrab.grabclipboard() @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") def test_grabclipboard_file(self) -> None: diff --git a/pyproject.toml b/pyproject.toml index 700d5e7fec4..cd724866984 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,3 +155,11 @@ follow_imports = "silent" warn_redundant_casts = true warn_unreachable = true warn_unused_ignores = true +exclude = [ + '^Tests/oss-fuzz/fuzz_font.py$', + '^Tests/oss-fuzz/fuzz_pillow.py$', + '^Tests/test_qt_image_qapplication.py$', + '^Tests/test_font_pcf_charsets.py$', + '^Tests/test_font_pcf.py$', + '^Tests/test_file_tar.py$', +] diff --git a/src/PIL/Image.py b/src/PIL/Image.py index dbb72ed6afa..565abe71d45 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1886,7 +1886,7 @@ def alpha_composite( def point( self, - lut: Sequence[float] | Callable[[int], float] | ImagePointHandler, + lut: Sequence[float] | NumpyArray | Callable[[int], float] | ImagePointHandler, mode: str | None = None, ) -> Image: """ @@ -1996,7 +1996,7 @@ def putalpha(self, alpha: Image | int) -> None: def putdata( self, - data: Sequence[float] | Sequence[Sequence[int]], + data: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, scale: float = 1.0, offset: float = 0.0, ) -> None: diff --git a/tox.ini b/tox.ini index 18941523785..c1bc3b17db3 100644 --- a/tox.ini +++ b/tox.ini @@ -38,9 +38,11 @@ deps = ipython numpy packaging + pytest types-defusedxml types-olefile + types-setuptools extras = typing commands = - mypy src {posargs} + mypy src Tests {posargs}