python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
cmd.py
       1  """distutils.cmd
       2  
       3  Provides the Command class, the base class for the command classes
       4  in the distutils.command package.
       5  """
       6  
       7  import sys
       8  import os
       9  import re
      10  from distutils.errors import DistutilsOptionError
      11  from distutils import util, dir_util, file_util, archive_util, dep_util
      12  from distutils import log
      13  
      14  
      15  class ESC[4;38;5;81mCommand:
      16      """Abstract base class for defining command classes, the "worker bees"
      17      of the Distutils.  A useful analogy for command classes is to think of
      18      them as subroutines with local variables called "options".  The options
      19      are "declared" in 'initialize_options()' and "defined" (given their
      20      final values, aka "finalized") in 'finalize_options()', both of which
      21      must be defined by every command class.  The distinction between the
      22      two is necessary because option values might come from the outside
      23      world (command line, config file, ...), and any options dependent on
      24      other options must be computed *after* these outside influences have
      25      been processed -- hence 'finalize_options()'.  The "body" of the
      26      subroutine, where it does all its work based on the values of its
      27      options, is the 'run()' method, which must also be implemented by every
      28      command class.
      29      """
      30  
      31      # 'sub_commands' formalizes the notion of a "family" of commands,
      32      # eg. "install" as the parent with sub-commands "install_lib",
      33      # "install_headers", etc.  The parent of a family of commands
      34      # defines 'sub_commands' as a class attribute; it's a list of
      35      #    (command_name : string, predicate : unbound_method | string | None)
      36      # tuples, where 'predicate' is a method of the parent command that
      37      # determines whether the corresponding command is applicable in the
      38      # current situation.  (Eg. we "install_headers" is only applicable if
      39      # we have any C header files to install.)  If 'predicate' is None,
      40      # that command is always applicable.
      41      #
      42      # 'sub_commands' is usually defined at the *end* of a class, because
      43      # predicates can be unbound methods, so they must already have been
      44      # defined.  The canonical example is the "install" command.
      45      sub_commands = []
      46  
      47      # -- Creation/initialization methods -------------------------------
      48  
      49      def __init__(self, dist):
      50          """Create and initialize a new Command object.  Most importantly,
      51          invokes the 'initialize_options()' method, which is the real
      52          initializer and depends on the actual command being
      53          instantiated.
      54          """
      55          # late import because of mutual dependence between these classes
      56          from distutils.dist import Distribution
      57  
      58          if not isinstance(dist, Distribution):
      59              raise TypeError("dist must be a Distribution instance")
      60          if self.__class__ is Command:
      61              raise RuntimeError("Command is an abstract class")
      62  
      63          self.distribution = dist
      64          self.initialize_options()
      65  
      66          # Per-command versions of the global flags, so that the user can
      67          # customize Distutils' behaviour command-by-command and let some
      68          # commands fall back on the Distribution's behaviour.  None means
      69          # "not defined, check self.distribution's copy", while 0 or 1 mean
      70          # false and true (duh).  Note that this means figuring out the real
      71          # value of each flag is a touch complicated -- hence "self._dry_run"
      72          # will be handled by __getattr__, below.
      73          # XXX This needs to be fixed.
      74          self._dry_run = None
      75  
      76          # verbose is largely ignored, but needs to be set for
      77          # backwards compatibility (I think)?
      78          self.verbose = dist.verbose
      79  
      80          # Some commands define a 'self.force' option to ignore file
      81          # timestamps, but methods defined *here* assume that
      82          # 'self.force' exists for all commands.  So define it here
      83          # just to be safe.
      84          self.force = None
      85  
      86          # The 'help' flag is just used for command-line parsing, so
      87          # none of that complicated bureaucracy is needed.
      88          self.help = 0
      89  
      90          # 'finalized' records whether or not 'finalize_options()' has been
      91          # called.  'finalize_options()' itself should not pay attention to
      92          # this flag: it is the business of 'ensure_finalized()', which
      93          # always calls 'finalize_options()', to respect/update it.
      94          self.finalized = 0
      95  
      96      # XXX A more explicit way to customize dry_run would be better.
      97      def __getattr__(self, attr):
      98          if attr == 'dry_run':
      99              myval = getattr(self, "_" + attr)
     100              if myval is None:
     101                  return getattr(self.distribution, attr)
     102              else:
     103                  return myval
     104          else:
     105              raise AttributeError(attr)
     106  
     107      def ensure_finalized(self):
     108          if not self.finalized:
     109              self.finalize_options()
     110          self.finalized = 1
     111  
     112      # Subclasses must define:
     113      #   initialize_options()
     114      #     provide default values for all options; may be customized by
     115      #     setup script, by options from config file(s), or by command-line
     116      #     options
     117      #   finalize_options()
     118      #     decide on the final values for all options; this is called
     119      #     after all possible intervention from the outside world
     120      #     (command-line, option file, etc.) has been processed
     121      #   run()
     122      #     run the command: do whatever it is we're here to do,
     123      #     controlled by the command's various option values
     124  
     125      def initialize_options(self):
     126          """Set default values for all the options that this command
     127          supports.  Note that these defaults may be overridden by other
     128          commands, by the setup script, by config files, or by the
     129          command-line.  Thus, this is not the place to code dependencies
     130          between options; generally, 'initialize_options()' implementations
     131          are just a bunch of "self.foo = None" assignments.
     132  
     133          This method must be implemented by all command classes.
     134          """
     135          raise RuntimeError(
     136              "abstract method -- subclass %s must override" % self.__class__
     137          )
     138  
     139      def finalize_options(self):
     140          """Set final values for all the options that this command supports.
     141          This is always called as late as possible, ie.  after any option
     142          assignments from the command-line or from other commands have been
     143          done.  Thus, this is the place to code option dependencies: if
     144          'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as
     145          long as 'foo' still has the same value it was assigned in
     146          'initialize_options()'.
     147  
     148          This method must be implemented by all command classes.
     149          """
     150          raise RuntimeError(
     151              "abstract method -- subclass %s must override" % self.__class__
     152          )
     153  
     154      def dump_options(self, header=None, indent=""):
     155          from distutils.fancy_getopt import longopt_xlate
     156  
     157          if header is None:
     158              header = "command options for '%s':" % self.get_command_name()
     159          self.announce(indent + header, level=log.INFO)
     160          indent = indent + "  "
     161          for (option, _, _) in self.user_options:
     162              option = option.translate(longopt_xlate)
     163              if option[-1] == "=":
     164                  option = option[:-1]
     165              value = getattr(self, option)
     166              self.announce(indent + "{} = {}".format(option, value), level=log.INFO)
     167  
     168      def run(self):
     169          """A command's raison d'etre: carry out the action it exists to
     170          perform, controlled by the options initialized in
     171          'initialize_options()', customized by other commands, the setup
     172          script, the command-line, and config files, and finalized in
     173          'finalize_options()'.  All terminal output and filesystem
     174          interaction should be done by 'run()'.
     175  
     176          This method must be implemented by all command classes.
     177          """
     178          raise RuntimeError(
     179              "abstract method -- subclass %s must override" % self.__class__
     180          )
     181  
     182      def announce(self, msg, level=1):
     183          """If the current verbosity level is of greater than or equal to
     184          'level' print 'msg' to stdout.
     185          """
     186          log.log(level, msg)
     187  
     188      def debug_print(self, msg):
     189          """Print 'msg' to stdout if the global DEBUG (taken from the
     190          DISTUTILS_DEBUG environment variable) flag is true.
     191          """
     192          from distutils.debug import DEBUG
     193  
     194          if DEBUG:
     195              print(msg)
     196              sys.stdout.flush()
     197  
     198      # -- Option validation methods -------------------------------------
     199      # (these are very handy in writing the 'finalize_options()' method)
     200      #
     201      # NB. the general philosophy here is to ensure that a particular option
     202      # value meets certain type and value constraints.  If not, we try to
     203      # force it into conformance (eg. if we expect a list but have a string,
     204      # split the string on comma and/or whitespace).  If we can't force the
     205      # option into conformance, raise DistutilsOptionError.  Thus, command
     206      # classes need do nothing more than (eg.)
     207      #   self.ensure_string_list('foo')
     208      # and they can be guaranteed that thereafter, self.foo will be
     209      # a list of strings.
     210  
     211      def _ensure_stringlike(self, option, what, default=None):
     212          val = getattr(self, option)
     213          if val is None:
     214              setattr(self, option, default)
     215              return default
     216          elif not isinstance(val, str):
     217              raise DistutilsOptionError(
     218                  "'{}' must be a {} (got `{}`)".format(option, what, val)
     219              )
     220          return val
     221  
     222      def ensure_string(self, option, default=None):
     223          """Ensure that 'option' is a string; if not defined, set it to
     224          'default'.
     225          """
     226          self._ensure_stringlike(option, "string", default)
     227  
     228      def ensure_string_list(self, option):
     229          r"""Ensure that 'option' is a list of strings.  If 'option' is
     230          currently a string, we split it either on /,\s*/ or /\s+/, so
     231          "foo bar baz", "foo,bar,baz", and "foo,   bar baz" all become
     232          ["foo", "bar", "baz"].
     233          """
     234          val = getattr(self, option)
     235          if val is None:
     236              return
     237          elif isinstance(val, str):
     238              setattr(self, option, re.split(r',\s*|\s+', val))
     239          else:
     240              if isinstance(val, list):
     241                  ok = all(isinstance(v, str) for v in val)
     242              else:
     243                  ok = False
     244              if not ok:
     245                  raise DistutilsOptionError(
     246                      "'{}' must be a list of strings (got {!r})".format(option, val)
     247                  )
     248  
     249      def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
     250          val = self._ensure_stringlike(option, what, default)
     251          if val is not None and not tester(val):
     252              raise DistutilsOptionError(
     253                  ("error in '%s' option: " + error_fmt) % (option, val)
     254              )
     255  
     256      def ensure_filename(self, option):
     257          """Ensure that 'option' is the name of an existing file."""
     258          self._ensure_tested_string(
     259              option, os.path.isfile, "filename", "'%s' does not exist or is not a file"
     260          )
     261  
     262      def ensure_dirname(self, option):
     263          self._ensure_tested_string(
     264              option,
     265              os.path.isdir,
     266              "directory name",
     267              "'%s' does not exist or is not a directory",
     268          )
     269  
     270      # -- Convenience methods for commands ------------------------------
     271  
     272      def get_command_name(self):
     273          if hasattr(self, 'command_name'):
     274              return self.command_name
     275          else:
     276              return self.__class__.__name__
     277  
     278      def set_undefined_options(self, src_cmd, *option_pairs):
     279          """Set the values of any "undefined" options from corresponding
     280          option values in some other command object.  "Undefined" here means
     281          "is None", which is the convention used to indicate that an option
     282          has not been changed between 'initialize_options()' and
     283          'finalize_options()'.  Usually called from 'finalize_options()' for
     284          options that depend on some other command rather than another
     285          option of the same command.  'src_cmd' is the other command from
     286          which option values will be taken (a command object will be created
     287          for it if necessary); the remaining arguments are
     288          '(src_option,dst_option)' tuples which mean "take the value of
     289          'src_option' in the 'src_cmd' command object, and copy it to
     290          'dst_option' in the current command object".
     291          """
     292          # Option_pairs: list of (src_option, dst_option) tuples
     293          src_cmd_obj = self.distribution.get_command_obj(src_cmd)
     294          src_cmd_obj.ensure_finalized()
     295          for (src_option, dst_option) in option_pairs:
     296              if getattr(self, dst_option) is None:
     297                  setattr(self, dst_option, getattr(src_cmd_obj, src_option))
     298  
     299      def get_finalized_command(self, command, create=1):
     300          """Wrapper around Distribution's 'get_command_obj()' method: find
     301          (create if necessary and 'create' is true) the command object for
     302          'command', call its 'ensure_finalized()' method, and return the
     303          finalized command object.
     304          """
     305          cmd_obj = self.distribution.get_command_obj(command, create)
     306          cmd_obj.ensure_finalized()
     307          return cmd_obj
     308  
     309      # XXX rename to 'get_reinitialized_command()'? (should do the
     310      # same in dist.py, if so)
     311      def reinitialize_command(self, command, reinit_subcommands=0):
     312          return self.distribution.reinitialize_command(command, reinit_subcommands)
     313  
     314      def run_command(self, command):
     315          """Run some other command: uses the 'run_command()' method of
     316          Distribution, which creates and finalizes the command object if
     317          necessary and then invokes its 'run()' method.
     318          """
     319          self.distribution.run_command(command)
     320  
     321      def get_sub_commands(self):
     322          """Determine the sub-commands that are relevant in the current
     323          distribution (ie., that need to be run).  This is based on the
     324          'sub_commands' class attribute: each tuple in that list may include
     325          a method that we call to determine if the subcommand needs to be
     326          run for the current distribution.  Return a list of command names.
     327          """
     328          commands = []
     329          for (cmd_name, method) in self.sub_commands:
     330              if method is None or method(self):
     331                  commands.append(cmd_name)
     332          return commands
     333  
     334      # -- External world manipulation -----------------------------------
     335  
     336      def warn(self, msg):
     337          log.warn("warning: %s: %s\n", self.get_command_name(), msg)
     338  
     339      def execute(self, func, args, msg=None, level=1):
     340          util.execute(func, args, msg, dry_run=self.dry_run)
     341  
     342      def mkpath(self, name, mode=0o777):
     343          dir_util.mkpath(name, mode, dry_run=self.dry_run)
     344  
     345      def copy_file(
     346          self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1
     347      ):
     348          """Copy a file respecting verbose, dry-run and force flags.  (The
     349          former two default to whatever is in the Distribution object, and
     350          the latter defaults to false for commands that don't define it.)"""
     351          return file_util.copy_file(
     352              infile,
     353              outfile,
     354              preserve_mode,
     355              preserve_times,
     356              not self.force,
     357              link,
     358              dry_run=self.dry_run,
     359          )
     360  
     361      def copy_tree(
     362          self,
     363          infile,
     364          outfile,
     365          preserve_mode=1,
     366          preserve_times=1,
     367          preserve_symlinks=0,
     368          level=1,
     369      ):
     370          """Copy an entire directory tree respecting verbose, dry-run,
     371          and force flags.
     372          """
     373          return dir_util.copy_tree(
     374              infile,
     375              outfile,
     376              preserve_mode,
     377              preserve_times,
     378              preserve_symlinks,
     379              not self.force,
     380              dry_run=self.dry_run,
     381          )
     382  
     383      def move_file(self, src, dst, level=1):
     384          """Move a file respecting dry-run flag."""
     385          return file_util.move_file(src, dst, dry_run=self.dry_run)
     386  
     387      def spawn(self, cmd, search_path=1, level=1):
     388          """Spawn an external command respecting dry-run flag."""
     389          from distutils.spawn import spawn
     390  
     391          spawn(cmd, search_path, dry_run=self.dry_run)
     392  
     393      def make_archive(
     394          self, base_name, format, root_dir=None, base_dir=None, owner=None, group=None
     395      ):
     396          return archive_util.make_archive(
     397              base_name,
     398              format,
     399              root_dir,
     400              base_dir,
     401              dry_run=self.dry_run,
     402              owner=owner,
     403              group=group,
     404          )
     405  
     406      def make_file(
     407          self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1
     408      ):
     409          """Special case of 'execute()' for operations that process one or
     410          more input files and generate one output file.  Works just like
     411          'execute()', except the operation is skipped and a different
     412          message printed if 'outfile' already exists and is newer than all
     413          files listed in 'infiles'.  If the command defined 'self.force',
     414          and it is true, then the command is unconditionally run -- does no
     415          timestamp checks.
     416          """
     417          if skip_msg is None:
     418              skip_msg = "skipping %s (inputs unchanged)" % outfile
     419  
     420          # Allow 'infiles' to be a single string
     421          if isinstance(infiles, str):
     422              infiles = (infiles,)
     423          elif not isinstance(infiles, (list, tuple)):
     424              raise TypeError("'infiles' must be a string, or a list or tuple of strings")
     425  
     426          if exec_msg is None:
     427              exec_msg = "generating {} from {}".format(outfile, ', '.join(infiles))
     428  
     429          # If 'outfile' must be regenerated (either because it doesn't
     430          # exist, is out-of-date, or the 'force' flag is true) then
     431          # perform the action that presumably regenerates it
     432          if self.force or dep_util.newer_group(infiles, outfile):
     433              self.execute(func, args, exec_msg, level)
     434          # Otherwise, print the "skip" message
     435          else:
     436              log.debug(skip_msg)