This commit is contained in:
Andrew Murray 2025-07-08 16:58:16 +00:00 committed by GitHub
commit 4daa8a5573
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 349 additions and 156 deletions

View File

@ -101,6 +101,7 @@ def assert_image_equal_tofile(
msg: str | None = None, msg: str | None = None,
mode: str | None = None, mode: str | None = None,
) -> None: ) -> None:
img: Image.Image
with Image.open(filename) as img: with Image.open(filename) as img:
if mode: if mode:
img = img.convert(mode) img = img.convert(mode)
@ -144,6 +145,7 @@ def assert_image_similar_tofile(
epsilon: float, epsilon: float,
msg: str | None = None, msg: str | None = None,
) -> None: ) -> None:
img: Image.Image
with Image.open(filename) as img: with Image.open(filename) as img:
assert_image_similar(a, img, epsilon, msg) assert_image_similar(a, img, epsilon, msg)

View File

@ -94,8 +94,11 @@ def test_good() -> None:
for f in get_files("g"): for f in get_files("g"):
try: try:
im: Image.Image
with Image.open(f) as im: with Image.open(f) as im:
im.load() im.load()
compare: Image.Image
with Image.open(get_compare(f)) as compare: with Image.open(get_compare(f)) as compare:
compare.load() compare.load()
if im.mode == "P": if im.mode == "P":

View File

@ -277,25 +277,25 @@ def test_apng_mode() -> None:
assert isinstance(im, PngImagePlugin.PngImageFile) assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P" assert im.mode == "P"
im.seek(im.n_frames - 1) im.seek(im.n_frames - 1)
im = im.convert("RGB") rgb_im = im.convert("RGB")
assert im.getpixel((0, 0)) == (0, 255, 0) assert rgb_im.getpixel((0, 0)) == (0, 255, 0)
assert im.getpixel((64, 32)) == (0, 255, 0) assert rgb_im.getpixel((64, 32)) == (0, 255, 0)
with Image.open("Tests/images/apng/mode_palette_alpha.png") as im: with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
assert isinstance(im, PngImagePlugin.PngImageFile) assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P" assert im.mode == "P"
im.seek(im.n_frames - 1) im.seek(im.n_frames - 1)
im = im.convert("RGBA") rgb_im = im.convert("RGBA")
assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert rgb_im.getpixel((0, 0)) == (0, 255, 0, 255)
assert im.getpixel((64, 32)) == (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: with Image.open("Tests/images/apng/mode_palette_1bit_alpha.png") as im:
assert isinstance(im, PngImagePlugin.PngImageFile) assert isinstance(im, PngImagePlugin.PngImageFile)
assert im.mode == "P" assert im.mode == "P"
im.seek(im.n_frames - 1) im.seek(im.n_frames - 1)
im = im.convert("RGBA") rgb_im = im.convert("RGBA")
assert im.getpixel((0, 0)) == (0, 0, 255, 128) assert rgb_im.getpixel((0, 0)) == (0, 0, 255, 128)
assert im.getpixel((64, 32)) == (0, 0, 255, 128) assert rgb_im.getpixel((64, 32)) == (0, 0, 255, 128)
def test_apng_chunk_errors() -> None: def test_apng_chunk_errors() -> None:

View File

@ -14,6 +14,7 @@ import pytest
from PIL import ( from PIL import (
AvifImagePlugin, AvifImagePlugin,
GifImagePlugin,
Image, Image,
ImageDraw, ImageDraw,
ImageFile, ImageFile,
@ -220,6 +221,7 @@ class TestFileAvif:
def test_background_from_gif(self, tmp_path: Path) -> None: def test_background_from_gif(self, tmp_path: Path) -> None:
with Image.open("Tests/images/chi.gif") as im: with Image.open("Tests/images/chi.gif") as im:
original_value = im.convert("RGB").getpixel((1, 1)) original_value = im.convert("RGB").getpixel((1, 1))
assert isinstance(original_value, tuple)
# Save as AVIF # Save as AVIF
out_avif = tmp_path / "temp.avif" out_avif = tmp_path / "temp.avif"
@ -232,6 +234,7 @@ class TestFileAvif:
with Image.open(out_gif) as reread: with Image.open(out_gif) as reread:
reread_value = reread.convert("RGB").getpixel((1, 1)) 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)]) difference = sum([abs(original_value[i] - reread_value[i]) for i in range(3)])
assert difference <= 6 assert difference <= 6
@ -240,6 +243,7 @@ class TestFileAvif:
with Image.open("Tests/images/chi.gif") as im: with Image.open("Tests/images/chi.gif") as im:
im.save(temp_file) im.save(temp_file)
with Image.open(temp_file) as im: with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 1 assert im.n_frames == 1
def test_invalid_file(self) -> None: def test_invalid_file(self) -> None:
@ -598,10 +602,12 @@ class TestAvifAnimation:
""" """
with Image.open(TEST_AVIF_FILE) as im: with Image.open(TEST_AVIF_FILE) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 1 assert im.n_frames == 1
assert not im.is_animated assert not im.is_animated
with Image.open("Tests/images/avif/star.avifs") as im: with Image.open("Tests/images/avif/star.avifs") as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5 assert im.n_frames == 5
assert im.is_animated assert im.is_animated
@ -612,11 +618,13 @@ class TestAvifAnimation:
""" """
with Image.open("Tests/images/avif/star.gif") as original: with Image.open("Tests/images/avif/star.gif") as original:
assert isinstance(original, GifImagePlugin.GifImageFile)
assert original.n_frames > 1 assert original.n_frames > 1
temp_file = tmp_path / "temp.avif" temp_file = tmp_path / "temp.avif"
original.save(temp_file, save_all=True) original.save(temp_file, save_all=True)
with Image.open(temp_file) as im: with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == original.n_frames assert im.n_frames == original.n_frames
# Compare first frame in P mode to frame from original GIF # Compare first frame in P mode to frame from original GIF
@ -636,6 +644,7 @@ class TestAvifAnimation:
def check(temp_file: Path) -> None: def check(temp_file: Path) -> None:
with Image.open(temp_file) as im: with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 4 assert im.n_frames == 4
# Compare first frame to original # Compare first frame to original
@ -708,6 +717,7 @@ class TestAvifAnimation:
) )
with Image.open(temp_file) as im: with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5 assert im.n_frames == 5
assert im.is_animated assert im.is_animated
@ -737,6 +747,7 @@ class TestAvifAnimation:
) )
with Image.open(temp_file) as im: with Image.open(temp_file) as im:
assert isinstance(im, AvifImagePlugin.AvifImageFile)
assert im.n_frames == 5 assert im.n_frames == 5
assert im.is_animated assert im.is_animated

View File

@ -160,9 +160,9 @@ def test_save_dib(tmp_path: Path) -> None:
def test_rgba_bitfields() -> None: def test_rgba_bitfields() -> None:
# This test image has been manually hexedited # This test image has been manually hexedited
# to change the bitfield compression in the header from XBGR to RGBA # 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 # 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)) im = Image.merge("RGB", (r, g, b))
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp") assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")

View File

@ -61,6 +61,7 @@ def test_handler(tmp_path: Path) -> None:
def load(self, im: ImageFile.StubImageFile) -> Image.Image: def load(self, im: ImageFile.StubImageFile) -> Image.Image:
self.loaded = True self.loaded = True
assert im.fp is not None
im.fp.close() im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))

View File

@ -26,6 +26,7 @@ def test_invalid_file() -> None:
no_cursors_file = "Tests/images/no_cursors.cur" no_cursors_file = "Tests/images/no_cursors.cur"
cur = CurImagePlugin.CurImageFile(TEST_FILE) cur = CurImagePlugin.CurImageFile(TEST_FILE)
assert cur.fp is not None
cur.fp.close() cur.fp.close()
with open(no_cursors_file, "rb") as cur.fp: with open(no_cursors_file, "rb") as cur.fp:
with pytest.raises(TypeError): with pytest.raises(TypeError):

View File

@ -56,6 +56,7 @@ TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
) )
def test_sanity_dxt1_bc1(image_path: str) -> None: def test_sanity_dxt1_bc1(image_path: str) -> None:
"""Check DXT1 and BC1 images can be opened""" """Check DXT1 and BC1 images can be opened"""
target: Image.Image
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target: with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
target = target.convert("RGBA") target = target.convert("RGBA")
with Image.open(image_path) as im: with Image.open(image_path) as im:
@ -504,9 +505,9 @@ def test_save_dxt5(tmp_path: Path) -> None:
def test_save_dx10_bc5(tmp_path: Path) -> None: def test_save_dx10_bc5(tmp_path: Path) -> None:
out = tmp_path / "temp.dds" out = tmp_path / "temp.dds"
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im: with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as img:
im.save(out, pixel_format="BC5") img.save(out, pixel_format="BC5")
assert_image_similar_tofile(im, out, 9.56) assert_image_similar_tofile(img, out, 9.56)
im = hopper("L") im = hopper("L")
with pytest.raises(OSError, match="only RGB mode can be written as BC5"): with pytest.raises(OSError, match="only RGB mode can be written as BC5"):

View File

