Skip to content

Commit 3cdd49f

Browse files
authored
Merge pull request #7801 from radarhere/codecs
2 parents d8c8075 + 3199c0e commit 3cdd49f

File tree

4 files changed

+45
-57
lines changed

4 files changed

+45
-57
lines changed

Tests/test_file_jpeg.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -993,13 +993,7 @@ class InfiniteMockPyDecoder(ImageFile.PyDecoder):
993993
def decode(self, buffer: bytes) -> tuple[int, int]:
994994
return 0, 0
995995

996-
decoder = InfiniteMockPyDecoder(None)
997-
998-
def closure(mode: str, *args) -> InfiniteMockPyDecoder:
999-
decoder.__init__(mode, *args)
1000-
return decoder
1001-
1002-
Image.register_decoder("INFINITE", closure)
996+
Image.register_decoder("INFINITE", InfiniteMockPyDecoder)
1003997

1004998
with Image.open(TEST_FILE) as im:
1005999
im.tile = [

Tests/test_image.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
ExifTags,
1717
Image,
1818
ImageDraw,
19+
ImageFile,
1920
ImagePalette,
2021
UnidentifiedImageError,
2122
features,
@@ -1041,25 +1042,20 @@ def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None:
10411042
assert im.fp is None
10421043

10431044

1044-
class MockEncoder:
1045-
args: tuple[str, ...]
1046-
1047-
1048-
def mock_encode(*args: str) -> MockEncoder:
1049-
encoder = MockEncoder()
1050-
encoder.args = args
1051-
return encoder
1045+
class MockEncoder(ImageFile.PyEncoder):
1046+
pass
10521047

10531048

10541049
class TestRegistry:
10551050
def test_encode_registry(self) -> None:
1056-
Image.register_encoder("MOCK", mock_encode)
1051+
Image.register_encoder("MOCK", MockEncoder)
10571052
assert "MOCK" in Image.ENCODERS
10581053

10591054
enc = Image._getencoder("RGB", "MOCK", ("args",), extra=("extra",))
10601055

10611056
assert isinstance(enc, MockEncoder)
1062-
assert enc.args == ("RGB", "args", "extra")
1057+
assert enc.mode == "RGB"
1058+
assert enc.args == ("args", "extra")
10631059

10641060
def test_encode_registry_fail(self) -> None:
10651061
with pytest.raises(OSError):

Tests/test_imagefile.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
from io import BytesIO
4+
from typing import Any
45

56
import pytest
67

@@ -201,12 +202,22 @@ def test_broken_datastream_without_errors(self) -> None:
201202

202203

203204
class MockPyDecoder(ImageFile.PyDecoder):
205+
def __init__(self, mode: str, *args: Any) -> None:
206+
MockPyDecoder.last = self
207+
208+
super().__init__(mode, *args)
209+
204210
def decode(self, buffer):
205211
# eof
206212
return -1, 0
207213

208214

209215
class MockPyEncoder(ImageFile.PyEncoder):
216+
def __init__(self, mode: str, *args: Any) -> None:
217+
MockPyEncoder.last = self
218+
219+
super().__init__(mode, *args)
220+
210221
def encode(self, buffer):
211222
return 1, 1, b""
212223

@@ -228,19 +239,8 @@ def _open(self) -> None:
228239
class CodecsTest:
229240
@classmethod
230241
def setup_class(cls) -> None:
231-
cls.decoder = MockPyDecoder(None)
232-
cls.encoder = MockPyEncoder(None)
233-
234-
def decoder_closure(mode, *args):
235-
cls.decoder.__init__(mode, *args)
236-
return cls.decoder
237-
238-
def encoder_closure(mode, *args):
239-
cls.encoder.__init__(mode, *args)
240-
return cls.encoder
241-
242-
Image.register_decoder("MOCK", decoder_closure)
243-
Image.register_encoder("MOCK", encoder_closure)
242+
Image.register_decoder("MOCK", MockPyDecoder)
243+
Image.register_encoder("MOCK", MockPyEncoder)
244244

245245

246246
class TestPyDecoder(CodecsTest):
@@ -251,13 +251,13 @@ def test_setimage(self) -> None:
251251

252252
im.load()
253253

254-
assert self.decoder.state.xoff == xoff
255-
assert self.decoder.state.yoff == yoff
256-
assert self.decoder.state.xsize == xsize
257-
assert self.decoder.state.ysize == ysize
254+
assert MockPyDecoder.last.state.xoff == xoff
255+
assert MockPyDecoder.last.state.yoff == yoff
256+
assert MockPyDecoder.last.state.xsize == xsize
257+
assert MockPyDecoder.last.state.ysize == ysize
258258

259259
with pytest.raises(ValueError):
260-
self.decoder.set_as_raw(b"\x00")
260+
MockPyDecoder.last.set_as_raw(b"\x00")
261261

262262
def test_extents_none(self) -> None:
263263
buf = BytesIO(b"\x00" * 255)
@@ -267,10 +267,10 @@ def test_extents_none(self) -> None:
267267

268268
im.load()
269269

270-
assert self.decoder.state.xoff == 0
271-
assert self.decoder.state.yoff == 0
272-
assert self.decoder.state.xsize == 200
273-
assert self.decoder.state.ysize == 200
270+
assert MockPyDecoder.last.state.xoff == 0
271+
assert MockPyDecoder.last.state.yoff == 0
272+
assert MockPyDecoder.last.state.xsize == 200
273+
assert MockPyDecoder.last.state.ysize == 200
274274

275275
def test_negsize(self) -> None:
276276
buf = BytesIO(b"\x00" * 255)
@@ -315,10 +315,10 @@ def test_setimage(self) -> None:
315315
im, fp, [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 0, "RGB")]
316316
)
317317

