rewrite make_dict

This commit is contained in:
Emmanuel Garette 2013-04-04 11:24:00 +02:00
parent ccac34b2db
commit e9902d8ce2
5 changed files with 141 additions and 122 deletions

View File

@ -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():

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)