Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e005bcf
Added type hints
radarhere Sep 9, 2024
957db67
Use hasattr
radarhere Sep 9, 2024
15e0f1a
Merge branch 'main' into context_manager
radarhere Sep 10, 2024
6af0425
Merge branch 'main' into context_manager
radarhere Sep 18, 2024
956dd77
Merge branch 'main' into context_manager
radarhere Sep 25, 2024
241b325
Merge branch 'main' into context_manager
radarhere Oct 6, 2024
b48427d
Merge branch 'main' into context_manager
radarhere Oct 12, 2024
988a1fe
Merge branch 'main' into context_manager
radarhere Oct 13, 2024
e3f4200
Do not allow untyped functions
radarhere Oct 13, 2024
dc3c489
Merge branch 'main' into context_manager
radarhere Nov 6, 2024
8790593
Merge branch 'main' into context_manager
radarhere Nov 29, 2024
3a8eaf5
Merge branch 'main' into context_manager
radarhere Dec 22, 2024
3222d36
Do not remove second load call
radarhere Dec 26, 2024
f878cfd
Corrected argument types
radarhere Dec 27, 2024
68e9ee5
Merge branch 'main' into context_manager
radarhere Dec 28, 2024
dae3154
Merge branch 'main' into context_manager
radarhere Dec 28, 2024
5620b9e
Ignore sys.stdout type
radarhere Jan 9, 2025
ffc6939
Keep ImageFile type hint
radarhere Dec 31, 2024
66fe289
Merge branch 'main' into context_manager
radarhere Jan 9, 2025
0572221
Updated return type
radarhere Jan 9, 2025
68cbf93
Assert image type
radarhere Jan 9, 2025
a57d0e0
Merge branch 'main' into context_manager
radarhere Jan 10, 2025
0b6418d
Merge branch 'main' into context_manager
radarhere Jan 17, 2025
469f91e
Merge branch 'main' into context_manager
radarhere Jan 20, 2025
a8c88c2
Merge branch 'main' into context_manager
radarhere Jan 27, 2025
b9d3bb6
Merge branch 'main' into context_manager
radarhere Jan 31, 2025
9ca85eb
Merge branch 'main' into context_manager
radarhere Feb 6, 2025
01d5b16
Merge branch 'main' into context_manager
radarhere Feb 17, 2025
45034b2
Merge branch 'main' into context_manager
radarhere Feb 18, 2025
0dc62ba
Merge branch 'main' into context_manager
radarhere Mar 3, 2025
cb84166
Use new variable for different type
radarhere Mar 4, 2025
05fc1fa
Merge branch 'main' into context_manager
radarhere Mar 5, 2025
24a0c4d
Merge branch 'main' into context_manager
radarhere Mar 18, 2025
a1d0d95
Assert image type
radarhere Mar 18, 2025
7a1d854
Merge branch 'main' into context_manager
radarhere Mar 21, 2025
cd95e8a
Renamed variable
radarhere Mar 21, 2025
3abce85
Merge branch 'main' into context_manager
radarhere Mar 29, 2025
7e6a2f6
Updated type hints
radarhere Mar 30, 2025
55fe987
Merge branch 'main' into context_manager
radarhere Mar 30, 2025
52acc1c
Merge branch 'main' into context_manager
radarhere Mar 30, 2025
beab3fa
Merge branch 'main' into context_manager
radarhere Apr 3, 2025
cb3a4b4
Assert fp is not None
radarhere Apr 3, 2025
6ee9d53
Assert image type
radarhere Apr 3, 2025
36a536c
Assert colors is not None
radarhere Apr 3, 2025
4d153d6
Assert getpixel returns tuple
radarhere Apr 3, 2025
5406def
Added type hints
radarhere Apr 3, 2025
9db99fd
Merge branch 'main' into context_manager
radarhere Apr 17, 2025
26c7145
Merge branch 'main' into context_manager
radarhere May 30, 2025
254e929
Merge branch 'main' into context_manager
radarhere Jun 7, 2025
1c214e1
Removed duplicate code
radarhere Jun 7, 2025
bb32467
Merge branch 'main' into context_manager
radarhere Jun 26, 2025
7eacae2
Merge branch 'main' into context_manager
radarhere Jun 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def assert_image_equal_tofile(
msg: str | None = None,
mode: str | None = None,
) -> None:
img: Image.Image
with Image.open(filename) as img:
if mode:
img = img.convert(mode)
Expand Down Expand Up @@ -144,6 +145,7 @@ def assert_image_similar_tofile(
epsilon: float,
msg: str | None = None,
) -> None:
img: Image.Image
with Image.open(filename) as img:
assert_image_similar(a, img, epsilon, msg)

