Path: blob/main/test/lib/python3.9/site-packages/setuptools/archive_util.py
4798 views
"""Utilities for extracting common archive formats"""12import zipfile3import tarfile4import os5import shutil6import posixpath7import contextlib8from distutils.errors import DistutilsError910from ._path import ensure_directory1112__all__ = [13"unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",14"UnrecognizedFormat", "extraction_drivers", "unpack_directory",15]161718class UnrecognizedFormat(DistutilsError):19"""Couldn't recognize the archive type"""202122def default_filter(src, dst):23"""The default progress/filter callback; returns True for all files"""24return dst252627def unpack_archive(28filename, extract_dir, progress_filter=default_filter,29drivers=None):30"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``3132`progress_filter` is a function taking two arguments: a source path33internal to the archive ('/'-separated), and a filesystem path where it34will be extracted. The callback must return the desired extract path35(which may be the same as the one passed in), or else ``None`` to skip36that file or directory. The callback can thus be used to report on the37progress of the extraction, as well as to filter the items extracted or38alter their extraction paths.3940`drivers`, if supplied, must be a non-empty sequence of functions with the41same signature as this function (minus the `drivers` argument), that raise42``UnrecognizedFormat`` if they do not support extracting the designated43archive type. The `drivers` are tried in sequence until one is found that44does not raise an error, or until all are exhausted (in which case45``UnrecognizedFormat`` is raised). If you do not supply a sequence of46drivers, the module's ``extraction_drivers`` constant will be used, which47means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that48order.49"""50for driver in drivers or extraction_drivers:51try:52driver(filename, extract_dir, progress_filter)53except UnrecognizedFormat:54continue55else:56return57else:58raise UnrecognizedFormat(59"Not a recognized archive type: %s" % filename60)616263def unpack_directory(filename, extract_dir, progress_filter=default_filter):64""""Unpack" a directory, using the same interface as for archives6566Raises ``UnrecognizedFormat`` if `filename` is not a directory67"""68if not os.path.isdir(filename):69raise UnrecognizedFormat("%s is not a directory" % filename)7071paths = {72filename: ('', extract_dir),73}74for base, dirs, files in os.walk(filename):75src, dst = paths[base]76for d in dirs:77paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d)78for f in files:79target = os.path.join(dst, f)80target = progress_filter(src + f, target)81if not target:82# skip non-files83continue84ensure_directory(target)85f = os.path.join(base, f)86shutil.copyfile(f, target)87shutil.copystat(f, target)888990def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):91"""Unpack zip `filename` to `extract_dir`9293Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined94by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation95of the `progress_filter` argument.96"""9798if not zipfile.is_zipfile(filename):99raise UnrecognizedFormat("%s is not a zip file" % (filename,))100101with zipfile.ZipFile(filename) as z:102_unpack_zipfile_obj(z, extract_dir, progress_filter)103104105def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):106"""Internal/private API used by other parts of setuptools.107Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`108object instead of a filename.109"""110for info in zipfile_obj.infolist():111name = info.filename112113# don't extract absolute paths or ones with .. in them114if name.startswith('/') or '..' in name.split('/'):115continue116117target = os.path.join(extract_dir, *name.split('/'))118target = progress_filter(name, target)119if not target:120continue121if name.endswith('/'):122# directory123ensure_directory(target)124else:125# file126ensure_directory(target)127data = zipfile_obj.read(info.filename)128with open(target, 'wb') as f:129f.write(data)130unix_attributes = info.external_attr >> 16131if unix_attributes:132os.chmod(target, unix_attributes)133134135def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):136"""Resolve any links and extract link targets as normal files."""137while tar_member_obj is not None and (138tar_member_obj.islnk() or tar_member_obj.issym()):139linkpath = tar_member_obj.linkname140if tar_member_obj.issym():141base = posixpath.dirname(tar_member_obj.name)142linkpath = posixpath.join(base, linkpath)143linkpath = posixpath.normpath(linkpath)144tar_member_obj = tar_obj._getmember(linkpath)145146is_file_or_dir = (147tar_member_obj is not None and148(tar_member_obj.isfile() or tar_member_obj.isdir())149)150if is_file_or_dir:151return tar_member_obj152153raise LookupError('Got unknown file type')154155156def _iter_open_tar(tar_obj, extract_dir, progress_filter):157"""Emit member-destination pairs from a tar archive."""158# don't do any chowning!159tar_obj.chown = lambda *args: None160161with contextlib.closing(tar_obj):162for member in tar_obj:163name = member.name164# don't extract absolute paths or ones with .. in them165if name.startswith('/') or '..' in name.split('/'):166continue167168prelim_dst = os.path.join(extract_dir, *name.split('/'))169170try:171member = _resolve_tar_file_or_dir(tar_obj, member)172except LookupError:173continue174175final_dst = progress_filter(name, prelim_dst)176if not final_dst:177continue178179if final_dst.endswith(os.sep):180final_dst = final_dst[:-1]181182yield member, final_dst183184185def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):186"""Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`187188Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined189by ``tarfile.open()``). See ``unpack_archive()`` for an explanation190of the `progress_filter` argument.191"""192try:193tarobj = tarfile.open(filename)194except tarfile.TarError as e:195raise UnrecognizedFormat(196"%s is not a compressed or uncompressed tar file" % (filename,)197) from e198199for member, final_dst in _iter_open_tar(200tarobj, extract_dir, progress_filter,201):202try:203# XXX Ugh204tarobj._extract_member(member, final_dst)205except tarfile.ExtractError:206# chown/chmod/mkfifo/mknode/makedev failed207pass208209return True210211212extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile213214215