dist.py 49.2 KB
Newer Older
1 2 3
"""distutils.dist

Provides the Distribution class, which represents the module distribution
4 5
being built/installed/distributed.
"""
6

7 8 9
import sys
import os
import re
10
from email import message_from_file
11 12 13

try:
    import warnings
14
except ImportError:
15 16
    warnings = None

17
from distutils.errors import *
18
from distutils.fancy_getopt import FancyGetopt, translate_longopt
19
from distutils.util import check_environ, strtobool, rfc822_escape
20
from distutils import log
21
from distutils.debug import DEBUG
22 23 24 25 26

# Regex to define acceptable Distutils command names.  This is not *quite*
# the same as a Python NAME -- I don't allow leading underscores.  The fact
# that they're very similar is no coincidence; the default naming scheme is
# to look for a Python module named after the command.
27
command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
28 29


30 31 32 33 34 35 36 37 38 39 40 41 42 43
def _ensure_list(value, fieldname):
    if isinstance(value, str):
        # a string containing comma separated values is okay.  It will
        # be converted to a list by Distribution.finalize_options().
        pass
    elif not isinstance(value, list):
        # passing a tuple or an iterator perhaps, warn and convert
        typename = type(value).__name__
        msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'"
        log.log(log.WARN, msg)
        value = list(value)
    return value


44
class Distribution:
45 46 47 48 49 50 51 52 53 54 55 56
    """The core of the Distutils.  Most of the work hiding behind 'setup'
    is really done within a Distribution instance, which farms the work out
    to the Distutils commands specified on the command line.

    Setup scripts will almost never instantiate Distribution directly,
    unless the 'setup()' function is totally inadequate to their needs.
    However, it is conceivable that a setup script might wish to subclass
    Distribution for some specialized purpose, and then pass the subclass
    to 'setup()' as the 'distclass' keyword argument.  If so, it is
    necessary to respect the expectations that 'setup' has of Distribution.
    See the code for 'setup()', in core.py, for details.
    """
57 58

    # 'global_options' describes the command-line options that may be
59 60
    # supplied to the setup script prior to any actual commands.
    # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of
61 62 63 64
    # these global options.  This list should be kept to a bare minimum,
    # since every global option is also valid as a command option -- and we
    # don't want to pollute the commands with too many options that they
    # have minimal control over.
65
    # The fourth entry for verbose means that it can be repeated.
66 67 68 69 70 71 72
    global_options = [
        ('verbose', 'v', "run verbosely (default)", 1),
        ('quiet', 'q', "run quietly (turns verbosity off)"),
        ('dry-run', 'n', "don't actually do anything"),
        ('help', 'h', "show detailed help message"),
        ('no-user-cfg', None,
            'ignore pydistutils.cfg in your home directory'),
73
    ]
74

75 76 77 78 79 80 81 82 83
    # 'common_usage' is a short (2-3 line) string describing the common
    # usage of the setup script.
    common_usage = """\
Common commands: (see '--help-commands' for more)

  setup.py build      will build the package underneath 'build/'
  setup.py install    will install the package
"""

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    # options that are not propagated to the commands
    display_options = [
        ('help-commands', None,
         "list all available commands"),
        ('name', None,
         "print package name"),
        ('version', 'V',
         "print package version"),
        ('fullname', None,
         "print <package name>-<version>"),
        ('author', None,
         "print the author's name"),
        ('author-email', None,
         "print the author's email address"),
        ('maintainer', None,
         "print the maintainer's name"),
        ('maintainer-email', None,
         "print the maintainer's email address"),
        ('contact', None,
103
         "print the maintainer's name if known, else the author's"),
104
        ('contact-email', None,
105
         "print the maintainer's email address if known, else the author's"),
106 107 108
        ('url', None,
         "print the URL for this package"),
        ('license', None,
109 110 111
         "print the license of the package"),
        ('licence', None,
         "alias for --license"),
112 113
        ('description', None,
         "print the package description"),
114 115
        ('long-description', None,
         "print the long package description"),
116 117
        ('platforms', None,
         "print the list of platforms"),
118 119
        ('classifiers', None,
         "print the list of classifiers"),
120 121
        ('keywords', None,
         "print the list of keywords"),
122 123 124 125 126 127
        ('provides', None,
         "print the list of packages/modules provided"),
        ('requires', None,
         "print the list of packages/modules required"),
        ('obsoletes', None,
         "print the list of packages/modules made obsolete")
128
        ]
129
    display_option_names = [translate_longopt(x[0]) for x in display_options]
130 131

    # negative options are options that exclude other options
132 133 134
    negative_opt = {'quiet': 'verbose'}

    # -- Creation/initialization methods -------------------------------
Fred Drake's avatar
Fred Drake committed
135

136
    def __init__(self, attrs=None):
137
        """Construct a new Distribution instance: initialize all the
138 139 140 141 142 143 144 145
        attributes of a Distribution, and then use 'attrs' (a dictionary
        mapping attribute names to values) to assign some of those
        attributes their "real" values.  (Any attributes not mentioned in
        'attrs' will be assigned to some null value: 0, None, an empty list
        or dictionary, etc.)  Most importantly, initialize the
        'command_obj' attribute to the empty dictionary; this will be
        filled in with real command objects by 'parse_command_line()'.
        """
146 147 148 149 150

        # Default values for our command-line options
        self.verbose = 1
        self.dry_run = 0
        self.help = 0
151 152 153 154 155 156 157 158
        for attr in self.display_option_names:
            setattr(self, attr, 0)

        # Store the distribution meta-data (name, version, author, and so
        # forth) in a separate object -- we're getting to have enough
        # information here (and enough command-line options) that it's
        # worth it.  Also delegate 'get_XXX()' methods to the 'metadata'
        # object in a sneaky and underhanded (but efficient!) way.
159
        self.metadata = DistributionMetadata()
160
        for basename in self.metadata._METHOD_BASENAMES:
161 162
            method_name = "get_" + basename
            setattr(self, method_name, getattr(self.metadata, method_name))
163 164 165 166

        # 'cmdclass' maps command names to class objects, so we
        # can 1) quickly figure out which class to instantiate when
        # we need to create a new command object, and 2) have a way
167
        # for the setup script to override command classes
168 169
        self.cmdclass = {}

170 171 172 173 174 175 176 177
        # 'command_packages' is a list of packages in which commands
        # are searched for.  The factory for command 'foo' is expected
        # to be named 'foo' in the module 'foo' in one of the packages
        # named here.  This list is searched from the left; an error
        # is raised if no named package provides the command being
        # searched for.  (Always access using get_command_packages().)
        self.command_packages = None

178 179 180 181 182 183
        # 'script_name' and 'script_args' are usually set to sys.argv[0]
        # and sys.argv[1:], but they can be overridden when the caller is
        # not necessarily a setup script run from the command-line.
        self.script_name = None
        self.script_args = None

184 185 186 187 188
        # 'command_options' is where we store command options between
        # parsing them (from config files, the command-line, etc.) and when
        # they are actually needed -- ie. when the command in question is
        # instantiated.  It is a dictionary of dictionaries of 2-tuples:
        #   command_options = { command_name : { option : (source, value) } }
189 190
        self.command_options = {}

191 192 193 194 195 196 197 198 199
        # 'dist_files' is the list of (command, pyversion, file) that
        # have been created by any dist commands run so far. This is
        # filled regardless of whether the run is dry or not. pyversion
        # gives sysconfig.get_python_version() if the dist file is
        # specific to a Python version, 'any' if it is good for all
        # Python versions on the target platform, and '' for a source
        # file. pyversion should not be used to specify minimum or
        # maximum required Python versions; use the metainfo for that
        # instead.
200 201
        self.dist_files = []

202 203 204 205
        # These options are really the business of various commands, rather
        # than of the Distribution itself.  We provide aliases for them in
        # Distribution as a convenience to the developer.
        self.packages = None
206
        self.package_data = {}
207 208 209
        self.package_dir = None
        self.py_modules = None
        self.libraries = None
210
        self.headers = None
211 212 213 214
        self.ext_modules = None
        self.ext_package = None
        self.include_dirs = None
        self.extra_path = None
215
        self.scripts = None
216
        self.data_files = None
217
        self.password = ''
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

        # And now initialize bookkeeping stuff that can't be supplied by
        # the caller at all.  'command_obj' maps command names to
        # Command instances -- that's how we enforce that every command
        # class is a singleton.
        self.command_obj = {}

        # 'have_run' maps command names to boolean values; it keeps track
        # of whether we have actually run a particular command, to make it
        # cheap to "run" a command whenever we think we might need to -- if
        # it's already been done, no need for expensive filesystem
        # operations, we just check the 'have_run' dictionary and carry on.
        # It's only safe to query 'have_run' for a command class that has
        # been instantiated -- a false value will be inserted when the
        # command object is created, and replaced with a true value when
233
        # the command is successfully run.  Thus it's probably best to use
234 235 236 237
        # '.get()' rather than a straight lookup.
        self.have_run = {}

        # Now we'll use the attrs dictionary (ultimately, keyword args from
238 239 240
        # the setup script) to possibly override any or all of these
        # distribution options.

241 242 243 244 245
        if attrs:
            # Pull out the set of command options and work on them
            # specifically.  Note that this order guarantees that aliased
            # command options will override any supplied redundantly
            # through the general options dictionary.
246
            options = attrs.get('options')
247
            if options is not None:
248 249
                del attrs['options']
                for (command, cmd_options) in options.items():
250 251 252
                    opt_dict = self.get_option_dict(command)
                    for (opt, val) in cmd_options.items():
                        opt_dict[opt] = ("setup script", val)
253

254
            if 'licence' in attrs:
255 256 257 258 259 260 261 262
                attrs['license'] = attrs['licence']
                del attrs['licence']
                msg = "'licence' distribution option is deprecated; use 'license'"
                if warnings is not None:
                    warnings.warn(msg)
                else:
                    sys.stderr.write(msg + "\n")

263 264
            # Now work on the rest of the attributes.  Any attribute that's
            # not already defined is invalid!
265
            for (key, val) in attrs.items():
266 267 268
                if hasattr(self.metadata, "set_" + key):
                    getattr(self.metadata, "set_" + key)(val)
                elif hasattr(self.metadata, key):
269 270 271
                    setattr(self.metadata, key, val)
                elif hasattr(self, key):
                    setattr(self, key, val)
272
                else:
273
                    msg = "Unknown distribution option: %s" % repr(key)
274
                    warnings.warn(msg)
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
        # no-user-cfg is handled before other command line args
        # because other args override the config files, and this
        # one is needed before we can load the config files.
        # If attrs['script_args'] wasn't passed, assume false.
        #
        # This also make sure we just look at the global options
        self.want_user_cfg = True

        if self.script_args is not None:
            for arg in self.script_args:
                if not arg.startswith('-'):
                    break
                if arg == '--no-user-cfg':
                    self.want_user_cfg = False
                    break

292
        self.finalize_options()
Fred Drake's avatar
Fred Drake committed
293

294
    def get_option_dict(self, command):
295 296 297 298 299 300 301 302 303 304
        """Get the option dictionary for a given command.  If that
        command's option dictionary hasn't been created yet, then create it
        and return the new dictionary; otherwise, return the existing
        option dictionary.
        """
        dict = self.command_options.get(command)
        if dict is None:
            dict = self.command_options[command] = {}
        return dict