Expand Down
3 changes: 3 additions & 0 deletions Tests/test_bmp_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ def get_compare(f: str) -> str:

for f in get_files("g"):
try:
im: Image.Image
with Image.open(f) as im:
im.load()

compare: Image.Image
with Image.open(get_compare(f)) as compare:
compare.load()
if im.mode == "P":
Expand Down
18 changes: 9 additions & 9 deletions Tests/test_file_apng.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,25 +277,25 @@ def test_apng_mode() -> None:
assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P"
im.seek(im.n_frames - 1)
im = im.convert("RGB")
assert im.getpixel((0, 0)) == (0, 255, 0)
assert im.getpixel((64, 32)) == (0, 255, 0)
rgb_im = im.convert("RGB")
assert rgb_im.getpixel((0, 0)) == (0, 255, 0)
assert rgb_im.getpixel((64, 32)) == (0, 255, 0)

with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P"
im.seek(im.n_frames - 1)
im = im.convert("RGBA")
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
rgb_im = im.convert("RGBA")
assert rgb_im.getpixel((0, 0)) == (0, 255, 0, 255)
assert rgb_im.getpixel((64, 32)) == (0, 255, 0, 255)

with Image.open("Tests/images/apng/mode_palette_1bit_alpha.png") as im:
assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P"
im.seek(im.n_frames - 1)
im = im.convert("RGBA")
assert im.getpixel((0, 0)) == (0, 0, 255, 128)
assert im.getpixel((64, 32)) == (0, 0, 255, 128)
rgb_im = im.convert("RGBA")
assert rgb_im.getpixel((0, 0)) == (0, 0, 255, 128)
assert rgb_im.getpixel((64, 32)) == (0, 0, 255, 128)


def test_apng_chunk_errors() -> None:
Expand Down
11 changes: 11 additions & 0 deletions Tests/test_file_avif.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from PIL import (
AvifImagePlugin,
GifImagePlugin,
Image,
ImageDraw,
ImageFile,
Expand Down Expand Up @@ -220,6 +221,7 @@ def test_file_pointer_could_be_reused(self) -> None:
def test_background_from_gif(self, tmp_path: Path) -> None:
with Image.open("Tests/images/chi.gif") as im:
original_value = im.convert("RGB").getpixel((1, 1))
assert isinstance(original_value, tuple)

# Save as AVIF
out_avif = tmp_path / "temp.avif"
Expand All @@ -232,6 +234,7 @@ def test_background_from_gif(self, tmp_path: Path) -> None:

with Image.open(out_gif) as reread:
reread_value = reread.convert("RGB").getpixel((1, 1))
assert isinstance(reread_value, tuple)
difference = sum([abs(original_value[i] - reread_value[i]) for i in range(3)])
assert difference <= 6

Expand All @@ -240,6 +243,7 @@ def test_save_single_frame(self, tmp_path: Path) -> None:
with Image.open("Tests/images/chi.gif") as im:
im.save(temp_file)
with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 1

def test_invalid_file(self) -> None:
Expand Down Expand Up @@ -598,10 +602,12 @@ def test_n_frames(self) -> None:
"""

with Image.open(TEST_AVIF_FILE) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 1
assert not im.is_animated

with Image.open("Tests/images/avif/star.avifs") as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5
assert im.is_animated

Expand All @@ -612,11 +618,13 @@ def test_write_animation_P(self, tmp_path: Path) -> None:
"""

with Image.open("Tests/images/avif/star.gif") as original:
assert isinstance(original, GifImagePlugin.GifImageFile)
assert original.n_frames > 1

temp_file = tmp_path / "temp.avif"
original.save(temp_file, save_all=True)
with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == original.n_frames

# Compare first frame in P mode to frame from original GIF
Expand All @@ -636,6 +644,7 @@ def test_write_animation_RGBA(self, tmp_path: Path) -> None:

def check(temp_file: Path) -> None:
with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 4

# Compare first frame to original
Expand Down Expand Up @@ -708,6 +717,7 @@ def test_timestamp_and_duration(self, tmp_path: Path) -> None:
)

with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5
assert im.is_animated

Expand Down Expand Up @@ -737,6 +747,7 @@ def test_seeking(self, tmp_path: Path) -> None:
)

