You need to sign in or sign up before continuing.
dist.py 45.8 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

__revision__ = "$Id$"

9
import sys, os, re
10
from copy import copy
11 12 13

try:
    import warnings
Andrew M. Kuchling's avatar
Andrew M. Kuchling committed
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 27 28 29 30

# 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.
command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$')


class Distribution:
31 32 33 34 35 36 37 38 39 40 41 42
    """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.
    """
43 44 45


    # 'global_options' describes the command-line options that may be
46 47
    # supplied to the setup script prior to any actual commands.
    # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of
48 49 50 51
    # 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.
52 53
    # The fourth entry for verbose means that it can be repeated.
    global_options = [('verbose', 'v', "run verbosely (default)", 1),
54 55 56
                      ('quiet', 'q', "run quietly (turns verbosity off)"),
                      ('dry-run', 'n', "don't actually do anything"),
                      ('help', 'h', "show detailed help message"),
57
                     ]
58

59 60 61 62 63 64 65 66 67
    # '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
"""

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
    # 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,
87
         "print the maintainer's name if known, else the author's"),
88
        ('contact-email', None,
89
         "print the maintainer's email address if known, else the author's"),
90 91 92
        ('url', None,
         "print the URL for this package"),
        ('license', None,
93 94 95
         "print the license of the package"),
        ('licence', None,
         "alias for --license"),
96 97
        ('description', None,
         "print the package description"),
98 99
        ('long-description', None,
         "print the long package description"),
100 101
        ('platforms', None,
         "print the list of platforms"),
102 103
        ('classifiers', None,
         "print the list of classifiers"),
104 105
        ('keywords', None,
         "print the list of keywords"),
106 107 108 109 110 111
        ('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")
112
        ]
113
    display_option_names = [translate_longopt(x[0]) for x in display_options]
114 115

    # negative options are options that exclude other options
116 117 118 119
    negative_opt = {'quiet': 'verbose'}


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

121 122
    def __init__ (self, attrs=None):
        """Construct a new Distribution instance: initialize all the
123 124 125 126 127 128 129 130
        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()'.
        """
131 132 133 134 135

        # Default values for our command-line options
        self.verbose = 1
        self.dry_run = 0
        self.help = 0
136 137 138 139 140 141 142 143
        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.
144
        self.metadata = DistributionMetadata()
145
        for basename in self.metadata._METHOD_BASENAMES:
146 147
            method_name = "get_" + basename
            setattr(self, method_name, getattr(self.metadata, method_name))
148 149 150 151

        # '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
152
        # for the setup script to override command classes
153 154
        self.cmdclass = {}

155 156 157 158 159 160 161 162
        # '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

163 164 165 166 167 168
        # '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

169 170 171 172 173
        # '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) } }
174 175
        self.command_options = {}

176 177 178 179 180 181 182 183 184
        # '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.
185 186
        self.dist_files = []

187 188 189 190
        # 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
Fred Drake's avatar
Fred Drake committed
191
        self.package_data = {}
192 193 194
        self.package_dir = None
        self.py_modules = None
        self.libraries = None
195
        self.headers = None
196 197 198 199
        self.ext_modules = None
        self.ext_package = None
        self.include_dirs = None
        self.extra_path = None
200
        self.scripts = None
201
        self.data_files = None
202
        self.password = ''
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

        # 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
218
        # the command is successfully run.  Thus it's probably best to use
219 220 221 222
        # '.get()' rather than a straight lookup.
        self.have_run = {}

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

226 227 228 229 230
        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.
231
            options = attrs.get('options')
232
            if options is not None:
233 234
                del attrs['options']
                for (command, cmd_options) in options.items():
235 236 237
                    opt_dict = self.get_option_dict(command)
                    for (opt, val) in cmd_options.items():
                        opt_dict[opt] = ("setup script", val)
238

239
            if 'licence' in attrs:
240 241 242 243 244 245 246 247
                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")

248 249 250
            # Now work on the rest of the attributes.  Any attribute that's
            # not already defined is invalid!
            for (key,val) in attrs.items():
251 252 253
                if hasattr(self.metadata, "set_" + key):
                    getattr(self.metadata, "set_" + key)(val)
                elif hasattr(self.metadata, key):
254 255 256
                    setattr(self.metadata, key, val)
                elif hasattr(self, key):
                    setattr(self, key, val)
Anthony Baxter's avatar
Anthony Baxter committed
257
                else:
258 259 260 261 262
                    msg = "Unknown distribution option: %s" % repr(key)
                    if warnings is not None:
                        warnings.warn(msg)
                    else:
                        sys.stderr.write(msg + "\n")
263

264
        self.finalize_options()
Fred Drake's avatar
Fred Drake committed
265

266

267 268 269 270 271 272 273 274 275 276 277 278 279
    def get_option_dict (self, command):
        """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