305
    def dump_option_dicts(self, header=None, commands=None, indent=""):
306 307 308
        from pprint import pformat

        if commands is None:             # dump all command option dicts
309
            commands = sorted(self.command_options.keys())
310 311

        if header is not None:
312
            self.announce(indent + header)
313 314 315
            indent = indent + "  "

        if not commands:
316
            self.announce(indent + "no commands known yet")
317 318 319 320 321
            return

        for cmd_name in commands:
            opt_dict = self.command_options.get(cmd_name)
            if opt_dict is None:
322 323
                self.announce(indent +
                              "no option dict for '%s' command" % cmd_name)
324
            else:
325 326
                self.announce(indent +
                              "option dict for '%s' command:" % cmd_name)
327
                out = pformat(opt_dict)
328 329
                for line in out.split('\n'):
                    self.announce(indent + "  " + line)
330

331 332
    # -- Config file finding/parsing methods ---------------------------

333
    def find_config_files(self):
334 335 336 337 338
        """Find as many configuration files as should be processed for this
        platform, and return a list of filenames in the order in which they
        should be parsed.  The filenames returned are guaranteed to exist
        (modulo nasty race conditions).

339 340 341 342
        There are three possible config files: distutils.cfg in the
        Distutils installation directory (ie. where the top-level
        Distutils __inst__.py file lives), a file in the user's home
        directory named .pydistutils.cfg on Unix and pydistutils.cfg
343 344 345 346
        on Windows/Mac; and setup.cfg in the current directory.

        The file in the user's home directory can be disabled with the
        --no-user-cfg option.
347
        """
348
        files = []
Greg Ward's avatar
Greg Ward committed
349 350
        check_environ()

351 352 353 354 355 356 357 358 359 360
        # Where to look for the system-wide Distutils config file
        sys_dir = os.path.dirname(sys.modules['distutils'].__file__)

        # Look for the system config file
        sys_file = os.path.join(sys_dir, "distutils.cfg")
        if os.path.isfile(sys_file):
            files.append(sys_file)

        # What to call the per-user config file
        if os.name == 'posix':
361 362 363
            user_filename = ".pydistutils.cfg"
        else:
            user_filename = "pydistutils.cfg"
Greg Ward's avatar
Greg Ward committed
364

365
        # And look for the user config file
366 367 368 369
        if self.want_user_cfg:
            user_file = os.path.join(os.path.expanduser('~'), user_filename)
            if os.path.isfile(user_file):
                files.append(user_file)
370 371 372 373 374 375

        # All platforms support local setup.cfg
        local_file = "setup.cfg"
        if os.path.isfile(local_file):
            files.append(local_file)

376 377 378
        if DEBUG:
            self.announce("using config files: %s" % ', '.join(files))

379 380
        return files

381
    def parse_config_files(self, filenames=None):
382
        from configparser import ConfigParser
383

384 385 386 387 388 389 390 391 392 393 394 395
        # Ignore install directory options if we have a venv
        if sys.prefix != sys.base_prefix:
            ignore_options = [
                'install-base', 'install-platbase', 'install-lib',
                'install-platlib', 'install-purelib', 'install-headers',
                'install-scripts', 'install-data', 'prefix', 'exec-prefix',
                'home', 'user', 'root']
        else:
            ignore_options = []

        ignore_options = frozenset(ignore_options)

396 397 398
        if filenames is None:
            filenames = self.find_config_files()

399 400
        if DEBUG:
            self.announce("Distribution.parse_config_files():")
401

402
        parser = ConfigParser()
403
        for filename in filenames:
404
            if DEBUG:
405
                self.announce("  reading %s" % filename)
406 407 408
            parser.read(filename)
            for section in parser.sections():
                options = parser.options(section)
409
                opt_dict = self.get_option_dict(section)
410 411

                for opt in options:
412
                    if opt != '__name__' and opt not in ignore_options:
413
                        val = parser.get(section,opt)
414
                        opt = opt.replace('-', '_')
415
                        opt_dict[opt] = (filename, val)
416

417
            # Make the ConfigParser forget everything (so we retain
418
            # the original filenames that options come from)
419
            parser.__init__()
420

421 422 423
        # If there was a "global" section in the config file, use it
        # to set Distribution options.

424
        if 'global' in self.command_options:
425 426 427 428 429 430 431
            for (opt, (src, val)) in self.command_options['global'].items():
                alias = self.negative_opt.get(opt)
                try:
                    if alias:
                        setattr(self, alias, not strtobool(val))
                    elif opt in ('verbose', 'dry_run'): # ugh!
                        setattr(self, opt, strtobool(val))
432 433
                    else:
                        setattr(self, opt, val)
434
                except ValueError as msg:
435
                    raise DistutilsOptionError(msg)
436

437
    # -- Command-line parsing methods ----------------------------------
438

439
    def parse_command_line(self):
440 441 442 443 444 445 446 447 448 449 450 451 452
        """Parse the setup script's command line, taken from the
        'script_args' instance attribute (which defaults to 'sys.argv[1:]'
        -- see 'setup()' in core.py).  This list is first processed for
        "global options" -- options that set attributes of the Distribution
        instance.  Then, it is alternately scanned for Distutils commands
        and options for that command.  Each new command terminates the
        options for the previous command.  The allowed options for a
        command are determined by the 'user_options' attribute of the
        command class -- thus, we have to be able to load command classes
        in order to parse the command line.  Any error in that 'options'
        attribute raises DistutilsGetoptError; any error on the
        command-line raises DistutilsArgError.  If no Distutils commands
        were found on the command line, raises DistutilsArgError.  Return
453
        true if command-line was successfully parsed and we should carry
454 455 456
        on with executing commands; false if no errors but we shouldn't
        execute commands (currently, this only happens if user asks for
        help).
457
        """
