blob: 5c390ce9d19780fdbdadd9aeed842cd2ff148466 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Class representing the distribution being built/installed/etc."""
2
3import os
4import re
5
6from packaging.errors import (PackagingOptionError, PackagingArgError,
7 PackagingModuleError, PackagingClassError)
8from packaging.fancy_getopt import FancyGetopt
9from packaging.util import strtobool, resolve_name
10from packaging import logger
11from packaging.metadata import Metadata
12from packaging.config import Config
13from packaging.command import get_command_class, STANDARD_COMMANDS
14
15# Regex to define acceptable Packaging command names. This is not *quite*
16# the same as a Python NAME -- I don't allow leading underscores. The fact
17# that they're very similar is no coincidence; the default naming scheme is
18# to look for a Python module named after the command.
19command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
20
21USAGE = """\
22usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
23 or: %(script)s --help [cmd1 cmd2 ...]
24 or: %(script)s --help-commands
25 or: %(script)s cmd --help
26"""
27
28
29def gen_usage(script_name):
30 script = os.path.basename(script_name)
31 return USAGE % {'script': script}
32
33
34class Distribution:
35 """The core of the Packaging. Most of the work hiding behind 'setup'
36 is really done within a Distribution instance, which farms the work out
37 to the Packaging commands specified on the command line.
38
39 Setup scripts will almost never instantiate Distribution directly,
40 unless the 'setup()' function is totally inadequate to their needs.
41 However, it is conceivable that a setup script might wish to subclass
42 Distribution for some specialized purpose, and then pass the subclass
43 to 'setup()' as the 'distclass' keyword argument. If so, it is
44 necessary to respect the expectations that 'setup' has of Distribution.
45 See the code for 'setup()', in run.py, for details.
46 """
47
48 # 'global_options' describes the command-line options that may be
49 # supplied to the setup script prior to any actual commands.
50 # Eg. "./setup.py -n" or "./setup.py --dry-run" both take advantage of
51 # these global options. This list should be kept to a bare minimum,
52 # since every global option is also valid as a command option -- and we
53 # don't want to pollute the commands with too many options that they
54 # have minimal control over.
55 global_options = [
56 ('dry-run', 'n', "don't actually do anything"),
57 ('help', 'h', "show detailed help message"),
58 ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
59 ]
60
61 # 'common_usage' is a short (2-3 line) string describing the common
62 # usage of the setup script.
63 common_usage = """\
64Common commands: (see '--help-commands' for more)
65
66 setup.py build will build the package underneath 'build/'
67 setup.py install will install the package
68"""
69
70 # options that are not propagated to the commands
71 display_options = [
72 ('help-commands', None,
73 "list all available commands"),
74 ('name', None,
75 "print package name"),
76 ('version', 'V',
77 "print package version"),
78 ('fullname', None,
79 "print <package name>-<version>"),
80 ('author', None,
81 "print the author's name"),
82 ('author-email', None,
83 "print the author's email address"),
84 ('maintainer', None,
85 "print the maintainer's name"),
86 ('maintainer-email', None,
87 "print the maintainer's email address"),
88 ('contact', None,
89 "print the maintainer's name if known, else the author's"),
90 ('contact-email', None,
91 "print the maintainer's email address if known, else the author's"),
92 ('url', None,
93 "print the URL for this package"),
94 ('license', None,
95 "print the license of the package"),
96 ('licence', None,
97 "alias for --license"),
98 ('description', None,
99 "print the package description"),
100 ('long-description', None,
101 "print the long package description"),
102 ('platforms', None,
103 "print the list of platforms"),
104 ('classifier', None,
105 "print the list of classifiers"),
106 ('keywords', None,
107 "print the list of keywords"),
108 ('provides', None,
109 "print the list of packages/modules provided"),
110 ('requires', None,
111 "print the list of packages/modules required"),
112 ('obsoletes', None,
113 "print the list of packages/modules made obsolete"),
114 ('use-2to3', None,
115 "use 2to3 to make source python 3.x compatible"),
116 ('convert-2to3-doctests', None,
117 "use 2to3 to convert doctests in seperate text files"),
118 ]
119 display_option_names = [x[0].replace('-', '_') for x in display_options]
120
121 # negative options are options that exclude other options
122 negative_opt = {}
123
124 # -- Creation/initialization methods -------------------------------
125 def __init__(self, attrs=None):
126 """Construct a new Distribution instance: initialize all the
127 attributes of a Distribution, and then use 'attrs' (a dictionary
128 mapping attribute names to values) to assign some of those
129 attributes their "real" values. (Any attributes not mentioned in
130 'attrs' will be assigned to some null value: 0, None, an empty list
131 or dictionary, etc.) Most importantly, initialize the
132 'command_obj' attribute to the empty dictionary; this will be
133 filled in with real command objects by 'parse_command_line()'.
134 """
135
136 # Default values for our command-line options
137 self.dry_run = False
138 self.help = False
139 for attr in self.display_option_names:
140 setattr(self, attr, False)
141
142 # Store the configuration
143 self.config = Config(self)
144
145 # Store the distribution metadata (name, version, author, and so
146 # forth) in a separate object -- we're getting to have enough
147 # information here (and enough command-line options) that it's
148 # worth it.
149 self.metadata = Metadata()
150
151 # 'cmdclass' maps command names to class objects, so we
152 # can 1) quickly figure out which class to instantiate when
153 # we need to create a new command object, and 2) have a way
154 # for the setup script to override command classes
155 self.cmdclass = {}
156
157 # 'script_name' and 'script_args' are usually set to sys.argv[0]
158 # and sys.argv[1:], but they can be overridden when the caller is
159 # not necessarily a setup script run from the command line.
160 self.script_name = None
161 self.script_args = None
162
163 # 'command_options' is where we store command options between
164 # parsing them (from config files, the command line, etc.) and when
165 # they are actually needed -- ie. when the command in question is
166 # instantiated. It is a dictionary of dictionaries of 2-tuples:
167 # command_options = { command_name : { option : (source, value) } }
168 self.command_options = {}
169
170 # 'dist_files' is the list of (command, pyversion, file) that
171 # have been created by any dist commands run so far. This is
172 # filled regardless of whether the run is dry or not. pyversion
173 # gives sysconfig.get_python_version() if the dist file is
174 # specific to a Python version, 'any' if it is good for all
175 # Python versions on the target platform, and '' for a source
176 # file. pyversion should not be used to specify minimum or
177 # maximum required Python versions; use the metainfo for that
178 # instead.
179 self.dist_files = []
180
181 # These options are really the business of various commands, rather
182 # than of the Distribution itself. We provide aliases for them in
183 # Distribution as a convenience to the developer.
184 self.packages = []
185 self.package_data = {}
186 self.package_dir = None
187 self.py_modules = []
188 self.libraries = []
189 self.headers = []
190 self.ext_modules = []
191 self.ext_package = None
192 self.include_dirs = []
193 self.extra_path = None
194 self.scripts = []
195 self.data_files = {}
196 self.password = ''
197 self.use_2to3 = False
198 self.convert_2to3_doctests = []
199 self.extra_files = []
200
201 # And now initialize bookkeeping stuff that can't be supplied by
202 # the caller at all. 'command_obj' maps command names to
203 # Command instances -- that's how we enforce that every command
204 # class is a singleton.
205 self.command_obj = {}
206
207 # 'have_run' maps command names to boolean values; it keeps track
208 # of whether we have actually run a particular command, to make it
209 # cheap to "run" a command whenever we think we might need to -- if
210 # it's already been done, no need for expensive filesystem
211 # operations, we just check the 'have_run' dictionary and carry on.
212 # It's only safe to query 'have_run' for a command class that has
213 # been instantiated -- a false value will be inserted when the
214 # command object is created, and replaced with a true value when
215 # the command is successfully run. Thus it's probably best to use
216 # '.get()' rather than a straight lookup.
217 self.have_run = {}
218
219 # Now we'll use the attrs dictionary (ultimately, keyword args from
220 # the setup script) to possibly override any or all of these
221 # distribution options.
222
223 if attrs is not None:
224 # Pull out the set of command options and work on them
225 # specifically. Note that this order guarantees that aliased
226 # command options will override any supplied redundantly
227 # through the general options dictionary.
228 options = attrs.get('options')
229 if options is not None:
230 del attrs['options']
231 for command, cmd_options in options.items():
232 opt_dict = self.get_option_dict(command)
233 for opt, val in cmd_options.items():
234 opt_dict[opt] = ("setup script", val)
235
236 # Now work on the rest of the attributes. Any attribute that's
237 # not already defined is invalid!
238 for key, val in attrs.items():
239 if self.metadata.is_metadata_field(key):
240 self.metadata[key] = val
241 elif hasattr(self, key):
242 setattr(self, key, val)
243 else:
244 logger.warning(
245 'unknown argument given to Distribution: %r', key)
246
247 # no-user-cfg is handled before other command line args
248 # because other args override the config files, and this
249 # one is needed before we can load the config files.
250 # If attrs['script_args'] wasn't passed, assume false.
251 #
252 # This also make sure we just look at the global options
253 self.want_user_cfg = True
254
255 if self.script_args is not None:
256 for arg in self.script_args:
257 if not arg.startswith('-'):
258 break
259 if arg == '--no-user-cfg':
260 self.want_user_cfg = False
261 break
262
263 self.finalize_options()
264
265 def get_option_dict(self, command):
266 """Get the option dictionary for a given command. If that
267 command's option dictionary hasn't been created yet, then create it
268 and return the new dictionary; otherwise, return the existing
269 option dictionary.
270 """
271 d = self.command_options.get(command)
272 if d is None:
273 d = self.command_options[command] = {}
274 return d
275
276 def get_fullname(self):
277 return self.metadata.get_fullname()
278
279 def dump_option_dicts(self, header=None, commands=None, indent=""):
280 from pprint import pformat
281
282 if commands is None: # dump all command option dicts
283 commands = sorted(self.command_options)
284
285 if header is not None:
286 logger.info(indent + header)
287 indent = indent + " "
288
289 if not commands:
290 logger.info(indent + "no commands known yet")
291 return
292
293 for cmd_name in commands:
294 opt_dict = self.command_options.get(cmd_name)
295 if opt_dict is None:
296 logger.info(indent + "no option dict for %r command",
297 cmd_name)
298 else:
299 logger.info(indent + "option dict for %r command:", cmd_name)
300 out = pformat(opt_dict)
301 for line in out.split('\n'):
302 logger.info(indent + " " + line)
303
304 # -- Config file finding/parsing methods ---------------------------
305 # XXX to be removed
306 def parse_config_files(self, filenames=None):
307 return self.config.parse_config_files(filenames)
308
309 def find_config_files(self):
310 return self.config.find_config_files()
311
312 # -- Command-line parsing methods ----------------------------------
313
314 def parse_command_line(self):
315 """Parse the setup script's command line, taken from the
316 'script_args' instance attribute (which defaults to 'sys.argv[1:]'
317 -- see 'setup()' in run.py). This list is first processed for
318 "global options" -- options that set attributes of the Distribution
319 instance. Then, it is alternately scanned for Packaging commands
320 and options for that command. Each new command terminates the
321 options for the previous command. The allowed options for a
322 command are determined by the 'user_options' attribute of the
323 command class -- thus, we have to be able to load command classes
324 in order to parse the command line. Any error in that 'options'
325 attribute raises PackagingGetoptError; any error on the
326 command line raises PackagingArgError. If no Packaging commands
327 were found on the command line, raises PackagingArgError. Return
328 true if command line was successfully parsed and we should carry
329 on with executing commands; false if no errors but we shouldn't
330 execute commands (currently, this only happens if user asks for
331 help).
332 """
333 #
334 # We now have enough information to show the Macintosh dialog
335 # that allows the user to interactively specify the "command line".
336 #
337 toplevel_options = self._get_toplevel_options()
338
339 # We have to parse the command line a bit at a time -- global
340 # options, then the first command, then its options, and so on --
341 # because each command will be handled by a different class, and
342 # the options that are valid for a particular class aren't known
343 # until we have loaded the command class, which doesn't happen
344 # until we know what the command is.
345
346 self.commands = []
347 parser = FancyGetopt(toplevel_options + self.display_options)
348 parser.set_negative_aliases(self.negative_opt)
349 parser.set_aliases({'licence': 'license'})
350 args = parser.getopt(args=self.script_args, object=self)
351 option_order = parser.get_option_order()
352
353 # for display options we return immediately
354 if self.handle_display_options(option_order):
355 return
356
357 while args:
358 args = self._parse_command_opts(parser, args)
359 if args is None: # user asked for help (and got it)
360 return
361
362 # Handle the cases of --help as a "global" option, ie.
363 # "setup.py --help" and "setup.py --help command ...". For the
364 # former, we show global options (--dry-run, etc.)
365 # and display-only options (--name, --version, etc.); for the
366 # latter, we omit the display-only options and show help for
367 # each command listed on the command line.
368 if self.help:
369 self._show_help(parser,
370 display_options=len(self.commands) == 0,
371 commands=self.commands)
372 return
373
374 return 1
375
376 def _get_toplevel_options(self):
377 """Return the non-display options recognized at the top level.
378
379 This includes options that are recognized *only* at the top
380 level as well as options recognized for commands.
381 """
382 return self.global_options
383
384 def _parse_command_opts(self, parser, args):
385 """Parse the command-line options for a single command.
386 'parser' must be a FancyGetopt instance; 'args' must be the list
387 of arguments, starting with the current command (whose options
388 we are about to parse). Returns a new version of 'args' with
389 the next command at the front of the list; will be the empty
390 list if there are no more commands on the command line. Returns
391 None if the user asked for help on this command.
392 """
393 # Pull the current command from the head of the command line
394 command = args[0]
395 if not command_re.match(command):
396 raise SystemExit("invalid command name %r" % command)
397 self.commands.append(command)
398
399 # Dig up the command class that implements this command, so we
400 # 1) know that it's a valid command, and 2) know which options
401 # it takes.
402 try:
403 cmd_class = get_command_class(command)
404 except PackagingModuleError as msg:
405 raise PackagingArgError(msg)
406
407 # XXX We want to push this in packaging.command
408 #
409 # Require that the command class be derived from Command -- want
410 # to be sure that the basic "command" interface is implemented.
411 for meth in ('initialize_options', 'finalize_options', 'run'):
412 if hasattr(cmd_class, meth):
413 continue
414 raise PackagingClassError(
415 'command %r must implement %r' % (cmd_class, meth))
416
417 # Also make sure that the command object provides a list of its
418 # known options.
419 if not (hasattr(cmd_class, 'user_options') and
420 isinstance(cmd_class.user_options, list)):
421 raise PackagingClassError(
422 "command class %s must provide "
423 "'user_options' attribute (a list of tuples)" % cmd_class)
424
425 # If the command class has a list of negative alias options,
426 # merge it in with the global negative aliases.
427 negative_opt = self.negative_opt
428 if hasattr(cmd_class, 'negative_opt'):
429 negative_opt = negative_opt.copy()
430 negative_opt.update(cmd_class.negative_opt)
431
432 # Check for help_options in command class. They have a different
433 # format (tuple of four) so we need to preprocess them here.
434 if (hasattr(cmd_class, 'help_options') and
435 isinstance(cmd_class.help_options, list)):
436 help_options = cmd_class.help_options[:]
437 else:
438 help_options = []
439
440 # All commands support the global options too, just by adding
441 # in 'global_options'.
442 parser.set_option_table(self.global_options +
443 cmd_class.user_options +
444 help_options)
445 parser.set_negative_aliases(negative_opt)
446 args, opts = parser.getopt(args[1:])
447 if hasattr(opts, 'help') and opts.help:
448 self._show_help(parser, display_options=False,
449 commands=[cmd_class])
450 return
451
452 if (hasattr(cmd_class, 'help_options') and
453 isinstance(cmd_class.help_options, list)):
454 help_option_found = False
455 for help_option, short, desc, func in cmd_class.help_options:
456 if hasattr(opts, help_option.replace('-', '_')):
457 help_option_found = True
458 if hasattr(func, '__call__'):
459 func()
460 else:
461 raise PackagingClassError(
462 "invalid help function %r for help option %r: "
463 "must be a callable object (function, etc.)"
464 % (func, help_option))
465
466 if help_option_found:
467 return
468
469 # Put the options from the command line into their official
470 # holding pen, the 'command_options' dictionary.
471 opt_dict = self.get_option_dict(command)
472 for name, value in vars(opts).items():
473 opt_dict[name] = ("command line", value)
474
475 return args
476
477 def finalize_options(self):
478 """Set final values for all the options on the Distribution
479 instance, analogous to the .finalize_options() method of Command
480 objects.
481 """
482 if getattr(self, 'convert_2to3_doctests', None):
483 self.convert_2to3_doctests = [os.path.join(p)
484 for p in self.convert_2to3_doctests]
485 else:
486 self.convert_2to3_doctests = []
487
488 def _show_help(self, parser, global_options=True, display_options=True,
489 commands=[]):
490 """Show help for the setup script command line in the form of
491 several lists of command-line options. 'parser' should be a
492 FancyGetopt instance; do not expect it to be returned in the
493 same state, as its option table will be reset to make it
494 generate the correct help text.
495
496 If 'global_options' is true, lists the global options:
497 --dry-run, etc. If 'display_options' is true, lists
498 the "display-only" options: --name, --version, etc. Finally,
499 lists per-command help for every command name or command class
500 in 'commands'.
501 """
502 # late import because of mutual dependence between these modules
503 from packaging.command.cmd import Command
504
505 if global_options:
506 if display_options:
507 options = self._get_toplevel_options()
508 else:
509 options = self.global_options
510 parser.set_option_table(options)
511 parser.print_help(self.common_usage + "\nGlobal options:")
Éric Araujo3cab2f12011-06-08 04:10:57 +0200512 print()
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200513
514 if display_options:
515 parser.set_option_table(self.display_options)
516 parser.print_help(
517 "Information display options (just display " +
518 "information, ignore any commands)")
Éric Araujo3cab2f12011-06-08 04:10:57 +0200519 print()
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200520
521 for command in self.commands:
522 if isinstance(command, type) and issubclass(command, Command):
523 cls = command
524 else:
525 cls = get_command_class(command)
526 if (hasattr(cls, 'help_options') and
527 isinstance(cls.help_options, list)):
528 parser.set_option_table(cls.user_options + cls.help_options)
529 else:
530 parser.set_option_table(cls.user_options)
531 parser.print_help("Options for %r command:" % cls.__name__)
Éric Araujo3cab2f12011-06-08 04:10:57 +0200532 print()
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200533
534 print(gen_usage(self.script_name))
535
536 def handle_display_options(self, option_order):
537 """If there were any non-global "display-only" options
538 (--help-commands or the metadata display options) on the command
539 line, display the requested info and return true; else return
540 false.
541 """
542 # User just wants a list of commands -- we'll print it out and stop
543 # processing now (ie. if they ran "setup --help-commands foo bar",
544 # we ignore "foo bar").
545 if self.help_commands:
546 self.print_commands()
Éric Araujo3cab2f12011-06-08 04:10:57 +0200547 print()
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200548 print(gen_usage(self.script_name))
549 return 1
550
551 # If user supplied any of the "display metadata" options, then
552 # display that metadata in the order in which the user supplied the
553 # metadata options.
554 any_display_options = False
555 is_display_option = set()
556 for option in self.display_options:
557 is_display_option.add(option[0])
558
559 for opt, val in option_order:
560 if val and opt in is_display_option:
561 opt = opt.replace('-', '_')
562 value = self.metadata[opt]
563 if opt in ('keywords', 'platform'):
564 print(','.join(value))
565 elif opt in ('classifier', 'provides', 'requires',
566 'obsoletes'):
567 print('\n'.join(value))
568 else:
569 print(value)
570 any_display_options = True
571
572 return any_display_options
573
574 def print_command_list(self, commands, header, max_length):
575 """Print a subset of the list of all commands -- used by
576 'print_commands()'.
577 """
578 print(header + ":")
579
580 for cmd in commands:
581 cls = self.cmdclass.get(cmd) or get_command_class(cmd)
582 description = getattr(cls, 'description',
583 '(no description available)')
584
585 print(" %-*s %s" % (max_length, cmd, description))
586
587 def _get_command_groups(self):
588 """Helper function to retrieve all the command class names divided
589 into standard commands (listed in
590 packaging2.command.STANDARD_COMMANDS) and extra commands (given in
591 self.cmdclass and not standard commands).
592 """
593 extra_commands = [cmd for cmd in self.cmdclass
594 if cmd not in STANDARD_COMMANDS]
595 return STANDARD_COMMANDS, extra_commands
596
597 def print_commands(self):
598 """Print out a help message listing all available commands with a
599 description of each. The list is divided into standard commands
600 (listed in packaging2.command.STANDARD_COMMANDS) and extra commands
601 (given in self.cmdclass and not standard commands). The
602 descriptions come from the command class attribute
603 'description'.
604 """
605 std_commands, extra_commands = self._get_command_groups()
606 max_length = 0
607 for cmd in (std_commands + extra_commands):
608 if len(cmd) > max_length:
609 max_length = len(cmd)
610
611 self.print_command_list(std_commands,
612 "Standard commands",
613 max_length)
614 if extra_commands:
615 print()
616 self.print_command_list(extra_commands,
617 "Extra commands",
618 max_length)
619
620 # -- Command class/object methods ----------------------------------
621
622 def get_command_obj(self, command, create=True):
623 """Return the command object for 'command'. Normally this object
624 is cached on a previous call to 'get_command_obj()'; if no command
625 object for 'command' is in the cache, then we either create and
626 return it (if 'create' is true) or return None.
627 """
628 cmd_obj = self.command_obj.get(command)
629 if not cmd_obj and create:
630 logger.debug("Distribution.get_command_obj(): " \
631 "creating %r command object", command)
632
633 cls = get_command_class(command)
634 cmd_obj = self.command_obj[command] = cls(self)
635 self.have_run[command] = 0
636
637 # Set any options that were supplied in config files
638 # or on the command line. (NB. support for error
639 # reporting is lame here: any errors aren't reported
640 # until 'finalize_options()' is called, which means
641 # we won't report the source of the error.)
642 options = self.command_options.get(command)
643 if options:
644 self._set_command_options(cmd_obj, options)
645
646 return cmd_obj
647
648 def _set_command_options(self, command_obj, option_dict=None):
649 """Set the options for 'command_obj' from 'option_dict'. Basically
650 this means copying elements of a dictionary ('option_dict') to
651 attributes of an instance ('command').
652
653 'command_obj' must be a Command instance. If 'option_dict' is not
654 supplied, uses the standard option dictionary for this command
655 (from 'self.command_options').
656 """
657 command_name = command_obj.get_command_name()
658 if option_dict is None:
659 option_dict = self.get_option_dict(command_name)
660
661 logger.debug(" setting options for %r command:", command_name)
662
663 for option, (source, value) in option_dict.items():
664 logger.debug(" %s = %s (from %s)", option, value, source)
665 try:
666 bool_opts = [x.replace('-', '_')
667 for x in command_obj.boolean_options]
668 except AttributeError:
669 bool_opts = []
670 try:
671 neg_opt = command_obj.negative_opt
672 except AttributeError:
673 neg_opt = {}
674
675 try:
676 is_string = isinstance(value, str)
677 if option in neg_opt and is_string:
678 setattr(command_obj, neg_opt[option], not strtobool(value))
679 elif option in bool_opts and is_string:
680 setattr(command_obj, option, strtobool(value))
681 elif hasattr(command_obj, option):
682 setattr(command_obj, option, value)
683 else:
684 raise PackagingOptionError(
685 "error in %s: command %r has no such option %r" %
686 (source, command_name, option))
687 except ValueError as msg:
688 raise PackagingOptionError(msg)
689
690 def get_reinitialized_command(self, command, reinit_subcommands=False):
691 """Reinitializes a command to the state it was in when first
692 returned by 'get_command_obj()': ie., initialized but not yet
693 finalized. This provides the opportunity to sneak option
694 values in programmatically, overriding or supplementing
695 user-supplied values from the config files and command line.
696 You'll have to re-finalize the command object (by calling
697 'finalize_options()' or 'ensure_finalized()') before using it for
698 real.
699
700 'command' should be a command name (string) or command object. If
701 'reinit_subcommands' is true, also reinitializes the command's
702 sub-commands, as declared by the 'sub_commands' class attribute (if
703 it has one). See the "install_dist" command for an example. Only
704 reinitializes the sub-commands that actually matter, ie. those
705 whose test predicates return true.
706
707 Returns the reinitialized command object.
708 """
709 from packaging.command.cmd import Command
710 if not isinstance(command, Command):
711 command_name = command
712 command = self.get_command_obj(command_name)
713 else:
714 command_name = command.get_command_name()
715
716 if not command.finalized:
717 return command
718 command.initialize_options()
719 self.have_run[command_name] = 0
720 command.finalized = False
721 self._set_command_options(command)
722
723 if reinit_subcommands:
724 for sub in command.get_sub_commands():
725 self.get_reinitialized_command(sub, reinit_subcommands)
726
727 return command
728
729 # -- Methods that operate on the Distribution ----------------------
730
731 def run_commands(self):
732 """Run each command that was seen on the setup script command line.
733 Uses the list of commands found and cache of command objects
734 created by 'get_command_obj()'.
735 """
736 for cmd in self.commands:
737 self.run_command(cmd)
738
739 # -- Methods that operate on its Commands --------------------------
740
741 def run_command(self, command, options=None):
742 """Do whatever it takes to run a command (including nothing at all,
743 if the command has already been run). Specifically: if we have
744 already created and run the command named by 'command', return
745 silently without doing anything. If the command named by 'command'
746 doesn't even have a command object yet, create one. Then invoke
747 'run()' on that command object (or an existing one).
748 """
749 # Already been here, done that? then return silently.
750 if self.have_run.get(command):
751 return
752
753 if options is not None:
754 self.command_options[command] = options
755
756 cmd_obj = self.get_command_obj(command)
757 cmd_obj.ensure_finalized()
758 self.run_command_hooks(cmd_obj, 'pre_hook')
759 logger.info("running %s", command)
760 cmd_obj.run()
761 self.run_command_hooks(cmd_obj, 'post_hook')
762 self.have_run[command] = 1
763
764 def run_command_hooks(self, cmd_obj, hook_kind):
765 """Run hooks registered for that command and phase.
766
767 *cmd_obj* is a finalized command object; *hook_kind* is either
768 'pre_hook' or 'post_hook'.
769 """
770 if hook_kind not in ('pre_hook', 'post_hook'):
771 raise ValueError('invalid hook kind: %r' % hook_kind)
772
773 hooks = getattr(cmd_obj, hook_kind, None)
774
775 if hooks is None:
776 return
777
778 for hook in hooks.values():
779 if isinstance(hook, str):
780 try:
781 hook_obj = resolve_name(hook)
782 except ImportError as e:
783 raise PackagingModuleError(e)
784 else:
785 hook_obj = hook
786
787 if not hasattr(hook_obj, '__call__'):
788 raise PackagingOptionError('hook %r is not callable' % hook)
789
790 logger.info('running %s %s for command %s',
791 hook_kind, hook, cmd_obj.get_command_name())
792 hook_obj(cmd_obj)
793
794 # -- Distribution query methods ------------------------------------
795 def has_pure_modules(self):
796 return len(self.packages or self.py_modules or []) > 0
797
798 def has_ext_modules(self):
799 return self.ext_modules and len(self.ext_modules) > 0
800
801 def has_c_libraries(self):
802 return self.libraries and len(self.libraries) > 0
803
804 def has_modules(self):
805 return self.has_pure_modules() or self.has_ext_modules()
806
807 def has_headers(self):
808 return self.headers and len(self.headers) > 0
809
810 def has_scripts(self):
811 return self.scripts and len(self.scripts) > 0
812
813 def has_data_files(self):
814 return self.data_files and len(self.data_files) > 0
815
816 def is_pure(self):
817 return (self.has_pure_modules() and
818 not self.has_ext_modules() and
819 not self.has_c_libraries())