280 281 282 283
    def dump_option_dicts (self, header=None, commands=None, indent=""):
        from pprint import pformat

        if commands is None:             # dump all command option dicts
284
            commands = sorted(self.command_options.keys())
285 286

        if header is not None:
287
            print(indent + header)
288 289 290
            indent = indent + "  "

        if not commands:
291
            print(indent + "no commands known yet")
292 293 294 295 296
            return

        for cmd_name in commands:
            opt_dict = self.command_options.get(cmd_name)
            if opt_dict is None:
297
                print(indent + "no option dict for '%s' command" % cmd_name)
298
            else:
299
                print(indent + "option dict for '%s' command:" % cmd_name)
300
                out = pformat(opt_dict)
301
                for line in out.split("\n"):
302
                    print(indent + "  " + line)
303 304 305



306 307
    # -- Config file finding/parsing methods ---------------------------

308 309 310 311 312 313
    def find_config_files (self):
        """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).

314 315 316 317 318
        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
        on Windows/Mac, and setup.cfg in the current directory.
319
        """
320
        files = []
Greg Ward's avatar
Greg Ward committed
321 322
        check_environ()

323 324 325 326 327 328 329 330 331 332
        # 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':
333 334 335
            user_filename = ".pydistutils.cfg"
        else:
            user_filename = "pydistutils.cfg"
Greg Ward's avatar
Greg Ward committed
336

337
        # And look for the user config file
Alexandre Vassalotti's avatar
Alexandre Vassalotti committed
338 339 340
        user_file = os.path.join(os.path.expanduser('~'), user_filename)
        if os.path.isfile(user_file):
            files.append(user_file)
341 342 343 344 345 346 347 348 349 350 351

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

        return files


    def parse_config_files (self, filenames=None):

352
        from configparser import ConfigParser
353 354 355 356

        if filenames is None:
            filenames = self.find_config_files()

357
        if DEBUG: print("Distribution.parse_config_files():")
358

359
        parser = ConfigParser()
360
        for filename in filenames:
361
            if DEBUG: print("  reading", filename)
362 363 364
            parser.read(filename)
            for section in parser.sections():
                options = parser.options(section)
365
                opt_dict = self.get_option_dict(section)
366 367 368

                for opt in options:
                    if opt != '__name__':
369
                        val = parser.get(section,opt)
370
                        opt = opt.replace('-', '_')
371
                        opt_dict[opt] = (filename, val)
372

373
            # Make the ConfigParser forget everything (so we retain
374
            # the original filenames that options come from)
375
            parser.__init__()
376

377 378 379
        # If there was a "global" section in the config file, use it
        # to set Distribution options.

380
        if 'global' in self.command_options:
381 382 383 384 385 386 387
            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))
388 389
                    else:
                        setattr(self, opt, val)
390
                except ValueError as msg:
391
                    raise DistutilsOptionError(msg)
392

393

394
    # -- Command-line parsing methods ----------------------------------
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409
    def parse_command_line (self):
        """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
410
        true if command-line was successfully parsed and we should carry
411 412 413
        on with executing commands; false if no errors but we shouldn't
        execute commands (currently, this only happens if user asks for
        help).
414
        """
415
        #
416 417
        # We now have enough information to show the Macintosh dialog
        # that allows the user to interactively specify the "command line".
418
        #
419
        toplevel_options = self._get_toplevel_options()
420 421 422 423
        if sys.platform == 'mac':
            import EasyDialogs
            cmdlist = self.get_command_list()
            self.script_args = EasyDialogs.GetArgv(
424
                toplevel_options + self.display_options, cmdlist)
Fred Drake's avatar
Fred Drake committed
425

426 427 428
        # 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
429 430 431
        # 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.
432 433

        self.commands = []
