Path: blob/main/test/lib/python3.9/site-packages/setuptools/wheel.py
4798 views
"""Wheels support."""12from distutils.util import get_platform3from distutils import log4import email5import itertools6import os7import posixpath8import re9import zipfile1011import pkg_resources12import setuptools13from pkg_resources import parse_version14from setuptools.extern.packaging.tags import sys_tags15from setuptools.extern.packaging.utils import canonicalize_name16from setuptools.command.egg_info import write_requirements17from setuptools.archive_util import _unpack_zipfile_obj181920WHEEL_NAME = re.compile(21r"""^(?P<project_name>.+?)-(?P<version>\d.*?)22((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)23)\.whl$""",24re.VERBOSE).match2526NAMESPACE_PACKAGE_INIT = \27"__import__('pkg_resources').declare_namespace(__name__)\n"282930def unpack(src_dir, dst_dir):31'''Move everything under `src_dir` to `dst_dir`, and delete the former.'''32for dirpath, dirnames, filenames in os.walk(src_dir):33subdir = os.path.relpath(dirpath, src_dir)34for f in filenames:35src = os.path.join(dirpath, f)36dst = os.path.join(dst_dir, subdir, f)37os.renames(src, dst)38for n, d in reversed(list(enumerate(dirnames))):39src = os.path.join(dirpath, d)40dst = os.path.join(dst_dir, subdir, d)41if not os.path.exists(dst):42# Directory does not exist in destination,43# rename it and prune it from os.walk list.44os.renames(src, dst)45del dirnames[n]46# Cleanup.47for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True):48assert not filenames49os.rmdir(dirpath)505152class Wheel:5354def __init__(self, filename):55match = WHEEL_NAME(os.path.basename(filename))56if match is None:57raise ValueError('invalid wheel name: %r' % filename)58self.filename = filename59for k, v in match.groupdict().items():60setattr(self, k, v)6162def tags(self):63'''List tags (py_version, abi, platform) supported by this wheel.'''64return itertools.product(65self.py_version.split('.'),66self.abi.split('.'),67self.platform.split('.'),68)6970def is_compatible(self):71'''Is the wheel is compatible with the current platform?'''72supported_tags = set(73(t.interpreter, t.abi, t.platform) for t in sys_tags())74return next((True for t in self.tags() if t in supported_tags), False)7576def egg_name(self):77return pkg_resources.Distribution(78project_name=self.project_name, version=self.version,79platform=(None if self.platform == 'any' else get_platform()),80).egg_name() + '.egg'8182def get_dist_info(self, zf):83# find the correct name of the .dist-info dir in the wheel file84for member in zf.namelist():85dirname = posixpath.dirname(member)86if (dirname.endswith('.dist-info') and87canonicalize_name(dirname).startswith(88canonicalize_name(self.project_name))):89return dirname90raise ValueError("unsupported wheel format. .dist-info not found")9192def install_as_egg(self, destination_eggdir):93'''Install wheel as an egg directory.'''94with zipfile.ZipFile(self.filename) as zf:95self._install_as_egg(destination_eggdir, zf)9697def _install_as_egg(self, destination_eggdir, zf):98dist_basename = '%s-%s' % (self.project_name, self.version)99dist_info = self.get_dist_info(zf)100dist_data = '%s.data' % dist_basename101egg_info = os.path.join(destination_eggdir, 'EGG-INFO')102103self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)104self._move_data_entries(destination_eggdir, dist_data)105self._fix_namespace_packages(egg_info, destination_eggdir)106107@staticmethod108def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):109def get_metadata(name):110with zf.open(posixpath.join(dist_info, name)) as fp:111value = fp.read().decode('utf-8')112return email.parser.Parser().parsestr(value)113114wheel_metadata = get_metadata('WHEEL')115# Check wheel format version is supported.116wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))117wheel_v1 = (118parse_version('1.0') <= wheel_version < parse_version('2.0dev0')119)120if not wheel_v1:121raise ValueError(122'unsupported wheel format version: %s' % wheel_version)123# Extract to target directory.124_unpack_zipfile_obj(zf, destination_eggdir)125# Convert metadata.126dist_info = os.path.join(destination_eggdir, dist_info)127dist = pkg_resources.Distribution.from_location(128destination_eggdir, dist_info,129metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),130)131132# Note: Evaluate and strip markers now,133# as it's difficult to convert back from the syntax:134# foobar; "linux" in sys_platform and extra == 'test'135def raw_req(req):136req.marker = None137return str(req)138install_requires = list(map(raw_req, dist.requires()))139extras_require = {140extra: [141req142for req in map(raw_req, dist.requires((extra,)))143if req not in install_requires144]145for extra in dist.extras146}147os.rename(dist_info, egg_info)148os.rename(149os.path.join(egg_info, 'METADATA'),150os.path.join(egg_info, 'PKG-INFO'),151)152setup_dist = setuptools.Distribution(153attrs=dict(154install_requires=install_requires,155extras_require=extras_require,156),157)158# Temporarily disable info traces.159log_threshold = log._global_log.threshold160log.set_threshold(log.WARN)161try:162write_requirements(163setup_dist.get_command_obj('egg_info'),164None,165os.path.join(egg_info, 'requires.txt'),166)167finally:168log.set_threshold(log_threshold)169170@staticmethod171def _move_data_entries(destination_eggdir, dist_data):172"""Move data entries to their correct location."""173dist_data = os.path.join(destination_eggdir, dist_data)174dist_data_scripts = os.path.join(dist_data, 'scripts')175if os.path.exists(dist_data_scripts):176egg_info_scripts = os.path.join(177destination_eggdir, 'EGG-INFO', 'scripts')178os.mkdir(egg_info_scripts)179for entry in os.listdir(dist_data_scripts):180# Remove bytecode, as it's not properly handled181# during easy_install scripts install phase.182if entry.endswith('.pyc'):183os.unlink(os.path.join(dist_data_scripts, entry))184else:185os.rename(186os.path.join(dist_data_scripts, entry),187os.path.join(egg_info_scripts, entry),188)189os.rmdir(dist_data_scripts)190for subdir in filter(os.path.exists, (191os.path.join(dist_data, d)192for d in ('data', 'headers', 'purelib', 'platlib')193)):194unpack(subdir, destination_eggdir)195if os.path.exists(dist_data):196os.rmdir(dist_data)197198@staticmethod199def _fix_namespace_packages(egg_info, destination_eggdir):200namespace_packages = os.path.join(201egg_info, 'namespace_packages.txt')202if os.path.exists(namespace_packages):203with open(namespace_packages) as fp:204namespace_packages = fp.read().split()205for mod in namespace_packages:206mod_dir = os.path.join(destination_eggdir, *mod.split('.'))207mod_init = os.path.join(mod_dir, '__init__.py')208if not os.path.exists(mod_dir):209os.mkdir(mod_dir)210if not os.path.exists(mod_init):211with open(mod_init, 'w') as fp:212fp.write(NAMESPACE_PACKAGE_INIT)213214215