From 2ae52552f787d5916763b7fc970a27d72d612e9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Apr 2022 09:54:17 +1000 Subject: [PATCH 1/4] Renamed __fp to _fp --- src/PIL/DcxImagePlugin.py | 12 ++++++------ src/PIL/FliImagePlugin.py | 14 +++++++------- src/PIL/GifImagePlugin.py | 16 ++++++++-------- src/PIL/ImImagePlugin.py | 12 ++++++------ src/PIL/Image.py | 8 ++++---- src/PIL/MicImagePlugin.py | 10 +++++----- src/PIL/MpoImagePlugin.py | 16 ++++++++-------- src/PIL/PngImagePlugin.py | 18 +++++++++--------- src/PIL/PsdImagePlugin.py | 12 ++++++------ src/PIL/SpiderImagePlugin.py | 12 ++++++------ src/PIL/TiffImagePlugin.py | 12 ++++++------ 11 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index de21db8f082..d5c7482261f 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -57,7 +57,7 @@ def _open(self): break self._offset.append(offset) - self.__fp = self.fp + self._fp = self.fp self.frame = None self.n_frames = len(self._offset) self.is_animated = self.n_frames > 1 @@ -67,21 +67,21 @@ def seek(self, frame): if not self._seek_check(frame): return self.frame = frame - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self._offset[frame]) PcxImageFile._open(self) def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None Image.register_open(DcxImageFile.format, DcxImageFile, _accept) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index ea950330555..7df301904c8 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -91,7 +91,7 @@ def _open(self): # set things up to decode first frame self.__frame = -1 - self.__fp = self.fp + self._fp = self.fp self.__rewind = self.fp.tell() self.seek(0) @@ -125,7 +125,7 @@ def seek(self, frame): def _seek(self, frame): if frame == 0: self.__frame = -1 - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.__offset = 128 else: # ensure that the previous frame was loaded @@ -136,7 +136,7 @@ def _seek(self, frame): self.__frame = frame # move to next frame - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self.__offset) s = self.fp.read(4) @@ -153,14 +153,14 @@ def _seek(self, frame): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index d36d8c61a8c..cfb6c0355b2 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -102,7 +102,7 @@ def _open(self): p = ImagePalette.raw("RGB", p) self.global_palette = self.palette = p - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() self._n_frames = None self._is_animated = None @@ -161,7 +161,7 @@ def _seek(self, frame, update_image=True): self.__offset = 0 self.dispose = None self.__frame = -1 - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.disposal_method = 0 else: # ensure that the previous frame was loaded @@ -171,7 +171,7 @@ def _seek(self, frame, update_image=True): if frame != self.__frame + 1: raise ValueError(f"cannot seek to frame {frame}") - self.fp = self.__fp + self.fp = self._fp if self.__offset: # backup to last frame self.fp.seek(self.__offset) @@ -281,7 +281,7 @@ def _seek(self, frame, update_image=True): s = None if interlace is None: - # self.__fp = None + # self._fp = None raise EOFError if not update_image: return @@ -443,14 +443,14 @@ def load_end(self): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index f7e690b355d..3c5739f3da8 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -245,7 +245,7 @@ def _open(self): self.__offset = offs = self.fp.tell() - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack if self.rawmode[:2] == "F;": @@ -294,21 +294,21 @@ def seek(self, frame): size = ((self.size[0] * bits + 7) // 8) * self.size[1] offs = self.__offset + frame * size - self.fp = self.__fp + self.fp = self._fp self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3c36178bd0d..16fad61c116 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,8 @@ def __enter__(self): def __exit__(self, *args): if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): - if hasattr(self, "_close__fp"): - self._close__fp() + if hasattr(self, "_close_fp"): + self._close_fp() if self.fp: self.fp.close() self.fp = None @@ -563,8 +563,8 @@ def close(self): more information. """ try: - if hasattr(self, "_close__fp"): - self._close__fp() + if hasattr(self, "_close_fp"): + self._close_fp() if self.fp: self.fp.close() self.fp = None diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 9248b1b6550..324c8eff415 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,7 @@ def _open(self): if not self.images: raise SyntaxError("not an MIC file; no image entries") - self.__fp = self.fp + self._fp = self.fp self.frame = None self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 @@ -89,14 +89,14 @@ def seek(self, frame): def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 88c1bfcc540..0d61746b73e 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -58,20 +58,20 @@ def _after_jpeg_open(self, mpheader=None): assert self.n_frames == len(self.__mpoffsets) del self.info["mpoffset"] # no longer needed self.is_animated = self.n_frames > 1 - self.__fp = self.fp # FIXME: hack - self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame + self._fp = self.fp # FIXME: hack + self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame self.__frame = 0 self.offset = 0 # for now we can only handle reading and individual frame extraction self.readonly = 1 def load_seek(self, pos): - self.__fp.seek(pos) + self._fp.seek(pos) def seek(self, frame): if not self._seek_check(frame): return - self.fp = self.__fp + self.fp = self._fp self.offset = self.__mpoffsets[frame] self.fp.seek(self.offset + 2) # skip SOI marker @@ -97,14 +97,14 @@ def seek(self, frame): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None @staticmethod def adopt(jpeg_instance, mpheader=None): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c939b86e776..313090e8d6d 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -689,7 +689,7 @@ def _open(self): if not _accept(self.fp.read(8)): raise SyntaxError("not a PNG file") - self.__fp = self.fp + self._fp = self.fp self.__frame = 0 # @@ -746,7 +746,7 @@ def _open(self): self._close_exclusive_fp_after_loading = False self.png.save_rewind() self.__rewind_idat = self.__prepare_idat - self.__rewind = self.__fp.tell() + self.__rewind = self._fp.tell() if self.default_image: # IDAT chunk contains default image and not first animation frame self.n_frames += 1 @@ -801,7 +801,7 @@ def seek(self, frame): def _seek(self, frame, rewind=False): if frame == 0: if rewind: - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.png.rewind() self.__prepare_idat = self.__rewind_idat self.im = None @@ -809,7 +809,7 @@ def _seek(self, frame, rewind=False): self.pyaccess = None self.info = self.png.im_info self.tile = self.png.im_tile - self.fp = self.__fp + self.fp = self._fp self._prev_im = None self.dispose = None self.default_image = self.info.get("default_image", False) @@ -828,7 +828,7 @@ def _seek(self, frame, rewind=False): self.im.paste(self.dispose, self.dispose_extent) self._prev_im = self.im.copy() - self.fp = self.__fp + self.fp = self._fp # advance to the next frame if self.__prepare_idat: @@ -1006,14 +1006,14 @@ def getxmp(self): else {} ) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 2832195798c..3be9aa2906b 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -132,7 +132,7 @@ def _open(self): self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) # keep the file open - self.__fp = self.fp + self._fp = self.fp self.frame = 1 self._min_frame = 1 @@ -146,7 +146,7 @@ def seek(self, layer): self.mode = mode self.tile = tile self.frame = layer - self.fp = self.__fp + self.fp = self._fp return name, bbox except IndexError as e: raise EOFError("no such layer") from e @@ -155,14 +155,14 @@ def tell(self): # return layer number (0=image, 1..max=layers) return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None def _layerinfo(fp, ct_bytes): diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 1a72f5c0451..0a65c286cd9 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -149,7 +149,7 @@ def _open(self): self.mode = "F" self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack @property def n_frames(self): @@ -172,7 +172,7 @@ def seek(self, frame): if not self._seek_check(frame): return self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self.stkoffset) self._open() @@ -191,14 +191,14 @@ def tkPhotoImage(self): return ImageTk.PhotoImage(self.convert2byte(), palette=256) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 000429991d6..ee737cb5935 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1073,7 +1073,7 @@ def _open(self): # setup frame pointers self.__first = self.__next = self.tag_v2.next self.__frame = -1 - self.__fp = self.fp + self._fp = self.fp self._frame_pos = [] self._n_frames = None @@ -1106,7 +1106,7 @@ def seek(self, frame): self.im = Image.core.new(self.mode, self.size) def _seek(self, frame): - self.fp = self.__fp + self.fp = self._fp # reset buffered io handle in case fp # was passed to libtiff, invalidating the buffer @@ -1515,14 +1515,14 @@ def _setup(self): self._tile_orientation = self.tag_v2.get(0x0112) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # From 4e075adcc5e13ffe67d25f6fab2e9b548a1d86d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 20:31:23 +1000 Subject: [PATCH 2/4] Merged _close_fp into close and __exit__ --- src/PIL/DcxImagePlugin.py | 9 --------- src/PIL/FliImagePlugin.py | 9 --------- src/PIL/GifImagePlugin.py | 9 --------- src/PIL/ImImagePlugin.py | 9 --------- src/PIL/Image.py | 12 ++++++++---- src/PIL/MicImagePlugin.py | 9 --------- src/PIL/MpoImagePlugin.py | 9 --------- src/PIL/PngImagePlugin.py | 9 --------- src/PIL/PsdImagePlugin.py | 9 --------- src/PIL/SpiderImagePlugin.py | 9 --------- src/PIL/TiffImagePlugin.py | 9 --------- 11 files changed, 8 insertions(+), 94 deletions(-) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index d5c7482261f..aeed1e7c7ba 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -74,15 +74,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - Image.register_open(DcxImageFile.format, DcxImageFile, _accept) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 7df301904c8..e13b1779ccc 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -153,15 +153,6 @@ def _seek(self, frame): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # registry diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index cfb6c0355b2..33c76586b93 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -443,15 +443,6 @@ def load_end(self): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # Write GIF files diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 3c5739f3da8..ee95a94cb55 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -301,15 +301,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 16fad61c116..1409a20d213 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,10 @@ def __enter__(self): def __exit__(self, *args): if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): - if hasattr(self, "_close_fp"): - self._close_fp() + if getattr(self, "_fp", False): + if self._fp != self.fp: + self._fp.close() + self._fp = None if self.fp: self.fp.close() self.fp = None @@ -563,8 +565,10 @@ def close(self): more information. """ try: - if hasattr(self, "_close_fp"): - self._close_fp() + if getattr(self, "_fp", False): + if self._fp != self.fp: + self._fp.close() + self._fp = None if self.fp: self.fp.close() self.fp = None diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 324c8eff415..0de37cf37c2 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -89,15 +89,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 0d61746b73e..fc3f8556ff7 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -97,15 +97,6 @@ def seek(self, frame): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - @staticmethod def adopt(jpeg_instance, mpheader=None): """ diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 313090e8d6d..856c218027f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1006,15 +1006,6 @@ def getxmp(self): else {} ) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # PNG writer diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 3be9aa2906b..9622e648a88 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -155,15 +155,6 @@ def tell(self): # return layer number (0=image, 1..max=layers) return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - def _layerinfo(fp, ct_bytes): # read layerinfo block diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 0a65c286cd9..154008c08dd 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -191,15 +191,6 @@ def tkPhotoImage(self): return ImageTk.PhotoImage(self.convert2byte(), palette=256) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # Image series diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ee737cb5935..c871072adf7 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1515,15 +1515,6 @@ def _setup(self): self._tile_orientation = self.tag_v2.get(0x0112) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- From f18688e84e1197872d2e724857573f7bd2ce4f2b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 Apr 2022 08:28:28 +1000 Subject: [PATCH 3/4] Removed unused variable --- src/PIL/MicImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 0de37cf37c2..d4f6c90f778 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,6 @@ def _open(self): if not self.images: raise SyntaxError("not an MIC file; no image entries") - self._fp = self.fp self.frame = None self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 From e62449f94cc150584c3192061feb3cca7cd5d4c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 Apr 2022 12:14:53 +1000 Subject: [PATCH 4/4] Added DeferredError to _fp --- Tests/test_file_apng.py | 9 +++++++++ Tests/test_file_fli.py | 9 +++++++++ Tests/test_file_gif.py | 13 +++++++++++++ Tests/test_file_mpo.py | 8 ++++++++ Tests/test_file_tiff.py | 9 +++++++++ src/PIL/Image.py | 4 ++-- 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index d1d5c85c1ae..ad61a07ccc5 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -637,6 +637,15 @@ def test_apng_save_blend(tmp_path): assert im.getpixel((0, 0)) == (0, 255, 0, 255) +def test_seek_after_close(): + im = Image.open("Tests/images/apng/delay.png") + im.seek(1) + im.close() + + with pytest.raises(ValueError): + im.seek(0) + + def test_constants_deprecation(): for enum, prefix in { PngImagePlugin.Disposal: "APNG_DISPOSE_", diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index c1ad4a7f0f9..a7d43d2e922 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -46,6 +46,15 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open(animated_test_file) + im.seek(1) + im.close() + + with pytest.raises(ValueError): + im.seek(0) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(static_test_file) as im: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index fd30cded07d..db64dd1aff0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -46,6 +46,19 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open("Tests/images/iss634.gif") + im.load() + im.close() + + with pytest.raises(ValueError): + im.is_animated + with pytest.raises(ValueError): + im.n_frames + with pytest.raises(ValueError): + im.seek(1) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(TEST_GIF) as im: diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index ca3ea84198f..d9b59321be4 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -48,6 +48,14 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open(test_files[0]) + im.close() + + with pytest.raises(ValueError): + im.seek(1) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(test_files[0]) as im: diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 16c43b00f27..8fdae4f1371 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -70,6 +70,15 @@ def test_closed_file(self): im.load() im.close() + def test_seek_after_close(self): + im = Image.open("Tests/images/multipage.tiff") + im.close() + + with pytest.raises(ValueError): + im.n_frames + with pytest.raises(ValueError): + im.seek(1) + def test_context_manager(self): with warnings.catch_warnings(): with Image.open("Tests/images/multipage.tiff") as im: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 1409a20d213..3de2c8cd2cc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -547,7 +547,7 @@ def __exit__(self, *args): if getattr(self, "_fp", False): if self._fp != self.fp: self._fp.close() - self._fp = None + self._fp = DeferredError(ValueError("Operation on closed image")) if self.fp: self.fp.close() self.fp = None @@ -568,7 +568,7 @@ def close(self): if getattr(self, "_fp", False): if self._fp != self.fp: self._fp.close() - self._fp = None + self._fp = DeferredError(ValueError("Operation on closed image")) if self.fp: self.fp.close() self.fp = None