mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
gh-74696: Do not change the current working directory in shutil.make_archive() if possible (GH-93160) (GH-94105)
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>
(cherry picked from commit fda4b2f063
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
3a119d277a
commit
c1bfff4f6b
4 changed files with 106 additions and 52 deletions
|
@ -896,7 +896,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'.
|
||||
|
||||
|
@ -953,14 +953,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
|
||||
|
@ -984,42 +990,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.
|
||||
|
@ -1050,7 +1074,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]
|
||||
|
@ -1074,36 +1098,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)
|
||||
|
@ -1216,6 +1242,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"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue