mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									f805d37641
								
							
						
					
					
						commit
						fda4b2f063
					
				
					 4 changed files with 106 additions and 52 deletions
				
			
		|  | @ -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 _set_uid_gid(tarinfo): | |||
| 
 | ||||
|     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"), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka