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