From a09e056a4ca9ab5283b14bfd9ccec9aeb0757643 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Jan 2024 18:42:43 +1100 Subject: [PATCH 1/4] Added type hints --- Tests/test_font_bdf.py | 4 +-- Tests/test_font_crash.py | 4 +-- Tests/test_font_leaks.py | 6 ++-- Tests/test_font_pcf.py | 21 +++++++----- Tests/test_font_pcf_charsets.py | 59 +++++++++++++++++++-------------- 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index e5e85618651..136070f9e7d 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -7,7 +7,7 @@ filename = "Tests/images/courB08.bdf" -def test_sanity(): +def test_sanity() -> None: with open(filename, "rb") as test_file: font = BdfFontFile.BdfFontFile(test_file) @@ -15,7 +15,7 @@ def test_sanity(): assert len([_f for _f in font.glyph if _f]) == 190 -def test_invalid_file(): +def test_invalid_file() -> None: with open("Tests/images/flower.jpg", "rb") as fp: with pytest.raises(SyntaxError): BdfFontFile.BdfFontFile(fp) diff --git a/Tests/test_font_crash.py b/Tests/test_font_crash.py index e3c72c1aebd..b82340ef70b 100644 --- a/Tests/test_font_crash.py +++ b/Tests/test_font_crash.py @@ -8,7 +8,7 @@ class TestFontCrash: - def _fuzz_font(self, font): + def _fuzz_font(self, font: ImageFont.FreeTypeFont) -> None: # from fuzzers.fuzz_font font.getbbox("ABC") font.getmask("test text") @@ -18,7 +18,7 @@ def _fuzz_font(self, font): draw.text((10, 10), "Test Text", font=font, fill="#000") @skip_unless_feature("freetype2") - def test_segfault(self): + def test_segfault(self) -> None: with pytest.raises(OSError): font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784") self._fuzz_font(font) diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 4e29a856bc9..241f455b813 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -10,7 +10,7 @@ class TestTTypeFontLeak(PillowLeakTestCase): iterations = 10 mem_limit = 4096 # k - def _test_font(self, font): + def _test_font(self, font: ImageFont.FreeTypeFont) -> None: im = Image.new("RGB", (255, 255), "white") draw = ImageDraw.ImageDraw(im) self._test_leak( @@ -20,7 +20,7 @@ def _test_font(self, font): ) @skip_unless_feature("freetype2") - def test_leak(self): + def test_leak(self) -> None: ttype = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20) self._test_font(ttype) @@ -30,6 +30,6 @@ class TestDefaultFontLeak(TestTTypeFontLeak): iterations = 100 mem_limit = 1024 # k - def test_leak(self): + def test_leak(self) -> None: default_font = ImageFont.load_default() self._test_font(default_font) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index e6abede0787..0f1eabdce39 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from pathlib import PosixPath import pytest @@ -20,7 +21,7 @@ pytestmark = skip_unless_feature("zlib") -def save_font(request, tmp_path): +def save_font(request: pytest.FixtureRequest, tmp_path: PosixPath) -> str: with open(fontname, "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file) assert isinstance(font, FontFile.FontFile) @@ -29,7 +30,7 @@ def save_font(request, tmp_path): tempname = str(tmp_path / "temp.pil") - def delete_tempfile(): + def delete_tempfile() -> None: try: os.remove(tempname[:-4] + ".pbm") except OSError: @@ -47,11 +48,11 @@ def delete_tempfile(): return tempname -def test_sanity(request, tmp_path): +def test_sanity(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: save_font(request, tmp_path) -def test_less_than_256_characters(): +def test_less_than_256_characters() -> None: with open("Tests/fonts/10x20-ISO8859-1-fewer-characters.pcf", "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file) assert isinstance(font, FontFile.FontFile) @@ -59,13 +60,13 @@ def test_less_than_256_characters(): assert len([_f for _f in font.glyph if _f]) == 127 -def test_invalid_file(): +def test_invalid_file() -> None: with open("Tests/images/flower.jpg", "rb") as fp: with pytest.raises(SyntaxError): PcfFontFile.PcfFontFile(fp) -def test_draw(request, tmp_path): +def test_draw(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) im = Image.new("L", (130, 30), "white") @@ -74,7 +75,7 @@ def test_draw(request, tmp_path): assert_image_similar_tofile(im, "Tests/images/test_draw_pbm_target.png", 0) -def test_textsize(request, tmp_path): +def test_textsize(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) for i in range(255): @@ -90,7 +91,9 @@ def test_textsize(request, tmp_path): assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20) -def _test_high_characters(request, tmp_path, message): +def _test_high_characters( + request: pytest.FixtureRequest, tmp_path: PosixPath, message: str | bytes +) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) im = Image.new("L", (750, 30), "white") @@ -99,7 +102,7 @@ def _test_high_characters(request, tmp_path, message): assert_image_similar_tofile(im, "Tests/images/high_ascii_chars.png", 0) -def test_high_characters(request, tmp_path): +def test_high_characters(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: message = "".join(chr(i + 1) for i in range(140, 232)) _test_high_characters(request, tmp_path, message) # accept bytes instances. diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index 4c2d7185e3c..9dfaa404e28 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from pathlib import PosixPath import pytest @@ -14,38 +15,40 @@ fontname = "Tests/fonts/ter-x20b.pcf" -charsets = { - "iso8859-1": { - "glyph_count": 223, - "message": "hello, world", - "image1": "Tests/images/test_draw_pbm_ter_en_target.png", - }, - "iso8859-2": { - "glyph_count": 223, - "message": "witaj świecie", - "image1": "Tests/images/test_draw_pbm_ter_pl_target.png", - }, - "cp1250": { - "glyph_count": 250, - "message": "witaj świecie", - "image1": "Tests/images/test_draw_pbm_ter_pl_target.png", - }, +charsets: dict[str, tuple[int, str, str]] = { + "iso8859-1": ( + 223, + "hello, world", + "Tests/images/test_draw_pbm_ter_en_target.png", + ), + "iso8859-2": ( + 223, + "witaj świecie", + "Tests/images/test_draw_pbm_ter_pl_target.png", + ), + "cp1250": ( + 250, + "witaj świecie", + "Tests/images/test_draw_pbm_ter_pl_target.png", + ), } pytestmark = skip_unless_feature("zlib") -def save_font(request, tmp_path, encoding): +def save_font( + request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str +) -> str: with open(fontname, "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file, encoding) assert isinstance(font, FontFile.FontFile) # check the number of characters in the font - assert len([_f for _f in font.glyph if _f]) == charsets[encoding]["glyph_count"] + assert len([_f for _f in font.glyph if _f]) == charsets[encoding][0] tempname = str(tmp_path / "temp.pil") - def delete_tempfile(): + def delete_tempfile() -> None: try: os.remove(tempname[:-4] + ".pbm") except OSError: @@ -64,23 +67,29 @@ def delete_tempfile(): @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) -def test_sanity(request, tmp_path, encoding): +def test_sanity( + request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str +) -> None: save_font(request, tmp_path, encoding) @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) -def test_draw(request, tmp_path, encoding): +def test_draw( + request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str +) -> None: tempname = save_font(request, tmp_path, encoding) font = ImageFont.load(tempname) im = Image.new("L", (150, 30), "white") draw = ImageDraw.Draw(im) - message = charsets[encoding]["message"].encode(encoding) + message = charsets[encoding][1].encode(encoding) draw.text((0, 0), message, "black", font=font) - assert_image_similar_tofile(im, charsets[encoding]["image1"], 0) + assert_image_similar_tofile(im, charsets[encoding][2], 0) @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) -def test_textsize(request, tmp_path, encoding): +def test_textsize( + request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str +) -> None: tempname = save_font(request, tmp_path, encoding) font = ImageFont.load(tempname) for i in range(255): @@ -90,7 +99,7 @@ def test_textsize(request, tmp_path, encoding): assert dy == 20 assert dx in (0, 10) assert font.getlength(bytearray([i])) == dx - message = charsets[encoding]["message"].encode(encoding) + message = charsets[encoding][1].encode(encoding) for i in range(len(message)): msg = message[: i + 1] assert font.getlength(msg) == len(msg) * 10 From 2521ec4732311ffd85444253d755e3acee89c10f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Jan 2024 22:08:45 +1100 Subject: [PATCH 2/4] Restored charsets dictionary --- Tests/test_font_pcf_charsets.py | 53 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index 9dfaa404e28..cb77128eff7 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -15,22 +15,22 @@ fontname = "Tests/fonts/ter-x20b.pcf" -charsets: dict[str, tuple[int, str, str]] = { - "iso8859-1": ( - 223, - "hello, world", - "Tests/images/test_draw_pbm_ter_en_target.png", - ), - "iso8859-2": ( - 223, - "witaj świecie", - "Tests/images/test_draw_pbm_ter_pl_target.png", - ), - "cp1250": ( - 250, - "witaj świecie", - "Tests/images/test_draw_pbm_ter_pl_target.png", - ), +charsets: dict[str, dict[str, int | str]] = { + "iso8859-1": { + "glyph_count": 223, + "message": "hello, world", + "image1": "Tests/images/test_draw_pbm_ter_en_target.png", + }, + "iso8859-2": { + "glyph_count": 223, + "message": "witaj świecie", + "image1": "Tests/images/test_draw_pbm_ter_pl_target.png", + }, + "cp1250": { + "glyph_count": 250, + "message": "witaj świecie", + "image1": "Tests/images/test_draw_pbm_ter_pl_target.png", + }, } @@ -44,7 +44,7 @@ def save_font( font = PcfFontFile.PcfFontFile(test_file, encoding) assert isinstance(font, FontFile.FontFile) # check the number of characters in the font - assert len([_f for _f in font.glyph if _f]) == charsets[encoding][0] + assert len([_f for _f in font.glyph if _f]) == charsets[encoding]["glyph_count"] tempname = str(tmp_path / "temp.pil") @@ -81,9 +81,14 @@ def test_draw( font = ImageFont.load(tempname) im = Image.new("L", (150, 30), "white") draw = ImageDraw.Draw(im) - message = charsets[encoding][1].encode(encoding) - draw.text((0, 0), message, "black", font=font) - assert_image_similar_tofile(im, charsets[encoding][2], 0) + + message = charsets[encoding]["message"] + assert isinstance(message, str) + draw.text((0, 0), message.encode(encoding), "black", font=font) + + expected_path = charsets[encoding]["image1"] + assert isinstance(expected_path, str) + assert_image_similar_tofile(im, expected_path, 0) @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) @@ -99,8 +104,10 @@ def test_textsize( assert dy == 20 assert dx in (0, 10) assert font.getlength(bytearray([i])) == dx - message = charsets[encoding][1].encode(encoding) - for i in range(len(message)): - msg = message[: i + 1] + message = charsets[encoding]["message"] + assert isinstance(message, str) + message_bytes = message.encode(encoding) + for i in range(len(message_bytes)): + msg = message_bytes[: i + 1] assert font.getlength(msg) == len(msg) * 10 assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20) From 16fd934b007d7090fc32ff4a1ad13182a32bf612 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jan 2024 09:55:25 +1100 Subject: [PATCH 3/4] Use TypedDict --- Tests/test_font_pcf_charsets.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index cb77128eff7..894d4eb56bf 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -2,6 +2,7 @@ import os from pathlib import PosixPath +from typing import TypedDict import pytest @@ -15,7 +16,14 @@ fontname = "Tests/fonts/ter-x20b.pcf" -charsets: dict[str, dict[str, int | str]] = { + +class Charset(TypedDict): + glyph_count: int + message: str + image1: str + + +charsets: dict[str, Charset] = { "iso8859-1": { "glyph_count": 223, "message": "hello, world", @@ -81,14 +89,9 @@ def test_draw( font = ImageFont.load(tempname) im = Image.new("L", (150, 30), "white") draw = ImageDraw.Draw(im) - - message = charsets[encoding]["message"] - assert isinstance(message, str) - draw.text((0, 0), message.encode(encoding), "black", font=font) - - expected_path = charsets[encoding]["image1"] - assert isinstance(expected_path, str) - assert_image_similar_tofile(im, expected_path, 0) + message = charsets[encoding]["message"].encode(encoding) + draw.text((0, 0), message, "black", font=font) + assert_image_similar_tofile(im, charsets[encoding]["image1"], 0) @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) @@ -104,10 +107,8 @@ def test_textsize( assert dy == 20 assert dx in (0, 10) assert font.getlength(bytearray([i])) == dx - message = charsets[encoding]["message"] - assert isinstance(message, str) - message_bytes = message.encode(encoding) - for i in range(len(message_bytes)): - msg = message_bytes[: i + 1] + message = charsets[encoding]["message"].encode(encoding) + for i in range(len(message)): + msg = message[: i + 1] assert font.getlength(msg) == len(msg) * 10 assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20) From 4814bee6c0b99b4e00fa08a5d15663f8238f063a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jan 2024 21:42:36 +1100 Subject: [PATCH 4/4] Use Path instead of PosixPath --- Tests/check_j2k_overflow.py | 4 ++-- Tests/check_large_memory.py | 8 ++++---- Tests/check_large_memory_numpy.py | 8 ++++---- Tests/test_font_pcf.py | 14 +++++++------- Tests/test_font_pcf_charsets.py | 16 +++++----------- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/Tests/check_j2k_overflow.py b/Tests/check_j2k_overflow.py index 8a85783fccb..dbdd5a4f557 100644 --- a/Tests/check_j2k_overflow.py +++ b/Tests/check_j2k_overflow.py @@ -1,13 +1,13 @@ from __future__ import annotations -from pathlib import PosixPath +from pathlib import Path import pytest from PIL import Image -def test_j2k_overflow(tmp_path: PosixPath) -> None: +def test_j2k_overflow(tmp_path: Path) -> None: im = Image.new("RGBA", (1024, 131584)) target = str(tmp_path / "temp.jpc") with pytest.raises(OSError): diff --git a/Tests/check_large_memory.py b/Tests/check_large_memory.py index 2c8c77800bb..a9ce79e57e6 100644 --- a/Tests/check_large_memory.py +++ b/Tests/check_large_memory.py @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from pathlib import PosixPath +from pathlib import Path from types import ModuleType import pytest @@ -31,18 +31,18 @@ pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system") -def _write_png(tmp_path: PosixPath, xdim: int, ydim: int) -> None: +def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None: f = str(tmp_path / "temp.png") im = Image.new("L", (xdim, ydim), 0) im.save(f) -def test_large(tmp_path: PosixPath) -> None: +def test_large(tmp_path: Path) -> None: """succeeded prepatch""" _write_png(tmp_path, XDIM, YDIM) -def test_2gpx(tmp_path: PosixPath) -> None: +def test_2gpx(tmp_path: Path) -> None: """failed prepatch""" _write_png(tmp_path, XDIM, XDIM) diff --git a/Tests/check_large_memory_numpy.py b/Tests/check_large_memory_numpy.py index 8609fe6d07b..f4ca8d0aa68 100644 --- a/Tests/check_large_memory_numpy.py +++ b/Tests/check_large_memory_numpy.py @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from pathlib import PosixPath +from pathlib import Path import pytest @@ -25,7 +25,7 @@ pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system") -def _write_png(tmp_path: PosixPath, xdim: int, ydim: int) -> None: +def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None: dtype = np.uint8 a = np.zeros((xdim, ydim), dtype=dtype) f = str(tmp_path / "temp.png") @@ -33,11 +33,11 @@ def _write_png(tmp_path: PosixPath, xdim: int, ydim: int) -> None: im.save(f) -def test_large(tmp_path: PosixPath) -> None: +def test_large(tmp_path: Path) -> None: """succeeded prepatch""" _write_png(tmp_path, XDIM, YDIM) -def test_2gpx(tmp_path: PosixPath) -> None: +def test_2gpx(tmp_path: Path) -> None: """failed prepatch""" _write_png(tmp_path, XDIM, XDIM) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 0f1eabdce39..997809e463b 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from pathlib import PosixPath +from pathlib import Path import pytest @@ -21,7 +21,7 @@ pytestmark = skip_unless_feature("zlib") -def save_font(request: pytest.FixtureRequest, tmp_path: PosixPath) -> str: +def save_font(request: pytest.FixtureRequest, tmp_path: Path) -> str: with open(fontname, "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file) assert isinstance(font, FontFile.FontFile) @@ -48,7 +48,7 @@ def delete_tempfile() -> None: return tempname -def test_sanity(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: +def test_sanity(request: pytest.FixtureRequest, tmp_path: Path) -> None: save_font(request, tmp_path) @@ -66,7 +66,7 @@ def test_invalid_file() -> None: PcfFontFile.PcfFontFile(fp) -def test_draw(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: +def test_draw(request: pytest.FixtureRequest, tmp_path: Path) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) im = Image.new("L", (130, 30), "white") @@ -75,7 +75,7 @@ def test_draw(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: assert_image_similar_tofile(im, "Tests/images/test_draw_pbm_target.png", 0) -def test_textsize(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: +def test_textsize(request: pytest.FixtureRequest, tmp_path: Path) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) for i in range(255): @@ -92,7 +92,7 @@ def test_textsize(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: def _test_high_characters( - request: pytest.FixtureRequest, tmp_path: PosixPath, message: str | bytes + request: pytest.FixtureRequest, tmp_path: Path, message: str | bytes ) -> None: tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) @@ -102,7 +102,7 @@ def _test_high_characters( assert_image_similar_tofile(im, "Tests/images/high_ascii_chars.png", 0) -def test_high_characters(request: pytest.FixtureRequest, tmp_path: PosixPath) -> None: +def test_high_characters(request: pytest.FixtureRequest, tmp_path: Path) -> None: message = "".join(chr(i + 1) for i in range(140, 232)) _test_high_characters(request, tmp_path, message) # accept bytes instances. diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index 894d4eb56bf..895458d9d82 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from pathlib import PosixPath +from pathlib import Path from typing import TypedDict import pytest @@ -45,9 +45,7 @@ class Charset(TypedDict): pytestmark = skip_unless_feature("zlib") -def save_font( - request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str -) -> str: +def save_font(request: pytest.FixtureRequest, tmp_path: Path, encoding: str) -> str: with open(fontname, "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file, encoding) assert isinstance(font, FontFile.FontFile) @@ -75,16 +73,12 @@ def delete_tempfile() -> None: @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) -def test_sanity( - request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str -) -> None: +def test_sanity(request: pytest.FixtureRequest, tmp_path: Path, encoding: str) -> None: save_font(request, tmp_path, encoding) @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) -def test_draw( - request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str -) -> None: +def test_draw(request: pytest.FixtureRequest, tmp_path: Path, encoding: str) -> None: tempname = save_font(request, tmp_path, encoding) font = ImageFont.load(tempname) im = Image.new("L", (150, 30), "white") @@ -96,7 +90,7 @@ def test_draw( @pytest.mark.parametrize("encoding", ("iso8859-1", "iso8859-2", "cp1250")) def test_textsize( - request: pytest.FixtureRequest, tmp_path: PosixPath, encoding: str + request: pytest.FixtureRequest, tmp_path: Path, encoding: str ) -> None: tempname = save_font(request, tmp_path, encoding) font = ImageFont.load(tempname)