From b26391e7455f3de5eacefc0ce8fb64fd91da2da6 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Fri, 4 Nov 2016 17:09:30 +0100 Subject: [PATCH] Close file handle in TiffImagePlugin when image is closed. --- PIL/Image.py | 4 ---- PIL/ImageFile.py | 10 ++++++++++ PIL/TiffImagePlugin.py | 5 +++++ Tests/test_file_tiff.py | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 000757594..1d8ec78fb 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -550,10 +550,6 @@ class Image(object): had their file read and closed by the :py:meth:`~PIL.Image.Image.load` method. """ - try: - self.fp.close() - except Exception as msg: - logger.debug("Error closing: %s", msg) # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 55cb701a1..540ee0f68 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -117,6 +117,16 @@ class ImageFile(Image.Image): # directly after open, and closes file when finished. self.fp = None + def close(self): + if getattr(self, 'map', None): + self.map = None + try: + if self.fp: + self.fp.close() + except Exception as msg: + logger.debug("Error closing: %s", msg) + Image.Image.close(self) + def load(self): "Load image data based on tile list" diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 3af38832d..2379c0385 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -948,6 +948,11 @@ class TiffImageFile(ImageFile.ImageFile): self.seek(current) return self._is_animated + def close(self): + if self.__fp: + self.__fp = None + ImageFile.ImageFile.close(self) + def seek(self, frame): "Select a given frame as current image" self._seek(max(frame, 0)) # Questionable backwards compatibility. diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 9913860ad..481eaa213 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -2,6 +2,7 @@ from __future__ import print_function import logging from io import BytesIO import struct +import sys from helper import unittest, PillowTestCase, hopper, py3 @@ -499,5 +500,23 @@ class TestFileTiff(PillowTestCase): with Image.open(mp) as im: self.assertEqual(im.n_frames, 3) + +@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") +class TestFileTiffW32(PillowTestCase): + def test_fd_leak(self): + tmpfile = self.tempfile("temp.tif") + import os + + with Image.open("Tests/images/uint16_1_4660.tif") as im: + im.save(tmpfile) + + im = Image.open(tmpfile) + im.load() + self.assertRaises(Exception, lambda: os.remove(tmpfile)) + + im.close() + os.remove(tmpfile) + + if __name__ == '__main__': unittest.main()