458
        #
459 460
        # We now have enough information to show the Macintosh dialog
        # that allows the user to interactively specify the "command line".
461
        #
462
        toplevel_options = self._get_toplevel_options()
Fred Drake's avatar
Fred Drake committed
463

464 465 466
        # We have to parse the command line a bit at a time -- global
        # options, then the first command, then its options, and so on --
        # because each command will be handled by a different class, and
467 468 469
        # the options that are valid for a particular class aren't known
        # until we have loaded the command class, which doesn't happen
        # until we know what the command is.
470 471

        self.commands = []
472
        parser = FancyGetopt(toplevel_options + self.display_options)
473
        parser.set_negative_aliases(self.negative_opt)
474
        parser.set_aliases({'licence': 'license'})
475
        args = parser.getopt(args=self.script_args, object=self)
476
        option_order = parser.get_option_order()
477
        log.set_verbosity(self.verbose)
478

479 480
        # for display options we return immediately
        if self.handle_display_options(option_order):
481 482
            return
        while args:
483 484
            args = self._parse_command_opts(parser, args)
            if args is None:            # user asked for help (and got it)
485 486
                return

487 488 489 490 491 492 493 494 495 496
        # Handle the cases of --help as a "global" option, ie.
        # "setup.py --help" and "setup.py --help command ...".  For the
        # former, we show global options (--verbose, --dry-run, etc.)
        # and display-only options (--name, --version, etc.); for the
        # latter, we omit the display-only options and show help for
        # each command listed on the command line.
        if self.help:
            self._show_help(parser,
                            display_options=len(self.commands) == 0,
                            commands=self.commands)
497 498 499 500
            return

        # Oops, no commands found -- an end-user error
        if not self.commands:
501
            raise DistutilsArgError("no commands supplied")
502 503

        # All is well: return true
504
        return True
505

506
    def _get_toplevel_options(self):
507 508 509 510 511 512 513 514 515 516
        """Return the non-display options recognized at the top level.

        This includes options that are recognized *only* at the top
        level as well as options recognized for commands.
        """
        return self.global_options + [
            ("command-packages=", None,
             "list of packages that provide distutils commands"),
            ]

517
    def _parse_command_opts(self, parser, args):
518 519 520 521 522 523 524 525 526 527 528 529 530
        """Parse the command-line options for a single command.
        'parser' must be a FancyGetopt instance; 'args' must be the list
        of arguments, starting with the current command (whose options
        we are about to parse).  Returns a new version of 'args' with
        the next command at the front of the list; will be the empty
        list if there are no more commands on the command line.  Returns
        None if the user asked for help on this command.
        """
        # late import because of mutual dependence between these modules
        from distutils.cmd import Command

        # Pull the current command from the head of the command line
        command = args[0]
531
        if not command_re.match(command):
532
            raise SystemExit("invalid command name '%s'" % command)
533
        self.commands.append(command)
534 535 536 537 538

        # Dig up the command class that implements this command, so we
        # 1) know that it's a valid command, and 2) know which options
        # it takes.
        try:
539
            cmd_class = self.get_command_class(command)
540
        except DistutilsModuleError as msg:
541
            raise DistutilsArgError(msg)
542 543 544

        # Require that the command class be derived from Command -- want
        # to be sure that the basic "command" interface is implemented.
545
        if not issubclass(cmd_class, Command):
546
            raise DistutilsClassError(
547
                "command class %s must subclass Command" % cmd_class)
548 549 550

        # Also make sure that the command object provides a list of its
        # known options.
551
        if not (hasattr(cmd_class, 'user_options') and
552
                isinstance(cmd_class.user_options, list)):
553 554 555
            msg = ("command class %s must provide "
                "'user_options' attribute (a list of tuples)")
            raise DistutilsClassError(msg % cmd_class)
556 557 558 559

        # If the command class has a list of negative alias options,
        # merge it in with the global negative aliases.
        negative_opt = self.negative_opt
560
        if hasattr(cmd_class, 'negative_opt'):
561
            negative_opt = negative_opt.copy()
562
            negative_opt.update(cmd_class.negative_opt)
563

Greg Ward's avatar
Greg Ward committed
564 565
        # Check for help_options in command class.  They have a different
        # format (tuple of four) so we need to preprocess them here.
566
        if (hasattr(cmd_class, 'help_options') and
567
                isinstance(cmd_class.help_options, list)):
568 569
            help_options = fix_help_options(cmd_class.help_options)
        else:
570
            help_options = []
571

572 573
        # All commands support the global options too, just by adding
        # in 'global_options'.
574 575 576 577 578
        parser.set_option_table(self.global_options +
                                cmd_class.user_options +
                                help_options)
        parser.set_negative_aliases(negative_opt)
        (args, opts) = parser.getopt(args[1:])
579
        if hasattr(opts, 'help') and opts.help:
580 581 582
            self._show_help(parser, display_options=0, commands=[cmd_class])
            return

583
        if (hasattr(cmd_class, 'help_options') and
584
                isinstance(cmd_class.help_options, list)):
585 586 587 588
            help_option_found=0
            for (help_option, short, desc, func) in cmd_class.help_options:
                if hasattr(opts, parser.get_attr_name(help_option)):
                    help_option_found=1
589
                    if callable(func):
590
                        func()
591
                    else:
592
                        raise DistutilsClassError(
593
                            "invalid help function %r for help option '%s': "
594
                            "must be a callable object (function, etc.)"
595
                            % (func, help_option))
596

Fred Drake's avatar
Fred Drake committed
597
            if help_option_found:
598
                return
599