@ -256,8 +256,8 @@ def test_bytesio_object() -> None:
with Image.open(img_bytes) as img: with Image.open(img_bytes) as img:
img.load() img.load()
with Image.open(FILE1_COMPARE) as image1_scale1_compare: with Image.open(FILE1_COMPARE) as im:
image1_scale1_compare = image1_scale1_compare.convert("RGB") image1_scale1_compare = im.convert("RGB")
image1_scale1_compare.load() image1_scale1_compare.load()
assert_image_similar(img, image1_scale1_compare, 5) assert_image_similar(img, image1_scale1_compare, 5)
@ -292,16 +292,16 @@ def test_render_scale1() -> None:
# Zero bounding box # Zero bounding box
with Image.open(FILE1) as image1_scale1: with Image.open(FILE1) as image1_scale1:
image1_scale1.load() image1_scale1.load()
with Image.open(FILE1_COMPARE) as image1_scale1_compare: with Image.open(FILE1_COMPARE) as im:
image1_scale1_compare = image1_scale1_compare.convert("RGB") image1_scale1_compare = im.convert("RGB")
image1_scale1_compare.load() image1_scale1_compare.load()
assert_image_similar(image1_scale1, image1_scale1_compare, 5) assert_image_similar(image1_scale1, image1_scale1_compare, 5)
# Non-zero bounding box # Non-zero bounding box
with Image.open(FILE2) as image2_scale1: with Image.open(FILE2) as image2_scale1:
image2_scale1.load() image2_scale1.load()
with Image.open(FILE2_COMPARE) as image2_scale1_compare: with Image.open(FILE2_COMPARE) as im:
image2_scale1_compare = image2_scale1_compare.convert("RGB") image2_scale1_compare = im.convert("RGB")
image2_scale1_compare.load() image2_scale1_compare.load()
assert_image_similar(image2_scale1, image2_scale1_compare, 10) assert_image_similar(image2_scale1, image2_scale1_compare, 10)
@ -315,8 +315,8 @@ def test_render_scale2() -> None:
with Image.open(FILE1) as image1_scale2: with Image.open(FILE1) as image1_scale2:
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile) assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
image1_scale2.load(scale=2) image1_scale2.load(scale=2)
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare: with Image.open(FILE1_COMPARE_SCALE2) as im:
image1_scale2_compare = image1_scale2_compare.convert("RGB") image1_scale2_compare = im.convert("RGB")
image1_scale2_compare.load() image1_scale2_compare.load()
assert_image_similar(image1_scale2, image1_scale2_compare, 5) assert_image_similar(image1_scale2, image1_scale2_compare, 5)
@ -324,8 +324,8 @@ def test_render_scale2() -> None:
with Image.open(FILE2) as image2_scale2: with Image.open(FILE2) as image2_scale2:
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile) assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
image2_scale2.load(scale=2) image2_scale2.load(scale=2)
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare: with Image.open(FILE2_COMPARE_SCALE2) as im:
image2_scale2_compare = image2_scale2_compare.convert("RGB") image2_scale2_compare = im.convert("RGB")
image2_scale2_compare.load() image2_scale2_compare.load()
assert_image_similar(image2_scale2, image2_scale2_compare, 10) assert_image_similar(image2_scale2, image2_scale2_compare, 10)
@ -335,9 +335,9 @@ def test_render_scale2() -> None:
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps") "filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
) )
def test_resize(filename: str) -> None: def test_resize(filename: str) -> None:
with Image.open(filename) as im: with Image.open(filename) as img:
new_size = (100, 100) new_size = (100, 100)
im = im.resize(new_size) im = img.resize(new_size)
assert im.size == new_size assert im.size == new_size

View File

@ -48,6 +48,8 @@ def test_sanity() -> None:
def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None: def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True) monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
with Image.open(animated_test_file_with_prefix_chunk) as im: with Image.open(animated_test_file_with_prefix_chunk) as im:
assert isinstance(im, FliImagePlugin.FliImageFile)
assert im.mode == "P" assert im.mode == "P"
assert im.size == (320, 200) assert im.size == (320, 200)
assert im.format == "FLI" assert im.format == "FLI"
@ -55,6 +57,7 @@ def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
assert im.is_animated assert im.is_animated
palette = im.getpalette() palette = im.getpalette()
assert palette is not None
assert palette[3:6] == [255, 255, 255] assert palette[3:6] == [255, 255, 255]
assert palette[381:384] == [204, 204, 12] assert palette[381:384] == [204, 204, 12]
assert palette[765:] == [252, 0, 0] assert palette[765:] == [252, 0, 0]

View File

@ -293,6 +293,7 @@ def test_roundtrip_save_all(tmp_path: Path) -> None:
im.save(out, save_all=True) im.save(out, save_all=True)
with Image.open(out) as reread: with Image.open(out) as reread:
assert isinstance(reread, GifImagePlugin.GifImageFile)
assert reread.n_frames == 5 assert reread.n_frames == 5
@ -318,6 +319,7 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
), ),
) )
def test_loading_multiple_palettes(path: str, mode: str) -> None: def test_loading_multiple_palettes(path: str, mode: str) -> None:
im: Image.Image
with Image.open(path) as im: with Image.open(path) as im:
assert im.mode == "P" assert im.mode == "P"
assert im.palette is not None assert im.palette is not None
@ -351,7 +353,7 @@ def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:
def test_palette_handling(tmp_path: Path) -> None: def test_palette_handling(tmp_path: Path) -> None:
# see https://github.com/python-pillow/Pillow/issues/513 # see https://github.com/python-pillow/Pillow/issues/513
im: Image.Image
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
im = im.convert("RGB") im = im.convert("RGB")
@ -375,8 +377,8 @@ def test_palette_434(tmp_path: Path) -> None:
return reloaded return reloaded
orig = "Tests/images/test.colors.gif" im: Image.Image
with Image.open(orig) as im: with Image.open("Tests/images/test.colors.gif") as im:
with roundtrip(im) as reloaded: with roundtrip(im) as reloaded:
assert_image_similar(im, reloaded, 1) assert_image_similar(im, reloaded, 1)
with roundtrip(im, optimize=True) as reloaded: with roundtrip(im, optimize=True) as reloaded:
@ -391,6 +393,7 @@ def test_palette_434(tmp_path: Path) -> None:
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None: def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
img: Image.Image
with Image.open(TEST_GIF) as img: with Image.open(TEST_GIF) as img:
img = img.convert("RGB") img = img.convert("RGB")
@ -403,6 +406,7 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_l_mode(tmp_path: Path) -> None: def test_save_netpbm_l_mode(tmp_path: Path) -> None:
img: Image.Image
with Image.open(TEST_GIF) as img: with Image.open(TEST_GIF) as img:
img = img.convert("L") img = img.convert("L")
@ -1032,9 +1036,9 @@ def test_webp_background(tmp_path: Path) -> None:
# Test opaque WebP background # Test opaque WebP background
if features.check("webp"): if features.check("webp"):
with Image.open("Tests/images/hopper.webp") as im: with Image.open("Tests/images/hopper.webp") as img:
assert im.info["background"] == (255, 255, 255, 255) assert img.info["background"] == (255, 255, 255, 255)
im.save(out) img.save(out)
# Test non-opaque WebP background # Test non-opaque WebP background
im = Image.new("L", (100, 100), "#000") im = Image.new("L", (100, 100), "#000")
@ -1043,6 +1047,7 @@ def test_webp_background(tmp_path: Path) -> None:
def test_comment(tmp_path: Path) -> None: def test_comment(tmp_path: Path) -> None:
im: Image.Image
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0" assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"
@ -1375,6 +1380,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
with Image.open(out) as im: with Image.open(out) as im:
# Assert that the frames are correct, and each frame has the same palette # Assert that the frames are correct, and each frame has the same palette
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB")) assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
assert isinstance(im, GifImagePlugin.GifImageFile)
assert im.palette is not None assert im.palette is not None
assert im.global_palette is not None assert im.global_palette is not None
assert im.palette.palette == im.global_palette.palette assert im.palette.palette == im.global_palette.palette

View File

@ -59,8 +59,9 @@ def test_handler(tmp_path: Path) -> None:
def open(self, im: Image.Image) -> None: def open(self, im: Image.Image) -> None:
self.opened = True self.opened = True
def load(self, im: Image.Image) -> Image.Image: def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True self.loaded = True
assert im.fp is not None
im.fp.close() im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))

View File

@ -61,8 +61,9 @@ def test_handler(tmp_path: Path) -> None:
def open(self, im: Image.Image) -> None: def open(self, im: Image.Image) -> None:
self.opened = True self.opened = True
def load(self, im: Image.Image) -> Image.Image: def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True self.loaded = True
assert im.fp is not None
im.fp.close() im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))

View File

@ -28,7 +28,7 @@ def test_getiptcinfo_jpg_none() -> None:
# Arrange # Arrange
with hopper() as im: with hopper() as im:
# Act # Act
iptc = IptcImagePlugin.getiptcinfo(im) iptc = IptcImagePlugin.getiptcinfo(im) # type: ignore[arg-type]
# Assert # Assert
assert iptc is None assert iptc is None

View File

@ -331,8 +331,10 @@ class TestFileJpeg:
# Reading # Reading
with Image.open("Tests/images/exif_gps.jpg") as im: with Image.open("Tests/images/exif_gps.jpg") as im:
exif = im._getexif() assert isinstance(im, JpegImagePlugin.JpegImageFile)
assert exif[gps_index] == expected_exif_gps exif_data = im._getexif()
assert exif_data is not None
assert exif_data[gps_index] == expected_exif_gps
# Writing # Writing
f = tmp_path / "temp.jpg" f = tmp_path / "temp.jpg"
@ -341,8 +343,10 @@ class TestFileJpeg:
hopper().save(f, exif=exif) hopper().save(f, exif=exif)
with Image.open(f) as reloaded: with Image.open(f) as reloaded:
exif = reloaded._getexif() assert isinstance(reloaded, JpegImagePlugin.JpegImageFile)
assert exif[gps_index] == expected_exif_gps exif_data = reloaded._getexif()
assert exif_data is not None
assert exif_data[gps_index] == expected_exif_gps
def test_empty_exif_gps(self) -> None: def test_empty_exif_gps(self) -> None:
with Image.open("Tests/images/empty_gps_ifd.jpg") as im: with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
@ -369,6 +373,7 @@ class TestFileJpeg:
exifs = [] exifs = []
for i in range(2): for i in range(2):
with Image.open("Tests/images/exif-200dpcm.jpg") as im: with Image.open("Tests/images/exif-200dpcm.jpg") as im:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
exifs.append(im._getexif()) exifs.append(im._getexif())
assert exifs[0] == exifs[1] assert exifs[0] == exifs[1]
@ -402,13 +407,18 @@ class TestFileJpeg:
} }
with Image.open("Tests/images/exif_gps.jpg") as im: with Image.open("Tests/images/exif_gps.jpg") as im:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
exif = im._getexif() exif = im._getexif()
assert exif is not None
for tag, value in expected_exif.items(): for tag, value in expected_exif.items():
assert value == exif[tag] assert value == exif[tag]
def test_exif_gps_typeerror(self) -> None: def test_exif_gps_typeerror(self) -> None:
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im: with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
# Should not raise a TypeError # Should not raise a TypeError
im._getexif() im._getexif()
@ -488,7 +498,9 @@ class TestFileJpeg:
def test_exif(self) -> None: def test_exif(self) -> None:
with Image.open("Tests/images/pil_sample_rgb.jpg") as im: with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
info = im._getexif() info = im._getexif()
assert info is not None
assert info[305] == "Adobe Photoshop CS Macintosh" assert info[305] == "Adobe Photoshop CS Macintosh"
def test_get_child_images(self) -> None: def test_get_child_images(self) -> None:
@ -692,10 +704,12 @@ class TestFileJpeg:
def test_save_multiple_16bit_qtables(self) -> None: def test_save_multiple_16bit_qtables(self) -> None:
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im: with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
im2 = self.roundtrip(im, qtables="keep") im2 = self.roundtrip(im, qtables="keep")
assert isinstance(im, JpegImagePlugin.JpegImageFile)
assert im.quantization == im2.quantization assert im.quantization == im2.quantization
def test_save_single_16bit_qtable(self) -> None: def test_save_single_16bit_qtable(self) -> None:
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im: with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
assert isinstance(im, JpegImagePlugin.JpegImageFile)
im2 = self.roundtrip(im, qtables={0: im.quantization[0]}) im2 = self.roundtrip(im, qtables={0: im.quantization[0]})
assert len(im2.quantization) == 1 assert len(im2.quantization) == 1
assert im2.quantization[0] == im.quantization[0] assert im2.quantization[0] == im.quantization[0]
@ -907,7 +921,10 @@ class TestFileJpeg:
# in contrast to normal 8 # in contrast to normal 8
with Image.open("Tests/images/exif-ifd-offset.jpg") as im: with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
# Act / Assert # Act / Assert
assert im._getexif()[306] == "2017:03:13 23:03:09" assert isinstance(im, JpegImagePlugin.JpegImageFile)
exif = im._getexif()
assert exif is not None
assert exif[306] == "2017:03:13 23:03:09"
def test_multiple_exif(self) -> None: def test_multiple_exif(self) -> None:
with Image.open("Tests/images/multiple_exif.jpg") as im: with Image.open("Tests/images/multiple_exif.jpg") as im:
@ -1126,8 +1143,9 @@ class TestFileCloseW32:
im.save(tmpfile) im.save(tmpfile)
im = Image.open(tmpfile) im = Image.open(tmpfile)
assert im.fp is not None
assert not im.fp.closed
fp = im.fp fp = im.fp
assert not fp.closed
with pytest.raises(OSError): with pytest.raises(OSError):
os.remove(tmpfile) os.remove(tmpfile)
im.load() im.load()

