Commit 53ad0cd2 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #20245: The open functions in the tarfile module now correctly handle empty mode.

parent af69fe23
...@@ -1429,10 +1429,11 @@ class TarFile(object): ...@@ -1429,10 +1429,11 @@ class TarFile(object):
can be determined, `mode' is overridden by `fileobj's mode. can be determined, `mode' is overridden by `fileobj's mode.
`fileobj' is not closed, when TarFile is closed. `fileobj' is not closed, when TarFile is closed.
""" """
if len(mode) > 1 or mode not in "raw": modes = {"r": "rb", "a": "r+b", "w": "wb"}
if mode not in modes:
raise ValueError("mode must be 'r', 'a' or 'w'") raise ValueError("mode must be 'r', 'a' or 'w'")
self.mode = mode self.mode = mode
self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] self._mode = modes[mode]
if not fileobj: if not fileobj:
if self.mode == "a" and not os.path.exists(name): if self.mode == "a" and not os.path.exists(name):
...@@ -1588,7 +1589,7 @@ class TarFile(object): ...@@ -1588,7 +1589,7 @@ class TarFile(object):
filemode = filemode or "r" filemode = filemode or "r"
comptype = comptype or "tar" comptype = comptype or "tar"
if filemode not in "rw": if filemode not in ("r", "w"):
raise ValueError("mode must be 'r' or 'w'") raise ValueError("mode must be 'r' or 'w'")
stream = _Stream(name, filemode, comptype, fileobj, bufsize) stream = _Stream(name, filemode, comptype, fileobj, bufsize)
...@@ -1600,7 +1601,7 @@ class TarFile(object): ...@@ -1600,7 +1601,7 @@ class TarFile(object):
t._extfileobj = False t._extfileobj = False
return t return t
elif mode in "aw": elif mode in ("a", "w"):
return cls.taropen(name, mode, fileobj, **kwargs) return cls.taropen(name, mode, fileobj, **kwargs)
raise ValueError("undiscernible mode") raise ValueError("undiscernible mode")
...@@ -1609,7 +1610,7 @@ class TarFile(object): ...@@ -1609,7 +1610,7 @@ class TarFile(object):
def taropen(cls, name, mode="r", fileobj=None, **kwargs): def taropen(cls, name, mode="r", fileobj=None, **kwargs):
"""Open uncompressed tar archive name for reading or writing. """Open uncompressed tar archive name for reading or writing.
""" """
if len(mode) > 1 or mode not in "raw": if mode not in ("r", "a", "w"):
raise ValueError("mode must be 'r', 'a' or 'w'") raise ValueError("mode must be 'r', 'a' or 'w'")
return cls(name, mode, fileobj, **kwargs) return cls(name, mode, fileobj, **kwargs)
...@@ -1618,7 +1619,7 @@ class TarFile(object): ...@@ -1618,7 +1619,7 @@ class TarFile(object):
"""Open gzip compressed tar archive name for reading or writing. """Open gzip compressed tar archive name for reading or writing.
Appending is not allowed. Appending is not allowed.
""" """
if len(mode) > 1 or mode not in "rw": if mode not in ("r", "w"):
raise ValueError("mode must be 'r' or 'w'") raise ValueError("mode must be 'r' or 'w'")
try: try:
...@@ -1649,7 +1650,7 @@ class TarFile(object): ...@@ -1649,7 +1650,7 @@ class TarFile(object):
"""Open bzip2 compressed tar archive name for reading or writing. """Open bzip2 compressed tar archive name for reading or writing.
Appending is not allowed. Appending is not allowed.
""" """
if len(mode) > 1 or mode not in "rw": if mode not in ("r", "w"):
raise ValueError("mode must be 'r' or 'w'.") raise ValueError("mode must be 'r' or 'w'.")
try: try:
......
...@@ -41,6 +41,7 @@ class TarTest: ...@@ -41,6 +41,7 @@ class TarTest:
tarname = tarname tarname = tarname
suffix = '' suffix = ''
open = io.FileIO open = io.FileIO
taropen = tarfile.TarFile.taropen
@property @property
def mode(self): def mode(self):
...@@ -51,18 +52,21 @@ class GzipTest: ...@@ -51,18 +52,21 @@ class GzipTest:
tarname = gzipname tarname = gzipname
suffix = 'gz' suffix = 'gz'
open = gzip.GzipFile if gzip else None open = gzip.GzipFile if gzip else None
taropen = tarfile.TarFile.gzopen
@support.requires_bz2 @support.requires_bz2
class Bz2Test: class Bz2Test:
tarname = bz2name tarname = bz2name
suffix = 'bz2' suffix = 'bz2'
open = bz2.BZ2File if bz2 else None open = bz2.BZ2File if bz2 else None
taropen = tarfile.TarFile.bz2open
@support.requires_lzma @support.requires_lzma
class LzmaTest: class LzmaTest:
tarname = xzname tarname = xzname
suffix = 'xz' suffix = 'xz'
open = lzma.LZMAFile if lzma else None open = lzma.LZMAFile if lzma else None
taropen = tarfile.TarFile.xzopen
class ReadTest(TarTest): class ReadTest(TarTest):
...@@ -287,6 +291,16 @@ class MiscReadTestBase(CommonReadTest): ...@@ -287,6 +291,16 @@ class MiscReadTestBase(CommonReadTest):
with tarfile.open(fileobj=fobj, mode=self.mode) as tar: with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
self.assertEqual(tar.name, None) self.assertEqual(tar.name, None)
def test_illegal_mode_arg(self):
with open(tmpname, 'wb'):
pass
with self.assertRaisesRegex(ValueError, 'mode must be '):
tar = self.taropen(tmpname, 'q')
with self.assertRaisesRegex(ValueError, 'mode must be '):
tar = self.taropen(tmpname, 'rw')
with self.assertRaisesRegex(ValueError, 'mode must be '):
tar = self.taropen(tmpname, '')
def test_fileobj_with_offset(self): def test_fileobj_with_offset(self):
# Skip the first member and store values from the second member # Skip the first member and store values from the second member
# of the testtar. # of the testtar.
......
...@@ -43,6 +43,9 @@ Core and Builtins ...@@ -43,6 +43,9 @@ Core and Builtins
Library Library
------- -------
- Issue #20245: The open functions in the tarfile module now correctly handle
empty mode.
- Issue #20242: Fixed basicConfig() format strings for the alternative - Issue #20242: Fixed basicConfig() format strings for the alternative
formatting styles. Thanks to kespindler for the bug report and patch. formatting styles. Thanks to kespindler for the bug report and patch.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment