diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 331acfce3afee3..a6fd06e0bdd0f6 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -589,7 +589,7 @@ Archiving operations High-level utilities to create and read compressed and archived files are also provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. -.. function:: make_archive(base_name, format, [root_dir, [base_dir, [verbose, [dry_run, [owner, [group, [logger]]]]]]]) +.. function:: make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=False, owner=None, group=None, logger=None) Create an archive file (such as zip or tar) and return its name. @@ -601,18 +601,20 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. :mod:`zlib` module is available), "bztar" (if the :mod:`bz2` module is available), or "xztar" (if the :mod:`lzma` module is available). - *root_dir* is a directory that will be the root directory of the - archive, all paths in the archive will be relative to it; for example, - we typically chdir into *root_dir* before creating the archive. + If specified, *root_dir* is a directory that will be the root directory of the + archive, all paths in the archive will be relative to it; it is equivalent + to chdir into *root_dir* before creating the archive. + By default the current directory is used. - *base_dir* is the directory where we start archiving from; + If specified, *base_dir* is the directory where we start archiving from; i.e. *base_dir* will be the common prefix of all files and directories in the archive. *base_dir* must be given relative - to *root_dir*. See :ref:`shutil-archiving-example-with-basedir` for how to + to *root_dir*. + By default all entries in the *root_dir* directory or the current directory + are added without prefix. + See :ref:`shutil-archiving-example-with-basedir` for how to use *base_dir* and *root_dir* together. - *root_dir* and *base_dir* both default to the current directory. - If *dry_run* is true, no archive is created, but the operations that would be executed are logged to *logger*. @@ -642,6 +644,13 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. This function is now made thread-safe during creation of standard ``.zip`` and tar archives. + .. versionchanged:: 3.14 + The ``'.'`` entry no longer added to tar arhives and the ``'./'`` prefix + no longer added to tar entries unless ``base_dir='.'`` is + explicitly specified. + The ``'.'`` entry is now added to zip arhives if ``base_dir='.'`` + is explicitly specified. + .. function:: get_archive_formats() Return a list of supported formats for archiving. diff --git a/Lib/shutil.py b/Lib/shutil.py index c9b4da34b1e19b..e151858538b419 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1030,14 +1030,19 @@ def _set_uid_gid(tarinfo): return tarinfo if not dry_run: - tar = tarfile.open(archive_name, 'w|%s' % tar_compression) - arcname = base_dir - if root_dir is not None: - base_dir = os.path.join(root_dir, base_dir) - try: - tar.add(base_dir, arcname, filter=_set_uid_gid) - finally: - tar.close() + with tarfile.open(archive_name, 'w|%s' % tar_compression) as tar: + if base_dir is not None: + arcname = base_dir + if root_dir is not None: + base_dir = os.path.join(root_dir, base_dir) + tar.add(base_dir, arcname, filter=_set_uid_gid) + elif root_dir is not None: + for name in os.listdir(root_dir): + path = os.path.join(root_dir, name) + tar.add(path, name, filter=_set_uid_gid) + else: + for name in os.listdir(): + tar.add(name, name, filter=_set_uid_gid) if root_dir is not None: archive_name = os.path.abspath(archive_name) @@ -1068,11 +1073,13 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, if not dry_run: with zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) as zf: - arcname = os.path.normpath(base_dir) - if root_dir is not None: - base_dir = os.path.join(root_dir, base_dir) - base_dir = os.path.normpath(base_dir) - if arcname != os.curdir: + if base_dir is None: + base_dir = root_dir if root_dir is not None else os.curdir + else: + arcname = os.path.normpath(base_dir) + if root_dir is not None: + base_dir = os.path.join(root_dir, base_dir) + base_dir = os.path.normpath(base_dir) zf.write(base_dir, arcname) if logger is not None: logger.info("adding '%s'", base_dir) @@ -1190,9 +1197,6 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, for arg, val in format_info[1]: kwargs[arg] = val - if base_dir is None: - base_dir = os.curdir - supports_root_dir = getattr(func, 'supports_root_dir', False) save_cwd = None if root_dir is not None: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 5b0aac67a0adeb..901853ac623000 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1648,9 +1648,9 @@ def test_make_tarfile(self): self.assertTrue(tarfile.is_tarfile(archive)) with tarfile.open(archive, 'r') as tf: self.assertCountEqual(tf.getnames(), - ['.', './dist', './dist/sub', './dist/sub2', - './dist/file1', './dist/file2', './dist/sub/file3', - './outer']) + ['dist', 'dist/sub', 'dist/sub2', + 'dist/file1', 'dist/file2', 'dist/sub/file3', + 'outer']) # Test with base_dir. with os_helper.temp_cwd(), no_chdir: @@ -1690,9 +1690,9 @@ def test_make_tarfile_without_rootdir(self): self.assertTrue(tarfile.is_tarfile(archive)) with tarfile.open(archive, 'r:gz') as tf: self.assertCountEqual(tf.getnames(), - ['.', './dist', './dist/sub', './dist/sub2', - './dist/file1', './dist/file2', './dist/sub/file3', - './outer']) + ['dist', 'dist/sub', 'dist/sub2', + 'dist/file1', 'dist/file2', 'dist/sub/file3', + 'outer']) # Test with base_dir. with os_helper.change_cwd(root_dir), no_chdir: @@ -1850,7 +1850,7 @@ def test_make_zipfile_with_explicit_curdir(self): self.assertTrue(zipfile.is_zipfile(archive)) with zipfile.ZipFile(archive) as zf: self.assertCountEqual(zf.namelist(), - ['dist/', 'dist/sub/', 'dist/sub2/', + ['./', 'dist/', 'dist/sub/', 'dist/sub2/', 'dist/file1', 'dist/file2', 'dist/sub/file3', 'outer']) diff --git a/Misc/NEWS.d/next/Library/2024-04-22-15-24-31.gh-issue-80145.eBM1s0.rst b/Misc/NEWS.d/next/Library/2024-04-22-15-24-31.gh-issue-80145.eBM1s0.rst new file mode 100644 index 00000000000000..4ca2c3f10dcc71 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-22-15-24-31.gh-issue-80145.eBM1s0.rst @@ -0,0 +1,4 @@ +:func:`shutil.make_archive` no longer adds the the ``'.'`` entry to tar +arhives and the ``'./'`` prefix to tar entries unless ``base_dir='.'`` is +explicitly specified. It now adds the ``'.'`` entry to zip arhives if +``base_dir='.'`` is explicitly specified.