View File

@ -164,7 +164,7 @@ def test_reduce() -> None:
with Image.open("Tests/images/test-card-lossless.jp2") as im: with Image.open("Tests/images/test-card-lossless.jp2") as im:
assert callable(im.reduce) assert callable(im.reduce)
im.reduce = 2 im.reduce = 2 # type: ignore[method-assign, assignment]
assert im.reduce == 2 assert im.reduce == 2
im.load() im.load()

View File

@ -11,7 +11,15 @@ from typing import Any, NamedTuple
import pytest import pytest
from PIL import Image, ImageFilter, ImageOps, TiffImagePlugin, TiffTags, features from PIL import (
Image,
ImageFile,
ImageFilter,
ImageOps,
TiffImagePlugin,
TiffTags,
features,
)
from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD
from .helper import ( from .helper import (
@ -27,7 +35,7 @@ from .helper import (
@skip_unless_feature("libtiff") @skip_unless_feature("libtiff")
class LibTiffTestCase: class LibTiffTestCase:
def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> None: def _assert_noerr(self, tmp_path: Path, im: ImageFile.ImageFile) -> None:
"""Helper tests that assert basic sanity about the g4 tiff reading""" """Helper tests that assert basic sanity about the g4 tiff reading"""
# 1 bit # 1 bit
assert im.mode == "1" assert im.mode == "1"
@ -429,7 +437,6 @@ class TestFileLibTiff(LibTiffTestCase):
assert isinstance(orig, TiffImagePlugin.TiffImageFile) assert isinstance(orig, TiffImagePlugin.TiffImageFile)
out = tmp_path / "temp.tif" out = tmp_path / "temp.tif"
orig.tag[269] = "temp.tif" orig.tag[269] = "temp.tif"
orig.save(out) orig.save(out)
@ -457,8 +464,8 @@ class TestFileLibTiff(LibTiffTestCase):
# test case from irc, how to do blur on b/w image # test case from irc, how to do blur on b/w image
# and save to compressed tif. # and save to compressed tif.
out = tmp_path / "temp.tif" out = tmp_path / "temp.tif"
with Image.open("Tests/images/pport_g4.tif") as im: with Image.open("Tests/images/pport_g4.tif") as img:
im = im.convert("L") im = img.convert("L")
im = im.filter(ImageFilter.GaussianBlur(4)) im = im.filter(ImageFilter.GaussianBlur(4))
im.save(out, compression="tiff_adobe_deflate") im.save(out, compression="tiff_adobe_deflate")
@ -552,8 +559,9 @@ class TestFileLibTiff(LibTiffTestCase):
im.save(out, compression=compression) im.save(out, compression=compression)
def test_fp_leak(self) -> None: def test_fp_leak(self) -> None:
im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif") im: ImageFile.ImageFile | None = Image.open("Tests/images/hopper_g4_500.tif")
assert im is not None assert im is not None
assert im.fp is not None
fn = im.fp.fileno() fn = im.fp.fileno()
os.fstat(fn) os.fstat(fn)
@ -1028,8 +1036,8 @@ class TestFileLibTiff(LibTiffTestCase):
# Set EXIF Orientation to 2 # Set EXIF Orientation to 2
data = data[:102] + b"\x02" + data[103:] data = data[:102] + b"\x02" + data[103:]
with Image.open(io.BytesIO(data)) as im: with Image.open(io.BytesIO(data)) as img:
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) im = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png") assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
def test_open_missing_samplesperpixel(self) -> None: def test_open_missing_samplesperpixel(self) -> None:
@ -1097,9 +1105,8 @@ class TestFileLibTiff(LibTiffTestCase):
with Image.open("Tests/images/g4_orientation_1.tif") as base_im: with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
for i in range(2, 9): for i in range(2, 9):
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im: with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
im = ImageOps.exif_transpose(im) transposed_im = ImageOps.exif_transpose(im)
assert_image_similar(base_im, transposed_im, 0.7)
assert_image_similar(base_im, im, 0.7)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_file", "test_file",

View File

@ -22,10 +22,10 @@ def test_sanity() -> None:
# Adjust for the gamma of 2.2 encoded into the file # Adjust for the gamma of 2.2 encoded into the file
lut = ImagePalette.make_gamma_lut(1 / 2.2) lut = ImagePalette.make_gamma_lut(1 / 2.2)
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()]) im1 = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
im2 = hopper("RGBA") im2 = hopper("RGBA")
assert_image_similar(im, im2, 10) assert_image_similar(im1, im2, 10)
def test_n_frames() -> None: def test_n_frames() -> None:

View File

