Merge pull request #551 from wiredfool/with

Image.close, Context manager support
This commit is contained in:
wiredfool 2014-04-17 22:20:30 -07:00
commit 5ca5652d0b
6 changed files with 59 additions and 12 deletions

View File

@ -92,7 +92,7 @@ except ImportError:
from PIL import ImageMode
from PIL._binary import i8, o8
from PIL._util import isPath, isStringType
from PIL._util import isPath, isStringType, deferred_error
import os, sys
@ -497,6 +497,35 @@ class Image:
_makeself = _new # compatibility
# Context Manager Support
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""
Closes the file pointer, if possible.
This operation will destroy the image core and release it's memory.
The image data will be unusable afterward.
This function is only required to close images that have not
had their file read and closed by the
:py:meth:`~PIL.Image.Image.load` method.
"""
try:
self.fp.close()
except Exception as msg:
if Image.DEBUG:
print ("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
# object is gone.
self.im = deferred_error(ValueError("Operation on closed image"))
def _copy(self):
self.load()
self.im = self.im.copy()
@ -642,7 +671,8 @@ class Image:
Allocates storage for the image and loads the pixel data. In
normal cases, you don't need to call this method, since the
Image class automatically loads an opened image when it is
accessed for the first time.
accessed for the first time. This method will close the file
associated with the image.
:returns: An image access object.
"""
@ -2074,10 +2104,11 @@ def open(fp, mode="r"):
"""
Opens and identifies the given image file.
This is a lazy operation; this function identifies the file, but the
actual image data is not read from the file until you try to process
the data (or call the :py:meth:`~PIL.Image.Image.load` method).
See :py:func:`~PIL.Image.new`.
This is a lazy operation; this function identifies the file, but
the file remains open and the actual image data is not read from
the file until you try to process the data (or call the
:py:meth:`~PIL.Image.Image.load` method). See
:py:func:`~PIL.Image.new`.
:param file: A filename (string) or a file object. The file object
must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and

View File

@ -89,8 +89,8 @@ try:
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from _util import import_err
_imagingcms = import_err(ex)
from _util import deferred_error
_imagingcms = deferred_error(ex)
from PIL._util import isStringType
core = _imagingcms

View File

@ -15,7 +15,7 @@ else:
def isDirectory(f):
return isPath(f) and os.path.isdir(f)
class import_err(object):
class deferred_error(object):
def __init__(self, ex):
self.ex = ex
def __getattr__(self, elt):

View File

@ -2,6 +2,8 @@ from tester import *
from PIL import Image
import os
def test_sanity():
im = lena()
@ -9,3 +11,17 @@ def test_sanity():
pix = im.load()
assert_equal(pix[0, 0], (223, 162, 133))
def test_close():
im = Image.open("Images/lena.gif")
assert_no_exception(lambda: im.close())
assert_exception(ValueError, lambda: im.load())
assert_exception(ValueError, lambda: im.getpixel((0,0)))
def test_contextmanager():
fn = None
with Image.open("Images/lena.gif") as im:
fn = im.fp.fileno()
assert_no_exception(lambda: os.fstat(fn))
assert_exception(OSError, lambda: os.fstat(fn))

View File

@ -126,8 +126,8 @@ Identify Image Files
for infile in sys.argv[1:]:
try:
im = Image.open(infile)
print(infile, im.format, "%dx%d" % im.size, im.mode)
with Image.open(infile) as im:
print(infile, im.format, "%dx%d" % im.size, im.mode)
except IOError:
pass

View File

@ -136,9 +136,9 @@ ITU-R 709, using the D65 luminant) to the CIE XYZ color space:
.. automethod:: PIL.Image.Image.verify
.. automethod:: PIL.Image.Image.fromstring
.. deprecated:: 2.0
.. automethod:: PIL.Image.Image.load
.. automethod:: PIL.Image.Image.close
Attributes
----------