318-
assert self.encoder.state.xoff == xoff
319-
assert self.encoder.state.yoff == yoff
320-
assert self.encoder.state.xsize == xsize
321-
assert self.encoder.state.ysize == ysize
318+
assert MockPyEncoder.last.state.xoff == xoff
319+
assert MockPyEncoder.last.state.yoff == yoff
320+
assert MockPyEncoder.last.state.xsize == xsize
321+
assert MockPyEncoder.last.state.ysize == ysize
322322

323323
def test_extents_none(self) -> None:
324324
buf = BytesIO(b"\x00" * 255)
@@ -329,23 +329,23 @@ def test_extents_none(self) -> None:
329329
fp = BytesIO()
330330
ImageFile._save(im, fp, [("MOCK", None, 0, "RGB")])
331331

332-
assert self.encoder.state.xoff == 0
333-
assert self.encoder.state.yoff == 0
334-
assert self.encoder.state.xsize == 200
335-
assert self.encoder.state.ysize == 200
332+
assert MockPyEncoder.last.state.xoff == 0
333+
assert MockPyEncoder.last.state.yoff == 0
334+
assert MockPyEncoder.last.state.xsize == 200
335+
assert MockPyEncoder.last.state.ysize == 200
336336

337337
def test_negsize(self) -> None:
338338
buf = BytesIO(b"\x00" * 255)
339339

340340
im = MockImageFile(buf)
341341

342342
fp = BytesIO()
343-
self.encoder.cleanup_called = False
343+
MockPyEncoder.last = None
344344
with pytest.raises(ValueError):
345345
ImageFile._save(
346346
im, fp, [("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")]
347347
)
348-
assert self.encoder.cleanup_called
348+
assert MockPyEncoder.last.cleanup_called
349349

350350
with pytest.raises(ValueError):
351351
ImageFile._save(

src/PIL/Image.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ class Quantize(IntEnum):
229229
SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
230230
SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {}
231231
EXTENSION: dict[str, str] = {}
232-
DECODERS: dict[str, object] = {}
233-
ENCODERS: dict[str, object] = {}
232+
DECODERS: dict[str, type[ImageFile.PyDecoder]] = {}
233+
ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {}
234234

235235
# --------------------------------------------------------------------
236236
# Modes
@@ -3526,28 +3526,26 @@ def registered_extensions():
35263526
return EXTENSION
35273527

35283528

3529-
def register_decoder(name: str, decoder) -> None:
3529+
def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None:
35303530
"""
35313531
Registers an image decoder. This function should not be
35323532
used in application code.
35333533
35343534
:param name: The name of the decoder
3535-
:param decoder: A callable(mode, args) that returns an
3536-
ImageFile.PyDecoder object
3535+
:param decoder: An ImageFile.PyDecoder object
35373536
35383537
.. versionadded:: 4.1.0
35393538
"""
35403539
DECODERS[name] = decoder
35413540

35423541

3543-
def register_encoder(name, encoder):
3542+
def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None:
35443543
"""
35453544
Registers an image encoder. This function should not be
35463545
used in application code.
35473546
35483547
:param name: The name of the encoder
3549-
:param encoder: A callable(mode, args) that returns an
3550-
ImageFile.PyEncoder object
3548+
:param encoder: An ImageFile.PyEncoder object
35513549
35523550
.. versionadded:: 4.1.0
35533551
"""

0 commit comments

Comments
 (0)