@ -120,9 +120,11 @@ def test_ignore_frame_size() -> None:
# Ignore the different size of the second frame # Ignore the different size of the second frame
# since this is not a "Large Thumbnail" image # since this is not a "Large Thumbnail" image
with Image.open("Tests/images/ignore_frame_size.mpo") as im: with Image.open("Tests/images/ignore_frame_size.mpo") as im:
assert isinstance(im, MpoImagePlugin.MpoImageFile)
assert im.size == (64, 64) assert im.size == (64, 64)
im.seek(1) im.seek(1)
assert im.mpinfo is not None
assert ( assert (
im.mpinfo[0xB002][1]["Attribute"]["MPType"] im.mpinfo[0xB002][1]["Attribute"]["MPType"]
== "Multi-Frame Image: (Disparity)" == "Multi-Frame Image: (Disparity)"
@ -155,6 +157,7 @@ def test_reload_exif_after_seek() -> None:
@pytest.mark.parametrize("test_file", test_files) @pytest.mark.parametrize("test_file", test_files)
def test_mp(test_file: str) -> None: def test_mp(test_file: str) -> None:
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert isinstance(im, MpoImagePlugin.MpoImageFile)
mpinfo = im._getmp() mpinfo = im._getmp()
assert mpinfo is not None assert mpinfo is not None
assert mpinfo[45056] == b"0100" assert mpinfo[45056] == b"0100"
@ -165,6 +168,7 @@ def test_mp_offset() -> None:
# This image has been manually hexedited to have an IFD offset of 10 # This image has been manually hexedited to have an IFD offset of 10
# in APP2 data, in contrast to normal 8 # in APP2 data, in contrast to normal 8
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im: with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
assert isinstance(im, MpoImagePlugin.MpoImageFile)
mpinfo = im._getmp() mpinfo = im._getmp()
assert mpinfo is not None assert mpinfo is not None
assert mpinfo[45056] == b"0100" assert mpinfo[45056] == b"0100"
@ -182,6 +186,7 @@ def test_mp_no_data() -> None:
@pytest.mark.parametrize("test_file", test_files) @pytest.mark.parametrize("test_file", test_files)
def test_mp_attribute(test_file: str) -> None: def test_mp_attribute(test_file: str) -> None:
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert isinstance(im, MpoImagePlugin.MpoImageFile)
mpinfo = im._getmp() mpinfo = im._getmp()
assert mpinfo is not None assert mpinfo is not None
for frame_number, mpentry in enumerate(mpinfo[0xB002]): for frame_number, mpentry in enumerate(mpinfo[0xB002]):
@ -284,6 +289,7 @@ def test_save(test_file: str) -> None:
def test_save_all() -> None: def test_save_all() -> None:
im: Image.Image
for test_file in test_files: for test_file in test_files:
with Image.open(test_file) as im: with Image.open(test_file) as im:
im_reloaded = roundtrip(im, save_all=True) im_reloaded = roundtrip(im, save_all=True)

View File

@ -93,6 +93,7 @@ class TestFilePng:
hopper("RGB").save(test_file) hopper("RGB").save(test_file)
im: Image.Image
with Image.open(test_file) as im: with Image.open(test_file) as im:
im.load() im.load()
assert im.mode == "RGB" assert im.mode == "RGB"
@ -103,6 +104,8 @@ class TestFilePng:
for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]: for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]:
im = hopper(mode) im = hopper(mode)
im.save(test_file) im.save(test_file)
reloaded: Image.Image
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
if mode == "I;16B": if mode == "I;16B":
reloaded = reloaded.convert(mode) reloaded = reloaded.convert(mode)
@ -225,11 +228,13 @@ class TestFilePng:
test_file = "Tests/images/pil123p.png" test_file = "Tests/images/pil123p.png"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert_image(im, "P", (162, 150)) assert_image(im, "P", (162, 150))
im = im.convert("RGBA") rgba_im = im.convert("RGBA")
assert_image(im, "RGBA", (162, 150)) assert_image(rgba_im, "RGBA", (162, 150))
# image has 124 unique alpha values # image has 124 unique alpha values
assert len(im.getchannel("A").getcolors()) == 124 colors = rgba_im.getchannel("A").getcolors()
assert colors is not None
assert len(colors) == 124
def test_load_transparent_rgb(self) -> None: def test_load_transparent_rgb(self) -> None:
test_file = "Tests/images/rgb_trns.png" test_file = "Tests/images/rgb_trns.png"
@ -237,11 +242,13 @@ class TestFilePng:
assert im.info["transparency"] == (0, 255, 52) assert im.info["transparency"] == (0, 255, 52)
assert_image(im, "RGB", (64, 64)) assert_image(im, "RGB", (64, 64))
im = im.convert("RGBA") rgba_im = im.convert("RGBA")
assert_image(im, "RGBA", (64, 64)) assert_image(rgba_im, "RGBA", (64, 64))
# image has 876 transparent pixels # image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876 colors = rgba_im.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == 876
def test_save_p_transparent_palette(self, tmp_path: Path) -> None: def test_save_p_transparent_palette(self, tmp_path: Path) -> None:
in_file = "Tests/images/pil123p.png" in_file = "Tests/images/pil123p.png"
@ -258,11 +265,13 @@ class TestFilePng:
assert len(im.info["transparency"]) == 256 assert len(im.info["transparency"]) == 256
assert_image(im, "P", (162, 150)) assert_image(im, "P", (162, 150))
im = im.convert("RGBA") rgba_im = im.convert("RGBA")
assert_image(im, "RGBA", (162, 150)) assert_image(rgba_im, "RGBA", (162, 150))
# image has 124 unique alpha values # image has 124 unique alpha values
assert len(im.getchannel("A").getcolors()) == 124 colors = rgba_im.getchannel("A").getcolors()
assert colors is not None
assert len(colors) == 124
def test_save_p_single_transparency(self, tmp_path: Path) -> None: def test_save_p_single_transparency(self, tmp_path: Path) -> None:
in_file = "Tests/images/p_trns_single.png" in_file = "Tests/images/p_trns_single.png"
@ -279,13 +288,15 @@ class TestFilePng:
assert im.info["transparency"] == 164 assert im.info["transparency"] == 164
assert im.getpixel((31, 31)) == 164 assert im.getpixel((31, 31)) == 164
assert_image(im, "P", (64, 64)) assert_image(im, "P", (64, 64))
im = im.convert("RGBA") rgba_im = im.convert("RGBA")
assert_image(im, "RGBA", (64, 64)) assert_image(rgba_im, "RGBA", (64, 64))
assert im.getpixel((31, 31)) == (0, 255, 52, 0) assert rgba_im.getpixel((31, 31)) == (0, 255, 52, 0)
# image has 876 transparent pixels # image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876 colors = rgba_im.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == 876
def test_save_p_transparent_black(self, tmp_path: Path) -> None: def test_save_p_transparent_black(self, tmp_path: Path) -> None:
# check if solid black image with full transparency # check if solid black image with full transparency
@ -313,7 +324,9 @@ class TestFilePng:
assert im.info["transparency"] == 255 assert im.info["transparency"] == 255
im_rgba = im.convert("RGBA") im_rgba = im.convert("RGBA")
assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent colors = im_rgba.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == num_transparent
test_file = tmp_path / "temp.png" test_file = tmp_path / "temp.png"
im.save(test_file) im.save(test_file)
@ -324,7 +337,9 @@ class TestFilePng:
assert_image_equal(im, test_im) assert_image_equal(im, test_im)
test_im_rgba = test_im.convert("RGBA") test_im_rgba = test_im.convert("RGBA")
assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent colors = test_im_rgba.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == num_transparent
def test_save_rgb_single_transparency(self, tmp_path: Path) -> None: def test_save_rgb_single_transparency(self, tmp_path: Path) -> None:
in_file = "Tests/images/caption_6_33_22.png" in_file = "Tests/images/caption_6_33_22.png"
@ -671,6 +686,9 @@ class TestFilePng:
im.save(out, bits=4, save_all=save_all) im.save(out, bits=4, save_all=save_all)
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
assert reloaded.png is not None
assert reloaded.png.im_palette is not None
assert len(reloaded.png.im_palette[1]) == 48 assert len(reloaded.png.im_palette[1]) == 48
def test_plte_length(self, tmp_path: Path) -> None: def test_plte_length(self, tmp_path: Path) -> None:
@ -681,6 +699,9 @@ class TestFilePng:
im.save(out) im.save(out)
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
assert reloaded.png is not None
assert reloaded.png.im_palette is not None
assert len(reloaded.png.im_palette[1]) == 3 assert len(reloaded.png.im_palette[1]) == 3
def test_getxmp(self) -> None: def test_getxmp(self) -> None:
@ -702,13 +723,17 @@ class TestFilePng:
def test_exif(self) -> None: def test_exif(self) -> None:
# With an EXIF chunk # With an EXIF chunk
with Image.open("Tests/images/exif.png") as im: with Image.open("Tests/images/exif.png") as im:
exif = im._getexif() assert isinstance(im, PngImagePlugin.PngImageFile)
assert exif[274] == 1 exif_data = im._getexif()
assert exif_data is not None
assert exif_data[274] == 1
# With an ImageMagick zTXt chunk # With an ImageMagick zTXt chunk
with Image.open("Tests/images/exif_imagemagick.png") as im: with Image.open("Tests/images/exif_imagemagick.png") as im:
exif = im._getexif() assert isinstance(im, PngImagePlugin.PngImageFile)
assert exif[274] == 1 exif_data = im._getexif()
assert exif_data is not None
assert exif_data[274] == 1
# Assert that info still can be extracted # Assert that info still can be extracted
# when the image is no longer a PngImageFile instance # when the image is no longer a PngImageFile instance
@ -717,8 +742,10 @@ class TestFilePng:
# With a tEXt chunk # With a tEXt chunk
with Image.open("Tests/images/exif_text.png") as im: with Image.open("Tests/images/exif_text.png") as im:
exif = im._getexif() assert isinstance(im, PngImagePlugin.PngImageFile)
assert exif[274] == 1 exif_data = im._getexif()
assert exif_data is not None
assert exif_data[274] == 1
# With XMP tags # With XMP tags
with Image.open("Tests/images/xmp_tags_orientation.png") as im: with Image.open("Tests/images/xmp_tags_orientation.png") as im:
@ -740,7 +767,9 @@ class TestFilePng:
im.save(test_file, exif=im.getexif()) im.save(test_file, exif=im.getexif())
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
exif = reloaded._getexif() exif = reloaded._getexif()
assert exif is not None
assert exif[274] == 1 assert exif[274] == 1
@mark_if_feature_version( @mark_if_feature_version(
@ -752,7 +781,9 @@ class TestFilePng:
im.save(test_file, exif=im.getexif()) im.save(test_file, exif=im.getexif())
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
exif = reloaded._getexif() exif = reloaded._getexif()
assert exif is not None
assert exif[305] == "Adobe Photoshop CS Macintosh" assert exif[305] == "Adobe Photoshop CS Macintosh"
def test_exif_argument(self, tmp_path: Path) -> None: def test_exif_argument(self, tmp_path: Path) -> None:
@ -776,7 +807,6 @@ class TestFilePng:
@pytest.mark.parametrize("buffer", (True, False)) @pytest.mark.parametrize("buffer", (True, False))
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None: def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
class MyStdOut: class MyStdOut:
buffer = BytesIO() buffer = BytesIO()
@ -785,7 +815,7 @@ class TestFilePng:
monkeypatch.setattr(sys, "stdout", mystdout) monkeypatch.setattr(sys, "stdout", mystdout)
with Image.open(TEST_PNG_FILE) as im: with Image.open(TEST_PNG_FILE) as im:
im.save(sys.stdout, "PNG") im.save(sys.stdout, "PNG") # type: ignore[arg-type]
if isinstance(mystdout, MyStdOut): if isinstance(mystdout, MyStdOut):
mystdout = mystdout.buffer mystdout = mystdout.buffer

View File

@ -367,7 +367,6 @@ def test_mimetypes(tmp_path: Path) -> None:
@pytest.mark.parametrize("buffer", (True, False)) @pytest.mark.parametrize("buffer", (True, False))
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None: def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
class MyStdOut: class MyStdOut:
buffer = BytesIO() buffer = BytesIO()
@ -376,7 +375,7 @@ def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(sys, "stdout", mystdout) monkeypatch.setattr(sys, "stdout", mystdout)
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
im.save(sys.stdout, "PPM") im.save(sys.stdout, "PPM") # type: ignore[arg-type]
if isinstance(mystdout, MyStdOut): if isinstance(mystdout, MyStdOut):
mystdout = mystdout.buffer mystdout = mystdout.buffer

View File

@ -84,8 +84,8 @@ def test_rgbx() -> None:
with Image.open(io.BytesIO(data)) as im: with Image.open(io.BytesIO(data)) as im:
r, g, b = im.split() r, g, b = im.split()
im = Image.merge("RGB", (b, g, r)) im_bgr = Image.merge("RGB", (b, g, r))
assert_image_equal_tofile(im, os.path.join(EXTRA_DIR, "32bpp.png")) assert_image_equal_tofile(im_bgr, os.path.join(EXTRA_DIR, "32bpp.png"))
@pytest.mark.skipif( @pytest.mark.skipif(

View File

@ -274,13 +274,17 @@ def test_save_l_transparency(tmp_path: Path) -> None:
in_file = "Tests/images/la.tga" in_file = "Tests/images/la.tga"
with Image.open(in_file) as im: with Image.open(in_file) as im:
assert im.mode == "LA" assert im.mode == "LA"
assert im.getchannel("A").getcolors()[0][0] == num_transparent colors = im.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == num_transparent
out = tmp_path / "temp.tga" out = tmp_path / "temp.tga"
im.save(out) im.save(out)
with Image.open(out) as test_im: with Image.open(out) as test_im:
assert test_im.mode == "LA" assert test_im.mode == "LA"
assert test_im.getchannel("A").getcolors()[0][0] == num_transparent colors = test_im.getchannel("A").getcolors()
assert colors is not None
assert colors[0][0] == num_transparent
assert_image_equal(im, test_im) assert_image_equal(im, test_im)

View File

@ -382,6 +382,7 @@ class TestFileTiff:
assert isinstance(im, TiffImagePlugin.TiffImageFile) assert isinstance(im, TiffImagePlugin.TiffImageFile)
# Act # Act
assert isinstance(im, TiffImagePlugin.TiffImageFile)
ret = str(im.ifd) ret = str(im.ifd)
# Assert # Assert
@ -754,13 +755,13 @@ class TestFileTiff:
def test_tiff_save_all(self) -> None: def test_tiff_save_all(self) -> None:
mp = BytesIO() mp = BytesIO()
with Image.open("Tests/images/multipage.tiff") as im: with Image.open("Tests/images/multipage.tiff") as img:
im.save(mp, format="tiff", save_all=True) img.save(mp, format="tiff", save_all=True)
mp.seek(0, os.SEEK_SET) mp.seek(0, os.SEEK_SET)
with Image.open(mp) as im: with Image.open(mp) as img:
assert isinstance(im, TiffImagePlugin.TiffImageFile) assert isinstance(img, TiffImagePlugin.TiffImageFile)
assert im.n_frames == 3 assert img.n_frames == 3
# Test appending images # Test appending images
mp = BytesIO() mp = BytesIO()
@ -971,6 +972,7 @@ class TestFileTiff:
im = Image.open(tmpfile) im = Image.open(tmpfile)
fp = im.fp fp = im.fp
assert fp is not None
assert not fp.closed assert not fp.closed
im.load() im.load()
assert fp.closed assert fp.closed
@ -984,6 +986,7 @@ class TestFileTiff:
with open(tmpfile, "rb") as f: with open(tmpfile, "rb") as f:
im = Image.open(f) im = Image.open(f)
fp = im.fp fp = im.fp
assert fp is not None
assert not fp.closed assert not fp.closed
im.load() im.load()
assert not fp.closed assert not fp.closed
@ -1034,8 +1037,9 @@ class TestFileTiffW32:
im.save(tmpfile) im.save(tmpfile)
im = Image.open(tmpfile) im = Image.open(tmpfile)
assert im.fp is not None
assert not im.fp.closed
fp = im.fp fp = im.fp
assert not fp.closed
with pytest.raises(OSError): with pytest.raises(OSError):
os.remove(tmpfile) os.remove(tmpfile)
im.load() im.load()

View File

@ -175,13 +175,13 @@ def test_change_stripbytecounts_tag_type(tmp_path: Path) -> None:
del info[278] del info[278]
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT # Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
im = im.resize((500, 500)) resized_im = im.resize((500, 500))
info[TiffImagePlugin.IMAGEWIDTH] = im.width info[TiffImagePlugin.IMAGEWIDTH] = resized_im.width
# STRIPBYTECOUNTS can be a SHORT or a LONG # STRIPBYTECOUNTS can be a SHORT or a LONG
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
im.save(out, tiffinfo=info) resized_im.save(out, tiffinfo=info)
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile) assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)

View File

@ -22,11 +22,13 @@ except ImportError:
def test_read_exif_metadata() -> None: def test_read_exif_metadata() -> None:
file_path = "Tests/images/flower.webp" file_path = "Tests/images/flower.webp"
with Image.open(file_path) as image: with Image.open(file_path) as image:
assert isinstance(image, WebPImagePlugin.WebPImageFile)
assert image.format == "WEBP" assert image.format == "WEBP"
exif_data = image.info.get("exif", None) exif_data = image.info.get("exif", None)
assert exif_data assert exif_data
exif = image._getexif() exif = image._getexif()
assert exif is not None
# Camera make # Camera make
assert exif[271] == "Canon" assert exif[271] == "Canon"

View File

@ -607,8 +607,8 @@ class TestImage:
assert im.mode == mode assert im.mode == mode
assert im.getpixel((0, 0)) == 0 assert im.getpixel((0, 0)) == 0
assert im.getpixel((255, 255)) == 255 assert im.getpixel((255, 255)) == 255
with Image.open(target_file) as target: with Image.open(target_file) as img:
target = target.convert(mode) target = img.convert(mode)
assert_image_equal(im, target) assert_image_equal(im, target)
def test_radial_gradient_wrong_mode(self) -> None: def test_radial_gradient_wrong_mode(self) -> None:
@ -632,8 +632,8 @@ class TestImage:
assert im.mode == mode assert im.mode == mode
assert im.getpixel((0, 0)) == 255 assert im.getpixel((0, 0)) == 255
assert im.getpixel((128, 128)) == 0 assert im.getpixel((128, 128)) == 0
with Image.open(target_file) as target: with Image.open(target_file) as img:
target = target.convert(mode) target = img.convert(mode)
assert_image_equal(im, target) assert_image_equal(im, target)
def test_register_extensions(self) -> None: def test_register_extensions(self) -> None:
@ -654,8 +654,8 @@ class TestImage:
def test_remap_palette(self) -> None: def test_remap_palette(self) -> None:
# Test identity transform # Test identity transform
with Image.open("Tests/images/hopper.gif") as im: with Image.open("Tests/images/hopper.gif") as img:
assert_image_equal(im, im.remap_palette(list(range(256)))) assert_image_equal(img, img.remap_palette(list(range(256))))
# Test identity transform with an RGBA palette # Test identity transform with an RGBA palette
im = Image.new("P", (256, 1)) im = Image.new("P", (256, 1))

View File

@ -76,8 +76,8 @@ def test_8bit() -> None:
def test_16bit() -> None: def test_16bit() -> None:
with Image.open("Tests/images/16bit.cropped.tif") as im: with Image.open("Tests/images/16bit.cropped.tif") as img:
_test_float_conversion(im) _test_float_conversion(img)
for color in (65535, 65536): for color in (65535, 65536):
im = Image.new("I", (1, 1), color) im = Image.new("I", (1, 1), color)

View File

@ -78,13 +78,13 @@ def test_crop_crash() -> None:
extents = (1, 1, 10, 10) extents = (1, 1, 10, 10)
# works prepatch # works prepatch
with Image.open(test_img) as img: with Image.open(test_img) as img:
img2 = img.crop(extents) img1 = img.crop(extents)
img2.load() img1.load()
# fail prepatch # fail prepatch
with Image.open(test_img) as img: with Image.open(test_img) as img:
img = img.crop(extents) img2 = img.crop(extents)
img.load() img2.load()
def test_crop_zero() -> None: def test_crop_zero() -> None:

View File

@ -38,6 +38,7 @@ def test_close_after_load(caplog: pytest.LogCaptureFixture) -> None:
def test_contextmanager() -> None: def test_contextmanager() -> None:
fn = None fn = None
with Image.open("Tests/images/hopper.gif") as im: with Image.open("Tests/images/hopper.gif") as im:
assert im.fp is not None
fn = im.fp.fileno() fn = im.fp.fileno()
os.fstat(fn) os.fstat(fn)

View File

@ -62,6 +62,7 @@ def test_putpalette_with_alpha_values() -> None:
expected = im.convert("RGBA") expected = im.convert("RGBA")
palette = im.getpalette() palette = im.getpalette()
assert palette is not None
transparency = im.info.pop("transparency") transparency = im.info.pop("transparency")
palette_with_alpha_values = [] palette_with_alpha_values = []

View File

@ -56,18 +56,18 @@ def test_rgba_quantize() -> None:
def test_quantize() -> None: def test_quantize() -> None:
with Image.open("Tests/images/caption_6_33_22.png") as image: with Image.open("Tests/images/caption_6_33_22.png") as image:
image = image.convert("RGB") converted = image.convert("RGB")
converted = image.quantize() converted = converted.quantize()
assert converted.mode == "P" assert converted.mode == "P"
assert_image_similar(converted.convert("RGB"), image, 1) assert_image_similar(converted.convert("RGB"), image, 1)
def test_quantize_no_dither() -> None: def test_quantize_no_dither() -> None:
image = hopper() palette: Image.Image
with Image.open("Tests/images/caption_6_33_22.png") as palette: with Image.open("Tests/images/caption_6_33_22.png") as palette:
palette = palette.convert("P") palette = palette.convert("P")
converted = image.quantize(dither=Image.Dither.NONE, palette=palette) converted = hopper().quantize(dither=Image.Dither.NONE, palette=palette)
assert converted.mode == "P" assert converted.mode == "P"
assert converted.palette is not None assert converted.palette is not None
assert palette.palette is not None assert palette.palette is not None
@ -94,8 +94,8 @@ def test_quantize_no_dither2() -> None:
def test_quantize_dither_diff() -> None: def test_quantize_dither_diff() -> None:
image = hopper() image = hopper()
with Image.open("Tests/images/caption_6_33_22.png") as palette: with Image.open("Tests/images/caption_6_33_22.png") as im:
palette = palette.convert("P") palette = im.convert("P")
dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette) dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette)
nodither = image.quantize(dither=Image.Dither.NONE, palette=palette) nodither = image.quantize(dither=Image.Dither.NONE, palette=palette)

