Skip to content

Commit 12828e5

Browse files
authored
gh-85809: Ensure shutil.make_archive accepts path-like objects in all cases (GH-143668)
1 parent 9b22261 commit 12828e5

File tree

4 files changed

+45
-10
lines changed

4 files changed

+45
-10
lines changed

Doc/library/shutil.rst

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -618,22 +618,23 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
618618

619619
Create an archive file (such as zip or tar) and return its name.
620620

621-
*base_name* is the name of the file to create, including the path, minus
622-
any format-specific extension.
621+
*base_name* is a string or :term:`path-like object` specifying the name of
622+
the file to create, including the path, minus any format-specific extension.
623623

624624
*format* is the archive format: one of
625625
"zip" (if the :mod:`zlib` module is available), "tar", "gztar" (if the
626626
:mod:`zlib` module is available), "bztar" (if the :mod:`bz2` module is
627627
available), "xztar" (if the :mod:`lzma` module is available), or "zstdtar"
628628
(if the :mod:`compression.zstd` module is available).
629629

630-
*root_dir* is a directory that will be the root directory of the
631-
archive, all paths in the archive will be relative to it; for example,
632-
we typically chdir into *root_dir* before creating the archive.
630+
*root_dir* is a string or :term:`path-like object` specifying a directory
631+
that will be the root directory of the archive, all paths in the archive
632+
will be relative to it; for example, we typically chdir into *root_dir*
633+
before creating the archive.
633634

634-
*base_dir* is the directory where we start archiving from;
635-
i.e. *base_dir* will be the common prefix of all files and
636-
directories in the archive. *base_dir* must be given relative
635+
*base_dir* is a string or :term:`path-like object` specifying a directory
636+
where we start archiving from; i.e. *base_dir* will be the common prefix of
637+
all files and directories in the archive. *base_dir* must be given relative
637638
to *root_dir*. See :ref:`shutil-archiving-example-with-basedir` for how to
638639
use *base_dir* and *root_dir* together.
639640

@@ -668,6 +669,10 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
668669
This function is now made thread-safe during creation of standard
669670
``.zip`` and tar archives.
670671

672+
.. versionchanged:: next
673+
Accepts a :term:`path-like object` for *base_name*, *root_dir* and
674+
*base_dir*.
675+
671676
.. function:: get_archive_formats()
672677

673678
Return a list of supported formats for archiving.

Lib/shutil.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,19 +1212,22 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
12121212
for arg, val in format_info[1]:
12131213
kwargs[arg] = val
12141214

1215+
base_name = os.fspath(base_name)
1216+
12151217
if base_dir is None:
12161218
base_dir = os.curdir
1219+
else:
1220+
base_dir = os.fspath(base_dir)
12171221

12181222
supports_root_dir = getattr(func, 'supports_root_dir', False)
12191223
save_cwd = None
12201224
if root_dir is not None:
1225+
root_dir = os.fspath(root_dir)
12211226
stmd = os.stat(root_dir).st_mode
12221227
if not stat.S_ISDIR(stmd):
12231228
raise NotADirectoryError(errno.ENOTDIR, 'Not a directory', root_dir)
12241229

12251230
if supports_root_dir:
1226-
# Support path-like base_name here for backwards-compatibility.
1227-
base_name = os.fspath(base_name)
12281231
kwargs['root_dir'] = root_dir
12291232
else:
12301233
save_cwd = os.getcwd()

Lib/test/test_shutil.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,32 @@ def test_make_zipfile_in_curdir(self):
20422042
self.assertEqual(make_archive('test', 'zip'), 'test.zip')
20432043
self.assertTrue(os.path.isfile('test.zip'))
20442044

2045+
def test_make_archive_pathlike_cwd_default(self):
2046+
called_args = []
2047+
def archiver(base_name, base_dir, **kw):
2048+
called_args.append((base_name, kw.get('root_dir')))
2049+
2050+
register_archive_format('xxx', archiver, [], 'xxx file')
2051+
self.addCleanup(unregister_archive_format, 'xxx')
2052+
with no_chdir:
2053+
make_archive(FakePath('basename'), 'xxx')
2054+
self.assertEqual(called_args, [('basename', None)])
2055+
2056+
def test_make_archive_pathlike_cwd_supports_root_dir(self):
2057+
root_dir = self.mkdtemp()
2058+
called_args = []
2059+
def archiver(base_name, base_dir, **kw):
2060+
called_args.append((base_name, base_dir, kw.get('root_dir')))
2061+
archiver.supports_root_dir = True
2062+
2063+
register_archive_format('xxx', archiver, [], 'xxx file')
2064+
self.addCleanup(unregister_archive_format, 'xxx')
2065+
with no_chdir:
2066+
make_archive(FakePath('basename'), 'xxx',
2067+
root_dir=FakePath(root_dir),
2068+
base_dir=FakePath('basedir'))
2069+
self.assertEqual(called_args, [('basename', 'basedir', root_dir)])
2070+
20452071
def test_register_archive_format(self):
20462072

20472073
self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added :term:`path-like object` support for :func:`shutil.make_archive`.

0 commit comments

Comments
 (0)