python (3.11.7)
       1  from distutils.util import convert_path
       2  from distutils import log
       3  from distutils.errors import DistutilsError, DistutilsOptionError
       4  import os
       5  import glob
       6  import io
       7  
       8  import pkg_resources
       9  from setuptools.command.easy_install import easy_install
      10  from setuptools import namespaces
      11  import setuptools
      12  
      13  
      14  class ESC[4;38;5;81mdevelop(ESC[4;38;5;149mnamespacesESC[4;38;5;149m.ESC[4;38;5;149mDevelopInstaller, ESC[4;38;5;149measy_install):
      15      """Set up package for development"""
      16  
      17      description = "install package in 'development mode'"
      18  
      19      user_options = easy_install.user_options + [
      20          ("uninstall", "u", "Uninstall this source package"),
      21          ("egg-path=", None, "Set the path to be used in the .egg-link file"),
      22      ]
      23  
      24      boolean_options = easy_install.boolean_options + ['uninstall']
      25  
      26      command_consumes_arguments = False  # override base
      27  
      28      def run(self):
      29          if self.uninstall:
      30              self.multi_version = True
      31              self.uninstall_link()
      32              self.uninstall_namespaces()
      33          else:
      34              self.install_for_development()
      35          self.warn_deprecated_options()
      36  
      37      def initialize_options(self):
      38          self.uninstall = None
      39          self.egg_path = None
      40          easy_install.initialize_options(self)
      41          self.setup_path = None
      42          self.always_copy_from = '.'  # always copy eggs installed in curdir
      43  
      44      def finalize_options(self):
      45          ei = self.get_finalized_command("egg_info")
      46          if ei.broken_egg_info:
      47              template = "Please rename %r to %r before using 'develop'"
      48              args = ei.egg_info, ei.broken_egg_info
      49              raise DistutilsError(template % args)
      50          self.args = [ei.egg_name]
      51  
      52          easy_install.finalize_options(self)
      53          self.expand_basedirs()
      54          self.expand_dirs()
      55          # pick up setup-dir .egg files only: no .egg-info
      56          self.package_index.scan(glob.glob('*.egg'))
      57  
      58          egg_link_fn = ei.egg_name + '.egg-link'
      59          self.egg_link = os.path.join(self.install_dir, egg_link_fn)
      60          self.egg_base = ei.egg_base
      61          if self.egg_path is None:
      62              self.egg_path = os.path.abspath(ei.egg_base)
      63  
      64          target = pkg_resources.normalize_path(self.egg_base)
      65          egg_path = pkg_resources.normalize_path(
      66              os.path.join(self.install_dir, self.egg_path)
      67          )
      68          if egg_path != target:
      69              raise DistutilsOptionError(
      70                  "--egg-path must be a relative path from the install"
      71                  " directory to " + target
      72              )
      73  
      74          # Make a distribution for the package's source
      75          self.dist = pkg_resources.Distribution(
      76              target,
      77              pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)),
      78              project_name=ei.egg_name,
      79          )
      80  
      81          self.setup_path = self._resolve_setup_path(
      82              self.egg_base,
      83              self.install_dir,
      84              self.egg_path,
      85          )
      86  
      87      @staticmethod
      88      def _resolve_setup_path(egg_base, install_dir, egg_path):
      89          """
      90          Generate a path from egg_base back to '.' where the
      91          setup script resides and ensure that path points to the
      92          setup path from $install_dir/$egg_path.
      93          """
      94          path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
      95          if path_to_setup != os.curdir:
      96              path_to_setup = '../' * (path_to_setup.count('/') + 1)
      97          resolved = pkg_resources.normalize_path(
      98              os.path.join(install_dir, egg_path, path_to_setup)
      99          )
     100          if resolved != pkg_resources.normalize_path(os.curdir):
     101              raise DistutilsOptionError(
     102                  "Can't get a consistent path to setup script from"
     103                  " installation directory",
     104                  resolved,
     105                  pkg_resources.normalize_path(os.curdir),
     106              )
     107          return path_to_setup
     108  
     109      def install_for_development(self):
     110          self.run_command('egg_info')
     111  
     112          # Build extensions in-place
     113          self.reinitialize_command('build_ext', inplace=1)
     114          self.run_command('build_ext')
     115  
     116          if setuptools.bootstrap_install_from:
     117              self.easy_install(setuptools.bootstrap_install_from)
     118              setuptools.bootstrap_install_from = None
     119  
     120          self.install_namespaces()
     121  
     122          # create an .egg-link in the installation dir, pointing to our egg
     123          log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
     124          if not self.dry_run:
     125              with open(self.egg_link, "w") as f:
     126                  f.write(self.egg_path + "\n" + self.setup_path)
     127          # postprocess the installed distro, fixing up .pth, installing scripts,
     128          # and handling requirements
     129          self.process_distribution(None, self.dist, not self.no_deps)
     130  
     131      def uninstall_link(self):
     132          if os.path.exists(self.egg_link):
     133              log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
     134              egg_link_file = open(self.egg_link)
     135              contents = [line.rstrip() for line in egg_link_file]
     136              egg_link_file.close()
     137              if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
     138                  log.warn("Link points to %s: uninstall aborted", contents)
     139                  return
     140              if not self.dry_run:
     141                  os.unlink(self.egg_link)
     142          if not self.dry_run:
     143              self.update_pth(self.dist)  # remove any .pth link to us
     144          if self.distribution.scripts:
     145              # XXX should also check for entry point scripts!
     146              log.warn("Note: you must uninstall or replace scripts manually!")
     147  
     148      def install_egg_scripts(self, dist):
     149          if dist is not self.dist:
     150              # Installing a dependency, so fall back to normal behavior
     151              return easy_install.install_egg_scripts(self, dist)
     152  
     153          # create wrapper scripts in the script dir, pointing to dist.scripts
     154  
     155          # new-style...
     156          self.install_wrapper_scripts(dist)
     157  
     158          # ...and old-style
     159          for script_name in self.distribution.scripts or []:
     160              script_path = os.path.abspath(convert_path(script_name))
     161              script_name = os.path.basename(script_path)
     162              with io.open(script_path) as strm:
     163                  script_text = strm.read()
     164              self.install_script(dist, script_name, script_text, script_path)
     165  
     166      def install_wrapper_scripts(self, dist):
     167          dist = VersionlessRequirement(dist)
     168          return easy_install.install_wrapper_scripts(self, dist)
     169  
     170  
     171  class ESC[4;38;5;81mVersionlessRequirement:
     172      """
     173      Adapt a pkg_resources.Distribution to simply return the project
     174      name as the 'requirement' so that scripts will work across
     175      multiple versions.
     176  
     177      >>> from pkg_resources import Distribution
     178      >>> dist = Distribution(project_name='foo', version='1.0')
     179      >>> str(dist.as_requirement())
     180      'foo==1.0'
     181      >>> adapted_dist = VersionlessRequirement(dist)
     182      >>> str(adapted_dist.as_requirement())
     183      'foo'
     184      """
     185  
     186      def __init__(self, dist):
     187          self.__dist = dist
     188  
     189      def __getattr__(self, name):
     190          return getattr(self.__dist, name)
     191  
     192      def as_requirement(self):
     193          return self.project_name