Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/archive_util.py
4799 views
"""distutils.archive_util12Utility functions for creating archive files (tarballs, zip files,3that sort of thing)."""45import os6from warnings import warn7import sys89try:10import zipfile11except ImportError:12zipfile = None131415from distutils.errors import DistutilsExecError16from distutils.spawn import spawn17from distutils.dir_util import mkpath18from distutils import log1920try:21from pwd import getpwnam22except ImportError:23getpwnam = None2425try:26from grp import getgrnam27except ImportError:28getgrnam = None2930def _get_gid(name):31"""Returns a gid, given a group name."""32if getgrnam is None or name is None:33return None34try:35result = getgrnam(name)36except KeyError:37result = None38if result is not None:39return result[2]40return None4142def _get_uid(name):43"""Returns an uid, given a user name."""44if getpwnam is None or name is None:45return None46try:47result = getpwnam(name)48except KeyError:49result = None50if result is not None:51return result[2]52return None5354def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,55owner=None, group=None):56"""Create a (possibly compressed) tar file from all the files under57'base_dir'.5859'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or60None. ("compress" will be deprecated in Python 3.2)6162'owner' and 'group' can be used to define an owner and a group for the63archive that is being built. If not provided, the current owner and group64will be used.6566The output tar file will be named 'base_dir' + ".tar", possibly plus67the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").6869Returns the output filename.70"""71tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '',72'compress': ''}73compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz',74'compress': '.Z'}7576# flags for compression program, each element of list will be an argument77if compress is not None and compress not in compress_ext.keys():78raise ValueError(79"bad value for 'compress': must be None, 'gzip', 'bzip2', "80"'xz' or 'compress'")8182archive_name = base_name + '.tar'83if compress != 'compress':84archive_name += compress_ext.get(compress, '')8586mkpath(os.path.dirname(archive_name), dry_run=dry_run)8788# creating the tarball89import tarfile # late import so Python build itself doesn't break9091log.info('Creating tar archive')9293uid = _get_uid(owner)94gid = _get_gid(group)9596def _set_uid_gid(tarinfo):97if gid is not None:98tarinfo.gid = gid99tarinfo.gname = group100if uid is not None:101tarinfo.uid = uid102tarinfo.uname = owner103return tarinfo104105if not dry_run:106tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])107try:108tar.add(base_dir, filter=_set_uid_gid)109finally:110tar.close()111112# compression using `compress`113if compress == 'compress':114warn("'compress' will be deprecated.", PendingDeprecationWarning)115# the option varies depending on the platform116compressed_name = archive_name + compress_ext[compress]117if sys.platform == 'win32':118cmd = [compress, archive_name, compressed_name]119else:120cmd = [compress, '-f', archive_name]121spawn(cmd, dry_run=dry_run)122return compressed_name123124return archive_name125126def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):127"""Create a zip file from all the files under 'base_dir'.128129The output zip file will be named 'base_name' + ".zip". Uses either the130"zipfile" Python module (if available) or the InfoZIP "zip" utility131(if installed and found on the default search path). If neither tool is132available, raises DistutilsExecError. Returns the name of the output zip133file.134"""135zip_filename = base_name + ".zip"136mkpath(os.path.dirname(zip_filename), dry_run=dry_run)137138# If zipfile module is not available, try spawning an external139# 'zip' command.140if zipfile is None:141if verbose:142zipoptions = "-r"143else:144zipoptions = "-rq"145146try:147spawn(["zip", zipoptions, zip_filename, base_dir],148dry_run=dry_run)149except DistutilsExecError:150# XXX really should distinguish between "couldn't find151# external 'zip' command" and "zip failed".152raise DistutilsExecError(("unable to create zip file '%s': "153"could neither import the 'zipfile' module nor "154"find a standalone zip utility") % zip_filename)155156else:157log.info("creating '%s' and adding '%s' to it",158zip_filename, base_dir)159160if not dry_run:161try:162zip = zipfile.ZipFile(zip_filename, "w",163compression=zipfile.ZIP_DEFLATED)164except RuntimeError:165zip = zipfile.ZipFile(zip_filename, "w",166compression=zipfile.ZIP_STORED)167168with zip:169if base_dir != os.curdir:170path = os.path.normpath(os.path.join(base_dir, ''))171zip.write(path, path)172log.info("adding '%s'", path)173for dirpath, dirnames, filenames in os.walk(base_dir):174for name in dirnames:175path = os.path.normpath(os.path.join(dirpath, name, ''))176zip.write(path, path)177log.info("adding '%s'", path)178for name in filenames:179path = os.path.normpath(os.path.join(dirpath, name))180if os.path.isfile(path):181zip.write(path, path)182log.info("adding '%s'", path)183184return zip_filename185186ARCHIVE_FORMATS = {187'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),188'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),189'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),190'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),191'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),192'zip': (make_zipfile, [],"ZIP file")193}194195def check_archive_formats(formats):196"""Returns the first format from the 'format' list that is unknown.197198If all formats are known, returns None199"""200for format in formats:201if format not in ARCHIVE_FORMATS:202return format203return None204205def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,206dry_run=0, owner=None, group=None):207"""Create an archive file (eg. zip or tar).208209'base_name' is the name of the file to create, minus any format-specific210extension; 'format' is the archive format: one of "zip", "tar", "gztar",211"bztar", "xztar", or "ztar".212213'root_dir' is a directory that will be the root directory of the214archive; ie. we typically chdir into 'root_dir' before creating the215archive. 'base_dir' is the directory where we start archiving from;216ie. 'base_dir' will be the common prefix of all files and217directories in the archive. 'root_dir' and 'base_dir' both default218to the current directory. Returns the name of the archive file.219220'owner' and 'group' are used when creating a tar archive. By default,221uses the current owner and group.222"""223save_cwd = os.getcwd()224if root_dir is not None:225log.debug("changing into '%s'", root_dir)226base_name = os.path.abspath(base_name)227if not dry_run:228os.chdir(root_dir)229230if base_dir is None:231base_dir = os.curdir232233kwargs = {'dry_run': dry_run}234235try:236format_info = ARCHIVE_FORMATS[format]237except KeyError:238raise ValueError("unknown archive format '%s'" % format)239240func = format_info[0]241for arg, val in format_info[1]:242kwargs[arg] = val243244if format != 'zip':245kwargs['owner'] = owner246kwargs['group'] = group247248try:249filename = func(base_name, base_dir, **kwargs)250finally:251if root_dir is not None:252log.debug("changing back to '%s'", save_cwd)253os.chdir(save_cwd)254255return filename256257258