Patch #1652681: create nonexistent files in append mode and
allow appending to empty files.
diff --git a/Doc/lib/libtarfile.tex b/Doc/lib/libtarfile.tex
index 5f277da..1a53a27 100644
--- a/Doc/lib/libtarfile.tex
+++ b/Doc/lib/libtarfile.tex
@@ -36,7 +36,8 @@
\lineii{'r:'}{Open for reading exclusively without compression.}
\lineii{'r:gz'}{Open for reading with gzip compression.}
\lineii{'r:bz2'}{Open for reading with bzip2 compression.}
- \lineii{'a' or 'a:'}{Open for appending with no compression.}
+ \lineii{'a' or 'a:'}{Open for appending with no compression. The file
+ is created if it does not exist.}
\lineii{'w' or 'w:'}{Open for uncompressed writing.}
\lineii{'w:gz'}{Open for gzip compressed writing.}
\lineii{'w:bz2'}{Open for bzip2 compressed writing.}
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 47bd9a7..54bb1b8 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1060,6 +1060,10 @@
self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode]
if not fileobj:
+ if self._mode == "a" and not os.path.exists(self.name):
+ # Create nonexistent files in append mode.
+ self._mode = "w"
+ self.mode = "wb"
fileobj = file(self.name, self.mode)
self._extfileobj = False
else:
@@ -1093,7 +1097,8 @@
self.fileobj.seek(0)
break
if tarinfo is None:
- self.fileobj.seek(- BLOCKSIZE, 1)
+ if self.offset > 0:
+ self.fileobj.seek(- BLOCKSIZE, 1)
break
if self._mode in "aw":
@@ -1120,7 +1125,7 @@
'r:' open for reading exclusively uncompressed
'r:gz' open for reading with gzip compression
'r:bz2' open for reading with bzip2 compression
- 'a' or 'a:' open for appending
+ 'a' or 'a:' open for appending, creating the file if necessary
'w' or 'w:' open for writing without compression
'w:gz' open for writing with gzip compression
'w:bz2' open for writing with bzip2 compression
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 1674594..642f376 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -305,6 +305,61 @@
self.assertEqual(self.dst.getnames(), [], "added the archive to itself")
+class AppendTest(unittest.TestCase):
+ # Test append mode (cp. patch #1652681).
+
+ def setUp(self):
+ self.tarname = tmpname()
+ if os.path.exists(self.tarname):
+ os.remove(self.tarname)
+
+ def _add_testfile(self, fileobj=None):
+ tar = tarfile.open(self.tarname, "a", fileobj=fileobj)
+ tar.addfile(tarfile.TarInfo("bar"))
+ tar.close()
+
+ def _create_testtar(self):
+ src = tarfile.open(tarname())
+ t = src.getmember("0-REGTYPE")
+ t.name = "foo"
+ f = src.extractfile(t)
+ tar = tarfile.open(self.tarname, "w")
+ tar.addfile(t, f)
+ tar.close()
+
+ def _test(self, names=["bar"], fileobj=None):
+ tar = tarfile.open(self.tarname, fileobj=fileobj)
+ self.assert_(tar.getnames() == names)
+
+ def test_non_existing(self):
+ self._add_testfile()
+ self._test()
+
+ def test_empty(self):
+ open(self.tarname, "w").close()
+ self._add_testfile()
+ self._test()
+
+ def test_empty_fileobj(self):
+ fobj = StringIO.StringIO()
+ self._add_testfile(fobj)
+ fobj.seek(0)
+ self._test(fileobj=fobj)
+
+ def test_fileobj(self):
+ self._create_testtar()
+ data = open(self.tarname).read()
+ fobj = StringIO.StringIO(data)
+ self._add_testfile(fobj)
+ fobj.seek(0)
+ self._test(names=["foo", "bar"], fileobj=fobj)
+
+ def test_existing(self):
+ self._create_testtar()
+ self._add_testfile()
+ self._test(names=["foo", "bar"])
+
+
class Write100Test(BaseTest):
# The name field in a tar header stores strings of at most 100 chars.
# If a string is shorter than 100 chars it has to be padded with '\0',
@@ -711,6 +766,7 @@
ReadAsteriskTest,
ReadStreamAsteriskTest,
WriteTest,
+ AppendTest,
Write100Test,
WriteSize0Test,
WriteStreamTest,
diff --git a/Misc/NEWS b/Misc/NEWS
index 982fd2a..0447d4b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -126,6 +126,9 @@
Library
-------
+- Patch #1652681: tarfile.py: create nonexistent files in append mode and
+ allow appending to empty files.
+
- Bug #1124861: Automatically create pipes if GetStdHandle fails in
subprocess.