600 601
        # Put the options from the command-line into their official
        # holding pen, the 'command_options' dictionary.
602
        opt_dict = self.get_option_dict(command)
603
        for (name, value) in vars(opts).items():
604
            opt_dict[name] = ("command line", value)
605 606 607

        return args

608
    def finalize_options(self):
609 610 611 612
        """Set final values for all the options on the Distribution
        instance, analogous to the .finalize_options() method of Command
        objects.
        """
613 614 615 616 617 618 619
        for attr in ('keywords', 'platforms'):
            value = getattr(self.metadata, attr)
            if value is None:
                continue
            if isinstance(value, str):
                value = [elm.strip() for elm in value.split(',')]
                setattr(self.metadata, attr, value)
620

621 622
    def _show_help(self, parser, global_options=1, display_options=1,
                   commands=[]):
623 624 625 626 627 628 629 630 631 632 633 634 635
        """Show help for the setup script command-line in the form of
        several lists of command-line options.  'parser' should be a
        FancyGetopt instance; do not expect it to be returned in the
        same state, as its option table will be reset to make it
        generate the correct help text.

        If 'global_options' is true, lists the global options:
        --verbose, --dry-run, etc.  If 'display_options' is true, lists
        the "display-only" options: --name, --version, etc.  Finally,
        lists per-command help for every command name or command class
        in 'commands'.
        """
        # late import because of mutual dependence between these modules
636
        from distutils.core import gen_usage
637 638 639
        from distutils.cmd import Command

        if global_options:
640 641 642 643 644
            if display_options:
                options = self._get_toplevel_options()
            else:
                options = self.global_options
            parser.set_option_table(options)
645
            parser.print_help(self.common_usage + "\nGlobal options:")
646
            print('')
647 648

        if display_options:
649 650
            parser.set_option_table(self.display_options)
            parser.print_help(
651 652
                "Information display options (just display " +
                "information, ignore any commands)")
653
            print('')
654 655

        for command in self.commands:
656
            if isinstance(command, type) and issubclass(command, Command):
657 658
                klass = command
            else:
659
                klass = self.get_command_class(command)
660
            if (hasattr(klass, 'help_options') and
661
                    isinstance(klass.help_options, list)):
662 663
                parser.set_option_table(klass.user_options +
                                        fix_help_options(klass.help_options))
664
            else:
665 666
                parser.set_option_table(klass.user_options)
            parser.print_help("Options for '%s' command:" % klass.__name__)
667
            print('')
668

669
        print(gen_usage(self.script_name))
670

671
    def handle_display_options(self, option_order):
672
        """If there were any non-global "display-only" options
673 674 675 676
        (--help-commands or the metadata display options) on the command
        line, display the requested info and return true; else return
        false.
        """
677
        from distutils.core import gen_usage
678 679 680 681 682

        # User just wants a list of commands -- we'll print it out and stop
        # processing now (ie. if they ran "setup --help-commands foo bar",
        # we ignore "foo bar").
        if self.help_commands:
683
            self.print_commands()
684 685
            print('')
            print(gen_usage(self.script_name))
686 687 688 689 690 691 692 693 694 695 696 697
            return 1

        # If user supplied any of the "display metadata" options, then
        # display that metadata in the order in which the user supplied the
        # metadata options.
        any_display_options = 0
        is_display_option = {}
        for option in self.display_options:
            is_display_option[option[0]] = 1

        for (opt, val) in option_order:
            if val and is_display_option.get(opt):
698
                opt = translate_longopt(opt)
699 700
                value = getattr(self.metadata, "get_"+opt)()
                if opt in ['keywords', 'platforms']:
701
                    print(','.join(value))
702 703
                elif opt in ('classifiers', 'provides', 'requires',
                             'obsoletes'):
704
                    print('\n'.join(value))
705
                else:
706
                    print(value)
707 708 709 710
                any_display_options = 1

        return any_display_options

711
    def print_command_list(self, commands, header, max_length):
712
        """Print a subset of the list of all commands -- used by
713 714
        'print_commands()'.
        """
715
        print(header + ":")
716 717

        for cmd in commands:
718
            klass = self.cmdclass.get(cmd)
719
            if not klass:
720
                klass = self.get_command_class(cmd)
721 722 723 724 725
            try:
                description = klass.description
            except AttributeError:
                description = "(no description available)"

726
            print("  %-*s  %s" % (max_length, cmd, description))
727

728
    def print_commands(self):
729 730 731 732 733 734 735
        """Print out a help message listing all available commands with a
        description of each.  The list is divided into "standard commands"
        (listed in distutils.command.__all__) and "extra commands"
        (mentioned in self.cmdclass, but not a standard command).  The
        descriptions come from the command class attribute
        'description'.
        """
736 737 738 739 740 741 742 743 744
        import distutils.command
        std_commands = distutils.command.__all__
        is_std = {}
        for cmd in std_commands:
            is_std[cmd] = 1

        extra_commands = []
        for cmd in self.cmdclass.keys():
            if not is_std.get(cmd):
745
                extra_commands.append(cmd)
746 747 748

        max_length = 0
        for cmd in (std_commands + extra_commands):
749 750
            if len(cmd) > max_length:
                max_length = len(cmd)
751

752 753 754
        self.print_command_list(std_commands,
                                "Standard commands",
                                max_length)
755
        if extra_commands:
756
            print()
757 758 759
            self.print_command_list(extra_commands,
                                    "Extra commands",
                                    max_length)
760

