python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
wheel.py
       1  """Wheels support."""
       2  
       3  import email
       4  import itertools
       5  import os
       6  import posixpath
       7  import re
       8  import zipfile
       9  import contextlib
      10  
      11  from distutils.util import get_platform
      12  
      13  import pkg_resources
      14  import setuptools
      15  from pkg_resources import parse_version
      16  from setuptools.extern.packaging.tags import sys_tags
      17  from setuptools.extern.packaging.utils import canonicalize_name
      18  from setuptools.command.egg_info import write_requirements
      19  from setuptools.archive_util import _unpack_zipfile_obj
      20  
      21  
      22  WHEEL_NAME = re.compile(
      23      r"""^(?P<project_name>.+?)-(?P<version>\d.*?)
      24      ((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)
      25      )\.whl$""",
      26      re.VERBOSE).match
      27  
      28  NAMESPACE_PACKAGE_INIT = \
      29      "__import__('pkg_resources').declare_namespace(__name__)\n"
      30  
      31  
      32  def unpack(src_dir, dst_dir):
      33      '''Move everything under `src_dir` to `dst_dir`, and delete the former.'''
      34      for dirpath, dirnames, filenames in os.walk(src_dir):
      35          subdir = os.path.relpath(dirpath, src_dir)
      36          for f in filenames:
      37              src = os.path.join(dirpath, f)
      38              dst = os.path.join(dst_dir, subdir, f)
      39              os.renames(src, dst)
      40          for n, d in reversed(list(enumerate(dirnames))):
      41              src = os.path.join(dirpath, d)
      42              dst = os.path.join(dst_dir, subdir, d)
      43              if not os.path.exists(dst):
      44                  # Directory does not exist in destination,
      45                  # rename it and prune it from os.walk list.
      46                  os.renames(src, dst)
      47                  del dirnames[n]
      48      # Cleanup.
      49      for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True):
      50          assert not filenames
      51          os.rmdir(dirpath)
      52  
      53  
      54  @contextlib.contextmanager
      55  def disable_info_traces():
      56      """
      57      Temporarily disable info traces.
      58      """
      59      from distutils import log
      60      saved = log.set_threshold(log.WARN)
      61      try:
      62          yield
      63      finally:
      64          log.set_threshold(saved)
      65  
      66  
      67  class ESC[4;38;5;81mWheel:
      68  
      69      def __init__(self, filename):
      70          match = WHEEL_NAME(os.path.basename(filename))
      71          if match is None:
      72              raise ValueError('invalid wheel name: %r' % filename)
      73          self.filename = filename
      74          for k, v in match.groupdict().items():
      75              setattr(self, k, v)
      76  
      77      def tags(self):
      78          '''List tags (py_version, abi, platform) supported by this wheel.'''
      79          return itertools.product(
      80              self.py_version.split('.'),
      81              self.abi.split('.'),
      82              self.platform.split('.'),
      83          )
      84  
      85      def is_compatible(self):
      86          '''Is the wheel is compatible with the current platform?'''
      87          supported_tags = set(
      88              (t.interpreter, t.abi, t.platform) for t in sys_tags())
      89          return next((True for t in self.tags() if t in supported_tags), False)
      90  
      91      def egg_name(self):
      92          return pkg_resources.Distribution(
      93              project_name=self.project_name, version=self.version,
      94              platform=(None if self.platform == 'any' else get_platform()),
      95          ).egg_name() + '.egg'
      96  
      97      def get_dist_info(self, zf):
      98          # find the correct name of the .dist-info dir in the wheel file
      99          for member in zf.namelist():
     100              dirname = posixpath.dirname(member)
     101              if (dirname.endswith('.dist-info') and
     102                      canonicalize_name(dirname).startswith(
     103                          canonicalize_name(self.project_name))):
     104                  return dirname
     105          raise ValueError("unsupported wheel format. .dist-info not found")
     106  
     107      def install_as_egg(self, destination_eggdir):
     108          '''Install wheel as an egg directory.'''
     109          with zipfile.ZipFile(self.filename) as zf:
     110              self._install_as_egg(destination_eggdir, zf)
     111  
     112      def _install_as_egg(self, destination_eggdir, zf):
     113          dist_basename = '%s-%s' % (self.project_name, self.version)
     114          dist_info = self.get_dist_info(zf)
     115          dist_data = '%s.data' % dist_basename
     116          egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
     117  
     118          self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
     119          self._move_data_entries(destination_eggdir, dist_data)
     120          self._fix_namespace_packages(egg_info, destination_eggdir)
     121  
     122      @staticmethod
     123      def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
     124          def get_metadata(name):
     125              with zf.open(posixpath.join(dist_info, name)) as fp:
     126                  value = fp.read().decode('utf-8')
     127                  return email.parser.Parser().parsestr(value)
     128  
     129          wheel_metadata = get_metadata('WHEEL')
     130          # Check wheel format version is supported.
     131          wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
     132          wheel_v1 = (
     133              parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
     134          )
     135          if not wheel_v1:
     136              raise ValueError(
     137                  'unsupported wheel format version: %s' % wheel_version)
     138          # Extract to target directory.
     139          _unpack_zipfile_obj(zf, destination_eggdir)
     140          # Convert metadata.
     141          dist_info = os.path.join(destination_eggdir, dist_info)
     142          dist = pkg_resources.Distribution.from_location(
     143              destination_eggdir, dist_info,
     144              metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),
     145          )
     146  
     147          # Note: Evaluate and strip markers now,
     148          # as it's difficult to convert back from the syntax:
     149          # foobar; "linux" in sys_platform and extra == 'test'
     150          def raw_req(req):
     151              req.marker = None
     152              return str(req)
     153          install_requires = list(map(raw_req, dist.requires()))
     154          extras_require = {
     155              extra: [
     156                  req
     157                  for req in map(raw_req, dist.requires((extra,)))
     158                  if req not in install_requires
     159              ]
     160              for extra in dist.extras
     161          }
     162          os.rename(dist_info, egg_info)
     163          os.rename(
     164              os.path.join(egg_info, 'METADATA'),
     165              os.path.join(egg_info, 'PKG-INFO'),
     166          )
     167          setup_dist = setuptools.Distribution(
     168              attrs=dict(
     169                  install_requires=install_requires,
     170                  extras_require=extras_require,
     171              ),
     172          )
     173          with disable_info_traces():
     174              write_requirements(
     175                  setup_dist.get_command_obj('egg_info'),
     176                  None,
     177                  os.path.join(egg_info, 'requires.txt'),
     178              )
     179  
     180      @staticmethod
     181      def _move_data_entries(destination_eggdir, dist_data):
     182          """Move data entries to their correct location."""
     183          dist_data = os.path.join(destination_eggdir, dist_data)
     184          dist_data_scripts = os.path.join(dist_data, 'scripts')
     185          if os.path.exists(dist_data_scripts):
     186              egg_info_scripts = os.path.join(
     187                  destination_eggdir, 'EGG-INFO', 'scripts')
     188              os.mkdir(egg_info_scripts)
     189              for entry in os.listdir(dist_data_scripts):
     190                  # Remove bytecode, as it's not properly handled
     191                  # during easy_install scripts install phase.
     192                  if entry.endswith('.pyc'):
     193                      os.unlink(os.path.join(dist_data_scripts, entry))
     194                  else:
     195                      os.rename(
     196                          os.path.join(dist_data_scripts, entry),
     197                          os.path.join(egg_info_scripts, entry),
     198                      )
     199              os.rmdir(dist_data_scripts)
     200          for subdir in filter(os.path.exists, (
     201              os.path.join(dist_data, d)
     202              for d in ('data', 'headers', 'purelib', 'platlib')
     203          )):
     204              unpack(subdir, destination_eggdir)
     205          if os.path.exists(dist_data):
     206              os.rmdir(dist_data)
     207  
     208      @staticmethod
     209      def _fix_namespace_packages(egg_info, destination_eggdir):
     210          namespace_packages = os.path.join(
     211              egg_info, 'namespace_packages.txt')
     212          if os.path.exists(namespace_packages):
     213              with open(namespace_packages) as fp:
     214                  namespace_packages = fp.read().split()
     215              for mod in namespace_packages:
     216                  mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
     217                  mod_init = os.path.join(mod_dir, '__init__.py')
     218                  if not os.path.exists(mod_dir):
     219                      os.mkdir(mod_dir)
     220                  if not os.path.exists(mod_init):
     221                      with open(mod_init, 'w') as fp:
     222                          fp.write(NAMESPACE_PACKAGE_INIT)