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