diff options
| author | Mark McLoughlin <markmc@redhat.com> | 2012-11-12 16:25:59 -0500 |
|---|---|---|
| committer | Mark McLoughlin <markmc@redhat.com> | 2012-11-15 15:01:14 -0500 |
| commit | 5b9cb4148c2c16c910cb7eae9e7875a9fc06d3da (patch) | |
| tree | 5ec357d1099d30148605d40be449eedd91479be6 /openstack | |
| parent | dbc72a6ef9a784c1bf5820a9750061d76529d571 (diff) | |
| download | oslo-5b9cb4148c2c16c910cb7eae9e7875a9fc06d3da.tar.gz oslo-5b9cb4148c2c16c910cb7eae9e7875a9fc06d3da.tar.xz oslo-5b9cb4148c2c16c910cb7eae9e7875a9fc06d3da.zip | |
Add support for positional arguments
argparse makes it awkward to implement the current cfg API where we
simply return the leftover arguments to the caller.
Very few cfg users actually rely on this functionality and for those
cases it probably makes more sense for them to explicitly register
positional arguments or sub-parsers.
Add support for positional arguments via a 'required' Opt attribute:
opt = StrOpt('foo', positional=True)
conf.register_cli_opt(opt)
conf(['bar'])
conf.foo == 'bar'
Change-Id: Iea746d710237e1ea26c1ef4871643941d1df09bd
Diffstat (limited to 'openstack')
| -rw-r--r-- | openstack/common/cfg.py | 87 |
1 files changed, 48 insertions, 39 deletions
diff --git a/openstack/common/cfg.py b/openstack/common/cfg.py index 7939d02..2430077 100644 --- a/openstack/common/cfg.py +++ b/openstack/common/cfg.py @@ -475,6 +475,8 @@ class Opt(object): a single character CLI option name default: the default value of the option + positional: + True if the option is a positional CLI argument metavar: the name shown as the argument to a CLI option in --help output help: @@ -483,8 +485,8 @@ class Opt(object): multi = False def __init__(self, name, dest=None, short=None, default=None, - metavar=None, help=None, secret=False, required=False, - deprecated_name=None): + positional=False, metavar=None, help=None, + secret=False, required=False, deprecated_name=None): """Construct an Opt object. The only required parameter is the option's name. However, it is @@ -494,6 +496,7 @@ class Opt(object): :param dest: the name of the corresponding ConfigOpts property :param short: a single character CLI option name :param default: the default value of the option + :param positional: True if the option is a positional CLI argument :param metavar: the option argument to show in --help :param help: an explanation of how the option is used :param secret: true iff the value should be obfuscated in log output @@ -507,6 +510,7 @@ class Opt(object): self.dest = dest self.short = short self.default = default + self.positional = positional self.metavar = metavar self.help = help self.secret = secret @@ -551,10 +555,10 @@ class Opt(object): kwargs = self._get_argparse_kwargs(group) prefix = self._get_argparse_prefix('', group) self._add_to_argparse(container, self.name, self.short, kwargs, prefix, - self.deprecated_name) + self.positional, self.deprecated_name) def _add_to_argparse(self, container, name, short, kwargs, prefix='', - deprecated_name=None): + positional=False, deprecated_name=None): """Add an option to an argparse parser or group. :param container: an argparse._ArgumentGroup object @@ -562,14 +566,17 @@ class Opt(object): :param short: the short opt name :param kwargs: the keyword arguments for add_argument() :param prefix: an optional prefix to prepend to the opt name + :param position: whether the optional is a positional CLI argument :raises: DuplicateOptError if a naming confict is detected """ - args = ['--' + prefix + name] - if short: - args += ['-' + short] + def hyphen(arg): + return arg if not positional else '' + args = [hyphen('--') + prefix + name] + if short: + args.append(hyphen('-') + short) if deprecated_name: - args += ['--' + prefix + deprecated_name] + args.append(hyphen('--') + prefix + deprecated_name) try: container.add_argument(*args, **kwargs) @@ -598,11 +605,14 @@ class Opt(object): :param kwargs: optional keyword arguments to add to :returns: a dict of keyword arguments """ - dest = self.dest - if group is not None: - dest = group.name + '_' + dest - kwargs.update({'dest': dest, - 'metavar': self.metavar, + if not self.positional: + dest = self.dest + if group is not None: + dest = group.name + '_' + dest + kwargs['dest'] = dest + else: + kwargs['nargs'] = '?' + kwargs.update({'metavar': self.metavar, 'help': self.help, }) return kwargs @@ -644,6 +654,11 @@ class BoolOpt(Opt): _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, '0': False, 'no': False, 'false': False, 'off': False} + def __init__(self, *args, **kwargs): + if 'positional' in kwargs: + raise ValueError('positional boolean args not supported') + super(BoolOpt, self).__init__(*args, **kwargs) + def _get_from_config_parser(self, cparser, section): """Retrieve the opt value as a boolean from ConfigParser.""" def convert_bool(v): @@ -668,7 +683,7 @@ class BoolOpt(Opt): prefix = self._get_argparse_prefix('no', group) kwargs["help"] = "The inverse of --" + self.name self._add_to_argparse(container, self.name, None, kwargs, prefix, - self.deprecated_name) + self.positional, self.deprecated_name) def _get_argparse_kwargs(self, group, action='store_true', **kwargs): """Extends the base argparse keyword dict for boolean options.""" @@ -733,16 +748,20 @@ class ListOpt(Opt): default=None, required=False, help=None, - metavar=None): + metavar=None, + nargs=None): argparse._StoreAction.__init__(self, option_strings=option_strings, dest=dest, default=default, required=required, - help=help) + help=help, + nargs=nargs) def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values.split(',')) + if values is not None: + values = values.split(',') + setattr(namespace, self.dest, values) def _get_from_config_parser(self, cparser, section): """Retrieve the opt value as a list from ConfigParser.""" @@ -767,8 +786,12 @@ class MultiStrOpt(Opt): def _get_argparse_kwargs(self, group, **kwargs): """Extends the base argparse keyword dict for multi str options.""" - return super(MultiStrOpt, - self)._get_argparse_kwargs(group, action='append') + kwargs = super(MultiStrOpt, self)._get_argparse_kwargs(group) + if not self.positional: + kwargs['action'] = 'append' + else: + kwargs['nargs'] = '*' + return kwargs def _cparser_get_with_deprecated(self, cparser, section): """If cannot find option as dest try deprecated_name alias.""" @@ -942,20 +965,6 @@ class ConfigCliParser(argparse.ArgumentParser): def add_subparsers(self, **kwargs): return super(ConfigCliParser, self).add_subparsers(**kwargs) - def parse_args(self, *args, **kwargs): - opts, args = super(ConfigCliParser, self).parse_known_args(*args, - **kwargs) - unknown_args = [] - for arg in args: - if 0 == arg.find("-"): - unknown_args.append(arg) - - if unknown_args: - msg = 'unrecognized arguments: %s' - self.error(msg % ' '.join(unknown_args)) - - return opts, args - class ConfigOpts(collections.Mapping): @@ -1090,14 +1099,12 @@ class ConfigOpts(collections.Mapping): self._setup(project, prog, version, usage, default_config_files) - self._cli_values, leftovers = self._parse_cli_opts(args) + self._cli_values = self._parse_cli_opts(args) self._parse_config_files() self._check_required_opts() - return leftovers - def __getattr__(self, name): """Look up an option value and perform string substitution. @@ -1452,6 +1459,10 @@ class ConfigOpts(collections.Mapping): if not opt.multi: return value + # argparse ignores default=None for nargs='*' + if opt.positional and not value: + value = opt.default + return value + values if values: @@ -1577,9 +1588,7 @@ class ConfigOpts(collections.Mapping): for opt, group in self._all_opts(): opt._add_to_cli(self._oparser, group) - values, leftovers = self._oparser.parse_args(args) - - return vars(values), leftovers + return vars(self._oparser.parse_args(args)) class GroupAttr(collections.Mapping): |
