Skip to content

Commit 761aee7

Browse files
committed
Replace ImageFile _seek_check() with SeekableImageFile seek()
1 parent 923a595 commit 761aee7

15 files changed

Lines changed: 47 additions & 52 deletions

‎Tests/test_file_spider.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,18 @@ def test_invalid_file() -> None:
158158

159159
def test_nonstack_file() -> None:
160160
with Image.open(TEST_FILE) as im:
161+
assert isinstance(im, SpiderImagePlugin.SpiderImageFile)
162+
im.imgnumber = 2
163+
161164
with pytest.raises(EOFError):
162165
im.seek(0)
163166

164167

165168
def test_nonstack_dos() -> None:
166169
with Image.open(TEST_FILE) as im:
170+
assert isinstance(im, SpiderImagePlugin.SpiderImageFile)
171+
im.imgnumber = 2
172+
167173
with pytest.raises(EOFError):
168174
im.seek(0)
169175

‎docs/reference/ImageFile.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ Classes
6262
:undoc-members:
6363
:show-inheritance:
6464

65+
.. autoclass:: PIL.ImageFile.SeekableImageFile()
66+
:members:
67+
:show-inheritance:
68+
6569
.. autoclass:: PIL.ImageFile.StubHandler()
6670
:members:
6771
:show-inheritance:

‎src/PIL/AvifImagePlugin.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def _get_default_max_threads() -> int:
6464
return os.cpu_count() or 1
6565

6666

67-
class AvifImageFile(ImageFile.ImageFile):
67+
class AvifImageFile(ImageFile.SeekableImageFile):
6868
format = "AVIF"
6969
format_description = "AVIF image"
7070
__frame = -1
@@ -112,9 +112,7 @@ def _open(self) -> None:
112112
self.info["exif"] = exif
113113
self.seek(0)
114114

115-
def seek(self, frame: int) -> None:
116-
if not self._seek_check(frame):
117-
return
115+
def _seekTo(self, frame: int) -> None:
118116
self.info.pop("timestamp", None)
119117
self.info.pop("duration", None)
120118

‎src/PIL/DcxImagePlugin.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
__lazy_modules__ = {"PIL._binary", "PIL._util"}
2626

27-
from . import Image
27+
from . import Image, ImageFile
2828
from ._binary import i32le as i32
2929
from ._util import DeferredError
3030
from .PcxImagePlugin import PcxImageFile
@@ -40,7 +40,7 @@ def _accept(prefix: bytes) -> bool:
4040
# Image plugin for the Intel DCX format.
4141

4242

43-
class DcxImageFile(PcxImageFile):
43+
class DcxImageFile(PcxImageFile, ImageFile.SeekableImageFile):
4444
format = "DCX"
4545
format_description = "Intel DCX"
4646
_close_exclusive_fp_after_loading = False
@@ -67,9 +67,7 @@ def _open(self) -> None:
6767
self.is_animated = self.n_frames > 1
6868
self.seek(0)
6969

70-
def seek(self, frame: int) -> None:
71-
if not self._seek_check(frame):
72-
return
70+
def _seekTo(self, frame: int) -> None:
7371
if isinstance(self._fp, DeferredError):
7472
raise self._fp.ex
7573
self.frame = frame

‎src/PIL/FliImagePlugin.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _accept(prefix: bytes) -> bool:
4242
# method to load individual frames.
4343

4444

45-
class FliImageFile(ImageFile.ImageFile):
45+
class FliImageFile(ImageFile.SeekableImageFile):
4646
format = "FLI"
4747
format_description = "Autodesk FLI/FLC Animation"
4848
_close_exclusive_fp_after_loading = False
@@ -135,9 +135,7 @@ def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None:
135135
palette[i] = (r, g, b)
136136
i += 1
137137

138-
def seek(self, frame: int) -> None:
139-
if not self._seek_check(frame):
140-
return
138+
def _seekTo(self, frame: int) -> None:
141139
if frame < self.__frame:
142140
self._seek(0)
143141

‎src/PIL/GifImagePlugin.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def _accept(prefix: bytes) -> bool:
8383
# GIF89 images.
8484