761
    def get_command_list(self):
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
        """Get a list of (command, description) tuples.
        The list is divided into "standard commands" (listed in
        distutils.command.__all__) and "extra commands" (mentioned in
        self.cmdclass, but not a standard command).  The descriptions come
        from the command class attribute 'description'.
        """
        # Currently this is only used on Mac OS, for the Mac-only GUI
        # Distutils interface (by Jack Jansen)
        import distutils.command
        std_commands = distutils.command.__all__
        is_std = {}
        for cmd in std_commands:
            is_std[cmd] = 1

        extra_commands = []
        for cmd in self.cmdclass.keys():
            if not is_std.get(cmd):
                extra_commands.append(cmd)

        rv = []
        for cmd in (std_commands + extra_commands):
            klass = self.cmdclass.get(cmd)
            if not klass:
                klass = self.get_command_class(cmd)
            try:
                description = klass.description
            except AttributeError:
                description = "(no description available)"
            rv.append((cmd, description))
        return rv
792 793 794

    # -- Command class/object methods ----------------------------------

795
    def get_command_packages(self):
796 797
        """Return a list of packages from which commands are loaded."""
        pkgs = self.command_packages
798 799 800 801
        if not isinstance(pkgs, list):
            if pkgs is None:
                pkgs = ''
            pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != '']
802 803 804 805 806
            if "distutils.command" not in pkgs:
                pkgs.insert(0, "distutils.command")
            self.command_packages = pkgs
        return pkgs

807
    def get_command_class(self, command):
808 809 810 811 812 813 814
        """Return the class that implements the Distutils command named by
        'command'.  First we check the 'cmdclass' dictionary; if the
        command is mentioned there, we fetch the class object from the
        dictionary and return it.  Otherwise we load the command module
        ("distutils.command." + command) and fetch the command class from
        the module.  The loaded class is also stored in 'cmdclass'
        to speed future calls to 'get_command_class()'.
815 816

        Raises DistutilsModuleError if the expected module could not be
817 818 819 820 821
        found, or if that module does not define the expected class.
        """
        klass = self.cmdclass.get(command)
        if klass:
            return klass
822

823 824 825
        for pkgname in self.get_command_packages():
            module_name = "%s.%s" % (pkgname, command)
            klass_name = command
826

827
            try:
828
                __import__(module_name)
829 830 831 832 833 834 835
                module = sys.modules[module_name]
            except ImportError:
                continue

            try:
                klass = getattr(module, klass_name)
            except AttributeError:
836
                raise DistutilsModuleError(
837 838
                    "invalid command '%s' (no class '%s' in module '%s')"
                    % (command, klass_name, module_name))
839 840 841 842 843

            self.cmdclass[command] = klass
            return klass

        raise DistutilsModuleError("invalid command '%s'" % command)
844

845
    def get_command_obj(self, command, create=1):
846
        """Return the command object for 'command'.  Normally this object
847
        is cached on a previous call to 'get_command_obj()'; if no command
848 849 850 851
        object for 'command' is in the cache, then we either create and
        return it (if 'create' is true) or return None.
        """
        cmd_obj = self.command_obj.get(command)
852
        if not cmd_obj and create:
853
            if DEBUG:
854
                self.announce("Distribution.get_command_obj(): "
855
                              "creating '%s' command object" % command)
856

857
            klass = self.get_command_class(command)
858 859 860 861 862 863 864 865 866 867
            cmd_obj = self.command_obj[command] = klass(self)
            self.have_run[command] = 0

            # Set any options that were supplied in config files
            # or on the command line.  (NB. support for error
            # reporting is lame here: any errors aren't reported
            # until 'finalize_options()' is called, which means
            # we won't report the source of the error.)
            options = self.command_options.get(command)
            if options:
868
                self._set_command_options(cmd_obj, options)
869 870 871

        return cmd_obj

872
    def _set_command_options(self, command_obj, option_dict=None):
873 874 875 876
        """Set the options for 'command_obj' from 'option_dict'.  Basically
        this means copying elements of a dictionary ('option_dict') to
        attributes of an instance ('command').

877
        'command_obj' must be a Command instance.  If 'option_dict' is not
878 879 880 881 882 883 884
        supplied, uses the standard option dictionary for this command
        (from 'self.command_options').
        """
        command_name = command_obj.get_command_name()
        if option_dict is None:
            option_dict = self.get_option_dict(command_name)

885 886
        if DEBUG:
            self.announce("  setting options for '%s' command:" % command_name)
887
        for (option, (source, value)) in option_dict.items():
888 889 890
            if DEBUG:
                self.announce("    %s = %s (from %s)" % (option, value,
                                                         source))
891
            try:
892 893
                bool_opts = [translate_longopt(o)
                             for o in command_obj.boolean_options]
894 895 896 897 898 899 900 901
            except AttributeError:
                bool_opts = []
            try:
                neg_opt = command_obj.negative_opt
            except AttributeError:
                neg_opt = {}

            try:
902
                is_string = isinstance(value, str)
903
                if option in neg_opt and is_string:
904
                    setattr(command_obj, neg_opt[option], not strtobool(value))
905
                elif option in bool_opts and is_string:
906 907 908 909
                    setattr(command_obj, option, strtobool(value))
                elif hasattr(command_obj, option):
                    setattr(command_obj, option, value)
                else:
910
                    raise DistutilsOptionError(
911 912
                        "error in %s: command '%s' has no such option '%s'"
                        % (source, command_name, option))
913
            except ValueError as msg:
914
                raise DistutilsOptionError(msg)
915

916
    def reinitialize_command(self, command, reinit_subcommands=0):
