rewrite make_dict
This commit is contained in:
parent
ccac34b2db
commit
e9902d8ce2
5 changed files with 141 additions and 122 deletions
|
@ -122,13 +122,13 @@ def test_make_dict():
|
|||
BoolOption("a", "", default=False)]),
|
||||
IntOption("int", "", default=42)])
|
||||
config = Config(descr)
|
||||
d = make_dict(config)
|
||||
d = config.make_dict()
|
||||
assert d == {"s1.a": False, "int": 42}
|
||||
config.int = 43
|
||||
config.s1.a = True
|
||||
d = make_dict(config)
|
||||
d = config.make_dict()
|
||||
assert d == {"s1.a": True, "int": 43}
|
||||
d2 = make_dict(config, flatten=True)
|
||||
d2 = config.make_dict(flatten=True)
|
||||
assert d2 == {'a': True, 'int': 43}
|
||||
|
||||
#def test_delattr():
|
||||
|
|
|
@ -46,12 +46,12 @@ def test_base_config():
|
|||
'general.mode_conteneur_actif': False, 'general.time_zone': 'Paris',
|
||||
'interface1.ip_admin_eth0.netmask_admin_eth0': None, 'general.nom_machine':
|
||||
'eoleng', 'general.activer_proxy_client': False}
|
||||
assert make_dict(config.creole) == result
|
||||
assert config.creole.make_dict() == result
|
||||
result = {'serveur_ntp': [], 'mode_conteneur_actif': False,
|
||||
'ip_admin_eth0': None, 'time_zone': 'Paris', 'numero_etab': None,
|
||||
'netmask_admin_eth0': None, 'nom_machine': 'eoleng', 'activer_proxy_client':
|
||||
False, 'nombre_interfaces': 1}
|
||||
assert make_dict(config.creole, flatten=True) == result
|
||||
assert config.creole.make_dict(flatten=True) == result
|
||||
|
||||
def test_get_group_type():
|
||||
descr = make_description()
|
||||
|
|
|
@ -124,7 +124,6 @@ class SubConfig(object):
|
|||
rootconfig = self.cfgimpl_get_context()
|
||||
path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
|
||||
return getattr(rootconfig, path)
|
||||
|
||||
self._validate(name, opt_or_descr, force_permissive=force_permissive)
|
||||
if isinstance(opt_or_descr, OptionDescription):
|
||||
children = self.cfgimpl_get_description()._children
|
||||
|
@ -283,23 +282,100 @@ class SubConfig(object):
|
|||
return context_descr.get_path_by_opt(descr)
|
||||
|
||||
def get(self, name):
|
||||
path = self.getpath()
|
||||
return self.cfgimpl_get_context().get(name, _subpath=path)
|
||||
"""
|
||||
same as a `find_first()` method in a config that has identical names:
|
||||
it returns the first item of an option named `name`
|
||||
|
||||
much like the attribute access way, except that
|
||||
the search for the option is performed recursively in the whole
|
||||
configuration tree.
|
||||
|
||||
:returns: option value.
|
||||
"""
|
||||
return self.cfgimpl_get_context()._find(byname=name, bytype=None,
|
||||
byvalue=None, byattrs=None,
|
||||
first=True, ret='value',
|
||||
_subpath=self.getpath())
|
||||
|
||||
def find(self, bytype=None, byname=None, byvalue=None, byattrs=None):
|
||||
path = self.getpath()
|
||||
return self.cfgimpl_get_context().find(bytype=bytype, byname=byname,
|
||||
byvalue=byvalue,
|
||||
byattrs=byattrs,
|
||||
_subpath=path)
|
||||
"""
|
||||
finds a list of options recursively in the config
|
||||
|
||||
:param bytype: Option class (BoolOption, StrOption, ...)
|
||||
:param byname: filter by Option._name
|
||||
:param byvalue: filter by the option's value
|
||||
:param byattrs: dict of option attributes (default, callback...)
|
||||
:returns: list of matching Option objects
|
||||
"""
|
||||
return self.cfgimpl_get_context()._find(bytype, byname, byvalue,
|
||||
byattrs, first=False,
|
||||
_subpath=self.getpath())
|
||||
|
||||
def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None):
|
||||
path = self.getpath()
|
||||
return self.cfgimpl_get_context().find_first(bytype=bytype,
|
||||
byname=byname,
|
||||
byvalue=byvalue,
|
||||
byattrs=byattrs,
|
||||
_subpath=path)
|
||||
"""
|
||||
finds an option recursively in the config
|
||||
|
||||
:param bytype: Option class (BoolOption, StrOption, ...)
|
||||
:param byname: filter by Option._name
|
||||
:param byvalue: filter by the option's value
|
||||
:param byattrs: dict of option attributes (default, callback...)
|
||||
:returns: list of matching Option objects
|
||||
"""
|
||||
return self.cfgimpl_get_context()._find(bytype, byname, byvalue,
|
||||
byattrs, first=True,
|
||||
_subpath=self.getpath())
|
||||
|
||||
def make_dict(self, flatten=False, _currpath=None, withoption=None, withvalue=None):
|
||||
"""export the whole config into a `dict`
|
||||
:returns: dict of Option's name (or path) and values"""
|
||||
pathsvalues = []
|
||||
if _currpath is None:
|
||||
_currpath = []
|
||||
if withoption is None and withvalue is not None:
|
||||
raise ValueError("make_dict can't filtering with value without option")
|
||||
if withoption is not None:
|
||||
mypath = self.getpath()
|
||||
for path in self.cfgimpl_get_context()._find(bytype=Option, byname=withoption,
|
||||
byvalue=withvalue, byattrs=None,
|
||||
first=False, ret='path', _subpath=mypath):
|
||||
path = '.'.join(path.split('.')[:-1])
|
||||
opt = self.cfgimpl_get_context().cfgimpl_get_description().get_opt_by_path(path)
|
||||
if mypath is not None:
|
||||
if mypath == path:
|
||||
withoption = None
|
||||
withvalue = None
|
||||
break
|
||||
else:
|
||||
tmypath = mypath + '.'
|
||||
if not path.startswith(tmypath):
|
||||
raise Exception('unexpected path {}, '
|
||||
'should start with {}'.format(path, mypath))
|
||||
path = path[len(tmypath):]
|
||||
self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
|
||||
#withoption can be set to None below !
|
||||
if withoption is None:
|
||||
for opt in self.cfgimpl_get_description().getchildren():
|
||||
path = opt._name
|
||||
self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
|
||||
if _currpath == []:
|
||||
options = dict(pathsvalues)
|
||||
return options
|
||||
return pathsvalues
|
||||
|
||||
def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten):
|
||||
if isinstance(opt, OptionDescription):
|
||||
pathsvalues += getattr(self, path).make_dict(flatten,
|
||||
_currpath + path.split('.'))
|
||||
else:
|
||||
try:
|
||||
value = self._getattr(opt._name)
|
||||
if flatten:
|
||||
name = opt._name
|
||||
else:
|
||||
name = '.'.join(_currpath + [opt._name])
|
||||
pathsvalues.append((name, value))
|
||||
except PropertiesOptionError:
|
||||
pass # this just a hidden or disabled option
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
|
@ -376,21 +452,10 @@ class Config(SubConfig):
|
|||
'there is no option that matches %s'
|
||||
' or the option is hidden or disabled' % (key, ))
|
||||
|
||||
def get(self, name, _subpath=None):
|
||||
"""
|
||||
same as a `find_first()` method in a config that has identical names:
|
||||
it returns the first item of an option named `name`
|
||||
def getpath(self):
|
||||
return None
|
||||
|
||||
much like the attribute access way, except that
|
||||
the search for the option is performed recursively in the whole
|
||||
configuration tree.
|
||||
|
||||
:returns: option value.
|
||||
"""
|
||||
return self._find(byname=name, bytype=None, byvalue=None, byattrs=None,
|
||||
first=True, getvalue=True, _subpath=_subpath)
|
||||
|
||||
def _find(self, bytype, byname, byvalue, byattrs, first, getvalue=False,
|
||||
def _find(self, bytype, byname, byvalue, byattrs, first, ret='option',
|
||||
_subpath=None):
|
||||
"""
|
||||
convenience method for finding an option that lives only in the subtree
|
||||
|
@ -436,14 +501,15 @@ class Config(SubConfig):
|
|||
else:
|
||||
continue
|
||||
return True
|
||||
|
||||
if ret not in ('option', 'path', 'value'):
|
||||
raise ValueError('unknown ret type {} for _find'.format(ret))
|
||||
find_results = []
|
||||
opts, paths = self.cfgimpl_get_description()._cache_paths
|
||||
for index in range(0, len(paths)):
|
||||
path = paths[index]
|
||||
option = opts[index]
|
||||
if isinstance(option, OptionDescription):
|
||||
continue
|
||||
path = paths[index]
|
||||
if _subpath is not None and not path.startswith(_subpath + '.'):
|
||||
continue
|
||||
if not _filter_by_name():
|
||||
|
@ -459,64 +525,21 @@ class Config(SubConfig):
|
|||
value = getattr(self, path)
|
||||
except: # a property restricts the access of the value
|
||||
continue
|
||||
if first:
|
||||
if getvalue:
|
||||
return value
|
||||
else:
|
||||
return option
|
||||
if ret == 'value':
|
||||
retval = value
|
||||
elif ret == 'path':
|
||||
retval = path
|
||||
else:
|
||||
if getvalue:
|
||||
find_results.append(value)
|
||||
else:
|
||||
find_results.append(option)
|
||||
retval = option
|
||||
if first:
|
||||
return retval
|
||||
else:
|
||||
find_results.append(retval)
|
||||
if find_results == []:
|
||||
raise NotFoundError("no option found in config with these criteria")
|
||||
else:
|
||||
return find_results
|
||||
|
||||
def find(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
|
||||
"""
|
||||
finds a list of options recursively in the config
|
||||
|
||||
:param bytype: Option class (BoolOption, StrOption, ...)
|
||||
:param byname: filter by Option._name
|
||||
:param byvalue: filter by the option's value
|
||||
:param byattrs: dict of option attributes (default, callback...)
|
||||
:returns: list of matching Option objects
|
||||
"""
|
||||
return self._find(bytype, byname, byvalue, byattrs, first=False, _subpath=_subpath)
|
||||
|
||||
def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
|
||||
"""
|
||||
finds an option recursively in the config
|
||||
|
||||
:param bytype: Option class (BoolOption, StrOption, ...)
|
||||
:param byname: filter by Option._name
|
||||
:param byvalue: filter by the option's value
|
||||
:param byattrs: dict of option attributes (default, callback...)
|
||||
:returns: list of matching Option objects
|
||||
"""
|
||||
return self._find(bytype, byname, byvalue, byattrs, first=True, _subpath=_subpath)
|
||||
|
||||
|
||||
def make_dict(config, flatten=False):
|
||||
"""export the whole config into a `dict`
|
||||
:returns: dict of Option's name (or path) and values"""
|
||||
paths = config.getpaths()
|
||||
pathsvalues = []
|
||||
for path in paths:
|
||||
if flatten:
|
||||
pathname = path.split('.')[-1]
|
||||
else:
|
||||
pathname = path
|
||||
try:
|
||||
value = getattr(config, path)
|
||||
pathsvalues.append((pathname, value))
|
||||
except:
|
||||
pass # this just a hidden or disabled option
|
||||
options = dict(pathsvalues)
|
||||
return options
|
||||
|
||||
|
||||
def mandatory_warnings(config):
|
||||
"""convenience function to trace Options that are mandatory and
|
||||
|
|
|
@ -408,63 +408,59 @@ class OptionDescription(BaseInformation):
|
|||
return self.get_information('doc')
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._children[0]:
|
||||
try:
|
||||
return self._children[1][self._children[0].index(name)]
|
||||
else:
|
||||
try:
|
||||
object.__getattr__(self, name)
|
||||
except AttributeError:
|
||||
raise AttributeError('unknown Option {} in OptionDescription {}'
|
||||
''.format(name, self._name))
|
||||
except ValueError:
|
||||
raise AttributeError('unknown Option {} in OptionDescription {}'
|
||||
''.format(name, self._name))
|
||||
|
||||
def getkey(self, config):
|
||||
return tuple([child.getkey(getattr(config, child._name))
|
||||
for child in self._children[1]])
|
||||
|
||||
def getpaths(self, include_groups=False, currpath=None):
|
||||
def getpaths(self, include_groups=False, _currpath=None):
|
||||
"""returns a list of all paths in self, recursively
|
||||
currpath should not be provided (helps with recursion)
|
||||
_currpath should not be provided (helps with recursion)
|
||||
"""
|
||||
#FIXME : cache
|
||||
if currpath is None:
|
||||
currpath = []
|
||||
if _currpath is None:
|
||||
_currpath = []
|
||||
paths = []
|
||||
for option in self._children[1]:
|
||||
attr = option._name
|
||||
if attr.startswith('_cfgimpl'):
|
||||
continue
|
||||
if isinstance(option, OptionDescription):
|
||||
if include_groups:
|
||||
paths.append('.'.join(currpath + [attr]))
|
||||
currpath.append(attr)
|
||||
paths.append('.'.join(_currpath + [attr]))
|
||||
paths += option.getpaths(include_groups=include_groups,
|
||||
currpath=currpath)
|
||||
currpath.pop()
|
||||
_currpath=_currpath + [attr])
|
||||
else:
|
||||
paths.append('.'.join(currpath + [attr]))
|
||||
paths.append('.'.join(_currpath + [attr]))
|
||||
return paths
|
||||
|
||||
def build_cache(self, cache_path=None, cache_option=None, currpath=None):
|
||||
if currpath is None and self._cache_paths is not None:
|
||||
def getchildren(self):
|
||||
return self._children[1]
|
||||
|
||||
def build_cache(self, cache_path=None, cache_option=None, _currpath=None):
|
||||
if _currpath is None and self._cache_paths is not None:
|
||||
return
|
||||
if currpath is None:
|
||||
if _currpath is None:
|
||||
save = True
|
||||
currpath = []
|
||||
_currpath = []
|
||||
else:
|
||||
save = False
|
||||
if cache_path is None:
|
||||
cache_path = []
|
||||
cache_option = []
|
||||
cache_path = [self._name]
|
||||
cache_option = [self]
|
||||
for option in self._children[1]:
|
||||
attr = option._name
|
||||
if attr.startswith('_cfgimpl'):
|
||||
continue
|
||||
cache_option.append(option)
|
||||
cache_path.append(str('.'.join(currpath + [attr])))
|
||||
cache_path.append(str('.'.join(_currpath + [attr])))
|
||||
if isinstance(option, OptionDescription):
|
||||
currpath.append(attr)
|
||||
option.build_cache(cache_path, cache_option, currpath)
|
||||
currpath.pop()
|
||||
_currpath.append(attr)
|
||||
option.build_cache(cache_path, cache_option, _currpath)
|
||||
_currpath.pop()
|
||||
if save:
|
||||
#valid no duplicated option
|
||||
valid_child = copy(cache_option)
|
||||
|
|
|
@ -123,13 +123,13 @@ class Values(object):
|
|||
def _getitem(self, opt, force_properties=None):
|
||||
# options with callbacks
|
||||
value = self._get_value(opt)
|
||||
setting = self.context.cfgimpl_get_settings()
|
||||
if opt.has_callback():
|
||||
setting = self.context.cfgimpl_get_settings()
|
||||
if (not setting.has_property('frozen', opt) or
|
||||
(setting.has_property('frozen', opt) and
|
||||
not setting.has_property('force_default_on_freeze', opt)
|
||||
)) and not self.context.cfgimpl_get_values().is_default_owner(opt):
|
||||
return self._get_value(opt)
|
||||
is_frozen = setting.has_property('frozen', opt)
|
||||
if (not is_frozen or (is_frozen and
|
||||
not setting.has_property('force_default_on_freeze', opt)
|
||||
)) and not self.context.cfgimpl_get_values().is_default_owner(opt):
|
||||
return value
|
||||
try:
|
||||
result = opt.getcallback_value(self.context)
|
||||
except NoValueReturned:
|
||||
|
@ -149,7 +149,7 @@ class Values(object):
|
|||
raise ConfigError('invalid calculated value returned'
|
||||
' for option {0}'.format(opt._name))
|
||||
# frozen and force default
|
||||
if not opt.has_callback() and self.context.cfgimpl_get_settings().has_property('force_default_on_freeze', opt):
|
||||
elif setting.has_property('force_default_on_freeze', opt):
|
||||
value = opt.getdefault()
|
||||
if opt.is_multi():
|
||||
value = self.fill_multi(opt, value)
|
||||
|
|
Loading…
Reference in a new issue