From a606fd85a3cf5b72c42e876364e74e6157202b98 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Feb 2022 14:12:56 +1100 Subject: [PATCH 1/3] Run encoder cleanup method after errors as well --- Tests/test_imagefile.py | 5 ++++ src/PIL/ImageFile.py | 56 ++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index f3da73e3810..1c444fe27ad 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -200,6 +200,9 @@ class MockPyEncoder(ImageFile.PyEncoder): def encode(self, buffer): return 1, 1, b"" + def cleanup(self): + self.cleanup_called = True + xoff, yoff, xsize, ysize = 10, 20, 100, 100 @@ -327,10 +330,12 @@ def test_negsize(self): im = MockImageFile(buf) fp = BytesIO() + self.encoder.cleanup_called = False with pytest.raises(ValueError): ImageFile._save( im, fp, [("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")] ) + assert self.encoder.cleanup_called with pytest.raises(ValueError): ImageFile._save( diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index c63cc61459d..e2a9b66cfd3 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -503,36 +503,40 @@ def _save(im, fp, tile, bufsize=0): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) - if o > 0: - fp.seek(o) - e.setimage(im.im, b) - if e.pushes_fd: - e.setfd(fp) - l, s = e.encode_to_pyfd() - else: - while True: - l, s, d = e.encode(bufsize) - fp.write(d) - if s: - break - if s < 0: - raise OSError(f"encoder error {s} when writing image file") from exc - e.cleanup() + try: + if o > 0: + fp.seek(o) + e.setimage(im.im, b) + if e.pushes_fd: + e.setfd(fp) + l, s = e.encode_to_pyfd() + else: + while True: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break + if s < 0: + raise OSError(f"encoder error {s} when writing image file") from exc + finally: + e.cleanup() else: # slight speedup: compress to real file object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) - if o > 0: - fp.seek(o) - e.setimage(im.im, b) - if e.pushes_fd: - e.setfd(fp) - l, s = e.encode_to_pyfd() - else: - s = e.encode_to_file(fh, bufsize) - if s < 0: - raise OSError(f"encoder error {s} when writing image file") - e.cleanup() + try: + if o > 0: + fp.seek(o) + e.setimage(im.im, b) + if e.pushes_fd: + e.setfd(fp) + l, s = e.encode_to_pyfd() + else: + s = e.encode_to_file(fh, bufsize) + if s < 0: + raise OSError(f"encoder error {s} when writing image file") + finally: + e.cleanup() if hasattr(fp, "flush"): fp.flush() From 4d868abd8a702c5c873b9d2090b7dd2f3361f834 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Feb 2022 13:15:12 +1100 Subject: [PATCH 2/3] Moved non-codec code outside of try block --- src/PIL/ImageFile.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index e2a9b66cfd3..964943ecd1b 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -223,11 +223,11 @@ def load(self): ) ] for decoder_name, extents, offset, args in self.tile: + seek(offset) decoder = Image._getdecoder( self.mode, decoder_name, args, self.decoderconfig ) try: - seek(offset) decoder.setimage(self.im, extents) if decoder.pulls_fd: decoder.setfd(self.fp) @@ -502,10 +502,10 @@ def _save(im, fp, tile, bufsize=0): except (AttributeError, io.UnsupportedOperation) as exc: # compress to Python file-compatible object for e, b, o, a in tile: + if o > 0: + fp.seek(o) e = Image._getencoder(im.mode, e, a, im.encoderconfig) try: - if o > 0: - fp.seek(o) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) @@ -523,10 +523,10 @@ def _save(im, fp, tile, bufsize=0): else: # slight speedup: compress to real file object for e, b, o, a in tile: + if o > 0: + fp.seek(o) e = Image._getencoder(im.mode, e, a, im.encoderconfig) try: - if o > 0: - fp.seek(o) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) From bb9338e34d705501b87876ebe6a8212b88b96acb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Feb 2022 13:59:52 +1100 Subject: [PATCH 3/3] Removed duplicate code --- src/PIL/ImageFile.py | 55 ++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 964943ecd1b..18601fb371c 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -499,44 +499,33 @@ def _save(im, fp, tile, bufsize=0): try: fh = fp.fileno() fp.flush() - except (AttributeError, io.UnsupportedOperation) as exc: - # compress to Python file-compatible object - for e, b, o, a in tile: - if o > 0: - fp.seek(o) - e = Image._getencoder(im.mode, e, a, im.encoderconfig) - try: - e.setimage(im.im, b) - if e.pushes_fd: - e.setfd(fp) - l, s = e.encode_to_pyfd() - else: + exc = None + except (AttributeError, io.UnsupportedOperation) as e: + exc = e + for e, b, o, a in tile: + if o > 0: + fp.seek(o) + encoder = Image._getencoder(im.mode, e, a, im.encoderconfig) + try: + encoder.setimage(im.im, b) + if encoder.pushes_fd: + encoder.setfd(fp) + l, s = encoder.encode_to_pyfd() + else: + if exc: + # compress to Python file-compatible object while True: - l, s, d = e.encode(bufsize) + l, s, d = encoder.encode(bufsize) fp.write(d) if s: break - if s < 0: - raise OSError(f"encoder error {s} when writing image file") from exc - finally: - e.cleanup() - else: - # slight speedup: compress to real file object - for e, b, o, a in tile: - if o > 0: - fp.seek(o) - e = Image._getencoder(im.mode, e, a, im.encoderconfig) - try: - e.setimage(im.im, b) - if e.pushes_fd: - e.setfd(fp) - l, s = e.encode_to_pyfd() else: - s = e.encode_to_file(fh, bufsize) - if s < 0: - raise OSError(f"encoder error {s} when writing image file") - finally: - e.cleanup() + # slight speedup: compress to real file object + s = encoder.encode_to_file(fh, bufsize) + if s < 0: + raise OSError(f"encoder error {s} when writing image file") from exc + finally: + encoder.cleanup() if hasattr(fp, "flush"): fp.flush()