Skip to content

Commit

Permalink
Merge branch 'main' into homebrew-isolation
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere authored Oct 28, 2024
2 parents 43c34fc + bdd0b86 commit 3e4be4b
Show file tree
Hide file tree
Showing 31 changed files with 563 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .ci/requirements-mypy.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mypy==1.11.2
mypy==1.13.0
IceSpringPySideStubs-PyQt6
IceSpringPySideStubs-PySide6
ipython
Expand Down
12 changes: 12 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
Changelog (Pillow)
==================

11.1.0 (unreleased)
-------------------

- Corrected EMF DPI #8485
[radarhere]

- Fix IFDRational with a zero denominator #8474
[radarhere]

- Fixed disabling a feature during install #8469
[radarhere]

11.0.0 (2024-10-15)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Pillow is the friendly PIL fork. It is

Copyright © 2010-2024 by Jeffrey A. Clark and contributors

Like PIL, Pillow is licensed under the open source HPND License:
Like PIL, Pillow is licensed under the open source MIT-CMU License:

By obtaining, using, and/or copying this software and/or its associated
documentation, you agree that you have read, understood, and will comply
Expand Down
2 changes: 2 additions & 0 deletions Tests/test_bmp_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def test_bad() -> None:
for f in get_files("b"):
# Assert that there is no unclosed file warning
with warnings.catch_warnings():
warnings.simplefilter("error")

try:
with Image.open(f) as im:
im.load()
Expand Down
4 changes: 4 additions & 0 deletions Tests/test_file_dcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ def open() -> None:

def test_closed_file() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

im = Image.open(TEST_FILE)
im.load()
im.close()


def test_context_manager() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

with Image.open(TEST_FILE) as im:
im.load()

Expand Down
4 changes: 4 additions & 0 deletions Tests/test_file_fli.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def open() -> None:

def test_closed_file() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

im = Image.open(static_test_file)
im.load()
im.close()
Expand All @@ -81,6 +83,8 @@ def test_seek_after_close() -> None:

def test_context_manager() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

with Image.open(static_test_file) as im:
im.load()

Expand Down
4 changes: 4 additions & 0 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def open() -> None:

def test_closed_file() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

im = Image.open(TEST_GIF)
im.load()
im.close()
Expand All @@ -67,6 +69,8 @@ def test_seek_after_close() -> None:

def test_context_manager() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

with Image.open(TEST_GIF) as im:
im.load()

Expand Down
2 changes: 2 additions & 0 deletions Tests/test_file_icns.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ def test_sanity() -> None:
with Image.open(TEST_FILE) as im:
# Assert that there is no unclosed file warning
with warnings.catch_warnings():
warnings.simplefilter("error")

im.load()

assert im.mode == "RGBA"
Expand Down
4 changes: 4 additions & 0 deletions Tests/test_file_im.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,17 @@ def open() -> None:

def test_closed_file() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

im = Image.open(TEST_IM)
im.load()
im.close()


def test_context_manager() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

with Image.open(TEST_IM) as im:
im.load()

Expand Down
2 changes: 2 additions & 0 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,8 @@ def test_exif_x_resolution(self, tmp_path: Path) -> None:

out = str(tmp_path / "out.jpg")
with warnings.catch_warnings():
warnings.simplefilter("error")

im.save(out, exif=exif)

with Image.open(out) as reloaded:
Expand Down
121 changes: 68 additions & 53 deletions Tests/test_file_jpeg2k.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import re
from collections.abc import Generator
from io import BytesIO
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -29,8 +30,16 @@

pytestmark = skip_unless_feature("jpg_2000")

test_card = Image.open("Tests/images/test-card.png")
test_card.load()

@pytest.fixture
def card() -> Generator[ImageFile.ImageFile, None, None]:
with Image.open("Tests/images/test-card.png") as im:
im.load()
try:
yield im
finally:
im.close()


# OpenJPEG 2.0.0 outputs this debugging message sometimes; we should
# ignore it---it doesn't represent a test failure.
Expand Down Expand Up @@ -74,76 +83,76 @@ def test_invalid_file() -> None:
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)


def test_bytesio() -> None:
def test_bytesio(card: ImageFile.ImageFile) -> None:
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = BytesIO(f.read())
with Image.open(data) as im:
im.load()
assert_image_similar(im, test_card, 1.0e-3)
assert_image_similar(im, card, 1.0e-3)


# These two test pre-written JPEG 2000 files that were not written with
# PIL (they were made using Adobe Photoshop)


def test_lossless(tmp_path: Path) -> None:
def test_lossless(card: ImageFile.ImageFile, tmp_path: Path) -> None:
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.load()
outfile = str(tmp_path / "temp_test-card.png")
im.save(outfile)
assert_image_similar(im, test_card, 1.0e-3)
assert_image_similar(im, card, 1.0e-3)


def test_lossy_tiled() -> None:
assert_image_similar_tofile(
test_card, "Tests/images/test-card-lossy-tiled.jp2", 2.0
)
def test_lossy_tiled(card: ImageFile.ImageFile) -> None:
assert_image_similar_tofile(card, "Tests/images/test-card-lossy-tiled.jp2", 2.0)


def test_lossless_rt() -> None:
im = roundtrip(test_card)
assert_image_equal(im, test_card)
def test_lossless_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card)
assert_image_equal(im, card)