View File

@ -313,8 +313,8 @@ class TestImageResize:
@skip_unless_feature("libtiff") @skip_unless_feature("libtiff")
def test_transposed(self) -> None: def test_transposed(self) -> None:
with Image.open("Tests/images/g4_orientation_5.tif") as im: with Image.open("Tests/images/g4_orientation_5.tif") as img:
im = im.resize((64, 64)) im = img.resize((64, 64))
assert im.size == (64, 64) assert im.size == (64, 64)
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -40,8 +40,8 @@ def test_mode(mode: str) -> None:
@pytest.mark.parametrize("angle", (0, 90, 180, 270)) @pytest.mark.parametrize("angle", (0, 90, 180, 270))
def test_angle(angle: int) -> None: def test_angle(angle: int) -> None:
with Image.open("Tests/images/test-card.png") as im: with Image.open("Tests/images/test-card.png") as img:
rotate(im, im.mode, angle) rotate(img, img.mode, angle)
im = hopper() im = hopper()
assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1)) assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1))
@ -74,6 +74,7 @@ def test_center_0() -> None:
im = hopper() im = hopper()
im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC) im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC)
target: Image.Image
with Image.open("Tests/images/hopper_45.png") as target: with Image.open("Tests/images/hopper_45.png") as target:
target_origin = target.size[1] / 2 target_origin = target.size[1] / 2
target = target.crop((0, target_origin, 128, target_origin + 128)) target = target.crop((0, target_origin, 128, target_origin + 128))
@ -85,6 +86,7 @@ def test_center_14() -> None:
im = hopper() im = hopper()
im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC) im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC)
target: Image.Image
with Image.open("Tests/images/hopper_45.png") as target: with Image.open("Tests/images/hopper_45.png") as target:
target_origin = target.size[1] / 2 - 14 target_origin = target.size[1] / 2 - 14
target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) target = target.crop((6, target_origin, 128 + 6, target_origin + 128))
@ -94,6 +96,8 @@ def test_center_14() -> None:
def test_translate() -> None: def test_translate() -> None:
im = hopper() im = hopper()
target: Image.Image
with Image.open("Tests/images/hopper_45.png") as target: with Image.open("Tests/images/hopper_45.png") as target:
target_origin = (target.size[1] / 2 - 64) - 5 target_origin = (target.size[1] / 2 - 64) - 5
target = target.crop( target = target.crop(

View File

@ -156,6 +156,7 @@ def test_reducing_gap_values() -> None:
def test_reducing_gap_for_DCT_scaling() -> None: def test_reducing_gap_for_DCT_scaling() -> None:
ref: Image.Image
with Image.open("Tests/images/hopper.jpg") as ref: with Image.open("Tests/images/hopper.jpg") as ref:
# thumbnail should call draft with reducing_gap scale # thumbnail should call draft with reducing_gap scale
ref.draft(None, (18 * 3, 18 * 3)) ref.draft(None, (18 * 3, 18 * 3))

View File

@ -247,14 +247,14 @@ class TestImageTransform:
def test_missing_method_data(self) -> None: def test_missing_method_data(self) -> None:
with hopper() as im: with hopper() as im:
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.transform((100, 100), None) im.transform((100, 100), None) # type: ignore[arg-type]
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown")) @pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None: def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
with hopper() as im: with hopper() as im:
(w, h) = im.size (w, h) = im.size
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample) im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample) # type: ignore[arg-type]
class TestImageTransformAffine: class TestImageTransformAffine:

View File

@ -194,6 +194,7 @@ def test_bitmap() -> None:
# Arrange # Arrange
im = Image.new("RGB", (W, H)) im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
small: Image.Image
with Image.open("Tests/images/pil123rgba.png") as small: with Image.open("Tests/images/pil123rgba.png") as small:
small = small.resize((50, 50), Image.Resampling.NEAREST) small = small.resize((50, 50), Image.Resampling.NEAREST)

View File

@ -245,8 +245,8 @@ def test_colorize_2color() -> None:
# Test the colorizing function with 2-color functionality # Test the colorizing function with 2-color functionality
# Open test image (256px by 10px, black to white) # Open test image (256px by 10px, black to white)
with Image.open("Tests/images/bw_gradient.png") as im: with Image.open("Tests/images/bw_gradient.png") as img:
im = im.convert("L") im = img.convert("L")
# Create image with original 2-color functionality # Create image with original 2-color functionality
im_test = ImageOps.colorize(im, "red", "green") im_test = ImageOps.colorize(im, "red", "green")
@ -285,8 +285,8 @@ def test_colorize_2color_offset() -> None:
# Test the colorizing function with 2-color functionality and offset # Test the colorizing function with 2-color functionality and offset
# Open test image (256px by 10px, black to white) # Open test image (256px by 10px, black to white)
with Image.open("Tests/images/bw_gradient.png") as im: with Image.open("Tests/images/bw_gradient.png") as img:
im = im.convert("L") im = img.convert("L")
# Create image with original 2-color functionality with offsets # Create image with original 2-color functionality with offsets
im_test = ImageOps.colorize( im_test = ImageOps.colorize(
@ -327,8 +327,8 @@ def test_colorize_3color_offset() -> None:
# Test the colorizing function with 3-color functionality and offset # Test the colorizing function with 3-color functionality and offset
# Open test image (256px by 10px, black to white) # Open test image (256px by 10px, black to white)
with Image.open("Tests/images/bw_gradient.png") as im: with Image.open("Tests/images/bw_gradient.png") as img:
im = im.convert("L") im = img.convert("L")
# Create image with new three color functionality with offsets # Create image with new three color functionality with offsets
im_test = ImageOps.colorize( im_test = ImageOps.colorize(
@ -422,6 +422,7 @@ def test_exif_transpose() -> None:
check(orientation_im) check(orientation_im)
# Orientation from "XML:com.adobe.xmp" info key # Orientation from "XML:com.adobe.xmp" info key
im: Image.Image
for suffix in ("", "_exiftool"): for suffix in ("", "_exiftool"):
with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im: with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im:
assert im.getexif()[0x0112] == 3 assert im.getexif()[0x0112] == 3

View File

@ -76,9 +76,14 @@ def test_consecutive() -> None:
def test_palette_mmap() -> None: def test_palette_mmap() -> None:
# Using mmap in ImageFile can require to reload the palette. # Using mmap in ImageFile can require to reload the palette.
with Image.open("Tests/images/multipage-mmap.tiff") as im: with Image.open("Tests/images/multipage-mmap.tiff") as im:
color1 = im.getpalette()[:3] palette = im.getpalette()
assert palette is not None
color1 = palette[:3]
im.seek(0) im.seek(0)
color2 = im.getpalette()[:3]
palette = im.getpalette()
assert palette is not None
color2 = palette[:3]
assert color1 == color2 assert color1 == color2

View File

@ -17,6 +17,7 @@ def helper_pickle_file(
tmp_path: Path, protocol: int, test_file: str, mode: str | None tmp_path: Path, protocol: int, test_file: str, mode: str | None
) -> None: ) -> None:
# Arrange # Arrange
im: Image.Image
with Image.open(test_file) as im: with Image.open(test_file) as im:
filename = tmp_path / "temp.pkl" filename = tmp_path / "temp.pkl"
if mode: if mode:
@ -33,6 +34,7 @@ def helper_pickle_file(
def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None: def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None:
im: Image.Image
with Image.open(test_file) as im: with Image.open(test_file) as im:
if mode: if mode:
im = im.convert(mode) im = im.convert(mode)
@ -89,8 +91,8 @@ def test_pickle_jpeg() -> None:
def test_pickle_la_mode_with_palette(tmp_path: Path) -> None: def test_pickle_la_mode_with_palette(tmp_path: Path) -> None:
# Arrange # Arrange
filename = tmp_path / "temp.pkl" filename = tmp_path / "temp.pkl"
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as img:
im = im.convert("PA") im = img.convert("PA")
# Act / Assert # Act / Assert
for protocol in range(pickle.HIGHEST_PROTOCOL + 1): for protocol in range(pickle.HIGHEST_PROTOCOL + 1):

View File

@ -50,11 +50,13 @@ class TestShellInjection:
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None: def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
im = im.convert("RGB") im_rgb = im.convert("RGB")
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm) self.assert_save_filename_check(
tmp_path, im_rgb, GifImagePlugin._save_netpbm
)
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None: def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
im = im.convert("L") im_l = im.convert("L")
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm) self.assert_save_filename_check(tmp_path, im_l, GifImagePlugin._save_netpbm)

View File

@ -213,6 +213,7 @@ class DdsImageFile(ImageFile.ImageFile):
format_description = "DirectDraw Surface" format_description = "DirectDraw Surface"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):
msg = "not a DDS file" msg = "not a DDS file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -217,6 +217,7 @@ testpaths = [
python_version = "3.9" python_version = "3.9"
pretty = true pretty = true
disallow_any_generics = true disallow_any_generics = true
disallow_untyped_defs = true
enable_error_code = "ignore-without-code" enable_error_code = "ignore-without-code"
extra_checks = true extra_checks = true
follow_imports = "silent" follow_imports = "silent"

View File

@ -77,6 +77,8 @@ class AvifImageFile(ImageFile.ImageFile):
): ):
msg = "Invalid opening codec" msg = "Invalid opening codec"
raise ValueError(msg) raise ValueError(msg)
assert self.fp is not None
self._decoder = _avif.AvifDecoder( self._decoder = _avif.AvifDecoder(
self.fp.read(), self.fp.read(),
DECODE_CODEC_CHOICE, DECODE_CODEC_CHOICE,

View File

@ -258,6 +258,7 @@ class BlpImageFile(ImageFile.ImageFile):
format_description = "Blizzard Mipmap Format" format_description = "Blizzard Mipmap Format"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
self.magic = self.fp.read(4) self.magic = self.fp.read(4)
if not _accept(self.magic): if not _accept(self.magic):
msg = f"Bad BLP magic {repr(self.magic)}" msg = f"Bad BLP magic {repr(self.magic)}"

View File

@ -76,6 +76,7 @@ class BmpImageFile(ImageFile.ImageFile):
def _bitmap(self, header: int = 0, offset: int = 0) -> None: def _bitmap(self, header: int = 0, offset: int = 0) -> None:
"""Read relevant info about the BMP""" """Read relevant info about the BMP"""
assert self.fp is not None
read, seek = self.fp.read, self.fp.seek read, seek = self.fp.read, self.fp.seek
if header: if header:
seek(header) seek(header)
@ -311,6 +312,7 @@ class BmpImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
"""Open file, check magic number and read header""" """Open file, check magic number and read header"""
# read 14 bytes: magic number, filesize, reserved, header final offset # read 14 bytes: magic number, filesize, reserved, header final offset
assert self.fp is not None
head_data = self.fp.read(14) head_data = self.fp.read(14)
# choke if the file does not have the required magic bytes # choke if the file does not have the required magic bytes
if not _accept(head_data): if not _accept(head_data):

View File

@ -41,6 +41,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
format_description = "BUFR" format_description = "BUFR"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):
msg = "Not a BUFR file" msg = "Not a BUFR file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -38,6 +38,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
format_description = "Windows Cursor" format_description = "Windows Cursor"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
offset = self.fp.tell() offset = self.fp.tell()
# check magic # check magic

View File

@ -45,6 +45,7 @@ class DcxImageFile(PcxImageFile):
def _open(self) -> None: def _open(self) -> None:
# Header # Header
assert self.fp is not None
s = self.fp.read(4) s = self.fp.read(4)
if not _accept(s): if not _accept(s):
msg = "not a DCX file" msg = "not a DCX file"

View File

@ -333,6 +333,7 @@ class DdsImageFile(ImageFile.ImageFile):
format_description = "DirectDraw Surface" format_description = "DirectDraw Surface"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):
msg = "not a DDS file" msg = "not a DDS file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -189,6 +189,7 @@ class EpsImageFile(ImageFile.ImageFile):
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
(length, offset) = self._find_offset(self.fp) (length, offset) = self._find_offset(self.fp)
# go to offset - start of "%!PS" # go to offset - start of "%!PS"
@ -400,6 +401,7 @@ class EpsImageFile(ImageFile.ImageFile):
) -> Image.core.PixelAccess | None: ) -> Image.core.PixelAccess | None:
# Load EPS via Ghostscript # Load EPS via Ghostscript
if self.tile: if self.tile:
assert self.fp is not None
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self._mode = self.im.mode self._mode = self.im.mode
self._size = self.im.size self._size = self.im.size

View File

@ -48,6 +48,7 @@ class FliImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
# HEAD # HEAD
assert self.fp is not None
s = self.fp.read(128) s = self.fp.read(128)
if not (_accept(s) and s[20:22] == b"\x00\x00"): if not (_accept(s) and s[20:22] == b"\x00\x00"):
msg = "not an FLI/FLC file" msg = "not an FLI/FLC file"
@ -111,6 +112,7 @@ class FliImageFile(ImageFile.ImageFile):
# load palette # load palette
i = 0 i = 0
assert self.fp is not None
for e in range(i16(self.fp.read(2))): for e in range(i16(self.fp.read(2))):
s = self.fp.read(2) s = self.fp.read(2)
i = i + s[0] i = i + s[0]

View File

@ -58,6 +58,7 @@ class FpxImageFile(ImageFile.ImageFile):
# read the OLE directory and see if this is a likely # read the OLE directory and see if this is a likely
# to be a FlashPix file # to be a FlashPix file
assert self.fp is not None
try: try:
self.ole = olefile.OleFileIO(self.fp) self.ole = olefile.OleFileIO(self.fp)
except OSError as e: except OSError as e:
@ -229,6 +230,7 @@ class FpxImageFile(ImageFile.ImageFile):
if y >= ysize: if y >= ysize:
break # isn't really required break # isn't really required
assert self.fp is not None
self.stream = stream self.stream = stream
self._fp = self.fp self._fp = self.fp
self.fp = None self.fp = None

