cpython: 929c2d076a85 (original) (raw)

Mercurial > cpython

changeset 105674:929c2d076a85 3.5

Issue #14061: Misc fixes and cleanups in archiving code in shutil. Imporoved the documentation and tests for make_archive() and unpack_archive(). Improved error handling when corresponding compress module is not available. Brake circular dependency between shutil and tarfile modules. [#14061]

Serhiy Storchaka storchaka@gmail.com
date Fri, 16 Dec 2016 18:58:33 +0200
parents 092d4d83c50a
children 268d3763bb07 7f11020b64ef
files Doc/library/shutil.rst Lib/shutil.py Lib/test/test_shutil.py
diffstat 3 files changed, 110 insertions(+), 107 deletions(-)[+] [-] Doc/library/shutil.rst 44 Lib/shutil.py 82 Lib/test/test_shutil.py 91

line wrap: on

line diff

--- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -458,6 +458,10 @@ Archiving operations .. versionadded:: 3.2 +.. versionchanged:: 3.5

+ + High-level utilities to create and read compressed and archived files are also provided. They rely on the :mod:zipfile and :mod:tarfile modules. @@ -467,8 +471,9 @@ provided. They rely on the :mod:`zipfil base_name is the name of the file to create, including the path, minus any format-specific extension. format is the archive format: one of

- .. function:: get_archive_formats() @@ -502,11 +504,11 @@ provided. They rely on the :mod:zipfil[](#l1.36) [](#l1.37) By default :mod:shutil` provides these formats:

.. function:: register_unpack_format(name, extensions, function[, extra_args[, description]]) @@ -578,11 +581,12 @@ provided. They rely on the :mod:zipfil[](#l1.71) [](#l1.72) By default :mod:shutil` provides these formats:

--- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -10,7 +10,13 @@ import stat import fnmatch import collections import errno -import tarfile + +try:

+except ImportError:

try: import bz2 @@ -602,23 +608,22 @@ def _make_tarball(base_name, base_dir, c Returns the output filename. """

-

-

-

+

if not dry_run:

@@ -655,13 +660,10 @@ def _make_tarball(base_name, base_dir, c def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): """Create a zip file from all the files under 'base_dir'.

zip_filename = base_name + ".zip" archive_dir = os.path.dirname(base_name) @@ -700,10 +702,13 @@ def _make_zipfile(base_name, base_dir, v return zip_filename _ARCHIVE_FORMATS = {

+} + +if _ZLIB_SUPPORTED:

if _BZ2_SUPPORTED: _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], @@ -752,8 +757,8 @@ def make_archive(base_name, format, root """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific

'root_dir' is a directory that will be the root directory of the archive; ie. we typically chdir into 'root_dir' before creating the @@ -866,10 +871,7 @@ def _ensure_directory(path): def _unpack_zipfile(filename, extract_dir): """Unpack zip filename to extract_dir """

if not zipfile.is_zipfile(filename): raise ReadError("%s is not a zip file" % filename) @@ -903,6 +905,7 @@ def _unpack_zipfile(filename, extract_di def _unpack_tarfile(filename, extract_dir): """Unpack tar/tar.gz/tar.bz2/tar.xz filename to extract_dir """

_UNPACK_FORMATS = {

+} + +if _ZLIB_SUPPORTED:

if _BZ2_SUPPORTED: _UNPACK_FORMATS['bztar'] = (['.tar.bz2', '.tbz2'], _unpack_tarfile, [], @@ -942,10 +948,10 @@ def unpack_archive(filename, extract_dir extract_dir is the name of the target directory, where the archive is unpacked. If not provided, the current working directory is used.

In case none is found, a ValueError is raised. """

--- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -19,22 +19,11 @@ from shutil import (make_archive, unregister_unpack_format, get_unpack_formats, SameFileError) import tarfile +import zipfile import warnings from test import support -from test.support import TESTFN, check_warnings, captured_stdout, requires_zlib - -try:

-except ImportError:

- -try:

-except ImportError:

+from test.support import TESTFN, check_warnings, captured_stdout TESTFN2 = TESTFN + "2" @@ -45,12 +34,6 @@ try: except ImportError: UID_GID_SUPPORT = False -try:

-except ImportError:

- def _fake_rename(*args, **kwargs): # Pretend the destination path is on a different filesystem. raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link") @@ -964,7 +947,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(getattr(file1_stat, 'st_flags'), getattr(file2_stat, 'st_flags'))

@@ -1020,7 +1003,7 @@ class TestShutil(unittest.TestCase): write_file((root_dir, 'outer'), 'xxx') return root_dir, base_dir

@@ -1091,8 +1073,7 @@ class TestShutil(unittest.TestCase): ['dist/', 'dist/sub/', 'dist/sub2/', 'dist/file1', 'dist/file2', 'dist/sub/file3'])

@@ -1174,7 +1154,7 @@ class TestShutil(unittest.TestCase): self.assertTrue(os.path.isfile(res))

@@ -1219,7 +1199,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(make_archive('test', 'tar'), 'test.tar') self.assertTrue(os.path.isfile('test.tar'))

@@ -1243,33 +1223,46 @@ class TestShutil(unittest.TestCase): formats = [name for name, params in get_archive_formats()] self.assertNotIn('xxx', formats)

-

+

+ self.assertRaises(shutil.ReadError, unpack_archive, TESTFN) self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')

+

+

+

+

+ def test_unpack_registry(self): formats = get_unpack_formats()