Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions Doc/library/shutil.rst
Original file line number Diff line number Diff line change
Expand Up @@ -619,22 +619,23 @@ 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 a string or :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
:mod:`zlib` module is available), "bztar" (if the :mod:`bz2` module is
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 a string or :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 a string or :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.

Expand Down Expand Up @@ -669,6 +670,10 @@ 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:: next
Accepts a :term:`path-like object` for *base_name*, *root_dir* and
*base_dir*.

.. function:: get_archive_formats()

Return a list of supported formats for archiving.
Expand Down
5 changes: 3 additions & 2 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,19 +1212,20 @@ 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

supports_root_dir = getattr(func, 'supports_root_dir', False)
save_cwd = None
if root_dir is not None:
root_dir = os.fspath(root_dir)
stmd = os.stat(root_dir).st_mode
if not stat.S_ISDIR(stmd):
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()
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added :term:`path-like object` support for :func:`shutil.make_archive`.
Loading