8585

86-
class GifImageFile(ImageFile.ImageFile):
86+
class GifImageFile(ImageFile.SeekableImageFile):
8787
format = "GIF"
8888
format_description = "Compuserve GIF"
8989
_close_exclusive_fp_after_loading = False
@@ -160,9 +160,7 @@ def is_animated(self) -> bool:
160160
self.seek(current)
161161
return is_animated
162162

163-
def seek(self, frame: int) -> None:
164-
if not self._seek_check(frame):
165-
return
163+
def _seekTo(self, frame: int) -> None:
166164
if frame < self.__frame:
167165
self._im = None
168166
self._seek(0)

‎src/PIL/ImImagePlugin.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def number(s: Any) -> float:
124124
# Image plugin for the IFUNC IM file format.
125125

126126

127-
class ImImageFile(ImageFile.ImageFile):
127+
class ImImageFile(ImageFile.SeekableImageFile):
128128
format = "IM"
129129
format_description = "IFUNC Image Memory"
130130
_close_exclusive_fp_after_loading = False
@@ -293,9 +293,7 @@ def n_frames(self) -> int:
293293
def is_animated(self) -> bool:
294294
return self.info[FRAMES] > 1
295295

296-
def seek(self, frame: int) -> None:
297-
if not self._seek_check(frame):
298-
return
296+
def _seekTo(self, frame: int) -> None:
299297
if isinstance(self._fp, DeferredError):
300298
raise self._fp.ex
301299

‎src/PIL/ImageFile.py‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,6 @@ def __init__(
137137
) -> None:
138138
super().__init__()
139139

140-
self._min_frame = 0
141-
142140
self.custom_mimetype: str | None = None
143141

144142
self.tile: list[_Tile] = []
@@ -482,7 +480,11 @@ def _get_fp_length(self, fp: IO[bytes] | None = None) -> int:
482480
fp = self.fp
483481
return _get_fp_length(fp)
484482

485-
def _seek_check(self, frame: int) -> bool:
483+
484+
class SeekableImageFile(ImageFile):
485+
_min_frame = 0
486+
487+
def seek(self, frame: int) -> None:
486488
if (
487489
frame < self._min_frame
488490
# Only check upper limit on frames if additional seek operations
@@ -495,7 +497,14 @@ def _seek_check(self, frame: int) -> bool:
495497
msg = "attempt to seek outside sequence"
496498
raise EOFError(msg)
497499

498-
return self.tell() != frame
500+
if self.tell() == frame:
501+
return
502+
503+
self._seekTo(frame)
504+
505+
@abc.abstractmethod
506+
def _seekTo(self, frame: int) -> None:
507+
pass
499508

500509

501510
class StubHandler(abc.ABC):

‎src/PIL/MicImagePlugin.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,7 @@ def _open(self) -> None:
7171
self.__fp = self.fp
7272
self.seek(0)
7373

74-
def seek(self, frame: int) -> None:
75-
if not self._seek_check(frame):
76-
return
74+
def _seekTo(self, frame: int) -> None:
7775
filename = self.images[frame]
7876
self.fp = self.ole.openstream(filename)
7977

‎src/PIL/MpoImagePlugin.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
105105
# Image plugin for MPO images.
106106

107107

108-
class MpoImageFile(JpegImagePlugin.JpegImageFile):
108+
class MpoImageFile(JpegImagePlugin.JpegImageFile, ImageFile.SeekableImageFile):
109109
format = "MPO"
110110
format_description = "MPO (CIPA DC-007)"
111111
_close_exclusive_fp_after_loading = False
@@ -144,9 +144,7 @@ def load_seek(self, pos: int) -> None:
144144
raise self._fp.ex
145145
self._fp.seek(pos)
146146

147-
def seek(self, frame: int) -> None:
148-
if not self._seek_check(frame):
149-
return
147+
def _seekTo(self, frame: int) -> None:
150148
if isinstance(self._fp, DeferredError):
151149
raise self._fp.ex
152150
self.fp = self._fp

0 commit comments

Comments
 (0)