View File

@ -72,6 +72,7 @@ class FtexImageFile(ImageFile.ImageFile):
format_description = "Texture File Format (IW2:EOC)" format_description = "Texture File Format (IW2:EOC)"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):
msg = "not an FTEX file" msg = "not an FTEX file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -42,6 +42,7 @@ class GbrImageFile(ImageFile.ImageFile):
format_description = "GIMP brush file" format_description = "GIMP brush file"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
header_size = i32(self.fp.read(4)) header_size = i32(self.fp.read(4))
if header_size < 20: if header_size < 20:
msg = "not a GIMP brush" msg = "not a GIMP brush"
@ -91,6 +92,8 @@ class GbrImageFile(ImageFile.ImageFile):
def load(self) -> Image.core.PixelAccess | None: def load(self) -> Image.core.PixelAccess | None:
if self._im is None: if self._im is None:
self.im = Image.core.new(self.mode, self.size) self.im = Image.core.new(self.mode, self.size)
assert self.fp is not None
self.frombytes(self.fp.read(self._data_size)) self.frombytes(self.fp.read(self._data_size))
return Image.Image.load(self) return Image.Image.load(self)

View File

@ -85,6 +85,7 @@ class GifImageFile(ImageFile.ImageFile):
global_palette = None global_palette = None
def data(self) -> bytes | None: def data(self) -> bytes | None:
assert self.fp is not None
s = self.fp.read(1) s = self.fp.read(1)
if s and s[0]: if s and s[0]:
return self.fp.read(s[0]) return self.fp.read(s[0])
@ -98,6 +99,7 @@ class GifImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
# Screen # Screen
assert self.fp is not None
s = self.fp.read(13) s = self.fp.read(13)
if not _accept(s): if not _accept(s):
msg = "not a GIF file" msg = "not a GIF file"
@ -114,8 +116,8 @@ class GifImageFile(ImageFile.ImageFile):
# check if palette contains colour indices # check if palette contains colour indices
p = self.fp.read(3 << bits) p = self.fp.read(3 << bits)
if self._is_palette_needed(p): if self._is_palette_needed(p):
p = ImagePalette.raw("RGB", p) palette = ImagePalette.raw("RGB", p)
self.global_palette = self.palette = p self.global_palette = self.palette = palette
self._fp = self.fp # FIXME: hack self._fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
@ -254,7 +256,7 @@ class GifImageFile(ImageFile.ImageFile):
info["comment"] += b"\n" + comment info["comment"] += b"\n" + comment
else: else:
info["comment"] = comment info["comment"] = comment
s = None s = b""
continue continue
elif s[0] == 255 and frame == 0 and block is not None: elif s[0] == 255 and frame == 0 and block is not None:
# #
@ -297,7 +299,7 @@ class GifImageFile(ImageFile.ImageFile):
bits = self.fp.read(1)[0] bits = self.fp.read(1)[0]
self.__offset = self.fp.tell() self.__offset = self.fp.tell()
break break
s = None s = b""
if interlace is None: if interlace is None:
msg = "image not found in GIF frame" msg = "image not found in GIF frame"

View File

@ -41,6 +41,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
format_description = "GRIB" format_description = "GRIB"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(8)): if not _accept(self.fp.read(8)):
msg = "Not a GRIB file" msg = "Not a GRIB file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -41,6 +41,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
format_description = "HDF5" format_description = "HDF5"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(8)): if not _accept(self.fp.read(8)):
msg = "Not an HDF file" msg = "Not an HDF file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -265,6 +265,7 @@ class IcnsImageFile(ImageFile.ImageFile):
format_description = "Mac OS icns resource" format_description = "Mac OS icns resource"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
self.icns = IcnsFile(self.fp) self.icns = IcnsFile(self.fp)
self._mode = "RGBA" self._mode = "RGBA"
self.info["sizes"] = self.icns.itersizes() self.info["sizes"] = self.icns.itersizes()

View File

@ -326,6 +326,7 @@ class IcoImageFile(ImageFile.ImageFile):
format_description = "Windows Icon" format_description = "Windows Icon"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
self.ico = IcoFile(self.fp) self.ico = IcoFile(self.fp)
self.info["sizes"] = self.ico.sizes() self.info["sizes"] = self.ico.sizes()
self.size = self.ico.entry[0].dim self.size = self.ico.entry[0].dim

View File

@ -125,6 +125,7 @@ class ImImageFile(ImageFile.ImageFile):
# Quick rejection: if there's not an LF among the first # Quick rejection: if there's not an LF among the first
# 100 bytes, this is (probably) not a text header. # 100 bytes, this is (probably) not a text header.
assert self.fp is not None
if b"\n" not in self.fp.read(100): if b"\n" not in self.fp.read(100):
msg = "not an IM file" msg = "not an IM file"
raise SyntaxError(msg) raise SyntaxError(msg)
@ -304,6 +305,8 @@ class ImImageFile(ImageFile.ImageFile):
size = ((self.size[0] * bits + 7) // 8) * self.size[1] size = ((self.size[0] * bits + 7) // 8) * self.size[1]
offs = self.__offset + frame * size offs = self.__offset + frame * size
if isinstance(self._fp, DeferredError):
raise self._fp.ex
self.fp = self._fp self.fp = self._fp
self.tile = [ self.tile = [

View File

@ -586,16 +586,11 @@ class Image:
return new return new
# Context manager support # Context manager support
def __enter__(self): def __enter__(self) -> Image:
return self return self
def __exit__(self, *args): def __exit__(self, *args: object) -> None:
from . import ImageFile pass
if isinstance(self, ImageFile.ImageFile):
if getattr(self, "_exclusive_fp", False):
self._close_fp()
self.fp = None
def close(self) -> None: def close(self) -> None:
""" """
@ -1514,9 +1509,13 @@ class Image:
exif_info = bytes.fromhex( exif_info = bytes.fromhex(
"".join(self.info["Raw profile type exif"].split("\n")[3:]) "".join(self.info["Raw profile type exif"].split("\n")[3:])
) )
elif hasattr(self, "tag_v2"): else:
from . import TiffImagePlugin
if isinstance(self, TiffImagePlugin.TiffImageFile):
self._exif.bigtiff = self.tag_v2._bigtiff self._exif.bigtiff = self.tag_v2._bigtiff
self._exif.endian = self.tag_v2._endian self._exif.endian = self.tag_v2._endian
assert self.fp is not None
self._exif.load_from_fp(self.fp, self.tag_v2._offset) self._exif.load_from_fp(self.fp, self.tag_v2._offset)
if exif_info is not None: if exif_info is not None:
self._exif.load(exif_info) self._exif.load(exif_info)

View File

@ -119,6 +119,8 @@ class ImageFile(Image.Image):
self.decoderconfig: tuple[Any, ...] = () self.decoderconfig: tuple[Any, ...] = ()
self.decodermaxblock = MAXBLOCK self.decodermaxblock = MAXBLOCK
self.fp: IO[bytes] | None
self._fp: IO[bytes] | DeferredError
if is_path(fp): if is_path(fp):
# filename # filename
self.fp = open(fp, "rb") self.fp = open(fp, "rb")
@ -155,14 +157,23 @@ class ImageFile(Image.Image):
def _open(self) -> None: def _open(self) -> None:
pass pass
def _close_fp(self): # Context manager support
if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError): def __enter__(self) -> ImageFile:
return self
def _close_fp(self) -> None:
if hasattr(self, "_fp") and not isinstance(self._fp, DeferredError):
if self._fp != self.fp: if self._fp != self.fp:
self._fp.close() self._fp.close()
self._fp = DeferredError(ValueError("Operation on closed image")) self._fp = DeferredError(ValueError("Operation on closed image"))
if self.fp: if self.fp:
self.fp.close() self.fp.close()
def __exit__(self, *args: object) -> None:
if getattr(self, "_exclusive_fp", False):
self._close_fp()
self.fp = None
def close(self) -> None: def close(self) -> None:
""" """
Closes the file pointer, if possible. Closes the file pointer, if possible.
@ -255,7 +266,7 @@ class ImageFile(Image.Image):
# raise exception if something's wrong. must be called # raise exception if something's wrong. must be called
# directly after open, and closes file when finished. # directly after open, and closes file when finished.
if self._exclusive_fp: if self._exclusive_fp and self.fp:
self.fp.close() self.fp.close()
self.fp = None self.fp = None
@ -273,6 +284,7 @@ class ImageFile(Image.Image):
self.map: mmap.mmap | None = None self.map: mmap.mmap | None = None
use_mmap = self.filename and len(self.tile) == 1 use_mmap = self.filename and len(self.tile) == 1
assert self.fp is not None
readonly = 0 readonly = 0
# look for read/seek overrides # look for read/seek overrides

View File

@ -53,6 +53,7 @@ class IptcImageFile(ImageFile.ImageFile):
def field(self) -> tuple[tuple[int, int] | None, int]: def field(self) -> tuple[tuple[int, int] | None, int]:
# #
# get a IPTC field header # get a IPTC field header
assert self.fp is not None
s = self.fp.read(5) s = self.fp.read(5)
if not s.strip(b"\x00"): if not s.strip(b"\x00"):
return None, 0 return None, 0
@ -80,6 +81,7 @@ class IptcImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
# load descriptive fields # load descriptive fields
assert self.fp is not None
while True: while True:
offset = self.fp.tell() offset = self.fp.tell()
tag, size = self.field() tag, size = self.field()
@ -133,6 +135,7 @@ class IptcImageFile(ImageFile.ImageFile):
offset, compression = self.tile[0][2:] offset, compression = self.tile[0][2:]
assert self.fp is not None
self.fp.seek(offset) self.fp.seek(offset)
# Copy image data to temporary file # Copy image data to temporary file
@ -141,6 +144,7 @@ class IptcImageFile(ImageFile.ImageFile):
# To simplify access to the extracted file, # To simplify access to the extracted file,
# prepend a PPM header # prepend a PPM header
o.write(b"P5\n%d %d\n255\n" % self.size) o.write(b"P5\n%d %d\n255\n" % self.size)
assert self.fp is not None
while True: while True:
type, size = self.field() type, size = self.field()
if type != (8, 10): if type != (8, 10):

View File

@ -248,6 +248,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
format_description = "JPEG 2000 (ISO 15444)" format_description = "JPEG 2000 (ISO 15444)"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
sig = self.fp.read(4) sig = self.fp.read(4)
if sig == b"\xff\x4f\xff\x51": if sig == b"\xff\x4f\xff\x51":
self.codec = "j2k" self.codec = "j2k"
@ -300,6 +301,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
] ]
def _parse_comment(self) -> None: def _parse_comment(self) -> None:
assert self.fp is not None
while True: while True:
marker = self.fp.read(2) marker = self.fp.read(2)
if not marker: if not marker:

