mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Added type hints
This commit is contained in:
parent
5d6f22da12
commit
3f6422b512
|
@ -244,7 +244,7 @@ def fromstring(data: bytes) -> Image.Image:
|
||||||
return Image.open(BytesIO(data))
|
return Image.open(BytesIO(data))
|
||||||
|
|
||||||
|
|
||||||
def tostring(im: Image.Image, string_format: str, **options: dict[str, Any]) -> bytes:
|
def tostring(im: Image.Image, string_format: str, **options: Any) -> bytes:
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, string_format, **options)
|
im.save(out, string_format, **options)
|
||||||
return out.getvalue()
|
return out.getvalue()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ else:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
cffi = None
|
cffi = None
|
||||||
|
|
||||||
|
numpy: ModuleType | None
|
||||||
try:
|
try:
|
||||||
import numpy
|
import numpy
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -71,9 +73,10 @@ class TestImagePutPixel(AccessTest):
|
||||||
pix1 = im1.load()
|
pix1 = im1.load()
|
||||||
pix2 = im2.load()
|
pix2 = im2.load()
|
||||||
|
|
||||||
for x, y in ((0, "0"), ("0", 0)):
|
with pytest.raises(TypeError):
|
||||||
with pytest.raises(TypeError):
|
pix1[0, "0"]
|
||||||
pix1[x, y]
|
with pytest.raises(TypeError):
|
||||||
|
pix1["0", 0]
|
||||||
|
|
||||||
for y in range(im1.size[1]):
|
for y in range(im1.size[1]):
|
||||||
for x in range(im1.size[0]):
|
for x in range(im1.size[0]):
|
||||||
|
@ -123,12 +126,13 @@ class TestImagePutPixel(AccessTest):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
pix = im.load()
|
pix = im.load()
|
||||||
|
|
||||||
|
assert numpy is not None
|
||||||
assert pix[numpy.int32(1), numpy.int32(2)] == (18, 20, 59)
|
assert pix[numpy.int32(1), numpy.int32(2)] == (18, 20, 59)
|
||||||
|
|
||||||
|
|
||||||
class TestImageGetPixel(AccessTest):
|
class TestImageGetPixel(AccessTest):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def color(mode):
|
def color(mode: str) -> int | tuple[int, ...]:
|
||||||
bands = Image.getmodebands(mode)
|
bands = Image.getmodebands(mode)
|
||||||
if bands == 1:
|
if bands == 1:
|
||||||
return 1
|
return 1
|
||||||
|
@ -138,12 +142,13 @@ class TestImageGetPixel(AccessTest):
|
||||||
return (16, 32, 49)
|
return (16, 32, 49)
|
||||||
return tuple(range(1, bands + 1))
|
return tuple(range(1, bands + 1))
|
||||||
|
|
||||||
def check(self, mode, expected_color=None) -> None:
|
def check(self, mode: str, expected_color_int: int | None = None) -> None:
|
||||||
if self._need_cffi_access and mode.startswith("BGR;"):
|
if self._need_cffi_access and mode.startswith("BGR;"):
|
||||||
pytest.skip("Support not added to deprecated module for BGR;* modes")
|
pytest.skip("Support not added to deprecated module for BGR;* modes")
|
||||||
|
|
||||||
if not expected_color:
|
expected_color = (
|
||||||
expected_color = self.color(mode)
|
expected_color_int if expected_color_int is not None else self.color(mode)
|
||||||
|
)
|
||||||
|
|
||||||
# check putpixel
|
# check putpixel
|
||||||
im = Image.new(mode, (1, 1), None)
|
im = Image.new(mode, (1, 1), None)
|
||||||
|
@ -222,7 +227,7 @@ class TestImageGetPixel(AccessTest):
|
||||||
"YCbCr",
|
"YCbCr",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_basic(self, mode) -> None:
|
def test_basic(self, mode: str) -> None:
|
||||||
self.check(mode)
|
self.check(mode)
|
||||||
|
|
||||||
def test_list(self) -> None:
|
def test_list(self) -> None:
|
||||||
|
@ -231,14 +236,14 @@ class TestImageGetPixel(AccessTest):
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("I;16", "I;16B"))
|
@pytest.mark.parametrize("mode", ("I;16", "I;16B"))
|
||||||
@pytest.mark.parametrize("expected_color", (2**15 - 1, 2**15, 2**15 + 1, 2**16 - 1))
|
@pytest.mark.parametrize("expected_color", (2**15 - 1, 2**15, 2**15 + 1, 2**16 - 1))
|
||||||
def test_signedness(self, mode, expected_color) -> None:
|
def test_signedness(self, mode: str, expected_color: int) -> None:
|
||||||
# see https://github.com/python-pillow/Pillow/issues/452
|
# see https://github.com/python-pillow/Pillow/issues/452
|
||||||
# pixelaccess is using signed int* instead of uint*
|
# pixelaccess is using signed int* instead of uint*
|
||||||
self.check(mode, expected_color)
|
self.check(mode, expected_color)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("P", "PA"))
|
@pytest.mark.parametrize("mode", ("P", "PA"))
|
||||||
@pytest.mark.parametrize("color", ((255, 0, 0), (255, 0, 0, 255)))
|
@pytest.mark.parametrize("color", ((255, 0, 0), (255, 0, 0, 255)))
|
||||||
def test_p_putpixel_rgb_rgba(self, mode, color) -> None:
|
def test_p_putpixel_rgb_rgba(self, mode: str, color: tuple[int, ...]) -> None:
|
||||||
im = Image.new(mode, (1, 1))
|
im = Image.new(mode, (1, 1))
|
||||||
im.putpixel((0, 0), color)
|
im.putpixel((0, 0), color)
|
||||||
|
|
||||||
|
@ -262,7 +267,7 @@ class TestCffiGetPixel(TestImageGetPixel):
|
||||||
class TestCffi(AccessTest):
|
class TestCffi(AccessTest):
|
||||||
_need_cffi_access = True
|
_need_cffi_access = True
|
||||||
|
|
||||||
def _test_get_access(self, im) -> None:
|
def _test_get_access(self, im: Image.Image) -> None:
|
||||||
"""Do we get the same thing as the old pixel access
|
"""Do we get the same thing as the old pixel access
|
||||||
|
|
||||||
Using private interfaces, forcing a capi access and
|
Using private interfaces, forcing a capi access and
|
||||||
|
@ -299,7 +304,7 @@ class TestCffi(AccessTest):
|
||||||
# im = Image.new('I;32B', (10, 10), 2**10)
|
# im = Image.new('I;32B', (10, 10), 2**10)
|
||||||
# self._test_get_access(im)
|
# self._test_get_access(im)
|
||||||
|
|
||||||
def _test_set_access(self, im, color) -> None:
|
def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> None:
|
||||||
"""Are we writing the correct bits into the image?
|
"""Are we writing the correct bits into the image?
|
||||||
|
|
||||||
Using private interfaces, forcing a capi access and
|
Using private interfaces, forcing a capi access and
|
||||||
|
@ -359,7 +364,7 @@ class TestCffi(AccessTest):
|
||||||
assert px[i, 0] == 0
|
assert px[i, 0] == 0
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("P", "PA"))
|
@pytest.mark.parametrize("mode", ("P", "PA"))
|
||||||
def test_p_putpixel_rgb_rgba(self, mode) -> None:
|
def test_p_putpixel_rgb_rgba(self, mode: str) -> None:
|
||||||
for color in ((255, 0, 0), (255, 0, 0, 127 if mode == "PA" else 255)):
|
for color in ((255, 0, 0), (255, 0, 0, 127 if mode == "PA" else 255)):
|
||||||
im = Image.new(mode, (1, 1))
|
im = Image.new(mode, (1, 1))
|
||||||
with pytest.warns(DeprecationWarning):
|
with pytest.warns(DeprecationWarning):
|
||||||
|
@ -377,7 +382,7 @@ class TestImagePutPixelError(AccessTest):
|
||||||
INVALID_TYPES = ["foo", 1.0, None]
|
INVALID_TYPES = ["foo", 1.0, None]
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", IMAGE_MODES1)
|
@pytest.mark.parametrize("mode", IMAGE_MODES1)
|
||||||
def test_putpixel_type_error1(self, mode) -> None:
|
def test_putpixel_type_error1(self, mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
for v in self.INVALID_TYPES:
|
for v in self.INVALID_TYPES:
|
||||||
with pytest.raises(TypeError, match="color must be int or tuple"):
|
with pytest.raises(TypeError, match="color must be int or tuple"):
|
||||||
|
@ -400,14 +405,16 @@ class TestImagePutPixelError(AccessTest):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_putpixel_invalid_number_of_bands(self, mode, band_numbers, match) -> None:
|
def test_putpixel_invalid_number_of_bands(
|
||||||
|
self, mode: str, band_numbers: tuple[int, ...], match: str
|
||||||
|
) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
for band_number in band_numbers:
|
for band_number in band_numbers:
|
||||||
with pytest.raises(TypeError, match=match):
|
with pytest.raises(TypeError, match=match):
|
||||||
im.putpixel((0, 0), (0,) * band_number)
|
im.putpixel((0, 0), (0,) * band_number)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", IMAGE_MODES2)
|
@pytest.mark.parametrize("mode", IMAGE_MODES2)
|
||||||
def test_putpixel_type_error2(self, mode) -> None:
|
def test_putpixel_type_error2(self, mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
for v in self.INVALID_TYPES:
|
for v in self.INVALID_TYPES:
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
|
@ -416,7 +423,7 @@ class TestImagePutPixelError(AccessTest):
|
||||||
im.putpixel((0, 0), v)
|
im.putpixel((0, 0), v)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", IMAGE_MODES1 + IMAGE_MODES2)
|
@pytest.mark.parametrize("mode", IMAGE_MODES1 + IMAGE_MODES2)
|
||||||
def test_putpixel_overflow_error(self, mode) -> None:
|
def test_putpixel_overflow_error(self, mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
im.putpixel((0, 0), 2**80)
|
im.putpixel((0, 0), 2**80)
|
||||||
|
@ -428,7 +435,7 @@ class TestEmbeddable:
|
||||||
def test_embeddable(self) -> None:
|
def test_embeddable(self) -> None:
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
from setuptools.command.build_ext import new_compiler
|
from setuptools.command import build_ext
|
||||||
|
|
||||||
with open("embed_pil.c", "w", encoding="utf-8") as fh:
|
with open("embed_pil.c", "w", encoding="utf-8") as fh:
|
||||||
fh.write(
|
fh.write(
|
||||||
|
@ -457,7 +464,7 @@ int main(int argc, char* argv[])
|
||||||
% sys.prefix.replace("\\", "\\\\")
|
% sys.prefix.replace("\\", "\\\\")
|
||||||
)
|
)
|
||||||
|
|
||||||
compiler = new_compiler()
|
compiler = getattr(build_ext, "new_compiler")()
|
||||||
compiler.add_include_dir(sysconfig.get_config_var("INCLUDEPY"))
|
compiler.add_include_dir(sysconfig.get_config_var("INCLUDEPY"))
|
||||||
|
|
||||||
libdir = sysconfig.get_config_var("LIBDIR") or sysconfig.get_config_var(
|
libdir = sysconfig.get_config_var("LIBDIR") or sysconfig.get_config_var(
|
||||||
|
@ -471,7 +478,7 @@ int main(int argc, char* argv[])
|
||||||
env["PATH"] = sys.prefix + ";" + env["PATH"]
|
env["PATH"] = sys.prefix + ";" + env["PATH"]
|
||||||
|
|
||||||
# do not display the Windows Error Reporting dialog
|
# do not display the Windows Error Reporting dialog
|
||||||
ctypes.windll.kernel32.SetErrorMode(0x0002)
|
getattr(ctypes, "windll").kernel32.SetErrorMode(0x0002)
|
||||||
|
|
||||||
process = subprocess.Popen(["embed_pil.exe"], env=env)
|
process = subprocess.Popen(["embed_pil.exe"], env=env)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from packaging.version import parse as parse_version
|
from packaging.version import parse as parse_version
|
||||||
|
|
||||||
|
@ -13,7 +15,7 @@ im = hopper().resize((128, 100))
|
||||||
|
|
||||||
|
|
||||||
def test_toarray() -> None:
|
def test_toarray() -> None:
|
||||||
def test(mode):
|
def test(mode: str) -> tuple[tuple[int, ...], str, int]:
|
||||||
ai = numpy.array(im.convert(mode))
|
ai = numpy.array(im.convert(mode))
|
||||||
return ai.shape, ai.dtype.str, ai.nbytes
|
return ai.shape, ai.dtype.str, ai.nbytes
|
||||||
|
|
||||||
|
@ -50,14 +52,14 @@ def test_fromarray() -> None:
|
||||||
class Wrapper:
|
class Wrapper:
|
||||||
"""Class with API matching Image.fromarray"""
|
"""Class with API matching Image.fromarray"""
|
||||||
|
|
||||||
def __init__(self, img, arr_params) -> None:
|
def __init__(self, img: Image.Image, arr_params: dict[str, Any]) -> None:
|
||||||
self.img = img
|
self.img = img
|
||||||
self.__array_interface__ = arr_params
|
self.__array_interface__ = arr_params
|
||||||
|
|
||||||
def tobytes(self):
|
def tobytes(self) -> bytes:
|
||||||
return self.img.tobytes()
|
return self.img.tobytes()
|
||||||
|
|
||||||
def test(mode):
|
def test(mode: str) -> tuple[str, tuple[int, int], bool]:
|
||||||
i = im.convert(mode)
|
i = im.convert(mode)
|
||||||
a = numpy.array(i)
|
a = numpy.array(i)
|
||||||
# Make wrapper instance for image, new array interface
|
# Make wrapper instance for image, new array interface
|
||||||
|
|
|
@ -7,7 +7,12 @@ from .helper import fromstring, skip_unless_feature, tostring
|
||||||
pytestmark = skip_unless_feature("jpg")
|
pytestmark = skip_unless_feature("jpg")
|
||||||
|
|
||||||
|
|
||||||
def draft_roundtrip(in_mode, in_size, req_mode, req_size):
|
def draft_roundtrip(
|
||||||
|
in_mode: str,
|
||||||
|
in_size: tuple[int, int],
|
||||||
|
req_mode: str | None,
|
||||||
|
req_size: tuple[int, int] | None,
|
||||||
|
) -> Image.Image:
|
||||||
im = Image.new(in_mode, in_size)
|
im = Image.new(in_mode, in_size)
|
||||||
data = tostring(im, "JPEG")
|
data = tostring(im, "JPEG")
|
||||||
im = fromstring(data)
|
im = fromstring(data)
|
||||||
|
|
|
@ -4,7 +4,7 @@ from .helper import hopper
|
||||||
|
|
||||||
|
|
||||||
def test_entropy() -> None:
|
def test_entropy() -> None:
|
||||||
def entropy(mode):
|
def entropy(mode: str) -> float:
|
||||||
return hopper(mode).entropy()
|
return hopper(mode).entropy()
|
||||||
|
|
||||||
assert round(abs(entropy("1") - 0.9138803254693582), 7) == 0
|
assert round(abs(entropy("1") - 0.9138803254693582), 7) == 0
|
||||||
|
|
|
@ -36,7 +36,7 @@ from .helper import assert_image_equal, hopper
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
||||||
def test_sanity(filter_to_apply, mode) -> None:
|
def test_sanity(filter_to_apply: ImageFilter.Filter, mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
|
if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
|
||||||
out = im.filter(filter_to_apply)
|
out = im.filter(filter_to_apply)
|
||||||
|
@ -45,7 +45,7 @@ def test_sanity(filter_to_apply, mode) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
||||||
def test_sanity_error(mode) -> None:
|
def test_sanity_error(mode: str) -> None:
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.filter("hello")
|
im.filter("hello")
|
||||||
|
@ -53,7 +53,7 @@ def test_sanity_error(mode) -> None:
|
||||||
|
|
||||||
# crashes on small images
|
# crashes on small images
|
||||||
@pytest.mark.parametrize("size", ((1, 1), (2, 2), (3, 3)))
|
@pytest.mark.parametrize("size", ((1, 1), (2, 2), (3, 3)))
|
||||||
def test_crash(size) -> None:
|
def test_crash(size: tuple[int, int]) -> None:
|
||||||
im = Image.new("RGB", size)
|
im = Image.new("RGB", size)
|
||||||
im.filter(ImageFilter.SMOOTH)
|
im.filter(ImageFilter.SMOOTH)
|
||||||
|
|
||||||
|
@ -67,7 +67,10 @@ def test_crash(size) -> None:
|
||||||
("RGB", ((4, 0, 0), (0, 0, 0))),
|
("RGB", ((4, 0, 0), (0, 0, 0))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_modefilter(mode, expected) -> None:
|
def test_modefilter(
|
||||||
|
mode: str,
|
||||||
|
expected: tuple[int, int] | tuple[tuple[int, int, int], tuple[int, int, int]],
|
||||||
|
) -> None:
|
||||||
im = Image.new(mode, (3, 3), None)
|
im = Image.new(mode, (3, 3), None)
|
||||||
im.putdata(list(range(9)))
|
im.putdata(list(range(9)))
|
||||||
# image is:
|
# image is:
|
||||||
|
@ -90,7 +93,13 @@ def test_modefilter(mode, expected) -> None:
|
||||||
("F", (0.0, 4.0, 8.0)),
|
("F", (0.0, 4.0, 8.0)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_rankfilter(mode, expected) -> None:
|
def test_rankfilter(
|
||||||
|
mode: str,
|
||||||
|
expected: (
|
||||||
|
tuple[float, float, float]
|
||||||
|
| tuple[tuple[int, int, int], tuple[int, int, int], tuple[int, int, int]]
|
||||||
|
),
|
||||||
|
) -> None:
|
||||||
im = Image.new(mode, (3, 3), None)
|
im = Image.new(mode, (3, 3), None)
|
||||||
im.putdata(list(range(9)))
|
im.putdata(list(range(9)))
|
||||||
# image is:
|
# image is:
|
||||||
|
@ -106,7 +115,7 @@ def test_rankfilter(mode, expected) -> None:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"filter", (ImageFilter.MinFilter, ImageFilter.MedianFilter, ImageFilter.MaxFilter)
|
"filter", (ImageFilter.MinFilter, ImageFilter.MedianFilter, ImageFilter.MaxFilter)
|
||||||
)
|
)
|
||||||
def test_rankfilter_error(filter) -> None:
|
def test_rankfilter_error(filter: ImageFilter.RankFilter) -> None:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
im = Image.new("P", (3, 3), None)
|
im = Image.new("P", (3, 3), None)
|
||||||
im.putdata(list(range(9)))
|
im.putdata(list(range(9)))
|
||||||
|
@ -137,7 +146,7 @@ def test_kernel_not_enough_coefficients() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
||||||
def test_consistency_3x3(mode) -> None:
|
def test_consistency_3x3(mode: str) -> None:
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
reference_name = "hopper_emboss"
|
reference_name = "hopper_emboss"
|
||||||
reference_name += "_I.png" if mode == "I" else ".bmp"
|
reference_name += "_I.png" if mode == "I" else ".bmp"
|
||||||
|
@ -163,7 +172,7 @@ def test_consistency_3x3(mode) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
||||||
def test_consistency_5x5(mode) -> None:
|
def test_consistency_5x5(mode: str) -> None:
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
reference_name = "hopper_emboss_more"
|
reference_name = "hopper_emboss_more"
|
||||||
reference_name += "_I.png" if mode == "I" else ".bmp"
|
reference_name += "_I.png" if mode == "I" else ".bmp"
|
||||||
|
@ -199,7 +208,7 @@ def test_consistency_5x5(mode) -> None:
|
||||||
(2, -2),
|
(2, -2),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_invalid_box_blur_filter(radius) -> None:
|
def test_invalid_box_blur_filter(radius: int | tuple[int, int]) -> None:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ImageFilter.BoxBlur(radius)
|
ImageFilter.BoxBlur(radius)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .helper import hopper
|
||||||
|
|
||||||
|
|
||||||
def test_extrema() -> None:
|
def test_extrema() -> None:
|
||||||
def extrema(mode):
|
def extrema(mode: str) -> tuple[int, int] | tuple[tuple[int, int], ...]:
|
||||||
return hopper(mode).getextrema()
|
return hopper(mode).getextrema()
|
||||||
|
|
||||||
assert extrema("1") == (0, 255)
|
assert extrema("1") == (0, 255)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .helper import hopper
|
||||||
|
|
||||||
|
|
||||||
def test_palette() -> None:
|
def test_palette() -> None:
|
||||||
def palette(mode):
|
def palette(mode: str) -> list[int] | None:
|
||||||
p = hopper(mode).getpalette()
|
p = hopper(mode).getpalette()
|
||||||
if p:
|
if p:
|
||||||
return p[:10]
|
return p[:10]
|
||||||
|
|
|
@ -46,7 +46,7 @@ class TestImagingPaste:
|
||||||
self.assert_9points_image(im, expected)
|
self.assert_9points_image(im, expected)
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def mask_1(self):
|
def mask_1(self) -> Image.Image:
|
||||||
mask = Image.new("1", (self.size, self.size))
|
mask = Image.new("1", (self.size, self.size))
|
||||||
px = mask.load()
|
px = mask.load()
|
||||||
for y in range(mask.height):
|
for y in range(mask.height):
|
||||||
|
@ -55,11 +55,11 @@ class TestImagingPaste:
|
||||||
return mask
|
return mask
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def mask_L(self):
|
def mask_L(self) -> Image.Image:
|
||||||
return self.gradient_L.transpose(Image.Transpose.ROTATE_270)
|
return self.gradient_L.transpose(Image.Transpose.ROTATE_270)
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def gradient_L(self):
|
def gradient_L(self) -> Image.Image:
|
||||||
gradient = Image.new("L", (self.size, self.size))
|
gradient = Image.new("L", (self.size, self.size))
|
||||||
px = gradient.load()
|
px = gradient.load()
|
||||||
for y in range(gradient.height):
|
for y in range(gradient.height):
|
||||||
|
@ -68,7 +68,7 @@ class TestImagingPaste:
|
||||||
return gradient
|
return gradient
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def gradient_RGB(self):
|
def gradient_RGB(self) -> Image.Image:
|
||||||
return Image.merge(
|
return Image.merge(
|
||||||
"RGB",
|
"RGB",
|
||||||
[
|
[
|
||||||
|
@ -79,7 +79,7 @@ class TestImagingPaste:
|
||||||
)
|
)
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def gradient_LA(self):
|
def gradient_LA(self) -> Image.Image:
|
||||||
return Image.merge(
|
return Image.merge(
|
||||||
"LA",
|
"LA",
|
||||||
[
|
[
|
||||||
|
@ -89,7 +89,7 @@ class TestImagingPaste:
|
||||||
)
|
)
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def gradient_RGBA(self):
|
def gradient_RGBA(self) -> Image.Image:
|
||||||
return Image.merge(
|
return Image.merge(
|
||||||
"RGBA",
|
"RGBA",
|
||||||
[
|
[
|
||||||
|
@ -101,7 +101,7 @@ class TestImagingPaste:
|
||||||
)
|
)
|
||||||
|
|
||||||
@CachedProperty
|
@CachedProperty
|
||||||
def gradient_RGBa(self):
|
def gradient_RGBa(self) -> Image.Image:
|
||||||
return Image.merge(
|
return Image.merge(
|
||||||
"RGBa",
|
"RGBa",
|
||||||
[
|
[
|
||||||
|
|
|
@ -31,7 +31,7 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
def test_long_integers() -> None:
|
def test_long_integers() -> None:
|
||||||
# see bug-200802-systemerror
|
# see bug-200802-systemerror
|
||||||
def put(value):
|
def put(value: int) -> tuple[int, int, int, int]:
|
||||||
im = Image.new("RGBA", (1, 1))
|
im = Image.new("RGBA", (1, 1))
|
||||||
im.putdata([value])
|
im.putdata([value])
|
||||||
return im.getpixel((0, 0))
|
return im.getpixel((0, 0))
|
||||||
|
@ -58,7 +58,7 @@ def test_mode_with_L_with_float() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("I", "I;16", "I;16L", "I;16B"))
|
@pytest.mark.parametrize("mode", ("I", "I;16", "I;16L", "I;16B"))
|
||||||
def test_mode_i(mode) -> None:
|
def test_mode_i(mode: str) -> None:
|
||||||
src = hopper("L")
|
src = hopper("L")
|
||||||
data = list(src.getdata())
|
data = list(src.getdata())
|
||||||
im = Image.new(mode, src.size, 0)
|
im = Image.new(mode, src.size, 0)
|
||||||
|
@ -79,7 +79,7 @@ def test_mode_F() -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
|
@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
|
||||||
def test_mode_BGR(mode) -> None:
|
def test_mode_BGR(mode: str) -> None:
|
||||||
data = [(16, 32, 49), (32, 32, 98)]
|
data = [(16, 32, 49), (32, 32, 98)]
|
||||||
im = Image.new(mode, (1, 2))
|
im = Image.new(mode, (1, 2))
|
||||||
im.putdata(data)
|
im.putdata(data)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
||||||
|
|
||||||
|
|
||||||
def test_putpalette() -> None:
|
def test_putpalette() -> None:
|
||||||
def palette(mode):
|
def palette(mode: str) -> str | tuple[str, list[int]]:
|
||||||
im = hopper(mode).copy()
|
im = hopper(mode).copy()
|
||||||
im.putpalette(list(range(256)) * 3)
|
im.putpalette(list(range(256)) * 3)
|
||||||
p = im.getpalette()
|
p = im.getpalette()
|
||||||
|
@ -81,7 +81,7 @@ def test_putpalette_with_alpha_values() -> None:
|
||||||
("RGBAX", (1, 2, 3, 4, 0)),
|
("RGBAX", (1, 2, 3, 4, 0)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_rgba_palette(mode, palette) -> None:
|
def test_rgba_palette(mode: str, palette: tuple[int, ...]) -> None:
|
||||||
im = Image.new("P", (1, 1))
|
im = Image.new("P", (1, 1))
|
||||||
im.putpalette(palette, mode)
|
im.putpalette(palette, mode)
|
||||||
assert im.getpalette() == [1, 2, 3]
|
assert im.getpalette() == [1, 2, 3]
|
||||||
|
|
|
@ -231,11 +231,13 @@ class TestImagingCoreResampleAccuracy:
|
||||||
|
|
||||||
|
|
||||||
class TestCoreResampleConsistency:
|
class TestCoreResampleConsistency:
|
||||||
def make_case(self, mode: str, fill: tuple[int, int, int] | float):
|
def make_case(
|
||||||
|
self, mode: str, fill: tuple[int, int, int] | float
|
||||||
|
) -> tuple[Image.Image, tuple[int, ...]]:
|
||||||
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) -> None:
|
def run_case(self, case: tuple[Image.Image, Image.Image]) -> 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]):
|
||||||
|
@ -353,7 +355,7 @@ class TestCoreResampleAlphaCorrect:
|
||||||
|
|
||||||
class TestCoreResamplePasses:
|
class TestCoreResamplePasses:
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def count(self, diff):
|
def count(self, diff: int) -> Generator[None, None, None]:
|
||||||
count = Image.core.get_stats()["new_count"]
|
count = Image.core.get_stats()["new_count"]
|
||||||
yield
|
yield
|
||||||
assert Image.core.get_stats()["new_count"] - count == diff
|
assert Image.core.get_stats()["new_count"] - count == diff
|
||||||
|
|
|
@ -12,7 +12,13 @@ from .helper import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def rotate(im, mode, angle, center=None, translate=None) -> None:
|
def rotate(
|
||||||
|
im: Image.Image,
|
||||||
|
mode: str,
|
||||||
|
angle: int,
|
||||||
|
center: tuple[int, int] | None = None,
|
||||||
|
translate: tuple[int, int] | None = None,
|
||||||
|
) -> None:
|
||||||
out = im.rotate(angle, center=center, translate=translate)
|
out = im.rotate(angle, center=center, translate=translate)
|
||||||
assert out.mode == mode
|
assert out.mode == mode
|
||||||
assert out.size == im.size # default rotate clips output
|
assert out.size == im.size # default rotate clips output
|
||||||
|
@ -27,13 +33,13 @@ def rotate(im, mode, angle, center=None, translate=None) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
|
@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
|
||||||
def test_mode(mode) -> None:
|
def test_mode(mode: str) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
rotate(im, mode, 45)
|
rotate(im, mode, 45)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
||||||
def test_angle(angle) -> 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 im:
|
||||||
rotate(im, im.mode, angle)
|
rotate(im, im.mode, angle)
|
||||||
|
|
||||||
|
@ -42,7 +48,7 @@ def test_angle(angle) -> None:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("angle", (0, 45, 90, 180, 270))
|
@pytest.mark.parametrize("angle", (0, 45, 90, 180, 270))
|
||||||
def test_zero(angle) -> None:
|
def test_zero(angle: int) -> None:
|
||||||
im = Image.new("RGB", (0, 0))
|
im = Image.new("RGB", (0, 0))
|
||||||
rotate(im, im.mode, angle)
|
rotate(im, im.mode, angle)
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ def test_load_first_unless_jpeg() -> None:
|
||||||
with Image.open("Tests/images/hopper.jpg") as im:
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
draft = im.draft
|
draft = im.draft
|
||||||
|
|
||||||
def im_draft(mode, size):
|
def im_draft(mode: str, size: tuple[int, int]):
|
||||||
result = draft(mode, size)
|
result = draft(mode, size)
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ class TestImageTransform:
|
||||||
("LA", (76, 0)),
|
("LA", (76, 0)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_fill(self, mode, expected_pixel) -> None:
|
def test_fill(self, mode: str, expected_pixel: tuple[int, ...]) -> None:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
transformed = im.transform(
|
transformed = im.transform(
|
||||||
|
@ -142,7 +143,9 @@ class TestImageTransform:
|
||||||
assert_image_equal(blank, transformed.crop((w // 2, 0, w, h // 2)))
|
assert_image_equal(blank, transformed.crop((w // 2, 0, w, h // 2)))
|
||||||
assert_image_equal(blank, transformed.crop((0, h // 2, w // 2, h)))
|
assert_image_equal(blank, transformed.crop((0, h // 2, w // 2, h)))
|
||||||
|
|
||||||
def _test_alpha_premult(self, op) -> None:
|
def _test_alpha_premult(
|
||||||
|
self, op: Callable[[Image.Image, tuple[int, int]], Image.Image]
|
||||||
|
) -> None:
|
||||||
# create image with half white, half black,
|
# create image with half white, half black,
|
||||||
# with the black half transparent.
|
# with the black half transparent.
|
||||||
# do op,
|
# do op,
|
||||||
|
@ -159,13 +162,13 @@ class TestImageTransform:
|
||||||
assert 40 * 10 == hist[-1]
|
assert 40 * 10 == hist[-1]
|
||||||
|
|
||||||
def test_alpha_premult_resize(self) -> None:
|
def test_alpha_premult_resize(self) -> None:
|
||||||
def op(im, sz):
|
def op(im: Image.Image, sz: tuple[int, int]) -> Image.Image:
|
||||||
return im.resize(sz, Image.Resampling.BILINEAR)
|
return im.resize(sz, Image.Resampling.BILINEAR)
|
||||||
|
|
||||||
self._test_alpha_premult(op)
|
self._test_alpha_premult(op)
|
||||||
|
|
||||||
def test_alpha_premult_transform(self) -> None:
|
def test_alpha_premult_transform(self) -> None:
|
||||||
def op(im, sz):
|
def op(im: Image.Image, sz: tuple[int, int]) -> Image.Image:
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
return im.transform(
|
return im.transform(
|
||||||
sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.BILINEAR
|
sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.BILINEAR
|
||||||
|
@ -173,7 +176,9 @@ class TestImageTransform:
|
||||||
|
|
||||||
self._test_alpha_premult(op)
|
self._test_alpha_premult(op)
|
||||||
|
|
||||||
def _test_nearest(self, op, mode) -> None:
|
def _test_nearest(
|
||||||
|
self, op: Callable[[Image.Image, tuple[int, int]], Image.Image], mode: str
|
||||||
|
) -> None:
|
||||||
# create white image with half transparent,
|
# create white image with half transparent,
|
||||||
# do op,
|
# do op,
|
||||||
# the image should remain white with half transparent
|
# the image should remain white with half transparent
|
||||||
|
@ -196,15 +201,15 @@ class TestImageTransform:
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
||||||
def test_nearest_resize(self, mode) -> None:
|
def test_nearest_resize(self, mode: str) -> None:
|
||||||
def op(im, sz):
|
def op(im: Image.Image, sz: tuple[int, int]) -> Image.Image:
|
||||||
return im.resize(sz, Image.Resampling.NEAREST)
|
return im.resize(sz, Image.Resampling.NEAREST)
|
||||||
|
|
||||||
self._test_nearest(op, mode)
|
self._test_nearest(op, mode)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
||||||
def test_nearest_transform(self, mode) -> None:
|
def test_nearest_transform(self, mode: str) -> None:
|
||||||
def op(im, sz):
|
def op(im: Image.Image, sz: tuple[int, int]) -> Image.Image:
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
return im.transform(
|
return im.transform(
|
||||||
sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.NEAREST
|
sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.NEAREST
|
||||||
|
@ -227,7 +232,9 @@ class TestImageTransform:
|
||||||
# Running by default, but I'd totally understand not doing it in
|
# Running by default, but I'd totally understand not doing it in
|
||||||
# the future
|
# the future
|
||||||
|
|
||||||
pattern = [Image.new("RGBA", (1024, 1024), (a, a, a, a)) for a in range(1, 65)]
|
pattern: list[Image.Image] | None = [
|
||||||
|
Image.new("RGBA", (1024, 1024), (a, a, a, a)) for a in range(1, 65)
|
||||||
|
]
|
||||||
|
|
||||||
# Yeah. Watch some JIT optimize this out.
|
# Yeah. Watch some JIT optimize this out.
|
||||||
pattern = None # noqa: F841
|
pattern = None # noqa: F841
|
||||||
|
@ -240,7 +247,7 @@ class TestImageTransform:
|
||||||
im.transform((100, 100), None)
|
im.transform((100, 100), None)
|
||||||
|
|
||||||
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
|
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
|
||||||
def test_unknown_resampling_filter(self, resample) -> 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):
|
||||||
|
@ -250,7 +257,7 @@ class TestImageTransform:
|
||||||
class TestImageTransformAffine:
|
class TestImageTransformAffine:
|
||||||
transform = Image.Transform.AFFINE
|
transform = Image.Transform.AFFINE
|
||||||
|
|
||||||
def _test_image(self):
|
def _test_image(self) -> Image.Image:
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
return im.crop((10, 20, im.width - 10, im.height - 20))
|
return im.crop((10, 20, im.width - 10, im.height - 20))
|
||||||
|
|
||||||
|
@ -263,7 +270,7 @@ class TestImageTransformAffine:
|
||||||
(270, Image.Transpose.ROTATE_270),
|
(270, Image.Transpose.ROTATE_270),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_rotate(self, deg, transpose) -> None:
|
def test_rotate(self, deg: int, transpose: Image.Transpose | None) -> None:
|
||||||
im = self._test_image()
|
im = self._test_image()
|
||||||
|
|
||||||
angle = -math.radians(deg)
|
angle = -math.radians(deg)
|
||||||
|
@ -313,7 +320,13 @@ class TestImageTransformAffine:
|
||||||
(Image.Resampling.BICUBIC, 1),
|
(Image.Resampling.BICUBIC, 1),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_resize(self, scale, epsilon_scale, resample, epsilon) -> None:
|
def test_resize(
|
||||||
|
self,
|
||||||
|
scale: float,
|
||||||
|
epsilon_scale: float,
|
||||||
|
resample: Image.Resampling,
|
||||||
|
epsilon: int,
|
||||||
|
) -> None:
|
||||||
im = self._test_image()
|
im = self._test_image()
|
||||||
|
|
||||||
size_up = int(round(im.width * scale)), int(round(im.height * scale))
|
size_up = int(round(im.width * scale)), int(round(im.height * scale))
|
||||||
|
@ -342,7 +355,14 @@ class TestImageTransformAffine:
|
||||||
(Image.Resampling.BICUBIC, 1),
|
(Image.Resampling.BICUBIC, 1),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_translate(self, x, y, epsilon_scale, resample, epsilon) -> None:
|
def test_translate(
|
||||||
|
self,
|
||||||
|
x: float,
|
||||||
|
y: float,
|
||||||
|
epsilon_scale: float,
|
||||||
|
resample: Image.Resampling,
|
||||||
|
epsilon: float,
|
||||||
|
) -> None:
|
||||||
im = self._test_image()
|
im = self._test_image()
|
||||||
|
|
||||||
size_up = int(round(im.width + x)), int(round(im.height + y))
|
size_up = int(round(im.width + x)), int(round(im.height + y))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user