summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_tarfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_tarfile.py')
-rw-r--r--Lib/test/test_tarfile.py320
1 files changed, 272 insertions, 48 deletions
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 68fe608..3091ce7 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -1,13 +1,12 @@
import sys
import os
import io
-import shutil
from hashlib import md5
import unittest
import tarfile
-from test import support
+from test import support, script_helper
# Check for our compression modules.
try:
@@ -27,11 +26,13 @@ def md5sum(data):
return md5(data).hexdigest()
TEMPDIR = os.path.abspath(support.TESTFN) + "-tardir"
+tarextdir = TEMPDIR + '-extract-test'
tarname = support.findfile("testtar.tar")
gzipname = os.path.join(TEMPDIR, "testtar.tar.gz")
bz2name = os.path.join(TEMPDIR, "testtar.tar.bz2")
xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
tmpname = os.path.join(TEMPDIR, "tmp.tar")
+dotlessname = os.path.join(TEMPDIR, "testtar")
md5_regtype = "65f477c818ad9e15f7feab0c6d37742f"
md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6"
@@ -271,7 +272,7 @@ class ListTest(ReadTest, unittest.TestCase):
# ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/conttype
# ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/regtype
# ...
- self.assertRegex(out, (br'-rw-r--r-- tarfile/tarfile\s+7011 '
+ self.assertRegex(out, (br'\?rw-r--r-- tarfile/tarfile\s+7011 '
br'\d{4}-\d\d-\d\d\s+\d\d:\d\d:\d\d '
br'ustar/\w+type ?\r?\n') * 2)
# Make sure it prints the source of link with verbose flag
@@ -319,12 +320,7 @@ class CommonReadTest(ReadTest):
def test_non_existent_tarfile(self):
# Test for issue11513: prevent non-existent gzipped tarfiles raising
# multiple exceptions.
- test = 'xxx'
- if sys.platform == 'win32' and '|' in self.mode:
- # Issue #20384: On Windows os.open() error message doesn't
- # contain file name.
- test = ''
- with self.assertRaisesRegex(FileNotFoundError, test):
+ with self.assertRaisesRegex(FileNotFoundError, "xxx"):
tarfile.open("xxx", self.mode)
def test_null_tarfile(self):
@@ -353,12 +349,41 @@ class CommonReadTest(ReadTest):
finally:
tar.close()
+ def test_premature_end_of_archive(self):
+ for size in (512, 600, 1024, 1200):
+ with tarfile.open(tmpname, "w:") as tar:
+ t = tarfile.TarInfo("foo")
+ t.size = 1024
+ tar.addfile(t, io.BytesIO(b"a" * 1024))
+
+ with open(tmpname, "r+b") as fobj:
+ fobj.truncate(size)
+
+ with tarfile.open(tmpname) as tar:
+ with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
+ for t in tar:
+ pass
+
+ with tarfile.open(tmpname) as tar:
+ t = tar.next()
+
+ with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
+ tar.extract(t, TEMPDIR)
+
+ with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
+ tar.extractfile(t).read()
class MiscReadTestBase(CommonReadTest):
+ def requires_name_attribute(self):
+ pass
+
def test_no_name_argument(self):
+ self.requires_name_attribute()
with open(self.tarname, "rb") as fobj:
- tar = tarfile.open(fileobj=fobj, mode=self.mode)
- self.assertEqual(tar.name, os.path.abspath(fobj.name))
+ self.assertIsInstance(fobj.name, str)
+ with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
+ self.assertIsInstance(tar.name, str)
+ self.assertEqual(tar.name, os.path.abspath(fobj.name))
def test_no_name_attribute(self):
with open(self.tarname, "rb") as fobj:
@@ -366,7 +391,7 @@ class MiscReadTestBase(CommonReadTest):
fobj = io.BytesIO(data)
self.assertRaises(AttributeError, getattr, fobj, "name")
tar = tarfile.open(fileobj=fobj, mode=self.mode)
- self.assertEqual(tar.name, None)
+ self.assertIsNone(tar.name)
def test_empty_name_attribute(self):
with open(self.tarname, "rb") as fobj:
@@ -374,7 +399,25 @@ class MiscReadTestBase(CommonReadTest):
fobj = io.BytesIO(data)
fobj.name = ""
with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
- self.assertEqual(tar.name, None)
+ self.assertIsNone(tar.name)
+
+ def test_int_name_attribute(self):
+ # Issue 21044: tarfile.open() should handle fileobj with an integer
+ # 'name' attribute.
+ fd = os.open(self.tarname, os.O_RDONLY)
+ with open(fd, 'rb') as fobj:
+ self.assertIsInstance(fobj.name, int)
+ with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
+ self.assertIsNone(tar.name)
+
+ def test_bytes_name_attribute(self):
+ self.requires_name_attribute()
+ tarname = os.fsencode(self.tarname)
+ with open(tarname, 'rb') as fobj:
+ self.assertIsInstance(fobj.name, bytes)
+ with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
+ self.assertIsInstance(tar.name, bytes)
+ self.assertEqual(tar.name, os.path.abspath(fobj.name))
def test_illegal_mode_arg(self):
with open(tmpname, 'wb'):
@@ -459,16 +502,16 @@ class MiscReadTestBase(CommonReadTest):
# Test hardlink extraction (e.g. bug #857297).
with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar:
tar.extract("ustar/regtype", TEMPDIR)
- self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/regtype"))
+ self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype"))
tar.extract("ustar/lnktype", TEMPDIR)
- self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/lnktype"))
+ self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype"))
with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
data = f.read()
self.assertEqual(md5sum(data), md5_regtype)
tar.extract("ustar/symtype", TEMPDIR)
- self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/symtype"))
+ self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype"))
with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
data = f.read()
self.assertEqual(md5sum(data), md5_regtype)
@@ -501,7 +544,7 @@ class MiscReadTestBase(CommonReadTest):
self.assertEqual(tarinfo.mtime, file_mtime, errmsg)
finally:
tar.close()
- shutil.rmtree(DIR)
+ support.rmtree(DIR)
def test_extract_directory(self):
dirtype = "ustar/dirtype"
@@ -516,7 +559,7 @@ class MiscReadTestBase(CommonReadTest):
if sys.platform != "win32":
self.assertEqual(os.stat(extracted).st_mode & 0o777, 0o755)
finally:
- shutil.rmtree(DIR)
+ support.rmtree(DIR)
def test_init_close_fobj(self):
# Issue #7341: Close the internal file object in the TarFile
@@ -552,11 +595,11 @@ class GzipMiscReadTest(GzipTest, MiscReadTestBase, unittest.TestCase):
pass
class Bz2MiscReadTest(Bz2Test, MiscReadTestBase, unittest.TestCase):
- def test_no_name_argument(self):
+ def requires_name_attribute(self):
self.skipTest("BZ2File have no name attribute")
class LzmaMiscReadTest(LzmaTest, MiscReadTestBase, unittest.TestCase):
- def test_no_name_argument(self):
+ def requires_name_attribute(self):
self.skipTest("LZMAFile have no name attribute")
@@ -880,7 +923,7 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
fobj.seek(4096)
fobj.truncate()
s = os.stat(name)
- os.remove(name)
+ support.unlink(name)
return s.st_blocks == 0
else:
return False
@@ -1013,7 +1056,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- os.rmdir(path)
+ support.rmdir(path)
@unittest.skipUnless(hasattr(os, "link"),
"Missing hardlink implementation")
@@ -1033,8 +1076,8 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- os.remove(target)
- os.remove(link)
+ support.unlink(target)
+ support.unlink(link)
@support.skip_unless_symlink
def test_symlink_size(self):
@@ -1048,7 +1091,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- os.remove(path)
+ support.unlink(path)
def test_add_self(self):
# Test for #1257255.
@@ -1061,10 +1104,8 @@ class WriteTest(WriteTestBase, unittest.TestCase):
self.assertEqual(tar.getnames(), [],
"added the archive to itself")
- cwd = os.getcwd()
- os.chdir(TEMPDIR)
- tar.add(dstname)
- os.chdir(cwd)
+ with support.change_cwd(TEMPDIR):
+ tar.add(dstname)
self.assertEqual(tar.getnames(), [],
"added the archive to itself")
finally:
@@ -1095,7 +1136,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- shutil.rmtree(tempdir)
+ support.rmtree(tempdir)
def test_filter(self):
tempdir = os.path.join(TEMPDIR, "filter")
@@ -1131,7 +1172,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- shutil.rmtree(tempdir)
+ support.rmtree(tempdir)
# Guarantee that stored pathnames are not modified. Don't
# remove ./ or ../ or double slashes. Still make absolute
@@ -1159,9 +1200,9 @@ class WriteTest(WriteTestBase, unittest.TestCase):
tar.close()
if not dir:
- os.remove(foo)
+ support.unlink(foo)
else:
- os.rmdir(foo)
+ support.rmdir(foo)
self.assertEqual(t.name, cmp_path or path.replace(os.sep, "/"))
@@ -1192,8 +1233,8 @@ class WriteTest(WriteTestBase, unittest.TestCase):
finally:
tar.close()
finally:
- os.unlink(temparchive)
- shutil.rmtree(tempdir)
+ support.unlink(temparchive)
+ support.rmtree(tempdir)
def test_pathnames(self):
self._test_pathname("foo")
@@ -1221,9 +1262,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
def test_cwd(self):
# Test adding the current working directory.
- cwd = os.getcwd()
- os.chdir(TEMPDIR)
- try:
+ with support.change_cwd(TEMPDIR):
tar = tarfile.open(tmpname, self.mode)
try:
tar.add(".")
@@ -1237,8 +1276,6 @@ class WriteTest(WriteTestBase, unittest.TestCase):
self.assertTrue(t.name.startswith("./"), t.name)
finally:
tar.close()
- finally:
- os.chdir(cwd)
def test_open_nonwritable_fileobj(self):
for exctype in OSError, EOFError, RuntimeError:
@@ -1293,7 +1330,7 @@ class StreamWriteTest(WriteTestBase, unittest.TestCase):
# Test for issue #8464: Create files with correct
# permissions.
if os.path.exists(tmpname):
- os.remove(tmpname)
+ support.unlink(tmpname)
original_umask = os.umask(0o022)
try:
@@ -1647,7 +1684,7 @@ class AppendTestBase:
def setUp(self):
self.tarname = tmpname
if os.path.exists(self.tarname):
- os.remove(self.tarname)
+ support.unlink(self.tarname)
def _create_testtar(self, mode="w:"):
with tarfile.open(tarname, encoding="iso8859-1") as src:
@@ -1822,6 +1859,10 @@ class MiscTest(unittest.TestCase):
self.assertEqual(tarfile.nti(b"\xff\x00\x00\x00\x00\x00\x00\x00"),
-0x100000000000000)
+ # Issue 24514: Test if empty number fields are converted to zero.
+ self.assertEqual(tarfile.nti(b"\0"), 0)
+ self.assertEqual(tarfile.nti(b" \0"), 0)
+
def test_write_number_fields(self):
self.assertEqual(tarfile.itn(1), b"0000001\x00")
self.assertEqual(tarfile.itn(0o7777777), b"7777777\x00")
@@ -1847,6 +1888,186 @@ class MiscTest(unittest.TestCase):
tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT)
+class CommandLineTest(unittest.TestCase):
+
+ def tarfilecmd(self, *args, **kwargs):
+ rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args,
+ **kwargs)
+ return out.replace(os.linesep.encode(), b'\n')
+
+ def tarfilecmd_failure(self, *args):
+ return script_helper.assert_python_failure('-m', 'tarfile', *args)
+
+ def make_simple_tarfile(self, tar_name):
+ files = [support.findfile('tokenize_tests.txt'),
+ support.findfile('tokenize_tests-no-coding-cookie-'
+ 'and-utf8-bom-sig-only.txt')]
+ self.addCleanup(support.unlink, tar_name)
+ with tarfile.open(tar_name, 'w') as tf:
+ for tardata in files:
+ tf.add(tardata, arcname=os.path.basename(tardata))
+
+ def test_test_command(self):
+ for tar_name in testtarnames:
+ for opt in '-t', '--test':
+ out = self.tarfilecmd(opt, tar_name)
+ self.assertEqual(out, b'')
+
+ def test_test_command_verbose(self):
+ for tar_name in testtarnames:
+ for opt in '-v', '--verbose':
+ out = self.tarfilecmd(opt, '-t', tar_name)
+ self.assertIn(b'is a tar archive.\n', out)
+
+ def test_test_command_invalid_file(self):
+ zipname = support.findfile('zipdir.zip')
+ rc, out, err = self.tarfilecmd_failure('-t', zipname)
+ self.assertIn(b' is not a tar archive.', err)
+ self.assertEqual(out, b'')
+ self.assertEqual(rc, 1)
+
+ for tar_name in testtarnames:
+ with self.subTest(tar_name=tar_name):
+ with open(tar_name, 'rb') as f:
+ data = f.read()
+ try:
+ with open(tmpname, 'wb') as f:
+ f.write(data[:511])
+ rc, out, err = self.tarfilecmd_failure('-t', tmpname)
+ self.assertEqual(out, b'')
+ self.assertEqual(rc, 1)
+ finally:
+ support.unlink(tmpname)
+
+ def test_list_command(self):
+ for tar_name in testtarnames:
+ with support.captured_stdout() as t:
+ with tarfile.open(tar_name, 'r') as tf:
+ tf.list(verbose=False)
+ expected = t.getvalue().encode('ascii', 'backslashreplace')
+ for opt in '-l', '--list':
+ out = self.tarfilecmd(opt, tar_name,
+ PYTHONIOENCODING='ascii')
+ self.assertEqual(out, expected)
+
+ def test_list_command_verbose(self):
+ for tar_name in testtarnames:
+ with support.captured_stdout() as t:
+ with tarfile.open(tar_name, 'r') as tf:
+ tf.list(verbose=True)
+ expected = t.getvalue().encode('ascii', 'backslashreplace')
+ for opt in '-v', '--verbose':
+ out = self.tarfilecmd(opt, '-l', tar_name,
+ PYTHONIOENCODING='ascii')
+ self.assertEqual(out, expected)
+
+ def test_list_command_invalid_file(self):
+ zipname = support.findfile('zipdir.zip')
+ rc, out, err = self.tarfilecmd_failure('-l', zipname)
+ self.assertIn(b' is not a tar archive.', err)
+ self.assertEqual(out, b'')
+ self.assertEqual(rc, 1)
+
+ def test_create_command(self):
+ files = [support.findfile('tokenize_tests.txt'),
+ support.findfile('tokenize_tests-no-coding-cookie-'
+ 'and-utf8-bom-sig-only.txt')]
+ for opt in '-c', '--create':
+ try:
+ out = self.tarfilecmd(opt, tmpname, *files)
+ self.assertEqual(out, b'')
+ with tarfile.open(tmpname) as tar:
+ tar.getmembers()
+ finally:
+ support.unlink(tmpname)
+
+ def test_create_command_verbose(self):
+ files = [support.findfile('tokenize_tests.txt'),
+ support.findfile('tokenize_tests-no-coding-cookie-'
+ 'and-utf8-bom-sig-only.txt')]
+ for opt in '-v', '--verbose':
+ try:
+ out = self.tarfilecmd(opt, '-c', tmpname, *files)
+ self.assertIn(b' file created.', out)
+ with tarfile.open(tmpname) as tar:
+ tar.getmembers()
+ finally:
+ support.unlink(tmpname)
+
+ def test_create_command_dotless_filename(self):
+ files = [support.findfile('tokenize_tests.txt')]
+ try:
+ out = self.tarfilecmd('-c', dotlessname, *files)
+ self.assertEqual(out, b'')
+ with tarfile.open(dotlessname) as tar:
+ tar.getmembers()
+ finally:
+ support.unlink(dotlessname)
+
+ def test_create_command_dot_started_filename(self):
+ tar_name = os.path.join(TEMPDIR, ".testtar")
+ files = [support.findfile('tokenize_tests.txt')]
+ try:
+ out = self.tarfilecmd('-c', tar_name, *files)
+ self.assertEqual(out, b'')
+ with tarfile.open(tar_name) as tar:
+ tar.getmembers()
+ finally:
+ support.unlink(tar_name)
+
+ def test_create_command_compressed(self):
+ files = [support.findfile('tokenize_tests.txt'),
+ support.findfile('tokenize_tests-no-coding-cookie-'
+ 'and-utf8-bom-sig-only.txt')]
+ for filetype in (GzipTest, Bz2Test, LzmaTest):
+ if not filetype.open:
+ continue
+ try:
+ tar_name = tmpname + '.' + filetype.suffix
+ out = self.tarfilecmd('-c', tar_name, *files)
+ with filetype.taropen(tar_name) as tar:
+ tar.getmembers()
+ finally:
+ support.unlink(tar_name)
+
+ def test_extract_command(self):
+ self.make_simple_tarfile(tmpname)
+ for opt in '-e', '--extract':
+ try:
+ with support.temp_cwd(tarextdir):
+ out = self.tarfilecmd(opt, tmpname)
+ self.assertEqual(out, b'')
+ finally:
+ support.rmtree(tarextdir)
+
+ def test_extract_command_verbose(self):
+ self.make_simple_tarfile(tmpname)
+ for opt in '-v', '--verbose':
+ try:
+ with support.temp_cwd(tarextdir):
+ out = self.tarfilecmd(opt, '-e', tmpname)
+ self.assertIn(b' file is extracted.', out)
+ finally:
+ support.rmtree(tarextdir)
+
+ def test_extract_command_different_directory(self):
+ self.make_simple_tarfile(tmpname)
+ try:
+ with support.temp_cwd(tarextdir):
+ out = self.tarfilecmd('-e', tmpname, 'spamdir')
+ self.assertEqual(out, b'')
+ finally:
+ support.rmtree(tarextdir)
+
+ def test_extract_command_invalid_file(self):
+ zipname = support.findfile('zipdir.zip')
+ with support.temp_cwd(tarextdir):
+ rc, out, err = self.tarfilecmd_failure('-e', zipname)
+ self.assertIn(b' is not a tar archive.', err)
+ self.assertEqual(out, b'')
+ self.assertEqual(rc, 1)
+
+
class ContextManagerTest(unittest.TestCase):
def test_basic(self):
@@ -1855,20 +2076,20 @@ class ContextManagerTest(unittest.TestCase):
self.assertTrue(tar.closed, "context manager failed")
def test_closed(self):
- # The __enter__() method is supposed to raise IOError
+ # The __enter__() method is supposed to raise OSError
# if the TarFile object is already closed.
tar = tarfile.open(tarname)
tar.close()
- with self.assertRaises(IOError):
+ with self.assertRaises(OSError):
with tar:
pass
def test_exception(self):
- # Test if the IOError exception is passed through properly.
+ # Test if the OSError exception is passed through properly.
with self.assertRaises(Exception) as exc:
with tarfile.open(tarname) as tar:
- raise IOError
- self.assertIsInstance(exc.exception, IOError,
+ raise OSError
+ self.assertIsInstance(exc.exception, OSError,
"wrong exception raised in context manager")
self.assertTrue(tar.closed, "context manager failed")
@@ -1974,6 +2195,8 @@ def setUpModule():
support.unlink(TEMPDIR)
os.makedirs(TEMPDIR)
+ global testtarnames
+ testtarnames = [tarname]
with open(tarname, "rb") as fobj:
data = fobj.read()
@@ -1981,12 +2204,13 @@ def setUpModule():
for c in GzipTest, Bz2Test, LzmaTest:
if c.open:
support.unlink(c.tarname)
+ testtarnames.append(c.tarname)
with c.open(c.tarname, "wb") as tar:
tar.write(data)
def tearDownModule():
if os.path.exists(TEMPDIR):
- shutil.rmtree(TEMPDIR)
+ support.rmtree(TEMPDIR)
if __name__ == "__main__":
unittest.main()