add some optimisations

This commit is contained in:
Emmanuel Garette 2015-12-22 22:06:14 +01:00
parent 93ce93e529
commit df233d3165
6 changed files with 123 additions and 106 deletions

View File

@ -606,6 +606,20 @@ def test_set_item():
raises(ValueError, 'c.cfgimpl_get_settings()[a] = ("test",)') raises(ValueError, 'c.cfgimpl_get_settings()[a] = ("test",)')
def test_optiondescription_requires():
a = BoolOption('activate_service', '', True)
b = BoolOption('ip_address_service', '', multi=True)
a, b
OptionDescription('service', '', [b], requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
def test_optiondescription_requires_multi():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '', multi=True)
a, b
raises(ValueError, "OptionDescription('service', '', [a], requires=[{'option': b, 'expected': False, 'action': 'disabled'}])")
def test_properties_conflict(): def test_properties_conflict():
a = BoolOption('activate_service', '', True) a = BoolOption('activate_service', '', True)
a a

View File

@ -248,7 +248,6 @@ def test_diff_opt_cache():
s = SymLinkOption('s', u) s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s]) o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o]) o1 = OptionDescription('o1', '', [o])
o1.impl_build_cache_consistency()
o1.impl_build_cache_option() o1.impl_build_cache_option()
a = dumps(o1) a = dumps(o1)
@ -263,7 +262,6 @@ def test_diff_opt_callback():
b4 = BoolOption("b4", "", callback=return_value, callback_params={'': ((None,),), 'value': ('string',)}) b4 = BoolOption("b4", "", callback=return_value, callback_params={'': ((None,),), 'value': ('string',)})
o = OptionDescription('o', '', [b, b2, b3, b4]) o = OptionDescription('o', '', [b, b2, b3, b4])
o1 = OptionDescription('o1', '', [o]) o1 = OptionDescription('o1', '', [o])
o1.impl_build_cache_consistency()
o1.impl_build_cache_option() o1.impl_build_cache_option()
a = dumps(o1) a = dumps(o1)

View File

@ -521,9 +521,8 @@ class _CommonConfig(SubConfig):
def _impl_build_all_caches(self): def _impl_build_all_caches(self):
descr = self.cfgimpl_get_description() descr = self.cfgimpl_get_description()
if not descr.impl_already_build_caches(): if not descr.impl_already_build_caches():
descr.impl_build_cache_consistency() descr.impl_build_cache()
descr.impl_build_cache_option() descr.impl_build_cache_option()
descr.impl_validate_options()
def read_only(self): def read_only(self):
"read only is a global config's setting, see `settings.py`" "read only is a global config's setting, see `settings.py`"

View File

@ -101,23 +101,26 @@ class Base(StorageBase):
allow_empty_list=undefined): allow_empty_list=undefined):
if not valid_name(name): # pragma: optional cover if not valid_name(name): # pragma: optional cover
raise ValueError(_("invalid name: {0} for option").format(name)) raise ValueError(_("invalid name: {0} for option").format(name))
if requires is not None:
calc_properties, requires = validate_requires_arg(
requires, name)
else:
calc_properties = frozenset()
requires = undefined
if not multi and default_multi is not None: # pragma: optional cover if not multi and default_multi is not None: # pragma: optional cover
raise ValueError(_("default_multi is set whereas multi is False" raise ValueError(_("default_multi is set whereas multi is False"
" in option: {0}").format(name)) " in option: {0}").format(name))
if multi is True: if multi is True:
is_multi = True
_multi = 0 _multi = 0
elif multi is False: elif multi is False:
is_multi = False
_multi = 1 _multi = 1
elif multi is submulti: elif multi is submulti:
is_multi = True
_multi = submulti _multi = submulti
else: else:
raise ValueError(_('invalid multi value')) raise ValueError(_('invalid multi value'))
if requires is not None:
calc_properties, requires = validate_requires_arg(is_multi,
requires, name)
else:
calc_properties = frozenset()
requires = undefined
if properties is None: if properties is None:
properties = tuple() properties = tuple()
if not isinstance(properties, tuple): # pragma: optional cover if not isinstance(properties, tuple): # pragma: optional cover
@ -139,8 +142,8 @@ class Base(StorageBase):
allow_empty_list) allow_empty_list)
if multi is not False and default is None: if multi is not False and default is None:
default = [] default = []
self.impl_validate(default, is_multi=_multi != 1) self.impl_validate(default, is_multi=is_multi)
self._set_default_values(default, default_multi, _multi != 1) self._set_default_values(default, default_multi, is_multi)
##callback is False in optiondescription ##callback is False in optiondescription
if callback is not False: if callback is not False:
self.impl_set_callback(callback, callback_params, _init=True) self.impl_set_callback(callback, callback_params, _init=True)
@ -792,7 +795,7 @@ class Option(OnlyOption):
"is calculated").format(self.impl_getname())) "is calculated").format(self.impl_getname()))
def validate_requires_arg(requires, name): def validate_requires_arg(multi, requires, name):
"""check malformed requirements """check malformed requirements
and tranform dict to internal tuple and tranform dict to internal tuple
@ -848,6 +851,10 @@ def validate_requires_arg(requires, name):
if not isinstance(option, Option): # pragma: optional cover if not isinstance(option, Option): # pragma: optional cover
raise ValueError(_('malformed requirements ' raise ValueError(_('malformed requirements '
'must be an option in option {0}').format(name)) 'must be an option in option {0}').format(name))
if not multi and option.impl_is_multi():
raise ValueError(_('malformed requirements '
'multi option must not set '
'as requires of non multi option {0}').format(name))
if expected is not None: if expected is not None:
try: try:
option._validate(expected) option._validate(expected)
@ -911,7 +918,7 @@ class SymLinkOption(OnlyOption):
del(self._stated) del(self._stated)
except AttributeError: # pragma: optional cover except AttributeError: # pragma: optional cover
pass pass
self._set_readonly() self._set_readonly(True)
def impl_get_information(self, key, default=undefined): def impl_get_information(self, key, default=undefined):
return self._impl_getopt().impl_get_information(key, default) return self._impl_getopt().impl_get_information(key, default)

View File