917 918
        """Reinitializes a command to the state it was in when first
        returned by 'get_command_obj()': ie., initialized but not yet
Greg Ward's avatar
Greg Ward committed
919
        finalized.  This provides the opportunity to sneak option
920 921 922 923
        values in programmatically, overriding or supplementing
        user-supplied values from the config files and command line.
        You'll have to re-finalize the command object (by calling
        'finalize_options()' or 'ensure_finalized()') before using it for
Fred Drake's avatar
Fred Drake committed
924
        real.
925

926 927 928 929 930 931 932
        'command' should be a command name (string) or command object.  If
        'reinit_subcommands' is true, also reinitializes the command's
        sub-commands, as declared by the 'sub_commands' class attribute (if
        it has one).  See the "install" command for an example.  Only
        reinitializes the sub-commands that actually matter, ie. those
        whose test predicates return true.

933 934 935 936 937 938 939 940 941 942
        Returns the reinitialized command object.
        """
        from distutils.cmd import Command
        if not isinstance(command, Command):
            command_name = command
            command = self.get_command_obj(command_name)
        else:
            command_name = command.get_command_name()

        if not command.finalized:
943
            return command
944 945
        command.initialize_options()
        command.finalized = 0
946
        self.have_run[command_name] = 0
947
        self._set_command_options(command)
948 949 950

        if reinit_subcommands:
            for sub in command.get_sub_commands():
Fred Drake's avatar
Fred Drake committed
951
                self.reinitialize_command(sub, reinit_subcommands)
952

953 954
        return command

955 956
    # -- Methods that operate on the Distribution ----------------------

957 958
    def announce(self, msg, level=log.INFO):
        log.log(level, msg)
959

960
    def run_commands(self):
961
        """Run each command that was seen on the setup script command line.
962
        Uses the list of commands found and cache of command objects
963 964
        created by 'get_command_obj()'.
        """
965
        for cmd in self.commands:
966
            self.run_command(cmd)
967 968 969

    # -- Methods that operate on its Commands --------------------------

970
    def run_command(self, command):
971
        """Do whatever it takes to run a command (including nothing at all,
972 973 974 975 976 977
        if the command has already been run).  Specifically: if we have
        already created and run the command named by 'command', return
        silently without doing anything.  If the command named by 'command'
        doesn't even have a command object yet, create one.  Then invoke
        'run()' on that command object (or an existing one).
        """
978
        # Already been here, done that? then return silently.
979
        if self.have_run.get(command):
980 981
            return

982
        log.info("running %s", command)
983 984 985
        cmd_obj = self.get_command_obj(command)
        cmd_obj.ensure_finalized()
        cmd_obj.run()
986 987 988 989
        self.have_run[command] = 1

    # -- Distribution query methods ------------------------------------

990
    def has_pure_modules(self):
991
        return len(self.packages or self.py_modules or []) > 0
992

993
    def has_ext_modules(self):
994
        return self.ext_modules and len(self.ext_modules) > 0
995

996
    def has_c_libraries(self):
997
        return self.libraries and len(self.libraries) > 0
998

999
    def has_modules(self):
1000 1001
        return self.has_pure_modules() or self.has_ext_modules()

1002
    def has_headers(self):
1003 1004
        return self.headers and len(self.headers) > 0

1005
    def has_scripts(self):
1006 1007
        return self.scripts and len(self.scripts) > 0

1008
    def has_data_files(self):
1009 1010
        return self.data_files and len(self.data_files) > 0

1011
    def is_pure(self):
1012 1013 1014 1015
        return (self.has_pure_modules() and
                not self.has_ext_modules() and
                not self.has_c_libraries())

1016 1017 1018 1019 1020 1021 1022 1023 1024
    # -- Metadata query methods ----------------------------------------

    # If you're looking for 'get_name()', 'get_version()', and so forth,
    # they are defined in a sneaky way: the constructor binds self.get_XXX
    # to self.metadata.get_XXX.  The actual code is in the
    # DistributionMetadata class, below.

class DistributionMetadata:
    """Dummy class to hold the distribution meta-data: name, version,
1025 1026
    author, and so forth.
    """
1027

1028 1029 1030 1031
    _METHOD_BASENAMES = ("name", "version", "author", "author_email",
                         "maintainer", "maintainer_email", "url",
                         "license", "description", "long_description",
                         "keywords", "platforms", "fullname", "contact",
1032
                         "contact_email", "classifiers", "download_url",
1033 1034 1035
                         # PEP 314
                         "provides", "requires", "obsoletes",
                         )
1036

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
    def __init__(self, path=None):
        if path is not None:
            self.read_pkg_file(open(path))
        else:
            self.name = None
            self.version = None
            self.author = None
            self.author_email = None
            self.maintainer = None
            self.maintainer_email = None
            self.url = None
            self.license = None
            self.description = None
            self.long_description = None
            self.keywords = None
            self.platforms = None
            self.classifiers = None
            self.download_url = None
            # PEP 314
            self.provides = None
            self.requires = None
            self.obsoletes = None

    def read_pkg_file(self, file):
        """Reads the metadata values from a file object."""
        msg = message_from_file(file)

        def _read_field(name):
            value = msg[name]
            if value == 'UNKNOWN':
                return None
            return value

        def _read_list(name):
            values = msg.get_all(name, None)
            if values == []:
                return None
            return values

        metadata_version = msg['metadata-version']
        self.name = _read_field('name')
        self.version = _read_field('version')
        self.description = _read_field('summary')
        # we are filling author only.
        self.author = _read_field('author')
1082
        self.maintainer = None
1083
        self.author_email = _read_field('author-email')
1084
        self.maintainer_email = None
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
        self.url = _read_field('home-page')
        self.license = _read_field('license')

        if 'download-url' in msg:
            self.download_url = _read_field('download-url')
        else:
            self.download_url = None

        self.long_description = _read_field('description')
        self.description = _read_field('summary')

        if 'keywords' in msg:
            self.keywords = _read_field('keywords').split(',')

        self.platforms = _read_list('platform')
        self.classifiers = _read_list('classifier')

        # PEP 314 - these fields only exist in 1.1
        if metadata_version == '1.1':
            self.requires = _read_list('requires')
            self.provides = _read_list('provides')
            self.obsoletes = _read_list('obsoletes')
        else:
            self.requires = None
            self.provides = None
            self.obsoletes = None
