diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 3a4631e7c657fe..bf44ef2756d708 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -619,8 +619,8 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Create an archive file (such as zip or tar) and return its name. - *base_name* is the name of the file to create, including the path, minus - any format-specific extension. + *base_name* is the :term:`path-like object` specifying the name of the file + to create, including the path, minus any format-specific extension. *format* is the archive format: one of "zip" (if the :mod:`zlib` module is available), "tar", "gztar" (if the @@ -628,13 +628,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. available), "xztar" (if the :mod:`lzma` module is available), or "zstdtar" (if the :mod:`compression.zstd` 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. + *root_dir* is the :term:`path-like object` specifying 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. - *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 + *base_dir* is the :term:`path-like object` specifying a 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 use *base_dir* and *root_dir* together. @@ -669,6 +670,9 @@ 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.15 + Accepts a :term:`path-like object` for *base_name*. + .. function:: get_archive_formats() Return a list of supported formats for archiving. @@ -814,10 +818,10 @@ Archiving example In this example, we create a gzip'ed tar-file archive containing all files found in the :file:`.ssh` directory of the user:: + >>> from pathlib import Path >>> from shutil import make_archive - >>> import os - >>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive')) - >>> root_dir = os.path.expanduser(os.path.join('~', '.ssh')) + >>> archive_name = Path.home() / 'myarchive' + >>> root_dir = Path.home() / '.ssh' >>> make_archive(archive_name, 'gztar', root_dir) '/Users/tarek/myarchive.tar.gz' @@ -858,9 +862,9 @@ we show how to use :func:`make_archive`, but this time with the usage of In the final archive, :file:`please_add.txt` should be included, but :file:`do_not_add.txt` should not. Therefore we use the following:: + >>> from pathlib import Path >>> from shutil import make_archive - >>> import os - >>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive')) + >>> archive_name = Path.home() / 'myarchive' >>> make_archive( ... archive_name, ... 'tar', diff --git a/Lib/shutil.py b/Lib/shutil.py index 8d8fe145567822..8279ea4bdf0a94 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1212,6 +1212,8 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, for arg, val in format_info[1]: kwargs[arg] = val + base_name = os.fspath(base_name) + if base_dir is None: base_dir = os.curdir @@ -1223,8 +1225,6 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, raise NotADirectoryError(errno.ENOTDIR, 'Not a directory', root_dir) if supports_root_dir: - # Support path-like base_name here for backwards-compatibility. - base_name = os.fspath(base_name) kwargs['root_dir'] = root_dir else: save_cwd = os.getcwd() diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index ebb6cf88336249..0dd912b16d4263 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2042,6 +2042,24 @@ def test_make_zipfile_in_curdir(self): self.assertEqual(make_archive('test', 'zip'), 'test.zip') self.assertTrue(os.path.isfile('test.zip')) + @support.requires_zlib() + def test_make_archive_accepts_pathlike(self): + tmpdir = self.mkdtemp() + with os_helper.change_cwd(tmpdir), no_chdir: + # Test path-like base_name without root_dir and base_dir + base_name = FakePath(os.path.join(tmpdir, 'archive')) + res = make_archive(base_name, 'zip') + self.assertEqual(res, os.path.join(tmpdir, 'archive.zip')) + self.assertTrue(os.path.isfile(res)) + + # Test with path-like base_name, root_dir, and base_dir + root_dir, base_dir = self._create_files() + base_name = FakePath(os.path.join(tmpdir, 'archive2')) + res = make_archive( + base_name, 'zip', FakePath(root_dir), FakePath(base_dir)) + self.assertEqual(res, os.path.join(tmpdir, 'archive2.zip')) + self.assertTrue(os.path.isfile(res)) + def test_register_archive_format(self): self.assertRaises(TypeError, register_archive_format, 'xxx', 1) diff --git a/Misc/NEWS.d/next/Library/2026-01-10-22-58-30.gh-issue-85809.0eW4wt.rst b/Misc/NEWS.d/next/Library/2026-01-10-22-58-30.gh-issue-85809.0eW4wt.rst new file mode 100644 index 00000000000000..69b3ca86887710 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-10-22-58-30.gh-issue-85809.0eW4wt.rst @@ -0,0 +1,2 @@ +Added ``pathlib.Path`` support for :func:`shutil.make_archive` with updated +documentation and lightweight tests.