python (3.11.7)
       1  """Extensions to the 'distutils' for large or complex distributions"""
       2  
       3  import functools
       4  import os
       5  import re
       6  import warnings
       7  
       8  import _distutils_hack.override  # noqa: F401
       9  
      10  import distutils.core
      11  from distutils.errors import DistutilsOptionError
      12  from distutils.util import convert_path as _convert_path
      13  
      14  from ._deprecation_warning import SetuptoolsDeprecationWarning
      15  
      16  import setuptools.version
      17  from setuptools.extension import Extension
      18  from setuptools.dist import Distribution
      19  from setuptools.depends import Require
      20  from setuptools.discovery import PackageFinder, PEP420PackageFinder
      21  from . import monkey
      22  from . import logging
      23  
      24  
      25  __all__ = [
      26      'setup',
      27      'Distribution',
      28      'Command',
      29      'Extension',
      30      'Require',
      31      'SetuptoolsDeprecationWarning',
      32      'find_packages',
      33      'find_namespace_packages',
      34  ]
      35  
      36  __version__ = setuptools.version.__version__
      37  
      38  bootstrap_install_from = None
      39  
      40  
      41  find_packages = PackageFinder.find
      42  find_namespace_packages = PEP420PackageFinder.find
      43  
      44  
      45  def _install_setup_requires(attrs):
      46      # Note: do not use `setuptools.Distribution` directly, as
      47      # our PEP 517 backend patch `distutils.core.Distribution`.
      48      class ESC[4;38;5;81mMinimalDistribution(ESC[4;38;5;149mdistutilsESC[4;38;5;149m.ESC[4;38;5;149mcoreESC[4;38;5;149m.ESC[4;38;5;149mDistribution):
      49          """
      50          A minimal version of a distribution for supporting the
      51          fetch_build_eggs interface.
      52          """
      53  
      54          def __init__(self, attrs):
      55              _incl = 'dependency_links', 'setup_requires'
      56              filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
      57              super().__init__(filtered)
      58              # Prevent accidentally triggering discovery with incomplete set of attrs
      59              self.set_defaults._disable()
      60  
      61          def _get_project_config_files(self, filenames=None):
      62              """Ignore ``pyproject.toml``, they are not related to setup_requires"""
      63              try:
      64                  cfg, toml = super()._split_standard_project_metadata(filenames)
      65                  return cfg, ()
      66              except Exception:
      67                  return filenames, ()
      68  
      69          def finalize_options(self):
      70              """
      71              Disable finalize_options to avoid building the working set.
      72              Ref #2158.
      73              """
      74  
      75      dist = MinimalDistribution(attrs)
      76  
      77      # Honor setup.cfg's options.
      78      dist.parse_config_files(ignore_option_errors=True)
      79      if dist.setup_requires:
      80          dist.fetch_build_eggs(dist.setup_requires)
      81  
      82  
      83  def setup(**attrs):
      84      # Make sure we have any requirements needed to interpret 'attrs'.
      85      logging.configure()
      86      _install_setup_requires(attrs)
      87      return distutils.core.setup(**attrs)
      88  
      89  
      90  setup.__doc__ = distutils.core.setup.__doc__
      91  
      92  
      93  _Command = monkey.get_unpatched(distutils.core.Command)
      94  
      95  
      96  class ESC[4;38;5;81mCommand(ESC[4;38;5;149m_Command):
      97      """
      98      Setuptools internal actions are organized using a *command design pattern*.
      99      This means that each action (or group of closely related actions) executed during
     100      the build should be implemented as a ``Command`` subclass.
     101  
     102      These commands are abstractions and do not necessarily correspond to a command that
     103      can (or should) be executed via a terminal, in a CLI fashion (although historically
     104      they would).
     105  
     106      When creating a new command from scratch, custom defined classes **SHOULD** inherit
     107      from ``setuptools.Command`` and implement a few mandatory methods.
     108      Between these mandatory methods, are listed:
     109  
     110      .. method:: initialize_options(self)
     111  
     112          Set or (reset) all options/attributes/caches used by the command
     113          to their default values. Note that these values may be overwritten during
     114          the build.
     115  
     116      .. method:: finalize_options(self)
     117  
     118          Set final values for all options/attributes used by the command.
     119          Most of the time, each option/attribute/cache should only be set if it does not
     120          have any value yet (e.g. ``if self.attr is None: self.attr = val``).
     121  
     122      .. method:: run(self)
     123  
     124          Execute the actions intended by the command.
     125          (Side effects **SHOULD** only take place when ``run`` is executed,
     126          for example, creating new files or writing to the terminal output).
     127  
     128      A useful analogy for command classes is to think of them as subroutines with local
     129      variables called "options".  The options are "declared" in ``initialize_options()``
     130      and "defined" (given their final values, aka "finalized") in ``finalize_options()``,
     131      both of which must be defined by every command class. The "body" of the subroutine,
     132      (where it does all the work) is the ``run()`` method.
     133      Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set
     134      the values for options/attributes based on user's input (or circumstance),
     135      which means that the implementation should be careful to not overwrite values in
     136      ``finalize_options`` unless necessary.
     137  
     138      Please note that other commands (or other parts of setuptools) may also overwrite
     139      the values of the command's options/attributes multiple times during the build
     140      process.
     141      Therefore it is important to consistently implement ``initialize_options()`` and
     142      ``finalize_options()``. For example, all derived attributes (or attributes that
     143      depend on the value of other attributes) **SHOULD** be recomputed in
     144      ``finalize_options``.
     145  
     146      When overwriting existing commands, custom defined classes **MUST** abide by the
     147      same APIs implemented by the original class. They also **SHOULD** inherit from the
     148      original class.
     149      """
     150  
     151      command_consumes_arguments = False
     152  
     153      def __init__(self, dist, **kw):
     154          """
     155          Construct the command for dist, updating
     156          vars(self) with any keyword parameters.
     157          """
     158          super().__init__(dist)
     159          vars(self).update(kw)
     160  
     161      def _ensure_stringlike(self, option, what, default=None):
     162          val = getattr(self, option)
     163          if val is None:
     164              setattr(self, option, default)
     165              return default
     166          elif not isinstance(val, str):
     167              raise DistutilsOptionError(
     168                  "'%s' must be a %s (got `%s`)" % (option, what, val)
     169              )
     170          return val
     171  
     172      def ensure_string_list(self, option):
     173          r"""Ensure that 'option' is a list of strings.  If 'option' is
     174          currently a string, we split it either on /,\s*/ or /\s+/, so
     175          "foo bar baz", "foo,bar,baz", and "foo,   bar baz" all become
     176          ["foo", "bar", "baz"].
     177  
     178          ..
     179             TODO: This method seems to be similar to the one in ``distutils.cmd``
     180             Probably it is just here for backward compatibility with old Python versions?
     181  
     182          :meta private:
     183          """
     184          val = getattr(self, option)
     185          if val is None:
     186              return
     187          elif isinstance(val, str):
     188              setattr(self, option, re.split(r',\s*|\s+', val))
     189          else:
     190              if isinstance(val, list):
     191                  ok = all(isinstance(v, str) for v in val)
     192              else:
     193                  ok = False
     194              if not ok:
     195                  raise DistutilsOptionError(
     196                      "'%s' must be a list of strings (got %r)" % (option, val)
     197                  )
     198  
     199      def reinitialize_command(self, command, reinit_subcommands=0, **kw):
     200          cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
     201          vars(cmd).update(kw)
     202          return cmd
     203  
     204  
     205  def _find_all_simple(path):
     206      """
     207      Find all files under 'path'
     208      """
     209      results = (
     210          os.path.join(base, file)
     211          for base, dirs, files in os.walk(path, followlinks=True)
     212          for file in files
     213      )
     214      return filter(os.path.isfile, results)
     215  
     216  
     217  def findall(dir=os.curdir):
     218      """
     219      Find all files under 'dir' and return the list of full filenames.
     220      Unless dir is '.', return full filenames with dir prepended.
     221      """
     222      files = _find_all_simple(dir)
     223      if dir == os.curdir:
     224          make_rel = functools.partial(os.path.relpath, start=dir)
     225          files = map(make_rel, files)
     226      return list(files)
     227  
     228  
     229  @functools.wraps(_convert_path)
     230  def convert_path(pathname):
     231      from inspect import cleandoc
     232  
     233      msg = """
     234      The function `convert_path` is considered internal and not part of the public API.
     235      Its direct usage by 3rd-party packages is considered deprecated and the function
     236      may be removed in the future.
     237      """
     238      warnings.warn(cleandoc(msg), SetuptoolsDeprecationWarning)
     239      return _convert_path(pathname)
     240  
     241  
     242  class ESC[4;38;5;81msic(ESC[4;38;5;149mstr):
     243      """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
     244  
     245  
     246  # Apply monkey patches
     247  monkey.patch_all()