Skip to content

Commit

Permalink
Merge pull request python-pillow#8090 from radarhere/type_hints_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored May 29, 2024
2 parents 759ab28 + a6d1dae commit a49a42a
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 31 deletions.
25 changes: 11 additions & 14 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,13 @@ def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
def skip_unless_feature_version(
feature: str, required: str, reason: str | None = None
) -> pytest.MarkDecorator:
if not features.check(feature):
version = features.version(feature)
if version is None:
return pytest.mark.skip(f"{feature} not available")
if reason is None:
reason = f"{feature} is older than {required}"
version_required = parse_version(required)
version_available = parse_version(features.version(feature))
version_available = parse_version(version)
return pytest.mark.skipif(version_available < version_required, reason=reason)


Expand All @@ -189,12 +190,13 @@ def mark_if_feature_version(
version_blacklist: str,
reason: str | None = None,
) -> pytest.MarkDecorator:
if not features.check(feature):
version = features.version(feature)
if version is None:
return pytest.mark.pil_noop_mark()
if reason is None:
reason = f"{feature} is {version_blacklist}"
version_required = parse_version(version_blacklist)
version_available = parse_version(features.version(feature))
version_available = parse_version(version)
if (
version_available.major == version_required.major
and version_available.minor == version_required.minor
Expand All @@ -220,16 +222,11 @@ def _get_mem_usage(self) -> float:
from resource import RUSAGE_SELF, getrusage

mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == "darwin":
# man 2 getrusage:
# ru_maxrss
# This is the maximum resident set size utilized (in bytes).
return mem / 1024 # Kb
# linux
# man 2 getrusage
# ru_maxrss (since Linux 2.6.32)
# This is the maximum resident set size used (in kilobytes).
return mem # Kb
# man 2 getrusage:
# ru_maxrss
# This is the maximum resident set size utilized
# in bytes on macOS, in kilobytes on Linux
return mem / 1024 if sys.platform == "darwin" else mem

def _test_leak(self, core: Callable[[], None]) -> None:
start_mem = self._get_mem_usage()
Expand Down
10 changes: 6 additions & 4 deletions Tests/test_file_webp_animated.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ def test_write_animation_L(tmp_path: Path) -> None:
assert_image_similar(im, orig.convert("RGBA"), 32.9)

if is_big_endian():
webp = parse_version(features.version_module("webp"))
if webp < parse_version("1.2.2"):
version = features.version_module("webp")
assert version is not None
if parse_version(version) < parse_version("1.2.2"):
pytest.skip("Fails with libwebp earlier than 1.2.2")
orig.seek(orig.n_frames - 1)
im.seek(im.n_frames - 1)
Expand All @@ -78,8 +79,9 @@ def check(temp_file) -> None:

# Compare second frame to original
if is_big_endian():
webp = parse_version(features.version_module("webp"))
if webp < parse_version("1.2.2"):
version = features.version_module("webp")
assert version is not None
if parse_version(version) < parse_version("1.2.2"):
pytest.skip("Fails with libwebp earlier than 1.2.2")
im.seek(1)
im.load()
Expand Down
19 changes: 14 additions & 5 deletions Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ def test_sanity() -> None:
assert list(map(type, v)) == [str, str, str, str]

# internal version number
assert re.search(r"\d+\.\d+(\.\d+)?$", features.version_module("littlecms2"))
version = features.version_module("littlecms2")
assert version is not None
assert re.search(r"\d+\.\d+(\.\d+)?$", version)

skip_missing()
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
assert i is not None
assert_image(i, "RGB", (128, 128))

i = hopper()
Expand All @@ -72,23 +75,27 @@ def test_sanity() -> None:

t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
i = ImageCms.applyTransform(hopper(), t)
assert i is not None
assert_image(i, "RGB", (128, 128))

with hopper() as i:
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
ImageCms.applyTransform(hopper(), t, inPlace=True)
assert i is not None
assert_image(i, "RGB", (128, 128))

p = ImageCms.createProfile("sRGB")
o = ImageCms.getOpenProfile(SRGB)
t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
i = ImageCms.applyTransform(hopper(), t)
assert i is not None
assert_image(i, "RGB", (128, 128))

t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB")
assert t.inputMode == "RGB"
assert t.outputMode == "RGB"
i = ImageCms.applyTransform(hopper(), t)
assert i is not None
assert_image(i, "RGB", (128, 128))

# test PointTransform convenience API
Expand Down Expand Up @@ -260,7 +267,7 @@ def test_simple_lab() -> None:
t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB")

i_lab = ImageCms.applyTransform(i, t)

assert i_lab is not None
assert i_lab.mode == "LAB"

k = i_lab.getpixel((0, 0))
Expand All @@ -284,6 +291,7 @@ def test_lab_color() -> None:
# Need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, and
# have that mapping work back to a PIL mode (likely RGB).
i = ImageCms.applyTransform(hopper(), t)
assert i is not None
assert_image(i, "LAB", (128, 128))

# i.save('temp.lab.tif') # visually verified vs PS.
Expand All @@ -298,6 +306,7 @@ def test_lab_srgb() -> None:

with Image.open("Tests/images/hopper.Lab.tif") as img:
img_srgb = ImageCms.applyTransform(img, t)
assert img_srgb is not None

# img_srgb.save('temp.srgb.tif') # visually verified vs ps.

Expand All @@ -317,11 +326,11 @@ def test_lab_roundtrip() -> None:
t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB")

i = ImageCms.applyTransform(hopper(), t)

assert i is not None
assert i.info["icc_profile"] == ImageCmsProfile(pLab).tobytes()

out = ImageCms.applyTransform(i, t2)

assert out is not None
assert_image_similar(hopper(), out, 2)


Expand Down Expand Up @@ -657,7 +666,7 @@ def test_auxiliary_channels_isolated() -> None:
reference_image = ImageCms.applyTransform(
source_image.convert(src_format[2]), reference_transform
)

assert reference_image is not None
assert_image_equal(test_image.convert(dst_format[2]), reference_image)


Expand Down
6 changes: 6 additions & 0 deletions Tests/test_imagefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ def test_broken_datastream_without_errors(self) -> None:


class MockPyDecoder(ImageFile.PyDecoder):
last: MockPyDecoder

def __init__(self, mode: str, *args: Any) -> None:
MockPyDecoder.last = self

Expand All @@ -213,6 +215,8 @@ def decode(self, buffer):


class MockPyEncoder(ImageFile.PyEncoder):
last: MockPyEncoder | None

def __init__(self, mode: str, *args: Any) -> None:
MockPyEncoder.last = self

Expand Down Expand Up @@ -315,6 +319,7 @@ def test_setimage(self) -> None:
im, fp, [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 0, "RGB")]
)

