[Python-checkins] r78623 - in python/trunk: Doc/library/tarfile.rst Lib/tarfile.py Lib/test/test_tarfile.py Misc/NEWS
lars.gustaebel
python-checkins at python.org
Wed Mar 3 12:55:49 CET 2010
Author: lars.gustaebel
Date: Wed Mar 3 12:55:48 2010
New Revision: 78623
Log:
Issue #7232: Add support for the context manager protocol
to the TarFile class.
Modified:
python/trunk/Doc/library/tarfile.rst
python/trunk/Lib/tarfile.py
python/trunk/Lib/test/test_tarfile.py
python/trunk/Misc/NEWS
Modified: python/trunk/Doc/library/tarfile.rst
==============================================================================
--- python/trunk/Doc/library/tarfile.rst (original)
+++ python/trunk/Doc/library/tarfile.rst Wed Mar 3 12:55:48 2010
@@ -234,6 +234,14 @@
archive several times. Each archive member is represented by a :class:`TarInfo`
object, see :ref:`tarinfo-objects` for details.
+A :class:`TarFile` object can be used as a context manager in a :keyword:`with`
+statement. It will automatically be closed when the block is completed. Please
+note that in the event of an exception an archive opened for writing will not
+be finalized, only the internally used file object will be closed. See the
+:ref:`tar-examples` section for a use case.
+
+.. versionadded:: 2.7
+ Added support for the context manager protocol.
.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors=None, pax_headers=None, debug=0, errorlevel=0)
@@ -650,6 +658,13 @@
tar.add(name)
tar.close()
+The same example using the :keyword:`with` statement::
+
+ import tarfile
+ with tarfile.open("sample.tar", "w") as tar:
+ for name in ["foo", "bar", "quux"]:
+ tar.add(name)
+
How to read a gzip compressed tar archive and display some member information::
import tarfile
Modified: python/trunk/Lib/tarfile.py
==============================================================================
--- python/trunk/Lib/tarfile.py (original)
+++ python/trunk/Lib/tarfile.py Wed Mar 3 12:55:48 2010
@@ -2411,6 +2411,20 @@
"""
if level <= self.debug:
print >> sys.stderr, msg
+
+ def __enter__(self):
+ self._check()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ if type is None:
+ self.close()
+ else:
+ # An exception occurred. We must not call close() because
+ # it would try to write end-of-archive blocks and padding.
+ if not self._extfileobj:
+ self.fileobj.close()
+ self.closed = True
# class TarFile
class TarIter:
Modified: python/trunk/Lib/test/test_tarfile.py
==============================================================================
--- python/trunk/Lib/test/test_tarfile.py (original)
+++ python/trunk/Lib/test/test_tarfile.py Wed Mar 3 12:55:48 2010
@@ -1292,6 +1292,65 @@
tarinfo.tobuf(tarfile.PAX_FORMAT)
+class ContextManagerTest(unittest.TestCase):
+
+ def test_basic(self):
+ with tarfile.open(tarname) as tar:
+ self.assertFalse(tar.closed, "closed inside runtime context")
+ self.assertTrue(tar.closed, "context manager failed")
+
+ def test_closed(self):
+ # The __enter__() method is supposed to raise IOError
+ # if the TarFile object is already closed.
+ tar = tarfile.open(tarname)
+ tar.close()
+ with self.assertRaises(IOError):
+ with tar:
+ pass
+
+ def test_exception(self):
+ # Test if the IOError exception is passed through properly.
+ with self.assertRaises(Exception) as exc:
+ with tarfile.open(tarname) as tar:
+ raise IOError
+ self.assertIsInstance(exc.exception, IOError,
+ "wrong exception raised in context manager")
+ self.assertTrue(tar.closed, "context manager failed")
+
+ def test_no_eof(self):
+ # __exit__() must not write end-of-archive blocks if an
+ # exception was raised.
+ try:
+ with tarfile.open(tmpname, "w") as tar:
+ raise Exception
+ except:
+ pass
+ self.assertEqual(os.path.getsize(tmpname), 0,
+ "context manager wrote an end-of-archive block")
+ self.assertTrue(tar.closed, "context manager failed")
+
+ def test_eof(self):
+ # __exit__() must write end-of-archive blocks, i.e. call
+ # TarFile.close() if there was no error.
+ with tarfile.open(tmpname, "w"):
+ pass
+ self.assertNotEqual(os.path.getsize(tmpname), 0,
+ "context manager wrote no end-of-archive block")
+
+ def test_fileobj(self):
+ # Test that __exit__() did not close the external file
+ # object.
+ fobj = open(tmpname, "wb")
+ try:
+ with tarfile.open(fileobj=fobj, mode="w") as tar:
+ raise Exception
+ except:
+ pass
+ self.assertFalse(fobj.closed, "external file object was closed")
+ self.assertTrue(tar.closed, "context manager failed")
+ fobj.close()
+
+
class GzipMiscReadTest(MiscReadTest):
tarname = gzipname
mode = "r:gz"
@@ -1371,6 +1430,7 @@
PaxUnicodeTest,
AppendTest,
LimitsTest,
+ ContextManagerTest,
]
if hasattr(os, "link"):
Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS (original)
+++ python/trunk/Misc/NEWS Wed Mar 3 12:55:48 2010
@@ -38,6 +38,9 @@
Library
-------
+- Issue #7232: Add support for the context manager protocol to the TarFile
+ class.
+
- Issue #7250: Fix info leak of os.environ across multi-run uses of
wsgiref.handlers.CGIHandler.
More information about the Python-checkins
mailing list