summaryrefslogtreecommitdiffstats
path: root/Lib/shutil.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-06-22 08:47:25 (GMT)
committerGitHub <noreply@github.com>2022-06-22 08:47:25 (GMT)
commitfda4b2f06364ae5ef91ecd9c09e2af380c8b0b4c (patch)
treefb0f9d691445d6d875857c65746493714e4142a7 /Lib/shutil.py
parentf805d37641bd982fd7f252e51e2fdae836fce61c (diff)
downloadcpython-fda4b2f06364ae5ef91ecd9c09e2af380c8b0b4c.zip
cpython-fda4b2f06364ae5ef91ecd9c09e2af380c8b0b4c.tar.gz
cpython-fda4b2f06364ae5ef91ecd9c09e2af380c8b0b4c.tar.bz2
gh-74696: Do not change the current working directory in shutil.make_archive() if possible (GH-93160)
It is no longer changed when create a zip or tar archive. It is still changed for custom archivers registered with shutil.register_archive_format() if root_dir is not None. Co-authored-by: Éric <merwok@netwok.org> Co-authored-by: Łukasz Langa <lukasz@langa.pl>
Diffstat (limited to 'Lib/shutil.py')
-rw-r--r--Lib/shutil.py99
1 files changed, 65 insertions, 34 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 2cbd808..f412864 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -897,7 +897,7 @@ def _get_uid(name):
return None
def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
- owner=None, group=None, logger=None):
+ owner=None, group=None, logger=None, root_dir=None):
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
@@ -954,14 +954,20 @@ def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
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, filter=_set_uid_gid)
+ tar.add(base_dir, arcname, filter=_set_uid_gid)
finally:
tar.close()
+ if root_dir is not None:
+ archive_name = os.path.abspath(archive_name)
return archive_name
-def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
+def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0,
+ logger=None, owner=None, group=None, root_dir=None):
"""Create a zip file from all the files under 'base_dir'.
The output zip file will be named 'base_name' + ".zip". Returns the
@@ -985,42 +991,60 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
if not dry_run:
with zipfile.ZipFile(zip_filename, "w",
compression=zipfile.ZIP_DEFLATED) as zf:
- path = os.path.normpath(base_dir)
- if path != os.curdir:
- zf.write(path, path)
+ 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:
+ zf.write(base_dir, arcname)
if logger is not None:
- logger.info("adding '%s'", path)
+ logger.info("adding '%s'", base_dir)
for dirpath, dirnames, filenames in os.walk(base_dir):
+ arcdirpath = dirpath
+ if root_dir is not None:
+ arcdirpath = os.path.relpath(arcdirpath, root_dir)
+ arcdirpath = os.path.normpath(arcdirpath)
for name in sorted(dirnames):
- path = os.path.normpath(os.path.join(dirpath, name))
- zf.write(path, path)
+ path = os.path.join(dirpath, name)
+ arcname = os.path.join(arcdirpath, name)
+ zf.write(path, arcname)
if logger is not None:
logger.info("adding '%s'", path)
for name in filenames:
- path = os.path.normpath(os.path.join(dirpath, name))
+ path = os.path.join(dirpath, name)
+ path = os.path.normpath(path)
if os.path.isfile(path):
- zf.write(path, path)
+ arcname = os.path.join(arcdirpath, name)
+ zf.write(path, arcname)
if logger is not None:
logger.info("adding '%s'", path)
+ if root_dir is not None:
+ zip_filename = os.path.abspath(zip_filename)
return zip_filename
+# Maps the name of the archive format to a tuple containing:
+# * the archiving function
+# * extra keyword arguments
+# * description
+# * does it support the root_dir argument?
_ARCHIVE_FORMATS = {
- 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"),
+ 'tar': (_make_tarball, [('compress', None)],
+ "uncompressed tar file", True),
}
if _ZLIB_SUPPORTED:
_ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')],
- "gzip'ed tar-file")
- _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file")
+ "gzip'ed tar-file", True)
+ _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file", True)
if _BZ2_SUPPORTED:
_ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')],
- "bzip2'ed tar-file")
+ "bzip2'ed tar-file", True)
if _LZMA_SUPPORTED:
_ARCHIVE_FORMATS['xztar'] = (_make_tarball, [('compress', 'xz')],
- "xz'ed tar-file")
+ "xz'ed tar-file", True)
def get_archive_formats():
"""Returns a list of supported formats for archiving and unarchiving.
@@ -1051,7 +1075,7 @@ def register_archive_format(name, function, extra_args=None, description=''):
if not isinstance(element, (tuple, list)) or len(element) !=2:
raise TypeError('extra_args elements are : (arg_name, value)')
- _ARCHIVE_FORMATS[name] = (function, extra_args, description)
+ _ARCHIVE_FORMATS[name] = (function, extra_args, description, False)
def unregister_archive_format(name):
del _ARCHIVE_FORMATS[name]
@@ -1075,36 +1099,38 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
uses the current owner and group.
"""
sys.audit("shutil.make_archive", base_name, format, root_dir, base_dir)
- save_cwd = os.getcwd()
- if root_dir is not None:
- if logger is not None:
- logger.debug("changing into '%s'", root_dir)
- base_name = os.path.abspath(base_name)
- if not dry_run:
- os.chdir(root_dir)
-
- if base_dir is None:
- base_dir = os.curdir
-
- kwargs = {'dry_run': dry_run, 'logger': logger}
-
try:
format_info = _ARCHIVE_FORMATS[format]
except KeyError:
raise ValueError("unknown archive format '%s'" % format) from None
+ kwargs = {'dry_run': dry_run, 'logger': logger,
+ 'owner': owner, 'group': group}
+
func = format_info[0]
for arg, val in format_info[1]:
kwargs[arg] = val
- if format != 'zip':
- kwargs['owner'] = owner
- kwargs['group'] = group
+ if base_dir is None:
+ base_dir = os.curdir
+
+ support_root_dir = format_info[3]
+ save_cwd = None
+ if root_dir is not None:
+ if support_root_dir:
+ kwargs['root_dir'] = root_dir
+ else:
+ save_cwd = os.getcwd()
+ if logger is not None:
+ logger.debug("changing into '%s'", root_dir)
+ base_name = os.path.abspath(base_name)
+ if not dry_run:
+ os.chdir(root_dir)
try:
filename = func(base_name, base_dir, **kwargs)
finally:
- if root_dir is not None:
+ if save_cwd is not None:
if logger is not None:
logger.debug("changing back to '%s'", save_cwd)
os.chdir(save_cwd)
@@ -1217,6 +1243,11 @@ def _unpack_tarfile(filename, extract_dir):
finally:
tarobj.close()
+# Maps the name of the unpack format to a tuple containing:
+# * extensions
+# * the unpacking function
+# * extra keyword arguments
+# * description
_UNPACK_FORMATS = {
'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"),