assert MockPyEncoder.last
assert MockPyEncoder.last.state.xoff == xoff
assert MockPyEncoder.last.state.yoff == yoff
assert MockPyEncoder.last.state.xsize == xsize
Expand All @@ -329,6 +334,7 @@ def test_extents_none(self) -> None:
fp = BytesIO()
ImageFile._save(im, fp, [("MOCK", None, 0, "RGB")])

assert MockPyEncoder.last
assert MockPyEncoder.last.state.xoff == 0
assert MockPyEncoder.last.state.yoff == 0
assert MockPyEncoder.last.state.xsize == 200
Expand Down
23 changes: 15 additions & 8 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@


def test_sanity() -> None:
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("freetype2"))
version = features.version_module("freetype2")
assert version is not None
assert re.search(r"\d+\.\d+\.\d+$", version)


@pytest.fixture(
Expand Down Expand Up @@ -547,11 +549,10 @@ def _test_fake_loading_font(path_to_fake: str, fontname: str) -> None:
def loadable_font(
filepath: str, size: int, index: int, encoding: str, *args: Any
):
_freeTypeFont = getattr(ImageFont, "_FreeTypeFont")
if filepath == path_to_fake:
return ImageFont._FreeTypeFont(
FONT_PATH, size, index, encoding, *args
)
return ImageFont._FreeTypeFont(filepath, size, index, encoding, *args)
return _freeTypeFont(FONT_PATH, size, index, encoding, *args)
return _freeTypeFont(filepath, size, index, encoding, *args)

m.setattr(ImageFont, "FreeTypeFont", loadable_font)
font = ImageFont.truetype(fontname)
Expand Down Expand Up @@ -630,7 +631,9 @@ def test_complex_font_settings() -> None:


def test_variation_get(font: ImageFont.FreeTypeFont) -> None:
freetype = parse_version(features.version_module("freetype2"))
version = features.version_module("freetype2")
assert version is not None
freetype = parse_version(version)
if freetype < parse_version("2.9.1"):
with pytest.raises(NotImplementedError):
font.get_variation_names()
Expand Down Expand Up @@ -700,7 +703,9 @@ def _check_text(font: ImageFont.FreeTypeFont, path: str, epsilon: float) -> None


def test_variation_set_by_name(font: ImageFont.FreeTypeFont) -> None:
freetype = parse_version(features.version_module("freetype2"))
version = features.version_module("freetype2")
assert version is not None
freetype = parse_version(version)
if freetype < parse_version("2.9.1"):
with pytest.raises(NotImplementedError):
font.set_variation_by_name("Bold")
Expand All @@ -725,7 +730,9 @@ def test_variation_set_by_name(font: ImageFont.FreeTypeFont) -> None:


def test_variation_set_by_axes(font: ImageFont.FreeTypeFont) -> None:
freetype = parse_version(features.version_module("freetype2"))
version = features.version_module("freetype2")
assert version is not None
freetype = parse_version(version)
if freetype < parse_version("2.9.1"):
with pytest.raises(NotImplementedError):
font.set_variation_by_axes([100])
Expand Down

0 comments on commit a49a42a

Please sign in to comment.