From 062753e6a831e15c3861da4bfb39b80efb47c559 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Mar 2023 00:17:52 +1100 Subject: [PATCH] Added "closed" attribute to images --- Tests/test_image.py | 11 +++++++++++ docs/reference/open_files.rst | 9 ++++++--- src/PIL/Image.py | 4 ++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 85e3ff55b..dd036e1db 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -928,6 +928,17 @@ class TestImage: im.apply_transparency() assert im.palette.colors[(27, 35, 6, 214)] == 24 + def test_closed(self): + im = Image.open("Tests/images/hopper.jpg") + assert not im.closed + + im.close() + assert im.closed + + with Image.open("Tests/images/hopper.jpg") as im: + assert not im.closed + assert im.closed + def test_categories_deprecation(self): with pytest.warns(DeprecationWarning): assert hopper().category == 0 diff --git a/docs/reference/open_files.rst b/docs/reference/open_files.rst index f31941c9a..eef8defa9 100644 --- a/docs/reference/open_files.rst +++ b/docs/reference/open_files.rst @@ -41,6 +41,7 @@ Image Lifecycle * ``Image.open()`` Filenames and ``Path`` objects are opened as a file. Metadata is read from the open file. The file is left open for further usage. + When an image is created, the ``closed`` property is set to ``False``. * ``Image.Image.load()`` When the pixel data from the image is required, ``load()`` is called. The current frame is read into @@ -58,14 +59,16 @@ Image Lifecycle (e.g. multipage TIFF and animated GIF) the image file is left open so that ``Image.Image.seek()`` can load the appropriate frame. -* ``Image.Image.close()`` Closes the file and destroys the core image object. +* ``Image.Image.close()`` Closes the file, sets the ``closed`` property to + ``True`` and destroys the core image object. - The Pillow context manager will also close the file, but will not destroy - the core image object. e.g.:: + The Pillow context manager will also close the file and update the ``closed`` + property, but will not destroy the core image object. e.g.:: with Image.open("test.jpg") as img: img.load() assert img.fp is None + assert img.closed img.save("test.png") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index cf9ab2df6..353da4981 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -519,6 +519,7 @@ class Image: self._category = 0 self.readonly = 0 self.pyaccess = None + self.closed = False self._exif = None def __getattr__(self, name): @@ -567,6 +568,7 @@ class Image: if self.fp: self.fp.close() self.fp = None + self.closed = True def close(self): """ @@ -594,6 +596,8 @@ class Image: if getattr(self, "map", None): self.map = None + self.closed = True + # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image # object is gone.