434
        parser = FancyGetopt(toplevel_options + self.display_options)
435
        parser.set_negative_aliases(self.negative_opt)
436
        parser.set_aliases({'licence': 'license'})
437
        args = parser.getopt(args=self.script_args, object=self)
438
        option_order = parser.get_option_order()
439
        log.set_verbosity(self.verbose)
440

441 442
        # for display options we return immediately
        if self.handle_display_options(option_order):
443
            return
Fred Drake's avatar
Fred Drake committed
444

445
        while args:
446 447
            args = self._parse_command_opts(parser, args)
            if args is None:            # user asked for help (and got it)
448 449
                return

450 451 452 453 454 455 456 457 458 459
        # 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)
460 461 462 463
            return

        # Oops, no commands found -- an end-user error
        if not self.commands:
464
            raise DistutilsArgError("no commands supplied")
465 466

        # All is well: return true
467
        return True
468

469 470 471 472 473 474 475 476 477 478 479
    def _get_toplevel_options (self):
        """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"),
            ]

480 481 482 483 484 485 486 487 488 489 490 491 492 493
    def _parse_command_opts (self, parser, args):
        """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]
494
        if not command_re.match(command):
495
            raise SystemExit("invalid command name '%s'" % command)
496
        self.commands.append(command)
497 498 499 500 501

        # 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:
502
            cmd_class = self.get_command_class(command)
503
        except DistutilsModuleError as msg:
504
            raise DistutilsArgError(msg)
505 506 507

        # Require that the command class be derived from Command -- want
        # to be sure that the basic "command" interface is implemented.
508
        if not issubclass(cmd_class, Command):
509 510
            raise DistutilsClassError(
                  "command class %s must subclass Command" % cmd_class)
511 512 513

        # Also make sure that the command object provides a list of its
        # known options.
514
        if not (hasattr(cmd_class, 'user_options') and
515
                isinstance(cmd_class.user_options, list)):
516
            raise DistutilsClassError(("command class %s must provide " +
517
                   "'user_options' attribute (a list of tuples)") % \
518
                  cmd_class)
519 520 521 522

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

Greg Ward's avatar
Greg Ward committed
527 528
        # Check for help_options in command class.  They have a different
        # format (tuple of four) so we need to preprocess them here.
529
        if (hasattr(cmd_class, 'help_options') and
530
            isinstance(cmd_class.help_options, list)):
531 532
            help_options = fix_help_options(cmd_class.help_options)
        else:
533
            help_options = []
534

535

536 537
        # All commands support the global options too, just by adding
        # in 'global_options'.
538 539 540 541 542
        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:])
543
        if hasattr(opts, 'help') and opts.help:
544 545 546
            self._show_help(parser, display_options=0, commands=[cmd_class])
            return

547
        if (hasattr(cmd_class, 'help_options') and
548
            isinstance(cmd_class.help_options, list)):
549 550 551 552
            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
Greg Ward's avatar
Greg Ward committed
553
                    #print "showing help for option %s of command %s" % \
554
                    #      (help_option[0],cmd_class)
555

556
                    if hasattr(func, '__call__'):
557
                        func()
558
                    else:
559
                        raise DistutilsClassError(
560
                            "invalid help function %r for help option '%s': "
561
                            "must be a callable object (function, etc.)"
562
                            % (func, help_option))
563

Fred Drake's avatar
Fred Drake committed
564
            if help_option_found:
565
                return
566

567 568
        # Put the options from the command-line into their official
        # holding pen, the 'command_options' dictionary.
569
        opt_dict = self.get_option_dict(command)
570
        for (name, value) in vars(opts).items():
571
            opt_dict[name] = ("command line", value)
572 573 574

        return args

575 576 577 578 579 580 581 582
    def finalize_options (self):
        """Set final values for all the options on the Distribution
        instance, analogous to the .finalize_options() method of Command
        objects.
        """

        keywords = self.metadata.keywords
        if keywords is not None:
583
            if isinstance(keywords, str):
584 585
                keywordlist = keywords.split(',')
                self.metadata.keywords = [x.strip() for x in keywordlist]
586 587 588

        platforms = self.metadata.platforms
        if platforms is not None:
589
            if isinstance(platforms, str):
590 591
                platformlist = platforms.split(',')
                self.metadata.platforms = [x.strip() for x in platformlist]
