add _close_exclusive_fp_after_load flag

This commit is contained in:
wiredfool 2017-03-14 23:16:38 +00:00
parent 900c5e5bf2
commit 86c1704646
11 changed files with 28 additions and 20 deletions

View File

@ -41,7 +41,8 @@ class DcxImageFile(PcxImageFile):
format = "DCX" format = "DCX"
format_description = "Intel DCX" format_description = "Intel DCX"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# Header # Header
@ -58,7 +59,6 @@ class DcxImageFile(PcxImageFile):
self._offset.append(offset) self._offset.append(offset)
self.__fp = self.fp self.__fp = self.fp
self._exclusive_fp = False
self.seek(0) self.seek(0)
@property @property

View File

@ -37,7 +37,8 @@ class FliImageFile(ImageFile.ImageFile):
format = "FLI" format = "FLI"
format_description = "Autodesk FLI/FLC Animation" format_description = "Autodesk FLI/FLC Animation"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# HEAD # HEAD
@ -84,7 +85,6 @@ class FliImageFile(ImageFile.ImageFile):
# set things up to decode first frame # set things up to decode first frame
self.__frame = -1 self.__frame = -1
self.__fp = self.fp self.__fp = self.fp
self._exclusive_fp = False
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
self._n_frames = None self._n_frames = None
self._is_animated = None self._is_animated = None

View File

@ -47,6 +47,8 @@ class GifImageFile(ImageFile.ImageFile):
format = "GIF" format = "GIF"
format_description = "Compuserve GIF" format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False
global_palette = None global_palette = None
def data(self): def data(self):
@ -80,7 +82,6 @@ class GifImageFile(ImageFile.ImageFile):
break break
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self._exclusive_fp = False
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
self._n_frames = None self._n_frames = None
self._is_animated = None self._is_animated = None

View File

@ -109,6 +109,7 @@ class ImImageFile(ImageFile.ImageFile):
format = "IM" format = "IM"
format_description = "IFUNC Image Memory" format_description = "IFUNC Image Memory"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
@ -234,7 +235,6 @@ class ImImageFile(ImageFile.ImageFile):
self.__offset = offs = self.fp.tell() self.__offset = offs = self.fp.tell()
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self._exclusive_fp = False
if self.rawmode[:2] == "F;": if self.rawmode[:2] == "F;":

View File

@ -506,6 +506,7 @@ class Image(object):
""" """
format = None format = None
format_description = None format_description = None
_close_exclusive_fp_after_loading = True
def __init__(self): def __init__(self):
# FIXME: take "new" parameters / other image? # FIXME: take "new" parameters / other image?
@ -560,6 +561,7 @@ class Image(object):
""" """
try: try:
self.fp.close() self.fp.close()
self.fp = None
except Exception as msg: except Exception as msg:
logger.debug("Error closing: %s", msg) logger.debug("Error closing: %s", msg)
@ -568,6 +570,13 @@ class Image(object):
# object is gone. # object is gone.
self.im = deferred_error(ValueError("Operation on closed image")) self.im = deferred_error(ValueError("Operation on closed image"))
if sys.version_info >= (3,4,0):
def __del__(self):
if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp')
and self.fp and self._exclusive_fp):
self.fp.close()
self.fp = None
def _copy(self): def _copy(self):
self.load() self.load()
self.im = self.im.copy() self.im = self.im.copy()
@ -2440,8 +2449,7 @@ def open(fp, mode="r"):
im = _open_core(fp, filename, prefix) im = _open_core(fp, filename, prefix)
if im: if im:
if getattr(im, '_exclusive_fp', 'undef') is None: im._exclusive_fp = exclusive_fp
im._exclusive_fp = exclusive_fp
return im return im
if exclusive_fp: if exclusive_fp:

View File

@ -242,7 +242,9 @@ class ImageFile(Image.Image):
self.tile = [] self.tile = []
self.readonly = readonly self.readonly = readonly
if self._exclusive_fp: self.load_end()
if self._exclusive_fp and self._close_exclusive_fp_after_loading:
self.fp.close() self.fp.close()
self.fp = None self.fp = None
@ -250,8 +252,6 @@ class ImageFile(Image.Image):
# still raised if decoder fails to return anything # still raised if decoder fails to return anything
raise_ioerror(err_code) raise_ioerror(err_code)
self.load_end()
return Image.Image.load(self) return Image.Image.load(self)
def load_prepare(self): def load_prepare(self):