with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5
assert im.is_animated

Expand Down
4 changes: 2 additions & 2 deletions Tests/test_file_bmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ def test_save_dib(tmp_path: Path) -> None:
def test_rgba_bitfields() -> None:
# This test image has been manually hexedited
# to change the bitfield compression in the header from XBGR to RGBA
with Image.open("Tests/images/rgb32bf-rgba.bmp") as im:
with Image.open("Tests/images/rgb32bf-rgba.bmp") as bmp_im:
# So before the comparing the image, swap the channels
b, g, r = im.split()[1:]
b, g, r = bmp_im.split()[1:]
im = Image.merge("RGB", (r, g, b))

assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
Expand Down
1 change: 1 addition & 0 deletions Tests/test_file_bufrstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def open(self, im: ImageFile.StubImageFile) -> None:

def load(self, im: ImageFile.StubImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
1 change: 1 addition & 0 deletions Tests/test_file_cur.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def test_invalid_file() -> None:
no_cursors_file = "Tests/images/no_cursors.cur"

cur = CurImagePlugin.CurImageFile(TEST_FILE)
assert cur.fp is not None
cur.fp.close()
with open(no_cursors_file, "rb") as cur.fp:
with pytest.raises(TypeError):
Expand Down
7 changes: 4 additions & 3 deletions Tests/test_file_dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
)
def test_sanity_dxt1_bc1(image_path: str) -> None:
"""Check DXT1 and BC1 images can be opened"""
target: Image.Image
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
target = target.convert("RGBA")
with Image.open(image_path) as im:
Expand Down Expand Up @@ -504,9 +505,9 @@ def test_save_dxt5(tmp_path: Path) -> None:

def test_save_dx10_bc5(tmp_path: Path) -> None:
out = tmp_path / "temp.dds"
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
im.save(out, pixel_format="BC5")
assert_image_similar_tofile(im, out, 9.56)
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as img:
img.save(out, pixel_format="BC5")
assert_image_similar_tofile(img, out, 9.56)

im = hopper("L")
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
Expand Down
24 changes: 12 additions & 12 deletions Tests/test_file_eps.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ def test_bytesio_object() -> None:
with Image.open(img_bytes) as img:
img.load()

with Image.open(FILE1_COMPARE) as image1_scale1_compare:
image1_scale1_compare = image1_scale1_compare.convert("RGB")
with Image.open(FILE1_COMPARE) as im:
image1_scale1_compare = im.convert("RGB")
image1_scale1_compare.load()
assert_image_similar(img, image1_scale1_compare, 5)

Expand Down Expand Up @@ -292,16 +292,16 @@ def test_render_scale1() -> None:
# Zero bounding box
with Image.open(FILE1) as image1_scale1:
image1_scale1.load()
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
image1_scale1_compare = image1_scale1_compare.convert("RGB")
with Image.open(FILE1_COMPARE) as im:
image1_scale1_compare = im.convert("RGB")
image1_scale1_compare.load()
assert_image_similar(image1_scale1, image1_scale1_compare, 5)

# Non-zero bounding box
with Image.open(FILE2) as image2_scale1:
image2_scale1.load()
with Image.open(FILE2_COMPARE) as image2_scale1_compare:
image2_scale1_compare = image2_scale1_compare.convert("RGB")
with Image.open(FILE2_COMPARE) as im:
image2_scale1_compare = im.convert("RGB")
image2_scale1_compare.load()
assert_image_similar(image2_scale1, image2_scale1_compare, 10)

Expand All @@ -315,17 +315,17 @@ def test_render_scale2() -> None:
with Image.open(FILE1) as image1_scale2:
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
image1_scale2.load(scale=2)
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
image1_scale2_compare = image1_scale2_compare.convert("RGB")
with Image.open(FILE1_COMPARE_SCALE2) as im:
image1_scale2_compare = im.convert("RGB")
image1_scale2_compare.load()
assert_image_similar(image1_scale2, image1_scale2_compare, 5)

# Non-zero bounding box
with Image.open(FILE2) as image2_scale2:
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
image2_scale2.load(scale=2)
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
image2_scale2_compare = image2_scale2_compare.convert("RGB")
with Image.open(FILE2_COMPARE_SCALE2) as im:
image2_scale2_compare = im.convert("RGB")
image2_scale2_compare.load()
assert_image_similar(image2_scale2, image2_scale2_compare, 10)