592

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
    def _show_help (self,
                    parser,
                    global_options=1,
                    display_options=1,
                    commands=[]):
        """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
611
        from distutils.core import gen_usage
612 613 614
        from distutils.cmd import Command

        if global_options:
615 616 617 618 619
            if display_options:
                options = self._get_toplevel_options()
            else:
                options = self.global_options
            parser.set_option_table(options)
620
            parser.print_help(self.common_usage + "\nGlobal options:")
621
            print()
622 623

        if display_options:
624 625
            parser.set_option_table(self.display_options)
            parser.print_help(
626 627
                "Information display options (just display " +
                "information, ignore any commands)")
628
            print()
629 630

        for command in self.commands:
631
            if isinstance(command, type) and issubclass(command, Command):
632 633
                klass = command
            else:
634
                klass = self.get_command_class(command)
635
            if (hasattr(klass, 'help_options') and
636
                isinstance(klass.help_options, list)):
637 638
                parser.set_option_table(klass.user_options +
                                        fix_help_options(klass.help_options))
639
            else:
640 641
                parser.set_option_table(klass.user_options)
            parser.print_help("Options for '%s' command:" % klass.__name__)
642
            print()
643

644
        print(gen_usage(self.script_name))
645 646 647
        return


648 649
    def handle_display_options (self, option_order):
        """If there were any non-global "display-only" options
650 651 652 653
        (--help-commands or the metadata display options) on the command
        line, display the requested info and return true; else return
        false.
        """
654
        from distutils.core import gen_usage
655 656 657 658 659

        # 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:
660
            self.print_commands()
661 662
            print()
            print(gen_usage(self.script_name))
663 664 665 666 667 668 669 670 671 672 673 674
            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):
675
                opt = translate_longopt(opt)
676 677
                value = getattr(self.metadata, "get_"+opt)()
                if opt in ['keywords', 'platforms']:
678
                    print(','.join(value))
679 680
                elif opt in ('classifiers', 'provides', 'requires',
                             'obsoletes'):
681
                    print('\n'.join(value))
682
                else:
683
                    print(value)
684 685 686 687
                any_display_options = 1

        return any_display_options

688 689
    def print_command_list (self, commands, header, max_length):
        """Print a subset of the list of all commands -- used by
690 691
        'print_commands()'.
        """
692
        print(header + ":")
693 694

        for cmd in commands:
695
            klass = self.cmdclass.get(cmd)
696
            if not klass:
697
                klass = self.get_command_class(cmd)
698 699 700 701 702
            try:
                description = klass.description
            except AttributeError:
                description = "(no description available)"

703
            print("  %-*s  %s" % (max_length, cmd, description))
704 705 706


    def print_commands (self):
707 708 709 710 711 712 713
        """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'.
        """
714 715 716 717 718 719 720 721 722
        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):
723
                extra_commands.append(cmd)
724 725 726

        max_length = 0
        for cmd in (std_commands + extra_commands):
727 728
            if len(cmd) > max_length:
                max_length = len(cmd)
729

730 731 732
        self.print_command_list(std_commands,
                                "Standard commands",
                                max_length)
733
        if extra_commands:
734
            print()
735 736 737
            self.print_command_list(extra_commands,
                                    "Extra commands",
                                    max_length)
738

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    def get_command_list (self):
        """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
770 771 772

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

773 774 775 776
    def get_command_packages (self):
        """Return a list of packages from which commands are loaded."""
        pkgs = self.command_packages
        if not isinstance(pkgs, type([])):
777
            pkgs = (pkgs or "").split(",")
778
            for i in range(len(pkgs)):
779
                pkgs[i] = pkgs[i].strip()
780
            pkgs = [p for p in pkgs if p]
781 782 783 784 785
            if "distutils.command" not in pkgs:
                pkgs.insert(0, "distutils.command")
            self.command_packages = pkgs
        return pkgs

786 787 788 789 790 791 792 793
    def get_command_class (self, command):
        """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()'.
794 795

        Raises DistutilsModuleError if the expected module could not be
796 797 798 799 800
        found, or if that module does not define the expected class.
        """
        klass = self.cmdclass.get(command)
        if klass:
            return klass
801

802 803 804
        for pkgname in self.get_command_packages():
            module_name = "%s.%s" % (pkgname, command)
            klass_name = command