View File

@ -60,6 +60,7 @@ if TYPE_CHECKING:
def Skip(self: JpegImageFile, marker: int) -> None: def Skip(self: JpegImageFile, marker: int) -> None:
assert self.fp is not None
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
ImageFile._safe_read(self.fp, n) ImageFile._safe_read(self.fp, n)
@ -69,6 +70,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
# Application marker. Store these in the APP dictionary. # Application marker. Store these in the APP dictionary.
# Also look for well-known application markers. # Also look for well-known application markers.
assert self.fp is not None
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n) s = ImageFile._safe_read(self.fp, n)
@ -173,6 +175,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
def COM(self: JpegImageFile, marker: int) -> None: def COM(self: JpegImageFile, marker: int) -> None:
# #
# Comment marker. Store these in the APP dictionary. # Comment marker. Store these in the APP dictionary.
assert self.fp is not None
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n) s = ImageFile._safe_read(self.fp, n)
@ -189,6 +192,7 @@ def SOF(self: JpegImageFile, marker: int) -> None:
# mode. Note that this could be made a bit brighter, by # mode. Note that this could be made a bit brighter, by
# looking for JFIF and Adobe APP markers. # looking for JFIF and Adobe APP markers.
assert self.fp is not None
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n) s = ImageFile._safe_read(self.fp, n)
self._size = i16(s, 3), i16(s, 1) self._size = i16(s, 3), i16(s, 1)
@ -237,6 +241,7 @@ def DQT(self: JpegImageFile, marker: int) -> None:
# FIXME: The quantization tables can be used to estimate the # FIXME: The quantization tables can be used to estimate the
# compression quality. # compression quality.
assert self.fp is not None
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n) s = ImageFile._safe_read(self.fp, n)
while len(s): while len(s):
@ -337,6 +342,7 @@ class JpegImageFile(ImageFile.ImageFile):
format_description = "JPEG (ISO 10918)" format_description = "JPEG (ISO 10918)"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
s = self.fp.read(3) s = self.fp.read(3)
if not _accept(s): if not _accept(s):
@ -405,6 +411,7 @@ class JpegImageFile(ImageFile.ImageFile):
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
so libjpeg can finish decoding so libjpeg can finish decoding
""" """
assert self.fp is not None
s = self.fp.read(read_bytes) s = self.fp.read(read_bytes)
if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"): if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"):

View File

@ -67,6 +67,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
self._n_frames = len(self.images) self._n_frames = len(self.images)
self.is_animated = self._n_frames > 1 self.is_animated = self._n_frames > 1
assert self.fp is not None
self.__fp = self.fp self.__fp = self.fp
self.seek(0) self.seek(0)

View File

@ -106,6 +106,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
self.fp.seek(0) # prep the fp in order to pass the JPEG test self.fp.seek(0) # prep the fp in order to pass the JPEG test
JpegImagePlugin.JpegImageFile._open(self) JpegImagePlugin.JpegImageFile._open(self)
self._after_jpeg_open() self._after_jpeg_open()
@ -125,6 +126,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
assert self.n_frames == len(self.__mpoffsets) assert self.n_frames == len(self.__mpoffsets)
del self.info["mpoffset"] # no longer needed del self.info["mpoffset"] # no longer needed
self.is_animated = self.n_frames > 1 self.is_animated = self.n_frames > 1
assert self.fp is not None
self._fp = self.fp # FIXME: hack self._fp = self.fp # FIXME: hack
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
self.__frame = 0 self.__frame = 0

View File

@ -755,6 +755,7 @@ class PngImageFile(ImageFile.ImageFile):
format_description = "Portable network graphics" format_description = "Portable network graphics"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(8)): if not _accept(self.fp.read(8)):
msg = "not a PNG file" msg = "not a PNG file"
raise SyntaxError(msg) raise SyntaxError(msg)
@ -986,6 +987,7 @@ class PngImageFile(ImageFile.ImageFile):
"""internal: read more image data""" """internal: read more image data"""
assert self.png is not None assert self.png is not None
assert self.fp is not None
while self.__idat == 0: while self.__idat == 0:
# end of chunk, skip forward to next one # end of chunk, skip forward to next one
@ -1019,6 +1021,7 @@ class PngImageFile(ImageFile.ImageFile):
def load_end(self) -> None: def load_end(self) -> None:
"""internal: finished reading image data""" """internal: finished reading image data"""
assert self.png is not None assert self.png is not None
assert self.fp is not None
if self.__idat != 0: if self.__idat != 0:
self.fp.read(self.__idat) self.fp.read(self.__idat)
while True: while True:

View File

@ -61,6 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
read = self.fp.read read = self.fp.read
# #
@ -178,6 +179,8 @@ class PsdImageFile(ImageFile.ImageFile):
self._mode = mode self._mode = mode
self.tile = tile self.tile = tile
self.frame = layer self.frame = layer
if isinstance(self._fp, DeferredError):
raise self._fp.ex
self.fp = self._fp self.fp = self._fp
def tell(self) -> int: def tell(self) -> int:

View File

@ -25,6 +25,7 @@ class QoiImageFile(ImageFile.ImageFile):
format_description = "Quite OK Image" format_description = "Quite OK Image"
def _open(self) -> None: def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):
msg = "not a QOI file" msg = "not a QOI file"
raise SyntaxError(msg) raise SyntaxError(msg)

View File

@ -103,6 +103,7 @@ class SpiderImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
# check header # check header
assert self.fp is not None
n = 27 * 4 # read 27 float values n = 27 * 4 # read 27 float values
f = self.fp.read(n) f = self.fp.read(n)
@ -184,6 +185,9 @@ class SpiderImageFile(ImageFile.ImageFile):
if isinstance(self._fp, DeferredError): if isinstance(self._fp, DeferredError):
raise self._fp.ex raise self._fp.ex
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
if isinstance(self._fp, DeferredError):
raise self._fp.ex
self.fp = self._fp self.fp = self._fp
self.fp.seek(self.stkoffset) self.fp.seek(self.stkoffset)
self._open() self._open()
@ -323,9 +327,9 @@ if __name__ == "__main__":
outfile = sys.argv[2] outfile = sys.argv[2]
# perform some image operation # perform some image operation
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
print( print(
f"saving a flipped version of {os.path.basename(filename)} " f"saving a flipped version of {os.path.basename(filename)} "
f"as {outfile} " f"as {outfile} "
) )
im.save(outfile, SpiderImageFile.format) transposed_im.save(outfile, SpiderImageFile.format)

View File

@ -1175,6 +1175,7 @@ class TiffImageFile(ImageFile.ImageFile):
"""Open the first image in a TIFF file""" """Open the first image in a TIFF file"""
# Header # Header
assert self.fp is not None
ifh = self.fp.read(8) ifh = self.fp.read(8)
if ifh[2] == 43: if ifh[2] == 43:
ifh += self.fp.read(8) ifh += self.fp.read(8)
@ -1341,6 +1342,7 @@ class TiffImageFile(ImageFile.ImageFile):
# To be nice on memory footprint, if there's a # To be nice on memory footprint, if there's a
# file descriptor, use that instead of reading # file descriptor, use that instead of reading
# into a string in python. # into a string in python.
assert self.fp is not None
try: try:
fp = hasattr(self.fp, "fileno") and self.fp.fileno() fp = hasattr(self.fp, "fileno") and self.fp.fileno()
# flush the file descriptor, prevents error on pypy 2.4+ # flush the file descriptor, prevents error on pypy 2.4+

View File

@ -39,6 +39,7 @@ class WalImageFile(ImageFile.ImageFile):
self._mode = "P" self._mode = "P"
# read header fields # read header fields
assert self.fp is not None
header = self.fp.read(32 + 24 + 32 + 12) header = self.fp.read(32 + 24 + 32 + 12)
self._size = i32(header, 32), i32(header, 36) self._size = i32(header, 32), i32(header, 36)
Image._decompression_bomb_check(self.size) Image._decompression_bomb_check(self.size)
@ -55,6 +56,7 @@ class WalImageFile(ImageFile.ImageFile):
def load(self) -> Image.core.PixelAccess | None: def load(self) -> Image.core.PixelAccess | None:
if self._im is None: if self._im is None:
assert self.fp is not None
self.im = Image.core.new(self.mode, self.size) self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self.size[0] * self.size[1])) self.frombytes(self.fp.read(self.size[0] * self.size[1]))
self.putpalette(quake2palette) self.putpalette(quake2palette)

View File

@ -43,6 +43,7 @@ class WebPImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
# Use the newer AnimDecoder API to parse the (possibly) animated file, # Use the newer AnimDecoder API to parse the (possibly) animated file,
# and access muxed chunks like ICC/EXIF/XMP. # and access muxed chunks like ICC/EXIF/XMP.
assert self.fp is not None
self._decoder = _webp.WebPAnimDecoder(self.fp.read()) self._decoder = _webp.WebPAnimDecoder(self.fp.read())
# Get info from decoder # Get info from decoder

View File

@ -49,6 +49,7 @@ if hasattr(Image.core, "drawwmf"):
self.bbox = im.info["wmf_bbox"] self.bbox = im.info["wmf_bbox"]
def load(self, im: ImageFile.StubImageFile) -> Image.Image: def load(self, im: ImageFile.StubImageFile) -> Image.Image:
assert im.fp is not None
im.fp.seek(0) # rewind im.fp.seek(0) # rewind
return Image.frombytes( return Image.frombytes(
"RGB", "RGB",
@ -81,6 +82,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
def _open(self) -> None: def _open(self) -> None:
# check placable header # check placable header
assert self.fp is not None
s = self.fp.read(44) s = self.fp.read(44)
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"): if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):

View File

@ -58,15 +58,15 @@ class XVThumbImageFile(ImageFile.ImageFile):
# skip info comments # skip info comments
while True: while True:
s = self.fp.readline() line = self.fp.readline()
if not s: if not line:
msg = "Unexpected EOF reading XV thumbnail file" msg = "Unexpected EOF reading XV thumbnail file"
raise SyntaxError(msg) raise SyntaxError(msg)
if s[0] != 35: # ie. when not a comment: '#' if line[0] != 35: # ie. when not a comment: '#'
break break
# parse header line (already read) # parse header line (already read)
s = s.strip().split() s = line.strip().split()
self._mode = "P" self._mode = "P"
self._size = int(s[0]), int(s[1]) self._size = int(s[0]), int(s[1])