mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 01:16:16 +03:00
Decoder and encoders subclass PyDecoder and PyEncoder
This commit is contained in:
parent
912a33f5e9
commit
3199c0ea40
|
@ -986,13 +986,7 @@ class TestFileJpeg:
|
||||||
def decode(self, buffer: bytes) -> tuple[int, int]:
|
def decode(self, buffer: bytes) -> tuple[int, int]:
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
decoder = InfiniteMockPyDecoder(None)
|
Image.register_decoder("INFINITE", InfiniteMockPyDecoder)
|
||||||
|
|
||||||
def closure(mode: str, *args) -> InfiniteMockPyDecoder:
|
|
||||||
decoder.__init__(mode, *args)
|
|
||||||
return decoder
|
|
||||||
|
|
||||||
Image.register_decoder("INFINITE", closure)
|
|
||||||
|
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.tile = [
|
im.tile = [
|
||||||
|
|
|
@ -16,6 +16,7 @@ from PIL import (
|
||||||
ExifTags,
|
ExifTags,
|
||||||
Image,
|
Image,
|
||||||
ImageDraw,
|
ImageDraw,
|
||||||
|
ImageFile,
|
||||||
ImagePalette,
|
ImagePalette,
|
||||||
UnidentifiedImageError,
|
UnidentifiedImageError,
|
||||||
features,
|
features,
|
||||||
|
@ -1038,25 +1039,20 @@ class TestImage:
|
||||||
assert im.fp is None
|
assert im.fp is None
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder:
|
class MockEncoder(ImageFile.PyEncoder):
|
||||||
args: tuple[str, ...]
|
pass
|
||||||
|
|
||||||
|
|
||||||
def mock_encode(*args: str) -> MockEncoder:
|
|
||||||
encoder = MockEncoder()
|
|
||||||
encoder.args = args
|
|
||||||
return encoder
|
|
||||||
|
|
||||||
|
|
||||||
class TestRegistry:
|
class TestRegistry:
|
||||||
def test_encode_registry(self) -> None:
|
def test_encode_registry(self) -> None:
|
||||||
Image.register_encoder("MOCK", mock_encode)
|
Image.register_encoder("MOCK", MockEncoder)
|
||||||
assert "MOCK" in Image.ENCODERS
|
assert "MOCK" in Image.ENCODERS
|
||||||
|
|
||||||
enc = Image._getencoder("RGB", "MOCK", ("args",), extra=("extra",))
|
enc = Image._getencoder("RGB", "MOCK", ("args",), extra=("extra",))
|
||||||
|
|
||||||
assert isinstance(enc, MockEncoder)
|
assert isinstance(enc, MockEncoder)
|
||||||
assert enc.args == ("RGB", "args", "extra")
|
assert enc.mode == "RGB"
|
||||||
|
assert enc.args == ("args", "extra")
|
||||||
|
|
||||||
def test_encode_registry_fail(self) -> None:
|
def test_encode_registry_fail(self) -> None:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -201,12 +202,22 @@ class TestImageFile:
|
||||||
|
|
||||||
|
|
||||||
class MockPyDecoder(ImageFile.PyDecoder):
|
class MockPyDecoder(ImageFile.PyDecoder):
|
||||||
|
def __init__(self, mode: str, *args: Any) -> None:
|
||||||
|
MockPyDecoder.last = self
|
||||||
|
|
||||||
|
super().__init__(mode, *args)
|
||||||
|
|
||||||
def decode(self, buffer):
|
def decode(self, buffer):
|
||||||
# eof
|
# eof
|
||||||
return -1, 0
|
return -1, 0
|
||||||
|
|
||||||
|
|
||||||
class MockPyEncoder(ImageFile.PyEncoder):
|
class MockPyEncoder(ImageFile.PyEncoder):
|
||||||
|
def __init__(self, mode: str, *args: Any) -> None:
|
||||||
|
MockPyEncoder.last = self
|
||||||
|
|
||||||
|
super().__init__(mode, *args)
|
||||||
|
|
||||||
def encode(self, buffer):
|
def encode(self, buffer):
|
||||||
return 1, 1, b""
|
return 1, 1, b""
|
||||||
|
|
||||||
|
@ -228,19 +239,8 @@ class MockImageFile(ImageFile.ImageFile):
|
||||||
class CodecsTest:
|
class CodecsTest:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls) -> None:
|
def setup_class(cls) -> None:
|
||||||
cls.decoder = MockPyDecoder(None)
|
Image.register_decoder("MOCK", MockPyDecoder)
|
||||||
cls.encoder = MockPyEncoder(None)
|
Image.register_encoder("MOCK", MockPyEncoder)
|
||||||
|
|
||||||
def decoder_closure(mode, *args):
|
|
||||||
cls.decoder.__init__(mode, *args)
|
|
||||||
return cls.decoder
|
|
||||||
|
|
||||||
def encoder_closure(mode, *args):
|
|
||||||
cls.encoder.__init__(mode, *args)
|
|
||||||
return cls.encoder
|
|
||||||
|
|
||||||
Image.register_decoder("MOCK", decoder_closure)
|
|
||||||
Image.register_encoder("MOCK", encoder_closure)
|
|
||||||
|
|
||||||
|
|
||||||
class TestPyDecoder(CodecsTest):
|
class TestPyDecoder(CodecsTest):
|
||||||
|
@ -251,13 +251,13 @@ class TestPyDecoder(CodecsTest):
|
||||||
|
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
assert self.decoder.state.xoff == xoff
|
assert MockPyDecoder.last.state.xoff == xoff
|
||||||
assert self.decoder.state.yoff == yoff
|
assert MockPyDecoder.last.state.yoff == yoff
|
||||||
assert self.decoder.state.xsize == xsize
|
assert MockPyDecoder.last.state.xsize == xsize
|
||||||
assert self.decoder.state.ysize == ysize
|
assert MockPyDecoder.last.state.ysize == ysize
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
self.decoder.set_as_raw(b"\x00")
|
MockPyDecoder.last.set_as_raw(b"\x00")
|
||||||
|
|
||||||
def test_extents_none(self) -> None:
|
def test_extents_none(self) -> None:
|
||||||
buf = BytesIO(b"\x00" * 255)
|
buf = BytesIO(b"\x00" * 255)
|
||||||
|
@ -267,10 +267,10 @@ class TestPyDecoder(CodecsTest):
|
||||||
|
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
assert self.decoder.state.xoff == 0
|
assert MockPyDecoder.last.state.xoff == 0
|
||||||
assert self.decoder.state.yoff == 0
|
assert MockPyDecoder.last.state.yoff == 0
|
||||||
assert self.decoder.state.xsize == 200
|
assert MockPyDecoder.last.state.xsize == 200
|
||||||
assert self.decoder.state.ysize == 200
|
assert MockPyDecoder.last.state.ysize == 200
|
||||||
|
|
||||||
def test_negsize(self) -> None:
|
def test_negsize(self) -> None:
|
||||||
buf = BytesIO(b"\x00" * 255)
|
buf = BytesIO(b"\x00" * 255)
|
||||||
|
@ -315,10 +315,10 @@ class TestPyEncoder(CodecsTest):
|
||||||
im, fp, [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 0, "RGB")]
|
im, fp, [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 0, "RGB")]
|
||||||
)
|
)
|
||||||
|
|
||||||
assert self.encoder.state.xoff == xoff
|
assert MockPyEncoder.last.state.xoff == xoff
|
||||||
assert self.encoder.state.yoff == yoff
|
assert MockPyEncoder.last.state.yoff == yoff
|
||||||
assert self.encoder.state.xsize == xsize
|
assert MockPyEncoder.last.state.xsize == xsize
|
||||||
assert self.encoder.state.ysize == ysize
|
assert MockPyEncoder.last.state.ysize == ysize
|
||||||
|
|
||||||
def test_extents_none(self) -> None:
|
def test_extents_none(self) -> None:
|
||||||
buf = BytesIO(b"\x00" * 255)
|
buf = BytesIO(b"\x00" * 255)
|
||||||
|
@ -329,10 +329,10 @@ class TestPyEncoder(CodecsTest):
|
||||||
fp = BytesIO()
|
fp = BytesIO()
|
||||||
ImageFile._save(im, fp, [("MOCK", None, 0, "RGB")])
|
ImageFile._save(im, fp, [("MOCK", None, 0, "RGB")])
|
||||||
|
|
||||||
assert self.encoder.state.xoff == 0
|
assert MockPyEncoder.last.state.xoff == 0
|
||||||
assert self.encoder.state.yoff == 0
|
assert MockPyEncoder.last.state.yoff == 0
|
||||||
assert self.encoder.state.xsize == 200
|
assert MockPyEncoder.last.state.xsize == 200
|
||||||
assert self.encoder.state.ysize == 200
|
assert MockPyEncoder.last.state.ysize == 200
|
||||||
|
|
||||||
def test_negsize(self) -> None:
|
def test_negsize(self) -> None:
|
||||||
buf = BytesIO(b"\x00" * 255)
|
buf = BytesIO(b"\x00" * 255)
|
||||||
|
@ -340,12 +340,12 @@ class TestPyEncoder(CodecsTest):
|
||||||
im = MockImageFile(buf)
|
im = MockImageFile(buf)
|
||||||
|
|
||||||
fp = BytesIO()
|
fp = BytesIO()
|
||||||
self.encoder.cleanup_called = False
|
MockPyEncoder.last = None
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
im, fp, [("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")]
|
im, fp, [("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")]
|
||||||
)
|
)
|
||||||
assert self.encoder.cleanup_called
|
assert MockPyEncoder.last.cleanup_called
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
|
|
|
@ -229,8 +229,8 @@ MIME: dict[str, str] = {}
|
||||||
SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
||||||
SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
|
||||||
EXTENSION: dict[str, str] = {}
|
EXTENSION: dict[str, str] = {}
|
||||||
DECODERS: dict[str, object] = {}
|
DECODERS: dict[str, type[ImageFile.PyDecoder]] = {}
|
||||||
ENCODERS: dict[str, object] = {}
|
ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {}
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Modes
|
# Modes
|
||||||
|
@ -3524,28 +3524,26 @@ def registered_extensions():
|
||||||
return EXTENSION
|
return EXTENSION
|
||||||
|
|
||||||
|
|
||||||
def register_decoder(name: str, decoder) -> None:
|
def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None:
|
||||||
"""
|
"""
|
||||||
Registers an image decoder. This function should not be
|
Registers an image decoder. This function should not be
|
||||||
used in application code.
|
used in application code.
|
||||||
|
|
||||||
:param name: The name of the decoder
|
:param name: The name of the decoder
|
||||||
:param decoder: A callable(mode, args) that returns an
|
:param decoder: An ImageFile.PyDecoder object
|
||||||
ImageFile.PyDecoder object
|
|
||||||
|
|
||||||
.. versionadded:: 4.1.0
|
.. versionadded:: 4.1.0
|
||||||
"""
|
"""
|
||||||
DECODERS[name] = decoder
|
DECODERS[name] = decoder
|
||||||
|
|
||||||
|
|
||||||
def register_encoder(name, encoder):
|
def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None:
|
||||||
"""
|
"""
|
||||||
Registers an image encoder. This function should not be
|
Registers an image encoder. This function should not be
|
||||||
used in application code.
|
used in application code.
|
||||||
|
|
||||||
:param name: The name of the encoder
|
:param name: The name of the encoder
|
||||||
:param encoder: A callable(mode, args) that returns an
|
:param encoder: An ImageFile.PyEncoder object
|
||||||
ImageFile.PyEncoder object
|
|
||||||
|
|
||||||
.. versionadded:: 4.1.0
|
.. versionadded:: 4.1.0
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user