805

806 807 808 809 810 811 812 813 814
            try:
                __import__ (module_name)
                module = sys.modules[module_name]
            except ImportError:
                continue

            try:
                klass = getattr(module, klass_name)
            except AttributeError:
815 816 817
                raise DistutilsModuleError(
                      "invalid command '%s' (no class '%s' in module '%s')"
                      % (command, klass_name, module_name))
818 819 820 821 822

            self.cmdclass[command] = klass
            return klass

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

824 825
    def get_command_obj (self, command, create=1):
        """Return the command object for 'command'.  Normally this object
826
        is cached on a previous call to 'get_command_obj()'; if no command
827 828 829 830
        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)
831
        if not cmd_obj and create:
832
            if DEBUG:
833 834
                print("Distribution.get_command_obj(): " \
                      "creating '%s' command object" % command)
835

836
            klass = self.get_command_class(command)
837 838 839 840 841 842 843 844 845 846
            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:
847
                self._set_command_options(cmd_obj, options)
848 849 850

        return cmd_obj

851 852 853 854 855
    def _set_command_options (self, command_obj, option_dict=None):
        """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').

856
        'command_obj' must be a Command instance.  If 'option_dict' is not
857 858 859 860 861 862 863
        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)

864
        if DEBUG: print("  setting options for '%s' command:" % command_name)
865
        for (option, (source, value)) in option_dict.items():
866
            if DEBUG: print("    %s = %s (from %s)" % (option, value, source))
867
            try:
868 869
                bool_opts = [translate_longopt(o)
                             for o in command_obj.boolean_options]
870 871 872 873 874 875 876 877
            except AttributeError:
                bool_opts = []
            try:
                neg_opt = command_obj.negative_opt
            except AttributeError:
                neg_opt = {}

            try:
878
                is_string = isinstance(value, str)
879
                if option in neg_opt and is_string:
880
                    setattr(command_obj, neg_opt[option], not strtobool(value))
881
                elif option in bool_opts and is_string:
882 883 884 885
                    setattr(command_obj, option, strtobool(value))
                elif hasattr(command_obj, option):
                    setattr(command_obj, option, value)
                else:
886 887 888
                    raise DistutilsOptionError(
                          "error in %s: command '%s' has no such option '%s'"
                          % (source, command_name, option))
889
            except ValueError as msg:
890
                raise DistutilsOptionError(msg)
891

892
    def reinitialize_command (self, command, reinit_subcommands=0):
893 894
        """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
895
        finalized.  This provides the opportunity to sneak option
896 897 898 899
        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
900
        real.
901

902 903 904 905 906 907 908
        '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.

909 910 911 912 913 914 915 916 917 918
        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:
919
            return command
920 921
        command.initialize_options()
        command.finalized = 0
922
        self.have_run[command_name] = 0
923
        self._set_command_options(command)
924 925 926

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

929 930
        return command

Fred Drake's avatar
Fred Drake committed
931

932 933 934
    # -- Methods that operate on the Distribution ----------------------

    def announce (self, msg, level=1):
935
        log.debug(msg)
936 937

    def run_commands (self):
938
        """Run each command that was seen on the setup script command line.
939
        Uses the list of commands found and cache of command objects
940 941
        created by 'get_command_obj()'.
        """
942
        for cmd in self.commands:
943
            self.run_command(cmd)
944 945 946 947 948 949


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

    def run_command (self, command):
        """Do whatever it takes to run a command (including nothing at all,
950 951 952 953 954 955
        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).
        """
956
        # Already been here, done that? then return silently.
957
        if self.have_run.get(command):
958 959
            return

960
        log.info("running %s", command)
961 962 963
        cmd_obj = self.get_command_obj(command)
        cmd_obj.ensure_finalized()
        cmd_obj.run()
964 965 966 967 968 969
        self.have_run[command] = 1


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

    def has_pure_modules (self):
970
        return len(self.packages or self.py_modules or []) > 0
971 972

    def has_ext_modules (self):
973
        return self.ext_modules and len(self.ext_modules) > 0
974 975

    def has_c_libraries (self):
976
        return self.libraries and len(self.libraries) > 0
977 978 979 980

    def has_modules (self):
        return self.has_pure_modules() or self.has_ext_modules()

981 982 983
    def has_headers (self):
        return self.headers and len(self.headers) > 0

984 985 986 987 988 989
    def has_scripts (self):
        return self.scripts and len(self.scripts) > 0

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

990 991 992 993 994
    def is_pure (self):
        return (self.has_pure_modules() and
                not self.has_ext_modules() and
                not self.has_c_libraries())

995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
    # -- 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 Distribution


class DistributionMetadata:
    """Dummy class to hold the distribution meta-data: name, version,
