Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Tests/test_imagesequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,24 @@ def test_all_frames() -> None:
for i, im_frame in enumerate(ims):
im.seek(i)
assert_image_equal(im.rotate(90), im_frame)


def test_all_frames_restores_position_after_seek_error() -> None:
class CustomImage(Image.Image):
def seek(self, frame: int) -> None:
if frame == 3:
msg = "seek failed"
raise ValueError(msg)
self.__frame = frame
self.im = Image.core.new("1", (1, 1))

def tell(self) -> int:
return self.__frame

with CustomImage() as im:
im.seek(1)

with pytest.raises(ValueError, match="seek failed"):
ImageSequence.all_frames(im)

assert im.tell() == 1
6 changes: 6 additions & 0 deletions docs/releasenotes/13.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ immediately at the release of 3.15.0 final (2026-10-01, :pep:`790`).

Pillow 13.0.0 now officially supports Python 3.15.

Restore image position if ImageSequence.all_frames() fails
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If processing a frame in :py:func:`~PIL.ImageSequence.all_frames` raises an exception,
the given image will now seek back to its original frame.

Fixed ImageChops.offset() for 16-bit images
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
8 changes: 4 additions & 4 deletions src/PIL/ImageSequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def all_frames(
ims = []
for imSequence in im:
current = imSequence.tell()

ims += [im_frame.copy() for im_frame in Iterator(imSequence)]

imSequence.seek(current)
try:
ims += [im_frame.copy() for im_frame in Iterator(imSequence)]
finally:
imSequence.seek(current)
return [func(im) for im in ims] if func else ims