def test_lossy_rt() -> None:
im = roundtrip(test_card, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_lossy_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, quality_layers=[20])
assert_image_similar(im, card, 2.0)


def test_tiled_rt() -> None:
im = roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, tile_size=(128, 128))
assert_image_equal(im, card)


def test_tiled_offset_rt() -> None:
im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32))
assert_image_equal(im, test_card)
def test_tiled_offset_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32))
assert_image_equal(im, card)


def test_tiled_offset_too_small() -> None:
def test_tiled_offset_too_small(card: ImageFile.ImageFile) -> None:
with pytest.raises(ValueError):
roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32))
roundtrip(card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32))


def test_irreversible_rt() -> None:
im = roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_irreversible_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, irreversible=True, quality_layers=[20])
assert_image_similar(im, card, 2.0)


def test_prog_qual_rt() -> None:
im = roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, card, 2.0)


def test_prog_res_rt() -> None:
im = roundtrip(test_card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, test_card)
def test_prog_res_rt(card: ImageFile.ImageFile) -> None:
im = roundtrip(card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, card)


@pytest.mark.parametrize("num_resolutions", range(2, 6))
def test_default_num_resolutions(num_resolutions: int) -> None:
def test_default_num_resolutions(
card: ImageFile.ImageFile, num_resolutions: int
) -> None:
d = 1 << (num_resolutions - 1)
im = test_card.resize((d - 1, d - 1))
im = card.resize((d - 1, d - 1))
with pytest.raises(OSError):
roundtrip(im, num_resolutions=num_resolutions)
reloaded = roundtrip(im)
Expand Down Expand Up @@ -205,31 +214,31 @@ def test_header_errors() -> None:
pass


def test_layers_type(tmp_path: Path) -> None:
def test_layers_type(card: ImageFile.ImageFile, tmp_path: Path) -> None:
outfile = str(tmp_path / "temp_layers.jp2")
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers)
card.save(outfile, quality_layers=quality_layers)

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


def test_layers() -> None:
def test_layers(card: ImageFile.ImageFile) -> None:
out = BytesIO()
test_card.save(out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP")
card.save(out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP")
out.seek(0)

with Image.open(out) as im:
im.layers = 1
im.load()
assert_image_similar(im, test_card, 13)
assert_image_similar(im, card, 13)

out.seek(0)
with Image.open(out) as im:
im.layers = 3
im.load()
assert_image_similar(im, test_card, 0.4)
assert_image_similar(im, card, 0.4)


@pytest.mark.parametrize(
Expand All @@ -245,24 +254,30 @@ def test_layers() -> None:
(None, {"no_jp2": False}, 4, b"jP"),
),
)
def test_no_jp2(name: str, args: dict[str, bool], offset: int, data: bytes) -> None:
def test_no_jp2(
card: ImageFile.ImageFile,
name: str,
args: dict[str, bool],
offset: int,
data: bytes,
) -> None:
out = BytesIO()
if name:
out.name = name
test_card.save(out, "JPEG2000", **args)
card.save(out, "JPEG2000", **args)
out.seek(offset)
assert out.read(2) == data


def test_mct() -> None:
def test_mct(card: ImageFile.ImageFile) -> None:
# Three component
for val in (0, 1):
out = BytesIO()
test_card.save(out, "JPEG2000", mct=val, no_jp2=True)
card.save(out, "JPEG2000", mct=val, no_jp2=True)

assert out.getvalue()[59] == val
with Image.open(out) as im:
assert_image_similar(im, test_card, 1.0e-3)
assert_image_similar(im, card, 1.0e-3)

# Single component should have MCT disabled
for val in (0, 1):
Expand Down Expand Up @@ -419,22 +434,22 @@ def test_comment() -> None:
pass


def test_save_comment() -> None:
def test_save_comment(card: ImageFile.ImageFile) -> None:
for comment in ("Created by Pillow", b"Created by Pillow"):
out = BytesIO()
test_card.save(out, "JPEG2000", comment=comment)
card.save(out, "JPEG2000", comment=comment)

with Image.open(out) as im:
assert im.info["comment"] == b"Created by Pillow"

out = BytesIO()
long_comment = b" " * 65531
test_card.save(out, "JPEG2000", comment=long_comment)
card.save(out, "JPEG2000", comment=long_comment)
with Image.open(out) as im:
assert im.info["comment"] == long_comment

with pytest.raises(ValueError):
test_card.save(out, "JPEG2000", comment=long_comment + b" ")
card.save(out, "JPEG2000", comment=long_comment + b" ")


@pytest.mark.parametrize(
Expand All @@ -457,10 +472,10 @@ def test_crashes(test_file: str) -> None:


@skip_unless_feature_version("jpg_2000", "2.4.0")
def test_plt_marker() -> None:
def test_plt_marker(card: ImageFile.ImageFile) -> None:
# Search the start of the codesteam for PLT
out = BytesIO()
test_card.save(out, "JPEG2000", no_jp2=True, plt=True)
card.save(out, "JPEG2000", no_jp2=True, plt=True)
out.seek(0)
while True:
marker = out.read(2)
Expand Down
Loading

0 comments on commit 3e4be4b

Please sign in to comment.