1007 1008
    author, and so forth.
    """
1009

1010 1011 1012 1013
    _METHOD_BASENAMES = ("name", "version", "author", "author_email",
                         "maintainer", "maintainer_email", "url",
                         "license", "description", "long_description",
                         "keywords", "platforms", "fullname", "contact",
1014
                         "contact_email", "license", "classifiers",
1015 1016 1017 1018
                         "download_url",
                         # PEP 314
                         "provides", "requires", "obsoletes",
                         )
1019

1020 1021 1022 1023 1024 1025 1026 1027
    def __init__ (self):
        self.name = None
        self.version = None
        self.author = None
        self.author_email = None
        self.maintainer = None
        self.maintainer_email = None
        self.url = None
1028
        self.license = None
1029
        self.description = None
1030
        self.long_description = None
1031 1032
        self.keywords = None
        self.platforms = None
1033
        self.classifiers = None
1034
        self.download_url = None
1035 1036 1037 1038
        # PEP 314
        self.provides = None
        self.requires = None
        self.obsoletes = None
Fred Drake's avatar
Fred Drake committed
1039

1040 1041 1042 1043 1044
    def write_pkg_info (self, base_dir):
        """Write the PKG-INFO file into the release tree.
        """
        pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w')

1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
        self.write_pkg_file(pkg_info)

        pkg_info.close()

    def write_pkg_file (self, file):
        """Write the PKG-INFO format data to a file object.
        """
        version = '1.0'
        if self.provides or self.requires or self.obsoletes:
            version = '1.1'

        file.write('Metadata-Version: %s\n' % version)
        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() )
1064
        if self.download_url:
1065
            file.write('Download-URL: %s\n' % self.download_url)
1066 1067

        long_desc = rfc822_escape( self.get_long_description() )
1068
        file.write('Description: %s\n' % long_desc)
1069

1070
        keywords = ','.join(self.get_keywords())
1071
        if keywords:
1072
            file.write('Keywords: %s\n' % keywords )
1073

1074 1075
        self._write_list(file, 'Platform', self.get_platforms())
        self._write_list(file, 'Classifier', self.get_classifiers())
1076

1077 1078 1079 1080
        # 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
1081

1082 1083 1084
    def _write_list (self, file, name, values):
        for value in values:
            file.write('%s: %s\n' % (name, value))
Fred Drake's avatar
Fred Drake committed
1085

1086 1087
    # -- Metadata query methods ----------------------------------------

1088 1089 1090
    def get_name (self):
        return self.name or "UNKNOWN"

1091
    def get_version(self):
1092
        return self.version or "0.0.0"
1093 1094 1095 1096 1097 1098

    def get_fullname (self):
        return "%s-%s" % (self.get_name(), self.get_version())

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

1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
    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):
        return (self.maintainer or
                self.author or
                "UNKNOWN")

    def get_contact_email(self):
        return (self.maintainer_email or
                self.author_email or
                "UNKNOWN")

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

1122 1123 1124
    def get_license(self):
        return self.license or "UNKNOWN"
    get_licence = get_license
Fred Drake's avatar
Fred Drake committed
1125

1126 1127
    def get_description(self):
        return self.description or "UNKNOWN"
1128 1129 1130 1131

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

1132 1133 1134 1135 1136 1137
    def get_keywords(self):
        return self.keywords or []

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

1138 1139 1140
    def get_classifiers(self):
        return self.classifiers or []

1141 1142 1143
    def get_download_url(self):
        return self.download_url or "UNKNOWN"

1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
    # 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)
        self.requires = value

    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
1162
            distutils.versionpredicate.split_provision(v)
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
        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)
        self.obsoletes = value

1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184

def fix_help_options (options):
    """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


1185
if __name__ == "__main__":
1186
    dist = Distribution()
1187
    print("ok")