Fred Drake's avatar
Fred Drake committed
1111

1112
    def write_pkg_info(self, base_dir):
1113 1114
        """Write the PKG-INFO file into the release tree.
        """
1115 1116
        with open(os.path.join(base_dir, 'PKG-INFO'), 'w',
                  encoding='UTF-8') as pkg_info:
1117
            self.write_pkg_file(pkg_info)
1118

1119
    def write_pkg_file(self, file):
1120 1121 1122
        """Write the PKG-INFO format data to a file object.
        """
        version = '1.0'
1123
        if (self.provides or self.requires or self.obsoletes or
1124
                self.classifiers or self.download_url):
1125 1126 1127
            version = '1.1'

        file.write('Metadata-Version: %s\n' % version)
1128 1129 1130 1131 1132 1133 1134
        file.write('Name: %s\n' % self.get_name())
        file.write('Version: %s\n' % self.get_version())
        file.write('Summary: %s\n' % self.get_description())
        file.write('Home-page: %s\n' % self.get_url())
        file.write('Author: %s\n' % self.get_contact())
        file.write('Author-email: %s\n' % self.get_contact_email())
        file.write('License: %s\n' % self.get_license())
1135
        if self.download_url:
1136
            file.write('Download-URL: %s\n' % self.download_url)
1137

1138
        long_desc = rfc822_escape(self.get_long_description())
1139
        file.write('Description: %s\n' % long_desc)
1140

1141
        keywords = ','.join(self.get_keywords())
1142
        if keywords:
1143
            file.write('Keywords: %s\n' % keywords)
1144

1145 1146
        self._write_list(file, 'Platform', self.get_platforms())
        self._write_list(file, 'Classifier', self.get_classifiers())
1147

1148 1149 1150 1151
        # PEP 314
        self._write_list(file, 'Requires', self.get_requires())
        self._write_list(file, 'Provides', self.get_provides())
        self._write_list(file, 'Obsoletes', self.get_obsoletes())
Fred Drake's avatar
Fred Drake committed
1152

1153
    def _write_list(self, file, name, values):
1154 1155
        for value in values:
            file.write('%s: %s\n' % (name, value))
Fred Drake's avatar
Fred Drake committed
1156

1157 1158
    # -- Metadata query methods ----------------------------------------

1159
    def get_name(self):
1160 1161
        return self.name or "UNKNOWN"

1162
    def get_version(self):
1163
        return self.version or "0.0.0"
1164

1165
    def get_fullname(self):
1166 1167 1168 1169
        return "%s-%s" % (self.get_name(), self.get_version())

    def get_author(self):
        return self.author or "UNKNOWN"
1170

1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
    def get_author_email(self):
        return self.author_email or "UNKNOWN"

    def get_maintainer(self):
        return self.maintainer or "UNKNOWN"

    def get_maintainer_email(self):
        return self.maintainer_email or "UNKNOWN"

    def get_contact(self):
1181
        return self.maintainer or self.author or "UNKNOWN"
1182 1183

    def get_contact_email(self):
1184
        return self.maintainer_email or self.author_email or "UNKNOWN"
1185 1186 1187 1188

    def get_url(self):
        return self.url or "UNKNOWN"

1189 1190 1191
    def get_license(self):
        return self.license or "UNKNOWN"
    get_licence = get_license
Fred Drake's avatar
Fred Drake committed
1192

1193 1194
    def get_description(self):
        return self.description or "UNKNOWN"
1195 1196 1197 1198

    def get_long_description(self):
        return self.long_description or "UNKNOWN"

1199 1200 1201
    def get_keywords(self):
        return self.keywords or []

1202
    def set_keywords(self, value):
1203
        self.keywords = _ensure_list(value, 'keywords')
1204

1205 1206 1207
    def get_platforms(self):
        return self.platforms or ["UNKNOWN"]

1208
    def set_platforms(self, value):
1209
        self.platforms = _ensure_list(value, 'platforms')
1210

1211 1212 1213
    def get_classifiers(self):
        return self.classifiers or []

1214
    def set_classifiers(self, value):
1215
        self.classifiers = _ensure_list(value, 'classifiers')
1216

1217 1218 1219
    def get_download_url(self):
        return self.download_url or "UNKNOWN"

1220 1221 1222 1223 1224 1225 1226 1227
    # PEP 314
    def get_requires(self):
        return self.requires or []

    def set_requires(self, value):
        import distutils.versionpredicate
        for v in value:
            distutils.versionpredicate.VersionPredicate(v)
1228
        self.requires = list(value)
1229 1230 1231 1232 1233 1234 1235 1236

    def get_provides(self):
        return self.provides or []

    def set_provides(self, value):
        value = [v.strip() for v in value]
        for v in value:
            import distutils.versionpredicate
1237
            distutils.versionpredicate.split_provision(v)
1238 1239 1240 1241 1242 1243 1244 1245 1246
        self.provides = value

    def get_obsoletes(self):
        return self.obsoletes or []

    def set_obsoletes(self, value):
        import distutils.versionpredicate
        for v in value:
            distutils.versionpredicate.VersionPredicate(v)
1247
        self.obsoletes = list(value)
1248

1249
def fix_help_options(options):
1250 1251 1252 1253 1254 1255 1256
    """Convert a 4-tuple 'help_options' list as found in various command
    classes to the 3-tuple form required by FancyGetopt.
    """
    new_options = []
    for help_tuple in options:
        new_options.append(help_tuple[0:3])
    return new_options