Sync with latest version of openstack.common.cfg

Changes since last sync:

 - make reset() clear defaults and overrides
 - automatically create option groups
 - allow options to be marked as required
 - use a list comprehension instead of map()

Change-Id: Id259a7ffe791a7e272fcc8bb21d7d0bc4f9e591d
This commit is contained in:
Mark McLoughlin 2012-05-13 18:12:00 +01:00
parent bb2fbb7030
commit dd14b16c5c

View File

@ -149,6 +149,14 @@ Options can be registered as belonging to a group::
conf.register_opt(rabbit_host_opt, group=rabbit_group)
conf.register_opt(rabbit_port_opt, group='rabbit')
If it no group attributes are required other than the group name, the group
need not be explicitly registered e.g.
def register_rabbit_opts(conf):
# The group will automatically be created, equivalent calling::
# conf.register_group(OptGroup(name='rabbit'))
conf.register_opt(rabbit_port_opt, group='rabbit')
If no group is specified, options belong to the 'DEFAULT' section of config
files::
@ -213,6 +221,9 @@ as the leftover arguments, but will instead return::
i.e. argument parsing is stopped at the first non-option argument.
Options may be declared as required so that an error is raised if the user
does not supply a value for the option.
Options may be declared as secret so that their values are not leaked into
log files:
@ -291,6 +302,21 @@ class DuplicateOptError(Error):
return "duplicate option: %s" % self.opt_name
class RequiredOptError(Error):
"""Raised if an option is required but no value is supplied by the user."""
def __init__(self, opt_name, group=None):
self.opt_name = opt_name
self.group = group
def __str__(self):
if self.group is None:
return "value required for option: %s" % self.opt_name
else:
return "value required for option: %s.%s" % (self.group.name,
self.opt_name)
class TemplateSubstitutionError(Error):
"""Raised if an error occurs substituting a variable in an opt value."""
@ -452,7 +478,7 @@ class Opt(object):
multi = False
def __init__(self, name, dest=None, short=None, default=None,
metavar=None, help=None, secret=False):
metavar=None, help=None, secret=False, required=False):
"""Construct an Opt object.
The only required parameter is the option's name. However, it is
@ -465,6 +491,7 @@ class Opt(object):
: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
:param required: true iff a value must be supplied for this option
"""
self.name = name
if dest is None:
@ -476,6 +503,7 @@ class Opt(object):
self.metavar = metavar
self.help = help
self.secret = secret
self.required = required
def _get_from_config_parser(self, cparser, section):
"""Retrieves the option value from a MultiConfigParser object.
@ -909,9 +937,10 @@ class ConfigOpts(collections.Mapping):
:params args: command line arguments (defaults to sys.argv[1:])
:returns: the list of arguments left over after parsing options
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
RequiredOptError
"""
self.reset()
self.clear()
self._args = args
@ -928,6 +957,8 @@ class ConfigOpts(collections.Mapping):
self._parse_config_files(from_file + from_dir)
self._check_required_opts()
return args
def __getattr__(self, name):
@ -956,11 +987,16 @@ class ConfigOpts(collections.Mapping):
"""Return the number of options and option groups."""
return len(self._opts) + len(self._groups)
@__clear_cache
def reset(self):
"""Reset the state of the object to before it was called."""
"""Clear the object state and unset overrides and defaults."""
self._unset_defaults_and_overrides()
self.clear()
@__clear_cache
def clear(self):
"""Clear the state of the object to before it was called."""
self._args = None
self._cli_values = None
self._cli_values = {}
self._cparser = None
@__clear_cache
@ -977,7 +1013,7 @@ class ConfigOpts(collections.Mapping):
:raises: DuplicateOptError
"""
if group is not None:
return self._get_group(group)._register_opt(opt)
return self._get_group(group, autocreate=True)._register_opt(opt)
if _is_opt_registered(self._opts, opt):
return False
@ -1012,7 +1048,7 @@ class ConfigOpts(collections.Mapping):
return False
if group is not None:
group = self._get_group(group)
group = self._get_group(group, autocreate=True)
opt._add_to_cli(self._oparser, group)
@ -1067,6 +1103,17 @@ class ConfigOpts(collections.Mapping):
opt_info = self._get_opt_info(name, group)
opt_info['default'] = default
def _unset_defaults_and_overrides(self):
"""Unset any default or override on all options."""
def unset(opts):
for info in opts.values():
info['default'] = None
info['override'] = None
unset(self._opts)
for group in self._groups.values():
unset(group._opts)
def disable_interspersed_args(self):
"""Set parsing to stop on the first non-option.
@ -1188,7 +1235,7 @@ class ConfigOpts(collections.Mapping):
return self.GroupAttr(self, self._get_group(name))
info = self._get_opt_info(name, group)
default, opt, override = map(lambda k: info[k], sorted(info.keys()))
default, opt, override = [info[k] for k in sorted(info.keys())]
if override is not None:
return override
@ -1241,7 +1288,7 @@ class ConfigOpts(collections.Mapping):
else:
return value
def _get_group(self, group_or_name):
def _get_group(self, group_or_name, autocreate=False):
"""Looks up a OptGroup object.
Helper function to return an OptGroup given a parameter which can
@ -1252,15 +1299,17 @@ class ConfigOpts(collections.Mapping):
the API have access to.
:param group_or_name: the group's name or the OptGroup object itself
:param autocreate: whether to auto-create the group if it's not found
:raises: NoSuchGroupError
"""
if isinstance(group_or_name, OptGroup):
group_name = group_or_name.name
else:
group_name = group_or_name
group = group_or_name if isinstance(group_or_name, OptGroup) else None
group_name = group.name if group else group_or_name
if not group_name in self._groups:
raise NoSuchGroupError(group_name)
if not group is None or not autocreate:
raise NoSuchGroupError(group_name)
self.register_group(OptGroup(name=group_name))
return self._groups[group_name]
@ -1298,6 +1347,28 @@ class ConfigOpts(collections.Mapping):
not_read_ok = filter(lambda f: f not in read_ok, config_files)
raise ConfigFilesNotFoundError(not_read_ok)
def _do_check_required_opts(self, opts, group=None):
for info in opts.values():
default, opt, override = [info[k] for k in sorted(info.keys())]
if opt.required:
if (default is not None or
override is not None):
continue
if self._get(opt.name, group) is None:
raise RequiredOptError(opt.name, group)
def _check_required_opts(self):
"""Check that all opts marked as required have values specified.
:raises: RequiredOptError
"""
self._do_check_required_opts(self._opts)
for group in self._groups.values():
self._do_check_required_opts(group._opts, group)
class GroupAttr(collections.Mapping):
"""