diff options
Diffstat (limited to 'src/lib/Bcfg2/Options')
-rw-r--r-- | src/lib/Bcfg2/Options/Options.py | 2 | ||||
-rw-r--r-- | src/lib/Bcfg2/Options/Parser.py | 11 | ||||
-rw-r--r-- | src/lib/Bcfg2/Options/Subcommands.py | 38 | ||||
-rw-r--r-- | src/lib/Bcfg2/Options/Types.py | 27 |
4 files changed, 42 insertions, 36 deletions
diff --git a/src/lib/Bcfg2/Options/Options.py b/src/lib/Bcfg2/Options/Options.py index 752e01b4e..c85cfd87c 100644 --- a/src/lib/Bcfg2/Options/Options.py +++ b/src/lib/Bcfg2/Options/Options.py @@ -363,7 +363,7 @@ class RepositoryMacroOption(Option): def transform_value(self, value): """transform the value after macro expansion. - this can be overridden to further transform the value set by + this can be overridden to further transform the value set by the user *after* macros are expanded, but before the user's ``type`` function is applied. principally exists for PathOption to canonicalize the path. diff --git a/src/lib/Bcfg2/Options/Parser.py b/src/lib/Bcfg2/Options/Parser.py index d146e3aa2..51e41850c 100644 --- a/src/lib/Bcfg2/Options/Parser.py +++ b/src/lib/Bcfg2/Options/Parser.py @@ -12,12 +12,14 @@ __all__ = ["setup", "OptionParserException", "Parser", "get_parser", "new_parser"] +# pylint: disable=C0103 #: The repository option. This is specified here (and imported into #: :module:`Bcfg2.Options.Common`) rather than vice-versa due to #: circular imports. -repository = PathOption( # pylint: disable=C0103 +repository = PathOption( '-Q', '--repository', cf=('server', 'repository'), - default='var/lib/bcfg2', help="Server repository path") + default='/var/lib/bcfg2', help="Server repository path") +# pylint: enable=C0103 #: A module-level :class:`argparse.Namespace` object that stores all @@ -141,6 +143,9 @@ class Parser(argparse.ArgumentParser): self.option_list.extend(option.list_options()) option.add_to_parser(self) + for opt in option.list_options(): + opt.default_from_config(self._cfp) + self._defaults_set.append(opt) def add_component(self, component): """ Add a component (and all of its options) to the @@ -299,7 +304,7 @@ class Parser(argparse.ArgumentParser): # check whether the specified bcfg2.conf exists if not self.unit_test and not os.path.exists(bootstrap.config): - self.error("Could not read %s" % bootstrap.config) + sys.stderr.write("Could not read %s\n" % bootstrap.config) self.add_config_file(self.configfile.dest, bootstrap.config, reparse=False) diff --git a/src/lib/Bcfg2/Options/Subcommands.py b/src/lib/Bcfg2/Options/Subcommands.py index 8972bde00..2ba81e18d 100644 --- a/src/lib/Bcfg2/Options/Subcommands.py +++ b/src/lib/Bcfg2/Options/Subcommands.py @@ -48,6 +48,14 @@ class Subcommand(object): #: one, ``bcfg2-admin`` does not.) interactive = True + #: Whether or not to expose this command as command line parameter + #: or only in an interactive :class:`cmd.Cmd` shell. + only_interactive = False + + #: Additional aliases for the command. The contents of the list gets + #: added to the default command name (the lowercased class name) + aliases = [] + _ws_re = re.compile(r'\s+', flags=re.MULTILINE) def __init__(self): @@ -82,6 +90,7 @@ class Subcommand(object): """ if args is not None: self.parser.namespace = copy.copy(master_setup) + self.parser.parsed = False alist = shlex.split(args) try: setup = self.parser.parse(alist) @@ -141,7 +150,9 @@ class Help(Subcommand): self._registry = registry def run(self, setup): - commands = self._registry.commands + commands = dict((name, cmd) + for (name, cmd) in self._registry.commands.items() + if not cmd.only_interactive) if setup.command: try: commands[setup.command].parser.print_help() @@ -207,15 +218,22 @@ class CommandRegistry(object): else: cmd_obj = cls_or_obj cmdcls = cmd_obj.__class__ - name = cmdcls.__name__.lower() - self.commands[name] = cmd_obj - # py2.5 can't mix *magic and non-magical keyword args, thus - # the **dict(...) - self.subcommand_options.append( - Subparser(*cmdcls.options, **dict(name=name, help=cmdcls.__doc__))) - if issubclass(self.__class__, cmd.Cmd) and cmdcls.interactive: - setattr(self, "do_%s" % name, cmd_obj) - setattr(self, "help_%s" % name, cmd_obj.parser.print_help) + names = [cmdcls.__name__.lower()] + if cmdcls.aliases: + names.extend(cmdcls.aliases) + + for name in names: + self.commands[name] = cmd_obj + + if not cmdcls.only_interactive: + # py2.5 can't mix *magic and non-magical keyword args, thus + # the **dict(...) + self.subcommand_options.append( + Subparser(*cmdcls.options, **dict(name=name, + help=cmdcls.__doc__))) + if issubclass(self.__class__, cmd.Cmd) and cmdcls.interactive: + setattr(self, "do_%s" % name, cmd_obj) + setattr(self, "help_%s" % name, cmd_obj.parser.print_help) return cmd_obj def register_commands(self, candidates, parent=Subcommand): diff --git a/src/lib/Bcfg2/Options/Types.py b/src/lib/Bcfg2/Options/Types.py index ac099e135..ad2e04f10 100644 --- a/src/lib/Bcfg2/Options/Types.py +++ b/src/lib/Bcfg2/Options/Types.py @@ -5,6 +5,7 @@ import os import re import pwd import grp +from Bcfg2.Compat import literal_eval _COMMA_SPLIT_RE = re.compile(r'\s*,\s*') @@ -32,28 +33,10 @@ def colon_list(value): return value.split(':') -def comma_dict(value): - """ Split an option string on commas, optionally surrounded by - whitespace, and split the resulting items again on equals signs, - returning a dict """ - result = dict() - if value: - items = comma_list(value) - for item in items: - if '=' in item: - key, value = item.split(r'=', 1) - if value in ["true", "yes", "on"]: - result[key] = True - elif value in ["false", "no", "off"]: - result[key] = False - else: - try: - result[key] = int(value) - except ValueError: - result[key] = value - else: - result[item] = True - return result +def literal_dict(value): + """ literally evaluate the option in order to allow for arbitrarily nested + dictionaries """ + return literal_eval(value) def anchored_regex_list(value): |