View File

@ -39,6 +39,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
format = "MIC" format = "MIC"
format_description = "Microsoft Image Composer" format_description = "Microsoft Image Composer"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
@ -64,7 +65,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
raise SyntaxError("not an MIC file; no image entries") raise SyntaxError("not an MIC file; no image entries")
self.__fp = self.fp self.__fp = self.fp
self._exclusive_fp = False
self.frame = 0 self.frame = 0
if len(self.images) > 1: if len(self.images) > 1:

View File

@ -39,7 +39,8 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
format = "MPO" format = "MPO"
format_description = "MPO (CIPA DC-007)" format_description = "MPO (CIPA DC-007)"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
self.fp.seek(0) # prep the fp in order to pass the JPEG test self.fp.seek(0) # prep the fp in order to pass the JPEG test
JpegImagePlugin.JpegImageFile._open(self) JpegImagePlugin.JpegImageFile._open(self)
@ -53,7 +54,6 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
assert self.__framecount == len(self.__mpoffsets) assert self.__framecount == len(self.__mpoffsets)
del self.info['mpoffset'] # no longer needed del self.info['mpoffset'] # no longer needed
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self._exclusive_fp = False
self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame
self.__frame = 0 self.__frame = 0
self.offset = 0 self.offset = 0

View File

@ -97,6 +97,7 @@ class SpiderImageFile(ImageFile.ImageFile):
format = "SPIDER" format = "SPIDER"
format_description = "Spider 2D image" format_description = "Spider 2D image"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# check header # check header
@ -154,7 +155,6 @@ class SpiderImageFile(ImageFile.ImageFile):
("raw", (0, 0) + self.size, offset, ("raw", (0, 0) + self.size, offset,
(self.rawmode, 0, 1))] (self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self._exclusive_fp = False
@property @property
def n_frames(self): def n_frames(self):

View File

@ -884,6 +884,7 @@ class TiffImageFile(ImageFile.ImageFile):
format = "TIFF" format = "TIFF"
format_description = "Adobe TIFF" format_description = "Adobe TIFF"
_close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
"Open the first image in a TIFF file" "Open the first image in a TIFF file"
@ -901,7 +902,6 @@ class TiffImageFile(ImageFile.ImageFile):
self.__first = self.__next = self.tag_v2.next self.__first = self.__next = self.tag_v2.next
self.__frame = -1 self.__frame = -1
self.__fp = self.fp self.__fp = self.fp
self._exclusive_fp = False
self._frame_pos = [] self._frame_pos = []
self._n_frames = None self._n_frames = None
self._is_animated = None self._is_animated = None
@ -1014,7 +1014,7 @@ class TiffImageFile(ImageFile.ImageFile):
# allow closing if we're on the first frame, there's no next # allow closing if we're on the first frame, there's no next
# This is the ImageFile.load path only, libtiff specific below. # This is the ImageFile.load path only, libtiff specific below.
if self.__frame == 0 and not self.__next: if self.__frame == 0 and not self.__next:
self._exclusive_fp = True self._close_exclusive_fp_after_loading = True
def _load_libtiff(self): def _load_libtiff(self):
""" Overload method triggered when we detect a compressed tiff """ Overload method triggered when we detect a compressed tiff
@ -1093,10 +1093,10 @@ class TiffImageFile(ImageFile.ImageFile):
self.tile = [] self.tile = []
self.readonly = 0 self.readonly = 0
# libtiff closed the fp in a, we need to close self.fp, if possible # libtiff closed the fp in a, we need to close self.fp, if possible
if hasattr(self.fp, 'close'): if self._exclusive_fp:
if self.__frame == 0 and not self.__next: if self.__frame == 0 and not self.__next:
self.fp.close() self.fp.close()
self.fp = None # might be shared self.fp = None # might be shared
if err < 0: if err < 0:
raise IOError(err) raise IOError(err)

View File

@ -472,7 +472,6 @@ class TestFileTiff(PillowTestCase):
def test_close_on_load(self): def test_close_on_load(self):
# same as test_fd_leak, but runs on unixlike os # same as test_fd_leak, but runs on unixlike os
tmpfile = self.tempfile("temp.tif") tmpfile = self.tempfile("temp.tif")
import os
with Image.open("Tests/images/uint16_1_4660.tif") as im: with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile) im.save(tmpfile)