Issue #7232: Add support for the context manager protocol

to the TarFile class.
This commit is contained in:
Lars Gustäbel 2010-03-03 11:55:48 +00:00
parent 8af970ab58
commit 6458104188
4 changed files with 92 additions and 0 deletions

View File

@ -234,6 +234,14 @@ a header block followed by data blocks. It is possible to store a file in a tar
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 @@ How to create an uncompressed tar archive from a list of filenames::
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

View File

@ -2411,6 +2411,20 @@ class TarFile(object):
"""
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:

View File

@ -1292,6 +1292,65 @@ class LimitsTest(unittest.TestCase):
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 @@ def test_main():
PaxUnicodeTest,
AppendTest,
LimitsTest,
ContextManagerTest,
]
if hasattr(os, "link"):

View File

@ -38,6 +38,9 @@ Core and Builtins
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.