mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 17:06:16 +03:00
Added type hints
This commit is contained in:
parent
2bd54260b6
commit
6d78d42769
|
@ -23,7 +23,10 @@ def _get_mem_usage() -> float:
|
||||||
|
|
||||||
|
|
||||||
def _test_leak(
|
def _test_leak(
|
||||||
min_iterations: int, max_iterations: int, fn: Callable[..., None], *args: Any
|
min_iterations: int,
|
||||||
|
max_iterations: int,
|
||||||
|
fn: Callable[..., Image.Image | None],
|
||||||
|
*args: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
mem_limit = None
|
mem_limit = None
|
||||||
for i in range(max_iterations):
|
for i in range(max_iterations):
|
||||||
|
|
|
@ -17,6 +17,7 @@ def test_ignore_dos_text() -> None:
|
||||||
finally:
|
finally:
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
|
|
||||||
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
for s in im.text.values():
|
for s in im.text.values():
|
||||||
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
|
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ def test_dos_text() -> None:
|
||||||
assert msg, "Decompressed Data Too Large"
|
assert msg, "Decompressed Data Too Large"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
for s in im.text.values():
|
for s in im.text.values():
|
||||||
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
|
assert len(s) < 1024 * 1024, "Text chunk larger than 1M"
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ def test_dos_total_memory() -> None:
|
||||||
return
|
return
|
||||||
|
|
||||||
total_len = 0
|
total_len = 0
|
||||||
|
assert isinstance(im2, PngImagePlugin.PngImageFile)
|
||||||
for txt in im2.text.values():
|
for txt in im2.text.values():
|
||||||
total_len += len(txt)
|
total_len += len(txt)
|
||||||
assert total_len < 64 * 1024 * 1024, "Total text chunks greater than 64M"
|
assert total_len < 64 * 1024 * 1024, "Total text chunks greater than 64M"
|
||||||
|
|
|
@ -351,7 +351,7 @@ def is_mingw() -> bool:
|
||||||
|
|
||||||
|
|
||||||
class CachedProperty:
|
class CachedProperty:
|
||||||
def __init__(self, func: Callable[[Any], None]) -> None:
|
def __init__(self, func: Callable[[Any], Any]) -> None:
|
||||||
self.func = func
|
self.func = func
|
||||||
|
|
||||||
def __get__(self, instance: Any, cls: type[Any] | None = None) -> Any:
|
def __get__(self, instance: Any, cls: type[Any] | None = None) -> Any:
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import ContainerIO, Image
|
from PIL import ContainerIO, Image
|
||||||
|
@ -22,14 +24,14 @@ def test_isatty() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"mode, expected_value",
|
"mode, expected_position",
|
||||||
(
|
(
|
||||||
(0, 33),
|
(0, 33),
|
||||||
(1, 66),
|
(1, 66),
|
||||||
(2, 100),
|
(2, 100),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_seek_mode(mode: int, expected_value: int) -> None:
|
def test_seek_mode(mode: Literal[0, 1, 2], expected_position: int) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
with open(TEST_FILE, "rb") as fh:
|
with open(TEST_FILE, "rb") as fh:
|
||||||
container = ContainerIO.ContainerIO(fh, 22, 100)
|
container = ContainerIO.ContainerIO(fh, 22, 100)
|
||||||
|
@ -39,7 +41,7 @@ def test_seek_mode(mode: int, expected_value: int) -> None:
|
||||||
container.seek(33, mode)
|
container.seek(33, mode)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert container.tell() == expected_value
|
assert container.tell() == expected_position
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bytesmode", (True, False))
|
@pytest.mark.parametrize("bytesmode", (True, False))
|
||||||
|
|
|
@ -6,7 +6,7 @@ import warnings
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -45,14 +45,20 @@ TEST_FILE = "Tests/images/hopper.jpg"
|
||||||
|
|
||||||
@skip_unless_feature("jpg")
|
@skip_unless_feature("jpg")
|
||||||
class TestFileJpeg:
|
class TestFileJpeg:
|
||||||
def roundtrip(self, im: Image.Image, **options: Any) -> Image.Image:
|
def roundtrip_with_bytes(
|
||||||
|
self, im: Image.Image, **options: Any
|
||||||
|
) -> tuple[JpegImagePlugin.JpegImageFile, int]:
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, "JPEG", **options)
|
im.save(out, "JPEG", **options)
|
||||||
test_bytes = out.tell()
|
test_bytes = out.tell()
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
im = Image.open(out)
|
reloaded = cast(JpegImagePlugin.JpegImageFile, Image.open(out))
|
||||||
im.bytes = test_bytes # for testing only
|
return reloaded, test_bytes
|
||||||
return im
|
|
||||||
|
def roundtrip(
|
||||||
|
self, im: Image.Image, **options: Any
|
||||||
|
) -> JpegImagePlugin.JpegImageFile:
|
||||||
|
return self.roundtrip_with_bytes(im, **options)[0]
|
||||||
|
|
||||||
def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Image:
|
def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Image:
|
||||||
"""Generates a very hard to compress file
|
"""Generates a very hard to compress file
|
||||||
|
@ -246,13 +252,13 @@ class TestFileJpeg:
|
||||||
im.save(f, progressive=True, quality=94, exif=b" " * 43668)
|
im.save(f, progressive=True, quality=94, exif=b" " * 43668)
|
||||||
|
|
||||||
def test_optimize(self) -> None:
|
def test_optimize(self) -> None:
|
||||||
im1 = self.roundtrip(hopper())
|
im1, im1_bytes = self.roundtrip_with_bytes(hopper())
|
||||||
im2 = self.roundtrip(hopper(), optimize=0)
|
im2, im2_bytes = self.roundtrip_with_bytes(hopper(), optimize=0)
|
||||||
im3 = self.roundtrip(hopper(), optimize=1)
|
im3, im3_bytes = self.roundtrip_with_bytes(hopper(), optimize=1)
|
||||||
assert_image_equal(im1, im2)
|
assert_image_equal(im1, im2)
|
||||||
assert_image_equal(im1, im3)
|
assert_image_equal(im1, im3)
|
||||||
assert im1.bytes >= im2.bytes
|
assert im1_bytes >= im2_bytes
|
||||||
assert im1.bytes >= im3.bytes
|
assert im1_bytes >= im3_bytes
|
||||||
|
|
||||||
def test_optimize_large_buffer(self, tmp_path: Path) -> None:
|
def test_optimize_large_buffer(self, tmp_path: Path) -> None:
|
||||||
# https://github.com/python-pillow/Pillow/issues/148
|
# https://github.com/python-pillow/Pillow/issues/148
|
||||||
|
@ -262,15 +268,15 @@ class TestFileJpeg:
|
||||||
im.save(f, format="JPEG", optimize=True)
|
im.save(f, format="JPEG", optimize=True)
|
||||||
|
|
||||||
def test_progressive(self) -> None:
|
def test_progressive(self) -> None:
|
||||||
im1 = self.roundtrip(hopper())
|
im1, im1_bytes = self.roundtrip_with_bytes(hopper())
|
||||||
im2 = self.roundtrip(hopper(), progressive=False)
|
im2 = self.roundtrip(hopper(), progressive=False)
|
||||||
im3 = self.roundtrip(hopper(), progressive=True)
|
im3, im3_bytes = self.roundtrip_with_bytes(hopper(), progressive=True)
|
||||||
assert not im1.info.get("progressive")
|
assert not im1.info.get("progressive")
|
||||||
assert not im2.info.get("progressive")
|
assert not im2.info.get("progressive")
|
||||||
assert im3.info.get("progressive")
|
assert im3.info.get("progressive")
|
||||||
|
|
||||||
assert_image_equal(im1, im3)
|
assert_image_equal(im1, im3)
|
||||||
assert im1.bytes >= im3.bytes
|
assert im1_bytes >= im3_bytes
|
||||||
|
|
||||||
def test_progressive_large_buffer(self, tmp_path: Path) -> None:
|
def test_progressive_large_buffer(self, tmp_path: Path) -> None:
|
||||||
f = str(tmp_path / "temp.jpg")
|
f = str(tmp_path / "temp.jpg")
|
||||||
|
@ -341,6 +347,7 @@ class TestFileJpeg:
|
||||||
assert exif.get_ifd(0x8825) == {}
|
assert exif.get_ifd(0x8825) == {}
|
||||||
|
|
||||||
transposed = ImageOps.exif_transpose(im)
|
transposed = ImageOps.exif_transpose(im)
|
||||||
|
assert transposed is not None
|
||||||
exif = transposed.getexif()
|
exif = transposed.getexif()
|
||||||
assert exif.get_ifd(0x8825) == {}
|
assert exif.get_ifd(0x8825) == {}
|
||||||
|
|
||||||
|
@ -419,14 +426,14 @@ class TestFileJpeg:
|
||||||
assert im3.info.get("progression")
|
assert im3.info.get("progression")
|
||||||
|
|
||||||
def test_quality(self) -> None:
|
def test_quality(self) -> None:
|
||||||
im1 = self.roundtrip(hopper())
|
im1, im1_bytes = self.roundtrip_with_bytes(hopper())
|
||||||
im2 = self.roundtrip(hopper(), quality=50)
|
im2, im2_bytes = self.roundtrip_with_bytes(hopper(), quality=50)
|
||||||
assert_image(im1, im2.mode, im2.size)
|
assert_image(im1, im2.mode, im2.size)
|
||||||
assert im1.bytes >= im2.bytes
|
assert im1_bytes >= im2_bytes
|
||||||
|
|
||||||
im3 = self.roundtrip(hopper(), quality=0)
|
im3, im3_bytes = self.roundtrip_with_bytes(hopper(), quality=0)
|
||||||
assert_image(im1, im3.mode, im3.size)
|
assert_image(im1, im3.mode, im3.size)
|
||||||
assert im2.bytes > im3.bytes
|
assert im2_bytes > im3_bytes
|
||||||
|
|
||||||
def test_smooth(self) -> None:
|
def test_smooth(self) -> None:
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
|
|
|
@ -40,10 +40,8 @@ test_card.load()
|
||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
|
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, "JPEG2000", **options)
|
im.save(out, "JPEG2000", **options)
|
||||||
test_bytes = out.tell()
|
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
with Image.open(out) as im:
|
with Image.open(out) as im:
|
||||||
im.bytes = test_bytes # for testing only
|
|
||||||
im.load()
|
im.load()
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
@ -77,7 +75,9 @@ def test_invalid_file() -> None:
|
||||||
def test_bytesio() -> None:
|
def test_bytesio() -> None:
|
||||||
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
|
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
|
||||||
data = BytesIO(f.read())
|
data = BytesIO(f.read())
|
||||||
assert_image_similar_tofile(test_card, data, 1.0e-3)
|
with Image.open(data) as im:
|
||||||
|
im.load()
|
||||||
|
assert_image_similar(im, test_card, 1.0e-3)
|
||||||
|
|
||||||
|
|
||||||
# These two test pre-written JPEG 2000 files that were not written with
|
# These two test pre-written JPEG 2000 files that were not written with
|
||||||
|
@ -340,6 +340,7 @@ def test_parser_feed() -> None:
|
||||||
p.feed(data)
|
p.feed(data)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
|
assert p.image is not None
|
||||||
assert p.image.size == (640, 480)
|
assert p.image.size == (640, 480)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from .helper import (
|
||||||
|
|
||||||
@skip_unless_feature("libtiff")
|
@skip_unless_feature("libtiff")
|
||||||
class LibTiffTestCase:
|
class LibTiffTestCase:
|
||||||
def _assert_noerr(self, tmp_path: Path, im: Image.Image) -> None:
|
def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> 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"
|
||||||
|
@ -524,7 +524,8 @@ 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.open("Tests/images/hopper_g4_500.tif")
|
im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif")
|
||||||
|
assert im is not None
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
|
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
@ -716,6 +717,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
f.write(src.read())
|
f.write(src.read())
|
||||||
|
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
im.n_frames
|
im.n_frames
|
||||||
im.close()
|
im.close()
|
||||||
# Should not raise PermissionError.
|
# Should not raise PermissionError.
|
||||||
|
@ -1097,6 +1099,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
with Image.open(out) as im:
|
with Image.open(out) as im:
|
||||||
# Assert that there are multiple strips
|
# Assert that there are multiple strips
|
||||||
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
assert len(im.tag_v2[STRIPOFFSETS]) > 1
|
assert len(im.tag_v2[STRIPOFFSETS]) > 1
|
||||||
|
|
||||||
@pytest.mark.parametrize("argument", (True, False))
|
@pytest.mark.parametrize("argument", (True, False))
|
||||||
|
@ -1113,6 +1116,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
im.save(out, **arguments)
|
im.save(out, **arguments)
|
||||||
|
|
||||||
with Image.open(out) as im:
|
with Image.open(out) as im:
|
||||||
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
assert len(im.tag_v2[STRIPOFFSETS]) == 1
|
assert len(im.tag_v2[STRIPOFFSETS]) == 1
|
||||||
finally:
|
finally:
|
||||||
TiffImagePlugin.STRIP_SIZE = 65536
|
TiffImagePlugin.STRIP_SIZE = 65536
|
||||||
|
|
|
@ -2,11 +2,11 @@ from __future__ import annotations
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image, MpoImagePlugin
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -20,14 +20,11 @@ test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
||||||
pytestmark = skip_unless_feature("jpg")
|
pytestmark = skip_unless_feature("jpg")
|
||||||
|
|
||||||
|
|
||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
|
def roundtrip(im: Image.Image, **options: Any) -> MpoImagePlugin.MpoImageFile:
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, "MPO", **options)
|
im.save(out, "MPO", **options)
|
||||||
test_bytes = out.tell()
|
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
im = Image.open(out)
|
return cast(MpoImagePlugin.MpoImageFile, Image.open(out))
|
||||||
im.bytes = test_bytes # for testing only
|
|
||||||
return im
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("test_file", test_files)
|
@pytest.mark.parametrize("test_file", test_files)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -59,11 +59,11 @@ def load(data: bytes) -> Image.Image:
|
||||||
return Image.open(BytesIO(data))
|
return Image.open(BytesIO(data))
|
||||||
|
|
||||||
|
|
||||||
def roundtrip(im: Image.Image, **options: Any) -> Image.Image:
|
def roundtrip(im: Image.Image, **options: Any) -> PngImagePlugin.PngImageFile:
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, "PNG", **options)
|
im.save(out, "PNG", **options)
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
return Image.open(out)
|
return cast(PngImagePlugin.PngImageFile, Image.open(out))
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("zlib")
|
@skip_unless_feature("zlib")
|
||||||
|
|
|
@ -9,7 +9,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal_tofile, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.spider"
|
TEST_FILE = "Tests/images/hopper.spider"
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ def test_nonstack_dos() -> None:
|
||||||
assert i <= 1, "Non-stack DOS file test failed"
|
assert i <= 1, "Non-stack DOS file test failed"
|
||||||
|
|
||||||
|
|
||||||
# for issue #4093
|
# for issue #4093s
|
||||||
def test_odd_size() -> None:
|
def test_odd_size() -> None:
|
||||||
data = BytesIO()
|
data = BytesIO()
|
||||||
width = 100
|
width = 100
|
||||||
|
@ -160,4 +160,5 @@ def test_odd_size() -> None:
|
||||||
im.save(data, format="SPIDER")
|
im.save(data, format="SPIDER")
|
||||||
|
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
assert_image_equal_tofile(im, data)
|
with Image.open(data) as im2:
|
||||||
|
assert_image_equal(im, im2)
|
||||||
|
|
|
@ -623,6 +623,7 @@ class TestFileTiff:
|
||||||
im.save(outfile, tiffinfo={278: 256})
|
im.save(outfile, tiffinfo={278: 256})
|
||||||
|
|
||||||
with Image.open(outfile) as im:
|
with Image.open(outfile) as im:
|
||||||
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
assert im.tag_v2[278] == 256
|
assert im.tag_v2[278] == 256
|
||||||
|
|
||||||
def test_strip_raw(self) -> None:
|
def test_strip_raw(self) -> None:
|
||||||
|
|
|
@ -138,13 +138,13 @@ class TestImage:
|
||||||
assert im.height == 2
|
assert im.height == 2
|
||||||
|
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
im.size = (3, 4)
|
im.size = (3, 4) # type: ignore[misc]
|
||||||
|
|
||||||
def test_set_mode(self) -> None:
|
def test_set_mode(self) -> None:
|
||||||
im = Image.new("RGB", (1, 1))
|
im = Image.new("RGB", (1, 1))
|
||||||
|
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
im.mode = "P"
|
im.mode = "P" # type: ignore[misc]
|
||||||
|
|
||||||
def test_invalid_image(self) -> None:
|
def test_invalid_image(self) -> None:
|
||||||
im = io.BytesIO(b"")
|
im = io.BytesIO(b"")
|
||||||
|
|
|
@ -14,6 +14,7 @@ from .helper import assert_image_equal, hopper, is_win32
|
||||||
|
|
||||||
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
|
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
|
||||||
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
|
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
|
||||||
|
cffi: ModuleType | None
|
||||||
if os.environ.get("PYTHONOPTIMIZE") == "2":
|
if os.environ.get("PYTHONOPTIMIZE") == "2":
|
||||||
cffi = None
|
cffi = None
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Generator
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -17,19 +16,16 @@ pytestmark = pytest.mark.skipif(
|
||||||
not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
|
not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ims = [
|
||||||
|
hopper(),
|
||||||
|
Image.open("Tests/images/transparent.png"),
|
||||||
|
Image.open("Tests/images/7x13.png"),
|
||||||
|
]
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def test_images() -> Generator[Image.Image, None, None]:
|
def teardown_module() -> None:
|
||||||
ims = [
|
for im in ims:
|
||||||
hopper(),
|
im.close()
|
||||||
Image.open("Tests/images/transparent.png"),
|
|
||||||
Image.open("Tests/images/7x13.png"),
|
|
||||||
]
|
|
||||||
try:
|
|
||||||
yield ims
|
|
||||||
finally:
|
|
||||||
for im in ims:
|
|
||||||
im.close()
|
|
||||||
|
|
||||||
|
|
||||||
def roundtrip(expected: Image.Image) -> None:
|
def roundtrip(expected: Image.Image) -> None:
|
||||||
|
@ -44,26 +40,26 @@ def roundtrip(expected: Image.Image) -> None:
|
||||||
assert_image_equal(result, expected.convert("RGB"))
|
assert_image_equal(result, expected.convert("RGB"))
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_1(test_images: Generator[Image.Image, None, None]) -> None:
|
def test_sanity_1() -> None:
|
||||||
for im in test_images:
|
for im in ims:
|
||||||
roundtrip(im.convert("1"))
|
roundtrip(im.convert("1"))
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_rgb(test_images: Generator[Image.Image, None, None]) -> None:
|
def test_sanity_rgb() -> None:
|
||||||
for im in test_images:
|
for im in ims:
|
||||||
roundtrip(im.convert("RGB"))
|
roundtrip(im.convert("RGB"))
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_rgba(test_images: Generator[Image.Image, None, None]) -> None:
|
def test_sanity_rgba() -> None:
|
||||||
for im in test_images:
|
for im in ims:
|
||||||
roundtrip(im.convert("RGBA"))
|
roundtrip(im.convert("RGBA"))
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_l(test_images: Generator[Image.Image, None, None]) -> None:
|
def test_sanity_l() -> None:
|
||||||
for im in test_images:
|
for im in ims:
|
||||||
roundtrip(im.convert("L"))
|
roundtrip(im.convert("L"))
|
||||||
|
|
||||||
|
|
||||||
def test_sanity_p(test_images: Generator[Image.Image, None, None]) -> None:
|
def test_sanity_p() -> None:
|
||||||
for im in test_images:
|
for im in ims:
|
||||||
roundtrip(im.convert("P"))
|
roundtrip(im.convert("P"))
|
||||||
|
|
|
@ -32,7 +32,7 @@ class TestImagingPaste:
|
||||||
def assert_9points_paste(
|
def assert_9points_paste(
|
||||||
self,
|
self,
|
||||||
im: Image.Image,
|
im: Image.Image,
|
||||||
im2: Image.Image,
|
im2: Image.Image | str | tuple[int, ...],
|
||||||
mask: Image.Image,
|
mask: Image.Image,
|
||||||
expected: list[tuple[int, int, int, int]],
|
expected: list[tuple[int, int, int, int]],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -237,7 +237,7 @@ class TestCoreResampleConsistency:
|
||||||
im = Image.new(mode, (512, 9), fill)
|
im = Image.new(mode, (512, 9), fill)
|
||||||
return im.resize((9, 512), Image.Resampling.LANCZOS), im.load()[0, 0]
|
return im.resize((9, 512), Image.Resampling.LANCZOS), im.load()[0, 0]
|
||||||
|
|
||||||
def run_case(self, case: tuple[Image.Image, Image.Image]) -> None:
|
def run_case(self, case: tuple[Image.Image, int | tuple[int, ...]]) -> None:
|
||||||
channel, color = case
|
channel, color = case
|
||||||
px = channel.load()
|
px = channel.load()
|
||||||
for x in range(channel.size[0]):
|
for x in range(channel.size[0]):
|
||||||
|
|
|
@ -154,7 +154,7 @@ class TestImagingCoreResize:
|
||||||
|
|
||||||
def test_unknown_filter(self) -> None:
|
def test_unknown_filter(self) -> None:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
self.resize(hopper(), (10, 10), 9)
|
self.resize(hopper(), (10, 10), 9) # type: ignore[arg-type]
|
||||||
|
|
||||||
def test_cross_platform(self, tmp_path: Path) -> None:
|
def test_cross_platform(self, tmp_path: Path) -> None:
|
||||||
# This test is intended for only check for consistent behaviour across
|
# This test is intended for only check for consistent behaviour across
|
||||||
|
|
|
@ -73,15 +73,16 @@ def test_lut(op: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_no_operator_loaded() -> None:
|
def test_no_operator_loaded() -> None:
|
||||||
|
im = Image.new("L", (1, 1))
|
||||||
mop = ImageMorph.MorphOp()
|
mop = ImageMorph.MorphOp()
|
||||||
with pytest.raises(Exception) as e:
|
with pytest.raises(Exception) as e:
|
||||||
mop.apply(None)
|
mop.apply(im)
|
||||||
assert str(e.value) == "No operator loaded"
|
assert str(e.value) == "No operator loaded"
|
||||||
with pytest.raises(Exception) as e:
|
with pytest.raises(Exception) as e:
|
||||||
mop.match(None)
|
mop.match(im)
|
||||||
assert str(e.value) == "No operator loaded"
|
assert str(e.value) == "No operator loaded"
|
||||||
with pytest.raises(Exception) as e:
|
with pytest.raises(Exception) as e:
|
||||||
mop.save_lut(None)
|
mop.save_lut("")
|
||||||
assert str(e.value) == "No operator loaded"
|
assert str(e.value) == "No operator loaded"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,12 @@ from .helper import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Deformer:
|
class Deformer(ImageOps.SupportsGetMesh):
|
||||||
def getmesh(self, im: Image.Image) -> list[tuple[tuple[int, ...], tuple[int, ...]]]:
|
def getmesh(
|
||||||
|
self, im: Image.Image
|
||||||
|
) -> list[
|
||||||
|
tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]]
|
||||||
|
]:
|
||||||
x, y = im.size
|
x, y = im.size
|
||||||
return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]
|
return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]
|
||||||
|
|
||||||
|
@ -376,6 +380,7 @@ def test_exif_transpose() -> None:
|
||||||
else:
|
else:
|
||||||
original_exif = im.info["exif"]
|
original_exif = im.info["exif"]
|
||||||
transposed_im = ImageOps.exif_transpose(im)
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert transposed_im is not None
|
||||||
assert_image_similar(base_im, transposed_im, 17)
|
assert_image_similar(base_im, transposed_im, 17)
|
||||||
if orientation_im is base_im:
|
if orientation_im is base_im:
|
||||||
assert "exif" not in im.info
|
assert "exif" not in im.info
|
||||||
|
@ -387,6 +392,7 @@ def test_exif_transpose() -> None:
|
||||||
|
|
||||||
# Repeat the operation to test that it does not keep transposing
|
# Repeat the operation to test that it does not keep transposing
|
||||||
transposed_im2 = ImageOps.exif_transpose(transposed_im)
|
transposed_im2 = ImageOps.exif_transpose(transposed_im)
|
||||||
|
assert transposed_im2 is not None
|
||||||
assert_image_equal(transposed_im2, transposed_im)
|
assert_image_equal(transposed_im2, transposed_im)
|
||||||
|
|
||||||
check(base_im)
|
check(base_im)
|
||||||
|
@ -402,6 +408,7 @@ def test_exif_transpose() -> None:
|
||||||
assert im.getexif()[0x0112] == 3
|
assert im.getexif()[0x0112] == 3
|
||||||
|
|
||||||
transposed_im = ImageOps.exif_transpose(im)
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert transposed_im is not None
|
||||||
assert 0x0112 not in transposed_im.getexif()
|
assert 0x0112 not in transposed_im.getexif()
|
||||||
|
|
||||||
transposed_im._reload_exif()
|
transposed_im._reload_exif()
|
||||||
|
@ -414,12 +421,14 @@ def test_exif_transpose() -> None:
|
||||||
assert im.getexif()[0x0112] == 3
|
assert im.getexif()[0x0112] == 3
|
||||||
|
|
||||||
transposed_im = ImageOps.exif_transpose(im)
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert transposed_im is not None
|
||||||
assert 0x0112 not in transposed_im.getexif()
|
assert 0x0112 not in transposed_im.getexif()
|
||||||
|
|
||||||
# Orientation set directly on Image.Exif
|
# Orientation set directly on Image.Exif
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.getexif()[0x0112] = 3
|
im.getexif()[0x0112] = 3
|
||||||
transposed_im = ImageOps.exif_transpose(im)
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert transposed_im is not None
|
||||||
assert 0x0112 not in transposed_im.getexif()
|
assert 0x0112 not in transposed_im.getexif()
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,7 +508,7 @@ def test_autocontrast_mask_real_input() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_autocontrast_preserve_tone() -> None:
|
def test_autocontrast_preserve_tone() -> None:
|
||||||
def autocontrast(mode: str, preserve_tone: bool) -> Image.Image:
|
def autocontrast(mode: str, preserve_tone: bool) -> list[int]:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
|
return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ def test_filter_api(test_images: dict[str, Image.Image]) -> None:
|
||||||
assert i.mode == "RGB"
|
assert i.mode == "RGB"
|
||||||
assert i.size == (128, 128)
|
assert i.size == (128, 128)
|
||||||
|
|
||||||
test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
|
test_filter2 = ImageFilter.UnsharpMask(2.0, 125, 8)
|
||||||
i = im.filter(test_filter)
|
i = im.filter(test_filter2)
|
||||||
assert i.mode == "RGB"
|
assert i.mode == "RGB"
|
||||||
assert i.size == (128, 128)
|
assert i.size == (128, 128)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ def test_sanity(tmp_path: Path) -> None:
|
||||||
assert index == 1
|
assert index == 1
|
||||||
|
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
ImageSequence.Iterator(0)
|
ImageSequence.Iterator(0) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
|
||||||
def test_iterator() -> None:
|
def test_iterator() -> None:
|
||||||
|
@ -72,6 +72,7 @@ def test_consecutive() -> None:
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
if first_frame is None:
|
if first_frame is None:
|
||||||
first_frame = frame.copy()
|
first_frame = frame.copy()
|
||||||
|
assert first_frame is not None
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
assert_image_equal(frame, first_frame)
|
assert_image_equal(frame, first_frame)
|
||||||
break
|
break
|
||||||
|
|
|
@ -68,10 +68,11 @@ def test_show_without_viewers() -> None:
|
||||||
def test_viewer() -> None:
|
def test_viewer() -> None:
|
||||||
viewer = ImageShow.Viewer()
|
viewer = ImageShow.Viewer()
|
||||||
|
|
||||||
assert viewer.get_format(None) is None
|
im = Image.new("L", (1, 1))
|
||||||
|
assert viewer.get_format(im) is None
|
||||||
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
viewer.get_command(None)
|
viewer.get_command("")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("viewer", ImageShow._viewers)
|
@pytest.mark.parametrize("viewer", ImageShow._viewers)
|
||||||
|
|
|
@ -78,7 +78,7 @@ def test_basic(tmp_path: Path, mode: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_tobytes() -> None:
|
def test_tobytes() -> None:
|
||||||
def tobytes(mode: str) -> Image.Image:
|
def tobytes(mode: str) -> bytes:
|
||||||
return Image.new(mode, (1, 1), 1).tobytes()
|
return Image.new(mode, (1, 1), 1).tobytes()
|
||||||
|
|
||||||
order = 1 if Image._ENDIAN == "<" else -1
|
order = 1 if Image._ENDIAN == "<" else -1
|
||||||
|
|
|
@ -47,9 +47,8 @@ def test_tiff_crashes(test_file: str) -> None:
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
if not on_ci():
|
if on_ci():
|
||||||
pytest.skip("test image not found")
|
raise
|
||||||
return
|
pytest.skip("test image not found")
|
||||||
raise
|
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -38,7 +38,7 @@ from ._deprecate import deprecate
|
||||||
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
||||||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
||||||
|
|
||||||
gs_binary = None
|
gs_binary: str | bool | None = None
|
||||||
gs_windows_binary = None
|
gs_windows_binary = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ class DecompressionBombError(Exception):
|
||||||
|
|
||||||
|
|
||||||
# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image
|
# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image
|
||||||
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3)
|
MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -384,7 +384,7 @@ class Parser:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
incremental = None
|
incremental = None
|
||||||
image = None
|
image: Image.Image | None = None
|
||||||
data = None
|
data = None
|
||||||
decoder = None
|
decoder = None
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user