diff --git a/Lib/tarfile.py b/Lib/tarfile.py index aec7009fedd..40d438c45c4 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1405,10 +1405,11 @@ class TarFile(object): can be determined, `mode' is overridden by `fileobj's mode. `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'") self.mode = mode - self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + self._mode = modes[mode] if not fileobj: if self.mode == "a" and not os.path.exists(name): @@ -1564,7 +1565,7 @@ class TarFile(object): filemode = filemode or "r" comptype = comptype or "tar" - if filemode not in "rw": + if filemode not in ("r", "w"): raise ValueError("mode must be 'r' or 'w'") stream = _Stream(name, filemode, comptype, fileobj, bufsize) @@ -1576,7 +1577,7 @@ class TarFile(object): t._extfileobj = False return t - elif mode in "aw": + elif mode in ("a", "w"): return cls.taropen(name, mode, fileobj, **kwargs) raise ValueError("undiscernible mode") @@ -1585,7 +1586,7 @@ class TarFile(object): def taropen(cls, name, mode="r", fileobj=None, **kwargs): """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'") return cls(name, mode, fileobj, **kwargs) @@ -1594,7 +1595,7 @@ class TarFile(object): """Open gzip compressed tar archive name for reading or writing. 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'") try: @@ -1625,7 +1626,7 @@ class TarFile(object): """Open bzip2 compressed tar archive name for reading or writing. 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'.") try: diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 5bae9f474f3..72ae31addab 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -43,6 +43,7 @@ class TarTest: tarname = tarname suffix = '' open = io.FileIO + taropen = tarfile.TarFile.taropen @property def mode(self): @@ -53,18 +54,21 @@ class GzipTest: tarname = gzipname suffix = 'gz' open = gzip.GzipFile if gzip else None + taropen = tarfile.TarFile.gzopen @support.requires_bz2 class Bz2Test: tarname = bz2name suffix = 'bz2' open = bz2.BZ2File if bz2 else None + taropen = tarfile.TarFile.bz2open @support.requires_lzma class LzmaTest: tarname = xzname suffix = 'xz' open = lzma.LZMAFile if lzma else None + taropen = tarfile.TarFile.xzopen class ReadTest(TarTest): @@ -289,6 +293,16 @@ class MiscReadTestBase(CommonReadTest): with tarfile.open(fileobj=fobj, mode=self.mode) as tar: 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): # Skip the first member and store values from the second member # of the testtar. diff --git a/Misc/NEWS b/Misc/NEWS index 8eb968ae79d..a4aaafac62a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -25,6 +25,9 @@ Core and Builtins Library ------- +- Issue #20245: The open functions in the tarfile module now correctly handle + empty mode. + - Issue #20242: Fixed basicConfig() format strings for the alternative formatting styles. Thanks to kespindler for the bug report and patch.