@ -78,7 +78,6 @@ class OptionDescription(BaseOption, StorageOptionDescription):
_setattr(self, '_cache_consistencies', None) _setattr(self, '_cache_consistencies', None)
# the group_type is useful for filtering OptionDescriptions in a config # the group_type is useful for filtering OptionDescriptions in a config
_setattr(self, '_group_type', groups.default) _setattr(self, '_group_type', groups.default)
_setattr(self, '_is_build_cache', False)
def impl_getdoc(self): def impl_getdoc(self):
return self.impl_get_information('doc') return self.impl_get_information('doc')
@ -93,16 +92,25 @@ class OptionDescription(BaseOption, StorageOptionDescription):
""" """
return _impl_getpaths(self, include_groups, _currpath) return _impl_getpaths(self, include_groups, _currpath)
def impl_build_cache_consistency(self, _consistencies=None, cache_option=None): def impl_build_cache(self, _consistencies=None, cache_option=None):
if _consistencies is None: """validate duplicate option and set option has readonly option
"""
if cache_option is None:
init = True init = True
_consistencies = {} _consistencies = {}
cache_option = [] cache_option = []
else: else:
init = False init = False
for option in self._impl_getchildren(dyn=False): for option in self._impl_getchildren(dyn=False):
#FIXME specifique id for sqlalchemy?
#FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes)
cache_option.append(option._get_id()) cache_option.append(option._get_id())
if not isinstance(option, OptionDescription): if isinstance(option, OptionDescription):
option._set_readonly(False)
option.impl_build_cache(_consistencies, cache_option)
#cannot set multi option as OptionDescription requires
else:
option._set_readonly(True)
for func, all_cons_opts, params in option._get_consistencies(): for func, all_cons_opts, params in option._get_consistencies():
all_cons_opts[0]._valid_consistencies(all_cons_opts[1:]) all_cons_opts[0]._valid_consistencies(all_cons_opts[1:])
for opt in all_cons_opts: for opt in all_cons_opts:
@ -110,53 +118,46 @@ class OptionDescription(BaseOption, StorageOptionDescription):
[]).append((func, []).append((func,
all_cons_opts, all_cons_opts,
params)) params))
else: is_slave = None
option.impl_build_cache_consistency(_consistencies, cache_option) if option.impl_is_multi():
if init and _consistencies != {}: all_requires = option.impl_getrequires()
self._cache_consistencies = {} if all_requires != tuple():
for opt, cons in _consistencies.items(): for requires in all_requires:
if opt._get_id() not in cache_option: # pragma: optional cover for require in requires:
raise ConfigError(_('consistency with option {0} ' #if option in require is a multi:
'which is not in Config').format( # * option in require must be a master or a slave
opt.impl_getname())) # * current option must be a slave (and only a slave)
self._cache_consistencies[opt] = tuple(cons) # * option in require and current option must be in same master/slaves
require_opt = require[0]
def impl_validate_options(self, cache_option=None): if require_opt.impl_is_multi():
"""validate duplicate option and set option has readonly option if is_slave is None:
""" is_slave = option.impl_is_master_slaves('slave')
if cache_option is None: if is_slave:
init = True masterslaves = option.impl_get_master_slaves()
cache_option = [] if is_slave and require_opt.impl_is_master_slaves():
else: if masterslaves != require_opt.impl_get_master_slaves():
init = False raise ValueError(_('malformed requirements option {0} '
for option in self._impl_getchildren(dyn=False): 'must be in same master/slaves for {1}').format(
#FIXME specifique id for sqlalchemy? require_opt.impl_getname(), option.impl_getname()))
#FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes) else:
oid = option._get_id() raise ValueError(_('malformed requirements option {0} '
cache_option.append(oid) 'must not be a multi for {1}').format(
option._set_readonly() require_opt.impl_getname(), option.impl_getname()))
if isinstance(option, OptionDescription):
option.impl_validate_options(cache_option)
if option.impl_getrequires() != []:
for requires in option.impl_getrequires():
for require in requires:
if require[0].impl_is_multi():
if option.impl_is_master_slaves('slave') and require[0].impl_is_master_slaves():
if option.impl_get_master_slaves() != require[0].impl_get_master_slaves():
raise ValueError(_('malformed requirements option {0} '
'must be in same master/slaves for {1}').format(
require[0].impl_getname(), option.impl_getname()))
else:
raise ValueError(_('malformed requirements option {0} '
'must not be a multi for {1}').format(
require[0].impl_getname(), option.impl_getname()))
if init: if init:
if len(cache_option) != len(set(cache_option)): if len(cache_option) != len(set(cache_option)):
for idx in xrange(1, len(cache_option) + 1): for idx in xrange(1, len(cache_option) + 1):
opt = cache_option.pop(0) opt = cache_option.pop(0)
if opt in cache_option: if opt in cache_option:
raise ConflictError(_('duplicate option: {0}').format(opt)) raise ConflictError(_('duplicate option: {0}').format(opt))
self._set_readonly() if _consistencies != {}:
self._cache_consistencies = {}
for opt, cons in _consistencies.items():
if opt._get_id() not in cache_option: # pragma: optional cover
raise ConfigError(_('consistency with option {0} '
'which is not in Config').format(
opt.impl_getname()))
self._cache_consistencies[opt] = tuple(cons)
self._set_readonly(False)
# ____________________________________________________________ # ____________________________________________________________
def impl_set_group_type(self, group_type): def impl_set_group_type(self, group_type):
@ -182,8 +183,6 @@ class OptionDescription(BaseOption, StorageOptionDescription):
:param descr: parent :class:`tiramisu.option.OptionDescription` :param descr: parent :class:`tiramisu.option.OptionDescription`
""" """
if descr is None: if descr is None:
#FIXME faut le desactiver ?
#self.impl_build_cache_consistency()
self.impl_build_cache_option() self.impl_build_cache_option()
descr = self descr = self
super(OptionDescription, self)._impl_getstate(descr) super(OptionDescription, self)._impl_getstate(descr)

View File

@ -20,6 +20,8 @@
from ...i18n import _ from ...i18n import _
from ...setting import undefined from ...setting import undefined
from ...error import ConfigError from ...error import ConfigError
static_tuple = tuple()
static_set = frozenset()
#____________________________________________________________ #____________________________________________________________
@ -55,6 +57,7 @@ class StorageBase(object):
'_stated', '_stated',
'_state_consistencies', '_state_consistencies',
'_state_informations', '_state_informations',
'_state_extra',
'_state_readonly', '_state_readonly',
'__weakref__' '__weakref__'
) )
@ -125,7 +128,7 @@ class StorageBase(object):
""" """
error = False error = False
dico = self._informations dico = self._informations
if dico is None or isinstance(dico, str) or isinstance(dico, unicode): if isinstance(dico, str) or isinstance(dico, unicode):
if key == 'doc': if key == 'doc':
return dico return dico
if default is not undefined: if default is not undefined:
@ -161,10 +164,7 @@ class StorageBase(object):
self._consistencies.pop(-1) self._consistencies.pop(-1)
def _get_consistencies(self): def _get_consistencies(self):
try: return getattr(self, '_consistencies', static_tuple)
return self._consistencies
except AttributeError:
return tuple()
def _set_callback(self, callback, callback_params): def _set_callback(self, callback, callback_params):
if callback_params is None or callback_params == {}: if callback_params is None or callback_params == {}:
@ -193,16 +193,10 @@ class StorageBase(object):
return ret_call return ret_call
def impl_get_calc_properties(self): def impl_get_calc_properties(self):
try: return getattr(self, '_calc_properties', static_set)
return self._calc_properties
except AttributeError:
return frozenset()
def impl_getrequires(self): def impl_getrequires(self):
try: return getattr(self, '_requires', static_tuple)
return self._requires
except AttributeError:
return []
def _set_validator(self, validator, validator_params): def _set_validator(self, validator, validator_params):
if validator_params is None: if validator_params is None:
@ -239,22 +233,20 @@ class StorageBase(object):
def _impl_getopt(self): def _impl_getopt(self):
return self._opt return self._opt
def _set_readonly(self): def _set_readonly(self, has_extra):
if not self.impl_is_readonly(): if not self.impl_is_readonly():
dico = self._informations
_setattr = object.__setattr__ _setattr = object.__setattr__
if not (dico is None or isinstance(dico, str) or isinstance(dico, unicode)): dico = self._informations
keys = tuple(dico.keys()) keys = tuple(dico.keys())
if keys == ('doc',): if len(keys) == 1:
dico = dico['doc'] dico = dico['doc']
else: else:
dico = tuple([tuple(dico.keys()), tuple(dico.values())]) dico = tuple([keys, tuple(dico.values())])
_setattr(self, '_informations', dico) _setattr(self, '_informations', dico)
try: if has_extra:
extra = self._extra extra = getattr(self, '_extra', None)
_setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())])) if extra is not None:
except AttributeError: _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())]))
pass
def _impl_setsubdyn(self, subdyn): def _impl_setsubdyn(self, subdyn):
self._subdyn = subdyn self._subdyn = subdyn
@ -268,8 +260,7 @@ class StorageBase(object):
if isinstance(infos, tuple): if isinstance(infos, tuple):
self._state_informations = {} self._state_informations = {}
for idx, key in enumerate(infos[0]): for idx, key in enumerate(infos[0]):
value = infos[1][idx] self._state_informations[key] = infos[1][idx]
self._state_informations[key] = value
elif isinstance(infos, str) or isinstance(infos, unicode): elif isinstance(infos, str) or isinstance(infos, unicode):
self._state_informations = {'doc': infos} self._state_informations = {'doc': infos}
else: else:
@ -282,9 +273,26 @@ class StorageBase(object):
except AttributeError: except AttributeError:
pass pass
if self._state_readonly: if self._state_readonly:
self._set_readonly() self._set_readonly(True)
del(self._state_readonly) del(self._state_readonly)
def _impl_convert_extra(self, descr, load=False):
if not load:
try:
extra = self._extra
if isinstance(extra, tuple):
self._state_extra = {}
for idx, key in enumerate(extra[0]):
self._state_extra[key] = extra[1][idx]
except AttributeError:
pass
else:
try:
self._extra = self._state_extra
del(self._state_extra)
except AttributeError:
pass
def _impl_getattributes(self): def _impl_getattributes(self):
slots = set() slots = set()
for subclass in self.__class__.__mro__: for subclass in self.__class__.__mro__:
@ -302,10 +310,7 @@ class StorageBase(object):
return self._name return self._name
def impl_is_multi(self): def impl_is_multi(self):
try: _multi = getattr(self, '_multi', 1)
_multi = self._multi
except AttributeError:
return False
return _multi != 1 return _multi != 1
def impl_is_submulti(self): def impl_is_submulti(self):
@ -361,21 +366,20 @@ class StorageBase(object):
class StorageOptionDescription(StorageBase): class StorageOptionDescription(StorageBase):
__slots__ = ('_children', '_cache_paths', '_cache_consistencies', __slots__ = ('_children', '_cache_paths', '_cache_consistencies',
'_group_type', '_is_build_cache', '_state_group_type') '_group_type', '_state_group_type')
def __init__(self, name, multi, warnings_only, doc, extra): def __init__(self, name, multi, warnings_only, doc, extra):
super(StorageOptionDescription, self).__init__(name, multi, super(StorageOptionDescription, self).__init__(name, multi,
warnings_only, doc, warnings_only, doc,
None, undefined, None, undefined,
undefined, undefined) undefined, undefined)
self._cache_paths = None
def _add_children(self, child_names, children): def _add_children(self, child_names, children):
_setattr = object.__setattr__ _setattr = object.__setattr__
_setattr(self, '_children', (tuple(child_names), tuple(children))) _setattr(self, '_children', (tuple(child_names), tuple(children)))
def impl_already_build_caches(self): def impl_already_build_caches(self):
return self._is_build_cache return getattr(self, '_cache_paths', None) is not None
def impl_get_opt_by_path(self, path): def impl_get_opt_by_path(self, path):
try: try:
@ -384,7 +388,7 @@ class StorageOptionDescription(StorageBase):
raise AttributeError(_('no option for path {0}').format(path)) raise AttributeError(_('no option for path {0}').format(path))
def impl_get_path_by_opt(self, opt): def impl_get_path_by_opt(self, opt):
if self._cache_paths is None: if getattr(self, '_cache_paths', None) is None:
raise ConfigError(_('use impl_get_path_by_opt only with root OptionDescription')) raise ConfigError(_('use impl_get_path_by_opt only with root OptionDescription'))
try: try:
return self._cache_paths[1][self._cache_paths[0].index(opt)] return self._cache_paths[1][self._cache_paths[0].index(opt)]
@ -396,12 +400,8 @@ class StorageOptionDescription(StorageBase):
def impl_build_cache_option(self, _currpath=None, cache_path=None, def impl_build_cache_option(self, _currpath=None, cache_path=None,
cache_option=None): cache_option=None):
_setattr = object.__setattr__
try: if _currpath is None and getattr(self, '_cache_paths', None) is not None:
self._cache_paths
except AttributeError:
_setattr(self, '_cache_paths', None)
if _currpath is None and self._cache_paths is not None: # pragma: optional cover
# cache already set # cache already set
return return
if _currpath is None: if _currpath is None:
@ -423,8 +423,8 @@ class StorageOptionDescription(StorageBase):
cache_option) cache_option)
_currpath.pop() _currpath.pop()
if save: if save:
_setattr = object.__setattr__
_setattr(self, '_cache_paths', (tuple(cache_option), tuple(cache_path))) _setattr(self, '_cache_paths', (tuple(cache_option), tuple(cache_path)))
_setattr(self, '_is_build_cache', True)
def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context): def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context):
find_results = [] find_results = []