Expand All @@ -335,9 +335,9 @@ def test_render_scale2() -> None:
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
)
def test_resize(filename: str) -> None:
with Image.open(filename) as im:
with Image.open(filename) as img:
new_size = (100, 100)
im = im.resize(new_size)
im = img.resize(new_size)
assert im.size == new_size


Expand Down
3 changes: 3 additions & 0 deletions Tests/test_file_fli.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ def test_sanity() -> None:
def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
with Image.open(animated_test_file_with_prefix_chunk) as im:
assert isinstance(im, FliImagePlugin.FliImageFile)

assert im.mode == "P"
assert im.size == (320, 200)
assert im.format == "FLI"
assert im.info["duration"] == 171
assert im.is_animated

palette = im.getpalette()
assert palette is not None
assert palette[3:6] == [255, 255, 255]
assert palette[381:384] == [204, 204, 12]
assert palette[765:] == [252, 0, 0]
Expand Down
18 changes: 12 additions & 6 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ def test_roundtrip_save_all(tmp_path: Path) -> None:
im.save(out, save_all=True)

with Image.open(out) as reread:
assert isinstance(reread, GifImagePlugin.GifImageFile)
assert reread.n_frames == 5


Expand All @@ -306,6 +307,7 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
),
)
def test_loading_multiple_palettes(path: str, mode: str) -> None:
im: Image.Image
with Image.open(path) as im:
assert im.mode == "P"
assert im.palette is not None
Expand Down Expand Up @@ -339,7 +341,7 @@ def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:

def test_palette_handling(tmp_path: Path) -> None:
# see https://github.com/python-pillow/Pillow/issues/513

im: Image.Image
with Image.open(TEST_GIF) as im:
im = im.convert("RGB")

Expand All @@ -363,8 +365,8 @@ def roundtrip(im: Image.Image, **kwargs: bool) -> Image.Image:

return reloaded

orig = "Tests/images/test.colors.gif"
with Image.open(orig) as im:
im: Image.Image
with Image.open("Tests/images/test.colors.gif") as im:
with roundtrip(im) as reloaded:
assert_image_similar(im, reloaded, 1)
with roundtrip(im, optimize=True) as reloaded:
Expand All @@ -379,6 +381,7 @@ def roundtrip(im: Image.Image, **kwargs: bool) -> Image.Image:

@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
img: Image.Image
with Image.open(TEST_GIF) as img:
img = img.convert("RGB")

Expand All @@ -391,6 +394,7 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:

@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
img: Image.Image
with Image.open(TEST_GIF) as img:
img = img.convert("L")

Expand Down Expand Up @@ -1020,9 +1024,9 @@ def test_webp_background(tmp_path: Path) -> None:

# Test opaque WebP background
if features.check("webp"):
with Image.open("Tests/images/hopper.webp") as im:
assert im.info["background"] == (255, 255, 255, 255)
im.save(out)
with Image.open("Tests/images/hopper.webp") as img:
assert img.info["background"] == (255, 255, 255, 255)
img.save(out)

# Test non-opaque WebP background
im = Image.new("L", (100, 100), "#000")
Expand All @@ -1031,6 +1035,7 @@ def test_webp_background(tmp_path: Path) -> None:


def test_comment(tmp_path: Path) -> None:
im: Image.Image
with Image.open(TEST_GIF) as im:
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"

Expand Down Expand Up @@ -1361,6 +1366,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
with Image.open(out) as im:
# Assert that the frames are correct, and each frame has the same palette
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
assert isinstance(im, GifImagePlugin.GifImageFile)
assert im.palette is not None
assert im.global_palette is not None
assert im.palette.palette == im.global_palette.palette
Expand Down
3 changes: 2 additions & 1 deletion Tests/test_file_gribstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ class TestHandler(ImageFile.StubHandler):
def open(self, im: Image.Image) -> None:
self.opened = True

def load(self, im: Image.Image) -> Image.Image:
def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
3 changes: 2 additions & 1 deletion Tests/test_file_hdf5stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ class TestHandler(ImageFile.StubHandler):
def open(self, im: Image.Image) -> None:
self.opened = True

def load(self, im: Image.Image) -> Image.Image:
def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_iptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_getiptcinfo_jpg_none() -> None:
# Arrange
with hopper() as im:
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
iptc = IptcImagePlugin.getiptcinfo(im) # type: ignore[arg-type]

# Assert
assert iptc is None
Expand Down
Loading
Loading