diff --git a/test/test_storage.py b/test/test_storage.py index 7c0c7af..63b889d 100644 --- a/test/test_storage.py +++ b/test/test_storage.py @@ -44,7 +44,7 @@ def test_create_delete_not_persistent(): try: Config(o, session_id='test_persistent', persistent=True) except ValueError: - raises(ConfigError, "delete_session('option', 'test_persistent')") + raises(ValueError, "delete_session('option', 'test_persistent')") def test_list_sessions_persistent(): diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 61600bf..ddb4cf8 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -23,30 +23,29 @@ from types import FunctionType import warnings import sys +from ..i18n import _ +from ..setting import log, undefined, debug +from ..autolib import carry_out_calculation +from ..error import (ConfigError, ValueWarning, PropertiesOptionError, + display_list) + if sys.version_info[0] >= 3: # pragma: no cover from inspect import signature else: from inspect import getargspec -from ..i18n import _ -from ..setting import log, undefined, debug, groups -from ..autolib import carry_out_calculation -from ..error import (ConfigError, ValueWarning, PropertiesOptionError, - display_list) -from ..storage import get_storages_option -from . import MasterSlaves +static_tuple = tuple() if sys.version_info[0] >= 3: # pragma: no cover xrange = range -StorageBase = get_storages_option('base') submulti = 2 name_regexp = re.compile(r'^[a-z][a-zA-Z\d_]*$') -forbidden_names = frozenset(['iter_all', 'iter_group', 'find', 'find_first', +FORBIDDEN_NAMES = frozenset(['iter_all', 'iter_group', 'find', 'find_first', 'make_dict', 'unwrap_from_path', 'read_only', 'read_write', 'getowner', 'set_contexts']) -allowed_const_list = ['_cons_not_equal'] +ALLOWED_CONST_LIST = ['_cons_not_equal'] def valid_name(name): @@ -54,7 +53,7 @@ def valid_name(name): if not isinstance(name, str): return False if re.match(name_regexp, name) is not None and \ - name not in forbidden_names and \ + name not in FORBIDDEN_NAMES and \ not name.startswith('impl_') and \ not name.startswith('cfgimpl_'): return True @@ -113,14 +112,40 @@ def validate_callback(callback, callback_params, type_, callbackoption): # -class Base(StorageBase): - __slots__ = tuple() +class Base(object): + __slots__ = ('_name', + '_informations', + '_extra', + '_warnings_only', + '_allow_empty_list', + #multi + '_multi', + '_unique', + #value + '_default', + '_default_multi', + #calcul + '_subdyn', + '_requires', + '_properties', + '_calc_properties', + '_val_call', + # + '_consistencies', + '_master_slaves', + '_choice_values', + '_choice_values_params', + #other + '_has_dependency', + '_dependencies', + '__weakref__' + ) def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, unique=undefined, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, warnings_only=False, extra=None, - allow_empty_list=undefined, session=None): + allow_empty_list=undefined): if not valid_name(name): raise ValueError(_("invalid name: {0} for option").format(name)) if not multi and default_multi is not None: @@ -159,7 +184,11 @@ class Base(StorageBase): validator_params = self._build_validator_params(validator, validator_params) validate_callback(validator, validator_params, 'validator', self) - self._set_validator(validator, validator_params) + if validator_params is None: + val_call = (validator,) + else: + val_call = (validator, validator_params) + self._val_call = (val_call, None) self._set_has_dependency() if calc_properties != frozenset([]) and properties is not tuple(): set_forbidden_properties = calc_properties & set(properties) @@ -167,21 +196,50 @@ class Base(StorageBase): raise ValueError('conflict: properties already set in ' 'requirement {0}'.format( list(set_forbidden_properties))) - if session is None: - session = self.getsession() - StorageBase.__init__(self, name, _multi, warnings_only, doc, extra, - calc_properties, requires, properties, - allow_empty_list, unique, session=session) + _setattr = object.__setattr__ + _setattr(self, '_name', name) + if sys.version_info[0] < 3 and isinstance(doc, str): + doc = doc.decode('utf8') + if extra is not None: + _setattr(self, '_extra', extra) + _setattr(self, '_informations', {'doc': doc}) + if _multi != 1: + _setattr(self, '_multi', _multi) + if warnings_only is True: + _setattr(self, '_warnings_only', warnings_only) + if calc_properties is not undefined: + _setattr(self, '_calc_properties', calc_properties) + if requires is not undefined: + _setattr(self, '_requires', requires) + if properties is not undefined: + _setattr(self, '_properties', properties) if multi is not False and default is None: default = [] + if allow_empty_list is not undefined: + _setattr(self, '_allow_empty_list', allow_empty_list) + if unique is not undefined: + _setattr(self, '_unique', unique) err = self.impl_validate(default, is_multi=is_multi) if err: raise err - self._set_default_values(default, default_multi, is_multi) + if (is_multi and default != []) or \ + (not is_multi and default is not None): + if is_multi: + default = tuple(default) + _setattr(self, '_default', default) + + if is_multi and default_multi is not None: + err = self._validate(default_multi) + if err: + raise ValueError(_("invalid default_multi value {0} " + "for option {1}: {2}").format( + str(default_multi), + self.impl_getname(), str(err))) + _setattr(self, '_default_multi', default_multi) + ##callback is False in optiondescription if callback is not False: self.impl_set_callback(callback, callback_params, _init=True) - self.commit(session) def _build_validator_params(self, validator, validator_params): if sys.version_info[0] < 3: @@ -237,7 +295,12 @@ class Base(StorageBase): self._validate_callback(callback, callback_params) if callback is not None: validate_callback(callback, callback_params, 'callback', self) - self._set_callback(callback, callback_params) + val = getattr(self, '_val_call', (None,))[0] + if callback_params is None or callback_params == {}: + val_call = (callback,) + else: + val_call = tuple([callback, callback_params]) + self._val_call = (val, val_call) def impl_is_optiondescription(self): return self.__class__.__name__ in ['OptionDescription', @@ -248,6 +311,93 @@ class Base(StorageBase): return self.__class__.__name__ in ['DynOptionDescription', 'SynDynOptionDescription'] + def impl_getname(self): + return self._name + + def impl_is_multi(self): + return getattr(self, '_multi', 1) != 1 + + def impl_is_readonly(self): + return not isinstance(getattr(self, '_informations', dict()), dict) + + def impl_getproperties(self): + return self._properties + + def _set_readonly(self, has_extra): + if not self.impl_is_readonly(): + _setattr = object.__setattr__ + dico = self._informations + keys = tuple(dico.keys()) + if len(keys) == 1: + dico = dico['doc'] + else: + dico = tuple([keys, tuple(dico.values())]) + _setattr(self, '_informations', dico) + if has_extra: + extra = getattr(self, '_extra', None) + if extra is not None: + _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())])) + + def _impl_setsubdyn(self, subdyn): + self._subdyn = subdyn + + def impl_getrequires(self): + return getattr(self, '_requires', static_tuple) + + def impl_get_callback(self): + call = getattr(self, '_val_call', (None, None))[1] + if call is None: + ret_call = (None, {}) + elif len(call) == 1: + ret_call = (call[0], {}) + else: + ret_call = call + return ret_call + + # ____________________________________________________________ + # information + def impl_get_information(self, key, default=undefined): + """retrieves one information's item + + :param key: the item string (ex: "help") + """ + def _is_string(infos): + if sys.version_info[0] >= 3: # pragma: no cover + return isinstance(infos, str) + else: + return isinstance(infos, str) or isinstance(infos, unicode) + + dico = self._informations + if isinstance(dico, tuple): + if key in dico[0]: + return dico[1][dico[0].index(key)] + elif _is_string(dico): + if key == 'doc': + return dico + elif isinstance(dico, dict): + if key in dico: + return dico[key] + if default is not undefined: + return default + raise ValueError(_("information's item not found: {0}").format( + key)) + + def impl_set_information(self, key, value): + """updates the information's attribute + (which is a dictionary) + + :param key: information's key (ex: "help", "doc" + :param value: information's value (ex: "the help string") + """ + if self.impl_is_readonly(): + raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" + " read-only").format( + self.__class__.__name__, + self, + #self.impl_getname(), + key)) + self._informations[key] = value + class BaseOption(Base): """This abstract base class stands for attribute access @@ -429,6 +579,19 @@ class Option(OnlyOption): else: return err + def impl_is_unique(self): + return getattr(self, '_unique', False) + + def impl_get_validator(self): + val = getattr(self, '_val_call', (None,))[0] + if val is None: + ret_val = (None, {}) + elif len(val) == 1: + ret_val = (val[0], {}) + else: + ret_val = val + return ret_val + def impl_validate(self, value, context=undefined, validate=True, force_index=None, force_submulti_index=None, current_opt=undefined, is_multi=None, @@ -455,6 +618,7 @@ class Option(OnlyOption): if display_warnings and setting_properties is undefined and context is not undefined: setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=False) display_warnings = display_warnings and (setting_properties is undefined or 'warnings' in setting_properties) + def _is_not_unique(value): if display_error and self.impl_is_unique() and len(set(value)) != len(value): for idx, val in enumerate(value): @@ -511,16 +675,17 @@ class Option(OnlyOption): self.impl_get_display_name()) return ValueError(msg) error = None - if ((display_error and not self._is_warnings_only()) or - (display_warnings and self._is_warnings_only())): + is_warnings_only = getattr(self, '_warnings_only', False) + if ((display_error and not is_warnings_only) or + (display_warnings and is_warnings_only)): error = calculation_validator(_value, _index) if not error: - error = self._second_level_validation(_value, self._is_warnings_only()) + error = self._second_level_validation(_value, is_warnings_only) if error: if debug: # pragma: no cover log.debug(_('do_validation for {0}: error in value').format( self.impl_getname()), exc_info=True) - if self._is_warnings_only(): + if is_warnings_only: msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format( _value, self._display_name, self.impl_get_display_name(), error) warnings.warn_explicit(ValueWarning(msg, self), @@ -629,8 +794,7 @@ class Option(OnlyOption): return False def impl_get_master_slaves(self): - masterslaves = self._get_master_slave() - return masterslaves + return getattr(self, '_master_slaves', None) def impl_getdoc(self): "accesses the Option's doc" @@ -638,7 +802,7 @@ class Option(OnlyOption): def _valid_consistencies(self, other_opts, init=True, func=None): if self._is_subdyn(): - dynod = self._impl_getsubdyn() + dynod = self._subdyn else: dynod = None if self.impl_is_submulti(): @@ -653,10 +817,10 @@ class Option(OnlyOption): if dynod is None: raise ConfigError(_('almost one option in consistency is ' 'in a dynoptiondescription but not all')) - if dynod != opt._impl_getsubdyn(): + if dynod != opt._subdyn: raise ConfigError(_('option in consistency must be in same' ' dynoptiondescription')) - dynod = opt._impl_getsubdyn() + dynod = opt._subdyn elif dynod is not None: raise ConfigError(_('almost one option in consistency is in a ' 'dynoptiondescription but not all')) @@ -699,7 +863,7 @@ class Option(OnlyOption): if err: self._del_consistency() raise err - if func in allowed_const_list: + if func in ALLOWED_CONST_LIST: for opt in all_cons_opts: if getattr(opt, '_unique', undefined) == undefined: opt._unique = True @@ -779,6 +943,10 @@ class Option(OnlyOption): def _impl_to_dyn(self, name, path): return DynSymLinkOption(name, self, dyn=path) + def impl_getdefault_multi(self): + "accessing the default value for a multi" + return getattr(self, '_default_multi', None) + def _validate_callback(self, callback, callback_params): """callback_params: * None @@ -794,6 +962,52 @@ class Option(OnlyOption): raise ValueError(_("default value not allowed if option: {0} " "is calculated").format(self.impl_getname())) + def impl_getdefault(self): + "accessing the default value" + is_multi = self.impl_is_multi() + default = getattr(self, '_default', undefined) + if default is undefined: + if is_multi: + default = [] + else: + default = None + else: + if is_multi: + default = list(default) + return default + + def _get_extra(self, key): + extra = self._extra + if isinstance(extra, tuple): + return extra[1][extra[0].index(key)] + else: + return extra[key] + + def impl_is_submulti(self): + return getattr(self, '_multi', 1) == 2 + + def impl_allow_empty_list(self): + return getattr(self, '_allow_empty_list', undefined) + + #____________________________________________________________ + # consistency + def _add_consistency(self, func, all_cons_opts, params): + cons = (func, all_cons_opts, params) + consistencies = getattr(self, '_consistencies', None) + if consistencies is None: + self._consistencies = [cons] + else: + consistencies.append(cons) + + def _del_consistency(self): + self._consistencies.pop(-1) + + def _get_consistencies(self): + return getattr(self, '_consistencies', static_tuple) + + def _has_consistencies(self): + return hasattr(self, '_consistencies') + def validate_requires_arg(new_option, multi, requires, name): """check malformed requirements @@ -951,16 +1165,17 @@ class SymLinkOption(OnlyOption): raise ValueError(_('malformed symlinkoption ' 'must be an option ' 'for symlink {0}').format(name)) - session = self.getsession() - super(Base, self).__init__(name, undefined, undefined, undefined, - undefined, undefined, undefined, undefined, - undefined, undefined, opt=opt, session=session) + _setattr = object.__setattr__ + _setattr(self, '_name', name) + _setattr(self, '_opt', opt) opt._set_has_dependency() - self.commit(session) def __getattr__(self, name, context=undefined): return getattr(self._impl_getopt(), name) + def _impl_getopt(self): + return self._opt + def impl_get_information(self, key, default=undefined): return self._impl_getopt().impl_get_information(key, default) diff --git a/tiramisu/option/masterslave.py b/tiramisu/option/masterslave.py index 16996a2..23e00f7 100644 --- a/tiramisu/option/masterslave.py +++ b/tiramisu/option/masterslave.py @@ -22,57 +22,50 @@ from ..i18n import _ from ..setting import log, undefined, debug from ..error import SlaveError, PropertiesOptionError -from ..storage import get_storages_option - - -StorageMasterSlaves = get_storages_option('masterslaves') class MasterSlaves(object): - __slots__ = ('_p_') + __slots__ = ('master', 'slaves') def __init__(self, name, childs=None, validate=True, add=True): - if isinstance(name, StorageMasterSlaves): # pragma: no cover - # only for sqlalchemy - self._p_ = name + #if master (same name has group) is set + #for collect all slaves + slaves = [] + if childs[0].impl_getname() == name: + master = childs[0] else: - #if master (same name has group) is set - #for collect all slaves - slaves = [] - if childs[0].impl_getname() == name: - master = childs[0] - else: - raise ValueError(_('master group with wrong' - ' master name for {0}' - ).format(name)) - for child in childs[1:]: - if child.impl_getdefault() != []: - raise ValueError(_("not allowed default value for option {0} " - "in master/slave object {1}").format(child.impl_getname(), - name)) - slaves.append(child) - if validate: - callback, callback_params = master.impl_get_callback() - if callback is not None and callback_params != {}: - for callbacks in callback_params.values(): - for callbk in callbacks: - if isinstance(callbk, tuple): - if callbk[0] in slaves: - raise ValueError(_("callback of master's option shall " - "not refered a slave's ones")) - #everything is ok, store references - self._p_ = StorageMasterSlaves(master, slaves) - if add: - for child in childs: - child._set_master_slaves(self) + raise ValueError(_('master group with wrong' + ' master name for {0}' + ).format(name)) + for child in childs[1:]: + if child.impl_getdefault() != []: + raise ValueError(_("not allowed default value for option {0} " + "in master/slave object {1}").format(child.impl_getname(), + name)) + slaves.append(child) + if validate: + callback, callback_params = master.impl_get_callback() + if callback is not None and callback_params != {}: + for callbacks in callback_params.values(): + for callbk in callbacks: + if isinstance(callbk, tuple): + if callbk[0] in slaves: + raise ValueError(_("callback of master's option shall " + "not refered a slave's ones")) + #everything is ok, store references + self.master = master + self.slaves = tuple(slaves) + if add: + for child in childs: + child._master_slaves = self def is_master(self, opt): - master = self._p_._sm_getmaster().impl_getname() + master = self.master.impl_getname() return opt.impl_getname() == master or (opt.impl_is_dynsymlinkoption() and opt._opt.impl_getname() == master) def getmaster(self, opt): - master = self._p_._sm_getmaster() + master = self.master if opt.impl_is_dynsymlinkoption(): suffix = opt.impl_getsuffix() name = master.impl_getname() + suffix @@ -83,21 +76,21 @@ class MasterSlaves(object): def getslaves(self, opt): if opt.impl_is_dynsymlinkoption(): - for slave in self._p_._sm_getslaves(): + for slave in self.slaves: suffix = opt.impl_getsuffix() name = slave.impl_getname() + suffix base_path = opt._dyn.split('.')[0] + '.' path = base_path + name yield slave._impl_to_dyn(name, path) else: - for slave in self._p_._sm_getslaves(): + for slave in self.slaves: yield slave def in_same_group(self, opt): if opt.impl_is_dynsymlinkoption(): - return opt._opt == self._p_._sm_getmaster() or opt._opt in self._p_._sm_getslaves() + return opt._opt == self.master or opt._opt in self.slaves else: - return opt == self._p_._sm_getmaster() or opt in self._p_._sm_getslaves() + return opt == self.master or opt in self.slaves def reset(self, opt, values, setting_properties, _commit=True): for slave in self.getslaves(opt): diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 4eabb6c..5843ff3 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -56,8 +56,9 @@ class ChoiceOption(Option): if not isinstance(values, tuple): raise TypeError(_('values must be a tuple or a function for {0}' ).format(name)) - session = self.getsession() - self.impl_set_choice_values_params(values, values_params, session) + self._choice_values = values + if values_params is not None: + self._choice_values_params = values_params super(ChoiceOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, @@ -67,9 +68,7 @@ class ChoiceOption(Option): validator=validator, validator_params=validator_params, properties=properties, - warnings_only=warnings_only, - session=session) - self.commit(session) + warnings_only=warnings_only) def impl_get_values(self, context, current_opt=undefined): if current_opt is undefined: @@ -82,7 +81,7 @@ class ChoiceOption(Option): else: values = carry_out_calculation(current_opt, context=context, callback=values, - callback_params=self.impl_get_choice_values_params()) + callback_params=getattr(self, '_choice_values_params', {})) if isinstance(values, Exception): return values if values is not undefined and not isinstance(values, list): diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 73b059d..9d7c176 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -24,15 +24,12 @@ import re from ..i18n import _ from ..setting import groups, undefined, owners # , log -from .baseoption import BaseOption, SymLinkOption, Option, allowed_const_list +from .baseoption import BaseOption, SymLinkOption, Option, ALLOWED_CONST_LIST from . import MasterSlaves from ..error import ConfigError, ConflictError -from ..storage import get_storages_option from ..autolib import carry_out_calculation -StorageOptionDescription = get_storages_option('optiondescription') - name_regexp = re.compile(r'^[a-zA-Z\d\-_]*$') import sys @@ -41,61 +38,8 @@ if sys.version_info[0] >= 3: # pragma: no cover del(sys) -class OptionDescription(BaseOption, StorageOptionDescription): - """Config's schema (organisation, group) and container of Options - The `OptionsDescription` objects lives in the `tiramisu.config.Config`. - """ - __slots__ = tuple() - - def __init__(self, name, doc, children, requires=None, properties=None): - """ - :param children: a list of options (including optiondescriptions) - - """ - super(OptionDescription, self).__init__(name, doc=doc, - requires=requires, - properties=properties, - callback=False) - child_names = [] - dynopt_names = [] - for child in children: - name = child.impl_getname() - child_names.append(name) - if isinstance(child, DynOptionDescription): - dynopt_names.append(name) - - #better performance like this - valid_child = copy(child_names) - valid_child.sort() - old = None - for child in valid_child: - if child == old: # pragma: optional cover - raise ConflictError(_('duplicate option name: ' - '{0}').format(child)) - if dynopt_names: - for dynopt in dynopt_names: - if child != dynopt and child.startswith(dynopt): - raise ConflictError(_('option must not start as ' - 'dynoptiondescription')) - old = child - self._add_children(child_names, children) - _setattr = object.__setattr__ - _setattr(self, '_cache_consistencies', None) - # the group_type is useful for filtering OptionDescriptions in a config - _setattr(self, '_group_type', groups.default) - - def impl_getdoc(self): - return self.impl_get_information('doc') - - def impl_validate(self, *args, **kwargs): - """usefull for OptionDescription""" - pass - - def impl_getpaths(self, include_groups=False, _currpath=None): - """returns a list of all paths in self, recursively - _currpath should not be provided (helps with recursion) - """ - return _impl_getpaths(self, include_groups, _currpath) +class CacheOptionDescription(BaseOption): + __slots__ = ('_cache_paths', '_cache_consistencies', '_cache_force_store_values') def impl_build_cache(self, config, path='', _consistencies=None, cache_option=None, force_store_values=None): @@ -112,9 +56,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): else: init = 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) if path == '': subpath = option.impl_getname() else: @@ -138,7 +80,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): force_store_values.append((subpath, option)) for func, all_cons_opts, params in option._get_consistencies(): option._valid_consistencies(all_cons_opts[1:], init=False) - if func not in allowed_const_list and is_multi: + if func not in ALLOWED_CONST_LIST and is_multi: is_masterslaves = option.impl_is_master_slaves() if not is_masterslaves: raise ConfigError(_('malformed consistency option "{0}" ' @@ -146,7 +88,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): option.impl_getname())) masterslaves = option.impl_get_master_slaves() for opt in all_cons_opts: - if func not in allowed_const_list and is_multi: + if func not in ALLOWED_CONST_LIST and is_multi: if not opt.impl_is_master_slaves(): raise ConfigError(_('malformed consistency option "{0}" ' 'must not be a multi for "{1}"').format( @@ -193,7 +135,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): if _consistencies != {}: self._cache_consistencies = {} for opt, cons in _consistencies.items(): - if opt._get_id() not in cache_option: # pragma: optional cover + if opt not in cache_option: # pragma: optional cover raise ConfigError(_('consistency with option {0} ' 'which is not in Config').format( opt.impl_getname())) @@ -201,6 +143,8 @@ class OptionDescription(BaseOption, StorageOptionDescription): self._cache_force_store_values = force_store_values self._set_readonly(False) + def impl_already_build_caches(self): + return getattr(self, '_cache_paths', None) is not None def impl_build_force_store_values(self, config, force_store_values): session = config._impl_values._p_.getsession() @@ -226,6 +170,279 @@ class OptionDescription(BaseOption, StorageOptionDescription): if value_set: config._impl_values._p_.commit() + def impl_build_cache_option(self, _currpath=None, cache_path=None, + cache_option=None): + + if self.impl_is_readonly() or (_currpath is None and getattr(self, '_cache_paths', None) is not None): + # cache already set + return + if _currpath is None: + save = True + _currpath = [] + else: + save = False + if cache_path is None: + cache_path = [] + cache_option = [] + for option in self._impl_getchildren(dyn=False): + attr = option.impl_getname() + path = str('.'.join(_currpath + [attr])) + cache_option.append(option) + cache_path.append(path) + if option.impl_is_optiondescription(): + _currpath.append(attr) + option.impl_build_cache_option(_currpath, cache_path, + cache_option) + _currpath.pop() + if save: + _setattr = object.__setattr__ + _setattr(self, '_cache_paths', (tuple(cache_option), tuple(cache_path))) + + +class OptionDescriptionWalk(CacheOptionDescription): + __slots__ = ('_children',) + + def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context): + find_results = [] + + def _rebuild_dynpath(path, suffix, dynopt): + found = False + spath = path.split('.') + for length in xrange(1, len(spath)): + subpath = '.'.join(spath[0:length]) + subopt = self.impl_get_opt_by_path(subpath) + if dynopt == subopt: + found = True + break + if not found: # pragma: no cover + raise ConfigError(_('cannot find dynpath')) + subpath = subpath + suffix + for slength in xrange(length, len(spath)): + subpath = subpath + '.' + spath[slength] + suffix + return subpath + + def _filter_by_name(path, option): + name = option.impl_getname() + if option._is_subdyn(): + if byname.startswith(name): + found = False + for suffix in option._subdyn._impl_get_suffixes( + context): + if byname == name + suffix: + found = True + path = _rebuild_dynpath(path, suffix, + option._subdyn) + option = option._impl_to_dyn( + name + suffix, path) + break + if not found: + return False + else: + if not byname == name: + return False + find_results.append((path, option)) + return True + + def _filter_by_type(path, option): + if isinstance(option, bytype): + #if byname is not None, check option byname in _filter_by_name + #not here + if byname is None: + if option._is_subdyn(): + name = option.impl_getname() + for suffix in option._subdyn._impl_get_suffixes( + context): + spath = _rebuild_dynpath(path, suffix, + option._subdyn) + find_results.append((spath, option._impl_to_dyn( + name + suffix, spath))) + else: + find_results.append((path, option)) + return True + return False + + def _filter(path, option): + if bytype is not None: + retval = _filter_by_type(path, option) + if byname is None: + return retval + if byname is not None: + return _filter_by_name(path, option) + + opts, paths = self._cache_paths + for index in xrange(0, len(paths)): + option = opts[index] + if option.impl_is_optiondescription(): + continue + path = paths[index] + if _subpath is not None and not path.startswith(_subpath + '.'): + continue + if bytype == byname is None: + if option._is_subdyn(): + name = option.impl_getname() + for suffix in option._subdyn._impl_get_suffixes( + context): + spath = _rebuild_dynpath(path, suffix, + option._subdyn) + find_results.append((spath, option._impl_to_dyn( + name + suffix, spath))) + else: + find_results.append((path, option)) + else: + if _filter(path, option) is False: + continue + if only_first: + return find_results + return find_results + + def _impl_st_getchildren(self, context, only_dyn=False): + for child in self._children[1]: + if only_dyn is False or child.impl_is_dynoptiondescription(): + yield(child) + + def _getattr(self, name, suffix=undefined, context=undefined, dyn=True): + error = False + if suffix is not undefined: + if undefined in [suffix, context]: # pragma: no cover + raise ConfigError(_("suffix and context needed if " + "it's a dyn option")) + if name.endswith(suffix): + oname = name[:-len(suffix)] + child = self._children[1][self._children[0].index(oname)] + return self._impl_get_dynchild(child, suffix) + else: + error = True + else: + if name in self._children[0]: + child = self._children[1][self._children[0].index(name)] + if dyn and child.impl_is_dynoptiondescription(): + error = True + else: + return child + else: + child = self._impl_search_dynchild(name, context=context) + if child != []: + return child + error = True + if error: + raise AttributeError(_('unknown Option {0} ' + 'in OptionDescription {1}' + '').format(name, self.impl_getname())) + + def impl_getpaths(self, include_groups=False, _currpath=None): + """returns a list of all paths in self, recursively + _currpath should not be provided (helps with recursion) + """ + return _impl_getpaths(self, include_groups, _currpath) + + def impl_get_opt_by_path(self, path): + if getattr(self, '_cache_paths', None) is None: + raise ConfigError(_('use impl_get_opt_by_path only with root OptionDescription')) + if path not in self._cache_paths[1]: + raise AttributeError(_('no option for path {0}').format(path)) + return self._cache_paths[0][self._cache_paths[1].index(path)] + + def impl_get_path_by_opt(self, opt): + if getattr(self, '_cache_paths', None) is None: + raise ConfigError(_('use impl_get_path_by_opt only with root OptionDescription')) + if opt not in self._cache_paths[0]: + raise AttributeError(_('no option {0} found').format(opt)) + return self._cache_paths[1][self._cache_paths[0].index(opt)] + + def _impl_getchildren(self, dyn=True, context=undefined): + for child in self._impl_st_getchildren(context): + cname = child.impl_getname() + if dyn and child.impl_is_dynoptiondescription(): + path = cname + for value in child._impl_get_suffixes(context): + yield SynDynOptionDescription(child, + cname + value, + path + value, value) + else: + yield child + + def impl_getchildren(self): + return list(self._impl_getchildren()) + + def __getattr__(self, name, context=undefined): + if name.startswith('_'): # or name.startswith('impl_'): + return object.__getattribute__(self, name) + if '.' in name: + path = name.split('.')[0] + subpath = '.'.join(name.split('.')[1:]) + return self.__getattr__(path, context=context).__getattr__(subpath, context=context) + return self._getattr(name, context=context) + + def _impl_search_dynchild(self, name, context): + ret = [] + for child in self._impl_st_getchildren(context, only_dyn=True): + cname = child.impl_getname() + if name.startswith(cname): + path = cname + for value in child._impl_get_suffixes(context): + if name == cname + value: + return SynDynOptionDescription(child, name, path + value, value) + return ret + + def _impl_get_dynchild(self, child, suffix): + name = child.impl_getname() + suffix + path = self.impl_getname() + suffix + '.' + name + if isinstance(child, OptionDescription): + return SynDynOptionDescription(child, name, path, suffix) + else: + return child._impl_to_dyn(name, path) + + +class OptionDescription(OptionDescriptionWalk): + """Config's schema (organisation, group) and container of Options + The `OptionsDescription` objects lives in the `tiramisu.config.Config`. + """ + __slots__ = ('_group_type',) + + def __init__(self, name, doc, children, requires=None, properties=None): + """ + :param children: a list of options (including optiondescriptions) + + """ + super(OptionDescription, self).__init__(name, doc=doc, + requires=requires, + properties=properties, + callback=False) + child_names = [] + dynopt_names = [] + for child in children: + name = child.impl_getname() + child_names.append(name) + if isinstance(child, DynOptionDescription): + dynopt_names.append(name) + + #better performance like this + valid_child = copy(child_names) + valid_child.sort() + old = None + for child in valid_child: + if child == old: # pragma: optional cover + raise ConflictError(_('duplicate option name: ' + '{0}').format(child)) + if dynopt_names: + for dynopt in dynopt_names: + if child != dynopt and child.startswith(dynopt): + raise ConflictError(_('option must not start as ' + 'dynoptiondescription')) + old = child + _setattr = object.__setattr__ + _setattr(self, '_children', (tuple(child_names), tuple(children))) + _setattr(self, '_cache_consistencies', None) + # the group_type is useful for filtering OptionDescriptions in a config + _setattr(self, '_group_type', groups.default) + + def impl_getdoc(self): + return self.impl_get_information('doc') + + def impl_validate(self, *args, **kwargs): + """usefull for OptionDescription""" + pass + # ____________________________________________________________ def impl_set_group_type(self, group_type): """sets a given group object to an OptionDescription @@ -260,6 +477,9 @@ class OptionDescription(BaseOption, StorageOptionDescription): raise ValueError(_('group_type: {0}' ' not allowed').format(group_type)) + def impl_get_group_type(self): + return self._group_type + def __getstate__(self): raise NotImplementedError() @@ -275,49 +495,6 @@ class OptionDescription(BaseOption, StorageOptionDescription): raise ValueError(_("invalid suffix: {0} for option").format(val)) return values - def _impl_search_dynchild(self, name, context): - ret = [] - for child in self._impl_st_getchildren(context, only_dyn=True): - cname = child.impl_getname() - if name.startswith(cname): - path = cname - for value in child._impl_get_suffixes(context): - if name == cname + value: - return SynDynOptionDescription(child, name, path + value, value) - return ret - - def _impl_get_dynchild(self, child, suffix): - name = child.impl_getname() + suffix - path = self.impl_getname() + suffix + '.' + name - if isinstance(child, OptionDescription): - return SynDynOptionDescription(child, name, path, suffix) - else: - return child._impl_to_dyn(name, path) - - def _impl_getchildren(self, dyn=True, context=undefined): - for child in self._impl_st_getchildren(context): - cname = child.impl_getname() - if dyn and child.impl_is_dynoptiondescription(): - path = cname - for value in child._impl_get_suffixes(context): - yield SynDynOptionDescription(child, - cname + value, - path + value, value) - else: - yield child - - def impl_getchildren(self): - return list(self._impl_getchildren()) - - def __getattr__(self, name, context=undefined): - if name.startswith('_'): # or name.startswith('impl_'): - return object.__getattribute__(self, name) - if '.' in name: - path = name.split('.')[0] - subpath = '.'.join(name.split('.')[1:]) - return self.__getattr__(path, context=context).__getattr__(subpath, context=context) - return self._getattr(name, context=context) - class DynOptionDescription(OptionDescription): def __init__(self, name, doc, children, requires=None, properties=None, @@ -378,19 +555,19 @@ class SynDynOptionDescription(object): def _impl_getpaths(klass, include_groups, _currpath): - """returns a list of all paths in klass, recursively - _currpath should not be provided (helps with recursion) - """ - if _currpath is None: - _currpath = [] - paths = [] - for option in klass._impl_getchildren(): - attr = option.impl_getname() - if option.impl_is_optiondescription(): - if include_groups: - paths.append('.'.join(_currpath + [attr])) - paths += option.impl_getpaths(include_groups=include_groups, - _currpath=_currpath + [attr]) - else: + """returns a list of all paths in klass, recursively + _currpath should not be provided (helps with recursion) + """ + if _currpath is None: + _currpath = [] + paths = [] + for option in klass._impl_getchildren(): + attr = option.impl_getname() + if option.impl_is_optiondescription(): + if include_groups: paths.append('.'.join(_currpath + [attr])) - return paths + paths += option.impl_getpaths(include_groups=include_groups, + _currpath=_currpath + [attr]) + else: + paths.append('.'.join(_currpath + [attr])) + return paths diff --git a/tiramisu/setting.py b/tiramisu/setting.py index a3289c0..df40a6f 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -114,6 +114,7 @@ log = getLogger('tiramisu') #import logging #logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) debug = False +static_set = frozenset() # ____________________________________________________________ @@ -261,7 +262,7 @@ class Property(object): def _append(self, propname, save=True): if self._opt is not None and self._opt.impl_getrequires() is not None \ - and propname in self._opt.impl_get_calc_properties(): # pragma: optional cover + and propname in getattr(self._opt, '_calc_properties', static_set): # pragma: optional cover raise ValueError(_('cannot append {0} property for option {1}: ' 'this property is calculated').format( propname, self._opt.impl_getname())) diff --git a/tiramisu/storage/__init__.py b/tiramisu/storage/__init__.py index 2652f5a..9e925a3 100644 --- a/tiramisu/storage/__init__.py +++ b/tiramisu/storage/__init__.py @@ -77,8 +77,8 @@ class StorageType(object): storage_type = StorageType() -storage_option_type = StorageType() -storage_option_type.set(DEFAULT_STORAGE) +#storage_option_type = StorageType() +#storage_option_type.set(DEFAULT_STORAGE) default_validation = StorageType() default_validation.set(DEFAULT_STORAGE) @@ -109,14 +109,14 @@ def get_storages(context, session_id, persistent): return properties, permissives, values, session_id -def get_storages_option(type_): - imp = storage_option_type.get() - if type_ == 'base': - return imp.StorageBase - elif type_ == 'masterslaves': - return imp.StorageMasterSlaves - else: - return imp.StorageOptionDescription +#def get_storages_option(type_): +# imp = storage_option_type.get() +# if type_ == 'base': +# return imp.StorageBase +# elif type_ == 'masterslaves': +# return imp.StorageMasterSlaves +# else: +# return imp.StorageOptionDescription def get_default_values_storages(): @@ -136,10 +136,10 @@ def get_default_settings_storages(): def list_sessions(type_): # pragma: optional cover """List all available session (persistent or not persistent) """ - if type_ == 'option': - return storage_option_type.get().list_sessions() - else: - return storage_type.get().list_sessions() + #if type_ == 'option': + # return storage_option_type.get().list_sessions() + #else: + return storage_type.get().list_sessions() def delete_session(type_, session_id): # pragma: optional cover @@ -149,14 +149,14 @@ def delete_session(type_, session_id): # pragma: optional cover """ storage_module = storage_type.get() session = storage_module.storage.getsession() - if type_ == 'option': - storage_option_type.get().delete_session(session_id, session) - else: - storage_module.value.delete_session(session_id, session) - storage_module.storage.delete_session(session_id, session) - if session: - session.commit() - del(session) + #if type_ == 'option': + # storage_option_type.get().delete_session(session_id, session) + #else: + storage_module.value.delete_session(session_id, session) + storage_module.storage.delete_session(session_id, session) + if session: + session.commit() + del(session) __all__ = ('set_storage', 'list_sessions', 'delete_session') diff --git a/tiramisu/storage/dictionary/__init__.py b/tiramisu/storage/dictionary/__init__.py index 312f8ef..312d2ed 100644 --- a/tiramisu/storage/dictionary/__init__.py +++ b/tiramisu/storage/dictionary/__init__.py @@ -25,7 +25,6 @@ use it. But if something goes wrong, you will lost your modifications. from .value import Values from .setting import Properties, Permissives from .storage import setting, Storage, list_sessions, delete_session -from .option import StorageBase, StorageOptionDescription, StorageMasterSlaves __all__ = ('setting', 'Values', 'Properties', 'Permissives', 'Storage', 'list_sessions', - 'delete_session', 'StorageBase', 'StorageOptionDescription', 'StorageMasterSlaves') + 'delete_session') diff --git a/tiramisu/storage/dictionary/option.py b/tiramisu/storage/dictionary/option.py deleted file mode 100644 index 85b7c8f..0000000 --- a/tiramisu/storage/dictionary/option.py +++ /dev/null @@ -1,503 +0,0 @@ -# -*- coding: utf-8 -*- -"" -# Copyright (C) 2014-2017 Team tiramisu (see AUTHORS for all contributors) -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# ____________________________________________________________ -import sys -from ...i18n import _ -from ...setting import undefined -from ...error import ConfigError -static_tuple = tuple() -static_set = frozenset() -if sys.version_info[0] >= 3: # pragma: no cover - xrange = range - - -#____________________________________________________________ -# -# Base -#('_name', '_informations', '_multi', '_multitype', '_warnings_only', '_extra', '_readonly', '_subdyn) -class StorageBase(object): - __slots__ = ('_name', - '_informations', - '_extra', - '_warnings_only', - '_allow_empty_list', - #multi - '_multi', - '_unique', - #value - '_default', - '_default_multi', - #calcul - '_subdyn', - '_requires', - '_properties', - '_calc_properties', - '_val_call', - # - '_consistencies', - '_master_slaves', - '_choice_values', - '_choice_values_params', - #other - '_has_dependency', - '_dependencies', - '__weakref__' - ) - - def __init__(self, name, multi, warnings_only, doc, extra, calc_properties, - requires, properties, allow_empty_list, unique, opt=undefined, - session=None): - _setattr = object.__setattr__ - _setattr(self, '_name', name) - if doc is not undefined: - if sys.version_info[0] < 3 and isinstance(doc, str): - doc = doc.decode('utf8') - _setattr(self, '_informations', {'doc': doc}) - if multi != 1: - _setattr(self, '_multi', multi) - if extra is not None: - _setattr(self, '_extra', extra) - if warnings_only is True: - _setattr(self, '_warnings_only', warnings_only) - - if calc_properties is not undefined: - _setattr(self, '_calc_properties', calc_properties) - if requires is not undefined: - _setattr(self, '_requires', requires) - if properties is not undefined: - _setattr(self, '_properties', properties) - if opt is not undefined: - _setattr(self, '_opt', opt) - if allow_empty_list is not undefined: - _setattr(self, '_allow_empty_list', allow_empty_list) - if unique is not undefined: - setattr(self, '_unique', unique) - - def _set_default_values(self, default, default_multi, is_multi): - _setattr = object.__setattr__ - if (is_multi and default != []) or \ - (not is_multi and default is not None): - if is_multi: - default = tuple(default) - _setattr(self, '_default', default) - - if is_multi and default_multi is not None: - err = self._validate(default_multi) - if err: - raise ValueError(_("invalid default_multi value {0} " - "for option {1}: {2}").format( - str(default_multi), - self.impl_getname(), str(err))) - _setattr(self, '_default_multi', default_multi) - - # information - def impl_set_information(self, key, value): - """updates the information's attribute - (which is a dictionary) - - :param key: information's key (ex: "help", "doc" - :param value: information's value (ex: "the help string") - """ - if self.impl_is_readonly(): - raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" - " read-only").format( - self.__class__.__name__, - self, - #self.impl_getname(), - key)) - self._informations[key] = value - - def impl_get_information(self, key, default=undefined): - """retrieves one information's item - - :param key: the item string (ex: "help") - """ - dico = self._informations - if isinstance(dico, tuple): - if key in dico[0]: - return dico[1][dico[0].index(key)] - elif self._is_string(dico): - if key == 'doc': - return dico - elif isinstance(dico, dict): - if key in dico: - return dico[key] - if default is not undefined: - return default - raise ValueError(_("information's item not found: {0}").format( - key)) - - def _add_consistency(self, func, all_cons_opts, params): - cons = (func, all_cons_opts, params) - consistencies = getattr(self, '_consistencies', None) - if consistencies is None: - self._consistencies = [cons] - else: - consistencies.append(cons) - - def _del_consistency(self): - self._consistencies.pop(-1) - - def _get_consistencies(self): - return getattr(self, '_consistencies', static_tuple) - - def _has_consistencies(self): - return hasattr(self, '_consistencies') - - def _set_callback(self, callback, callback_params): - val = getattr(self, '_val_call', (None,))[0] - if callback_params is None or callback_params == {}: - val_call = (callback,) - else: - val_call = tuple([callback, callback_params]) - self._val_call = (val, val_call) - - def impl_set_choice_values_params(self, values, values_params, session): - self._choice_values = values - if values_params is not None: - self._choice_values_params = values_params - - - def impl_get_callback(self): - call = getattr(self, '_val_call', (None, None))[1] - if call is None: - ret_call = (None, {}) - elif len(call) == 1: - ret_call = (call[0], {}) - else: - ret_call = call - return ret_call - - def impl_get_choice_values_params(self): - return getattr(self, '_choice_values_params', {}) - - def impl_get_calc_properties(self): - return getattr(self, '_calc_properties', static_set) - - def impl_getrequires(self): - return getattr(self, '_requires', static_tuple) - - def _set_validator(self, validator, validator_params): - if validator_params is None: - val_call = (validator,) - else: - val_call = (validator, validator_params) - self._val_call = (val_call, None) - - def impl_get_validator(self): - val = getattr(self, '_val_call', (None,))[0] - if val is None: - ret_val = (None, {}) - elif len(val) == 1: - ret_val = (val[0], {}) - else: - ret_val = val - return ret_val - - def _get_id(self): - return id(self) - - def _impl_getsubdyn(self): - return self._subdyn - - def _impl_getopt(self): - return self._opt - - def _set_readonly(self, has_extra): - if not self.impl_is_readonly(): - _setattr = object.__setattr__ - dico = self._informations - keys = tuple(dico.keys()) - if len(keys) == 1: - dico = dico['doc'] - else: - dico = tuple([keys, tuple(dico.values())]) - _setattr(self, '_informations', dico) - if has_extra: - extra = getattr(self, '_extra', None) - if extra is not None: - _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())])) - - def impl_getproperties(self): - return self._properties - - def _impl_setsubdyn(self, subdyn): - self._subdyn = subdyn - - def _is_string(self, infos): - if sys.version_info[0] >= 3: # pragma: no cover - return isinstance(infos, str) - else: - return isinstance(infos, str) or isinstance(infos, unicode) - - def impl_is_readonly(self): - return not isinstance(getattr(self, '_informations', dict()), dict) - - def impl_getname(self): - return self._name - - def impl_is_multi(self): - return getattr(self, '_multi', 1) != 1 - - def impl_is_submulti(self): - return getattr(self, '_multi', 1) == 2 - - def impl_allow_empty_list(self): - return getattr(self, '_allow_empty_list', undefined) - - def impl_is_unique(self): - return getattr(self, '_unique', False) - - def _get_extra(self, key): - extra = self._extra - if isinstance(extra, tuple): - return extra[1][extra[0].index(key)] - else: - return extra[key] - - def _is_warnings_only(self): - return getattr(self, '_warnings_only', False) - - def impl_getdefault(self): - "accessing the default value" - is_multi = self.impl_is_multi() - default = getattr(self, '_default', undefined) - if default is undefined: - if is_multi: - default = [] - else: - default = None - else: - if is_multi: - default = list(default) - return default - - def impl_getdefault_multi(self): - "accessing the default value for a multi" - return getattr(self, '_default_multi', None) - - def _get_master_slave(self): - return getattr(self, '_master_slaves', None) - - def _set_master_slaves(self, option): - self._master_slaves = option - - def getsession(self): - pass - - def commit(self, session): - pass - - -class StorageOptionDescription(StorageBase): - __slots__ = ('_children', '_cache_paths', '_cache_consistencies', - '_group_type', '_cache_force_store_values') - - def _add_children(self, child_names, children): - _setattr = object.__setattr__ - _setattr(self, '_children', (tuple(child_names), tuple(children))) - - def impl_already_build_caches(self): - return getattr(self, '_cache_paths', None) is not None - - def impl_get_opt_by_path(self, path): - if getattr(self, '_cache_paths', None) is None: - raise ConfigError(_('use impl_get_opt_by_path only with root OptionDescription')) - if path not in self._cache_paths[1]: - raise AttributeError(_('no option for path {0}').format(path)) - return self._cache_paths[0][self._cache_paths[1].index(path)] - - def impl_get_path_by_opt(self, opt): - if getattr(self, '_cache_paths', None) is None: - raise ConfigError(_('use impl_get_path_by_opt only with root OptionDescription')) - if opt not in self._cache_paths[0]: - raise AttributeError(_('no option {0} found').format(opt)) - return self._cache_paths[1][self._cache_paths[0].index(opt)] - - def impl_get_group_type(self): - return self._group_type - - def impl_build_cache_option(self, _currpath=None, cache_path=None, - cache_option=None): - - if self.impl_is_readonly() or (_currpath is None and getattr(self, '_cache_paths', None) is not None): - # cache already set - return - if _currpath is None: - save = True - _currpath = [] - else: - save = False - if cache_path is None: - cache_path = [] - cache_option = [] - for option in self._impl_getchildren(dyn=False): - attr = option.impl_getname() - path = str('.'.join(_currpath + [attr])) - cache_option.append(option) - cache_path.append(path) - if option.impl_is_optiondescription(): - _currpath.append(attr) - option.impl_build_cache_option(_currpath, cache_path, - cache_option) - _currpath.pop() - if save: - _setattr = object.__setattr__ - _setattr(self, '_cache_paths', (tuple(cache_option), tuple(cache_path))) - - def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context): - find_results = [] - - def _rebuild_dynpath(path, suffix, dynopt): - found = False - spath = path.split('.') - for length in xrange(1, len(spath)): - subpath = '.'.join(spath[0:length]) - subopt = self.impl_get_opt_by_path(subpath) - if dynopt == subopt: - found = True - break - if not found: # pragma: no cover - raise ConfigError(_('cannot find dynpath')) - subpath = subpath + suffix - for slength in xrange(length, len(spath)): - subpath = subpath + '.' + spath[slength] + suffix - return subpath - - def _filter_by_name(path, option): - name = option.impl_getname() - if option._is_subdyn(): - if byname.startswith(name): - found = False - for suffix in option._subdyn._impl_get_suffixes( - context): - if byname == name + suffix: - found = True - path = _rebuild_dynpath(path, suffix, - option._subdyn) - option = option._impl_to_dyn( - name + suffix, path) - break - if not found: - return False - else: - if not byname == name: - return False - find_results.append((path, option)) - return True - - def _filter_by_type(path, option): - if isinstance(option, bytype): - #if byname is not None, check option byname in _filter_by_name - #not here - if byname is None: - if option._is_subdyn(): - name = option.impl_getname() - for suffix in option._subdyn._impl_get_suffixes( - context): - spath = _rebuild_dynpath(path, suffix, - option._subdyn) - find_results.append((spath, option._impl_to_dyn( - name + suffix, spath))) - else: - find_results.append((path, option)) - return True - return False - - def _filter(path, option): - if bytype is not None: - retval = _filter_by_type(path, option) - if byname is None: - return retval - if byname is not None: - return _filter_by_name(path, option) - - opts, paths = self._cache_paths - for index in xrange(0, len(paths)): - option = opts[index] - if option.impl_is_optiondescription(): - continue - path = paths[index] - if _subpath is not None and not path.startswith(_subpath + '.'): - continue - if bytype == byname is None: - if option._is_subdyn(): - name = option.impl_getname() - for suffix in option._subdyn._impl_get_suffixes( - context): - spath = _rebuild_dynpath(path, suffix, - option._subdyn) - find_results.append((spath, option._impl_to_dyn( - name + suffix, spath))) - else: - find_results.append((path, option)) - else: - if _filter(path, option) is False: - continue - if only_first: - return find_results - return find_results - - def _impl_st_getchildren(self, context, only_dyn=False): - for child in self._children[1]: - if only_dyn is False or child.impl_is_dynoptiondescription(): - yield(child) - - def _getattr(self, name, suffix=undefined, context=undefined, dyn=True): - error = False - if suffix is not undefined: - if undefined in [suffix, context]: # pragma: no cover - raise ConfigError(_("suffix and context needed if " - "it's a dyn option")) - if name.endswith(suffix): - oname = name[:-len(suffix)] - child = self._children[1][self._children[0].index(oname)] - return self._impl_get_dynchild(child, suffix) - else: - error = True - else: - if name in self._children[0]: - child = self._children[1][self._children[0].index(name)] - if dyn and child.impl_is_dynoptiondescription(): - error = True - else: - return child - else: - child = self._impl_search_dynchild(name, context=context) - if child != []: - return child - error = True - if error: - raise AttributeError(_('unknown Option {0} ' - 'in OptionDescription {1}' - '').format(name, self.impl_getname())) - - -class StorageMasterSlaves(object): - __slots__ = ('master', 'slaves') - - def __init__(self, master, slaves): - self.master = master - self.slaves = tuple(slaves) - - def _sm_getmaster(self): - return self.master - - def _sm_getslaves(self): - return self.slaves diff --git a/tiramisu/storage/sqlalchemy/option.py b/tiramisu/storage/sqlalchemy/option.py deleted file mode 100644 index db2675b..0000000 --- a/tiramisu/storage/sqlalchemy/option.py +++ /dev/null @@ -1,981 +0,0 @@ -# -*- coding: utf-8 -*- -"" -# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors) -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# ____________________________________________________________ -from tiramisu.i18n import _ -from tiramisu.setting import groups, undefined -from tiramisu.error import ConfigError -from .util import SqlAlchemyBase -import util - -from sqlalchemy import not_, or_, and_, inspect -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy import Column, Integer, String, Boolean, PickleType, \ - ForeignKey, Table -from sqlalchemy.orm import relationship, backref -from sqlalchemy.orm.collections import attribute_mapped_collection - -from itertools import chain - - -def load_requires(collection_type, proxy): - def getter(obj): - if obj is None: - return None - ret = [] - requires = getattr(obj, proxy.value_attr) - session = util.Session() - for require in requires: - option = session.query(_Base).filter_by(id=require.option).first() - ret.append(tuple([option, require.expected, require.action, require.inverse, require.transitive, require.same_action])) - return tuple(ret) - - def setter(obj, value): - setattr(obj, proxy.value_attr, value) - return getter, setter - - -class _Require(SqlAlchemyBase): - __tablename__ = "require" - id = Column(Integer, primary_key=True) - requires_id = Column(Integer, ForeignKey("baseoption.id"), nullable=False) - requires = relationship('_RequireOption') - - def __init__(self, requires): - for require in requires: - self.requires.append(_RequireOption(require)) - - -class _RequireOption(SqlAlchemyBase): - __tablename__ = 'requireoption' - id = Column(Integer, primary_key=True) - require_id = Column(Integer, ForeignKey("require.id"), nullable=False) - option = Column(Integer, nullable=False) - _expected = relationship("_RequireExpected", collection_class=list, - cascade="all, delete-orphan") - expected = association_proxy("_expected", "expected") - #expected = Column(String) - action = Column(String, nullable=False) - inverse = Column(Boolean, default=False) - transitive = Column(Boolean, default=True) - same_action = Column(Boolean, default=True) - - def __init__(self, values): - option, expected, action, inverse, transitive, same_action = values - self.option = option.id - self.expected = expected - self.action = action - self.inverse = inverse - self.transitive = transitive - self.same_action = same_action - - -class _RequireExpected(SqlAlchemyBase): - __tablename__ = 'expected' - id = Column(Integer, primary_key=True) - require = Column(Integer, ForeignKey('requireoption.id'), nullable=False) - expected = Column(PickleType) - - def __init__(self, expected): - #FIXME ne pas creer plusieurs fois la meme _expected_ - #FIXME pareil avec calc_properties - self.expected = expected - - -class _CalcProperties(SqlAlchemyBase): - __tablename__ = 'calcproperty' - id = Column(Integer, primary_key=True) - require = Column(Integer, ForeignKey('baseoption.id'), nullable=False) - name = Column(PickleType) - - def __init__(self, name): - #FIXME ne pas creer plusieurs fois la meme _expected_ - #FIXME pareil avec calc_properties - self.name = name - - -#____________________________________________________________ -# -# properties -class _PropertyOption(SqlAlchemyBase): - __tablename__ = 'propertyoption' - id = Column(Integer, primary_key=True) - option = Column(Integer, ForeignKey('baseoption.id'), nullable=False) - name = Column(String) - - def __init__(self, name): - self.name = name - - -#____________________________________________________________ -# -# information -class _Information(SqlAlchemyBase): - __tablename__ = 'information' - id = Column(Integer, primary_key=True) - option = Column(String, ForeignKey('baseoption.id'), nullable=False) - key = Column(String) - value = Column(PickleType) - -# def __init__(self, option, key, value): -# self.option = option -# self.key = key -# self.value = value - - -#____________________________________________________________ -# -# callback -def load_callback_parm(collection_type, proxy): - def getter(obj): - if obj is None: - return None - ret = [] - requires = getattr(obj, proxy.value_attr) - session = util.Session() - for require in requires: - if require.value is not None: - ret.append(require.value) - else: - option = session.query(_Base).filter_by(id=require.option).first() - ret.append((option, require.force_permissive)) - return tuple(ret) - - def setter(obj, value): - setattr(obj, proxy.value_attr, value) - return getter, setter - - -class _CallbackParamOption(SqlAlchemyBase): - __tablename__ = 'callback_param_option' - id = Column(Integer, primary_key=True) - callback_param = Column(Integer, ForeignKey('callback_param.id')) - option = Column(Integer) - force_permissive = Column(Boolean) - value = Column(PickleType) - - def __init__(self, option=undefined, force_permissive=undefined, value=undefined): - if value is not undefined: - self.value = value - elif option is not undefined: - self.option = option.id - self.force_permissive = force_permissive - - -class _CallbackParam(SqlAlchemyBase): - __tablename__ = 'callback_param' - id = Column(Integer, primary_key=True) - callback = Column(Integer, ForeignKey('baseoption.id')) - key = Column(String) - params = relationship('_CallbackParamOption') - - def __init__(self, key, params): - self.key = key - for param in params: - if isinstance(param, tuple): - if param == (None,): - self.params.append(_CallbackParamOption()) - else: - self.params.append(_CallbackParamOption(option=param[0], - force_permissive=param[1])) - else: - self.params.append(_CallbackParamOption(value=param)) - - -#____________________________________________________________ -# -# choice -class _ChoiceParamOption(SqlAlchemyBase): - __tablename__ = 'choice_param_option' - id = Column(Integer, primary_key=True) - choice = Column(Integer, index=True) - option = Column(Integer) - force_permissive = Column(Boolean) - value = Column(PickleType) - - def __init__(self, choice, option=undefined, force_permissive=undefined, value=undefined): - self.choice = choice.id - if value is not undefined: - self.value = value - elif option is not undefined: - self.option = option.id - self.force_permissive = force_permissive - - -class _ChoiceParam(SqlAlchemyBase): - __tablename__ = 'choice_param' - id = Column(Integer, primary_key=True) - option = Column(Integer, index=True) - key = Column(String) - - def __init__(self, option, key): - self.option = option.id - self.key = key - - -#def load_choice_parm(collection_type, proxy): -# def getter(obj): -# if obj is None: -# return None -# ret = [] -# requires = getattr(obj, proxy.value_attr) -# session = util.Session() -# for require in requires: -# if require.value is not None: -# ret.append(require.value) -# else: -# option = session.query(_Base).filter_by(id=require.option).first() -# ret.append((option, require.force_permissive)) -# return tuple(ret) -# -# def setter(obj, value): -# setattr(obj, proxy.value_attr, value) -# return getter, setter -# -# -#class _ChoiceParamOption(SqlAlchemyBase): -# __tablename__ = 'choice_param_option' -# id = Column(Integer, primary_key=True) -# valid_param = Column(Integer, ForeignKey('choice_param.id')) -# option = Column(Integer) -# force_permissive = Column(Boolean) -# value = Column(PickleType) -# -# def __init__(self, option=undefined, force_permissive=undefined, value=undefined): -# if value is not undefined: -# self.value = value -# elif option is not undefined: -# self.option = option.id -# self.force_permissive = force_permissive -# -# -#class _ChoiceParam(SqlAlchemyBase): -# __tablename__ = 'choice_param' -# id = Column(Integer, primary_key=True) -# choice = Column(Integer, ForeignKey('baseoption.id')) -# key = Column(String) -# params = relationship('_ChoiceParamOption') -# -# def __init__(self, key, params): -# self.key = key -# for param in params: -# if isinstance(param, tuple): -# if param == (None,): -# self.params.append(_ChoiceParamOption()) -# else: -# self.params.append(_ChoiceParamOption(option=param[0], -# force_permissive=param[1])) -# else: -# self.params.append(_ChoiceParamOption(value=param)) - - -#____________________________________________________________ -# -# validator -def load_validator_parm(collection_type, proxy): - def getter(obj): - if obj is None: - return None - ret = [] - requires = getattr(obj, proxy.value_attr) - session = util.Session() - for require in requires: - if require.value is not None: - ret.append(require.value) - else: - option = session.query(_Base).filter_by(id=require.option).first() - ret.append((option, require.force_permissive)) - return tuple(ret) - - def setter(obj, value): - setattr(obj, proxy.value_attr, value) - return getter, setter - - -class _ValidatorParamOption(SqlAlchemyBase): - __tablename__ = 'validator_param_option' - id = Column(Integer, primary_key=True) - validator_param = Column(Integer, ForeignKey('validator_param.id')) - option = Column(Integer) - force_permissive = Column(Boolean) - value = Column(PickleType) - - def __init__(self, option=undefined, force_permissive=undefined, value=undefined): - if value is not undefined: - self.value = value - elif option is not undefined: - self.option = option.id - self.force_permissive = force_permissive - - -class _ValidatorParam(SqlAlchemyBase): - __tablename__ = 'validator_param' - id = Column(Integer, primary_key=True) - validator = Column(Integer, ForeignKey('baseoption.id')) - key = Column(String) - params = relationship('_ValidatorParamOption') - - def __init__(self, key, params): - self.key = key - for param in params: - if isinstance(param, tuple): - if param == (None,): - self.params.append(_ValidatorParamOption()) - else: - self.params.append(_ValidatorParamOption(option=param[0], - force_permissive=param[1])) - else: - self.params.append(_ValidatorParamOption(value=param)) - - -#____________________________________________________________ -# -# consistency -consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata, - Column('id', Integer, primary_key=True), - Column('left_id', Integer, ForeignKey('consistency.id')), - Column('right_id', Integer, ForeignKey('baseoption.id')) - ) - - -class _Consistency(SqlAlchemyBase): - __tablename__ = 'consistency' - id = Column(Integer, primary_key=True) - func = Column(PickleType) - params = Column(PickleType) - - def __init__(self, func, all_cons_opts, params): - self.func = func - for option in all_cons_opts[1:]: - option._consistencies.append(self) - self.params = params - - -class _Parent(SqlAlchemyBase): - __tablename__ = 'parent' - id = Column(Integer, primary_key=True) - child_id = Column(Integer) - child_name = Column(String) - parent_id = Column(Integer) - - def __init__(self, parent, child): - self.parent_id = parent.id - self.child_id = child.id - self.child_name = child._name - - -#____________________________________________________________ -# -# Base -class _Base(SqlAlchemyBase): - __tablename__ = 'baseoption' - id = Column(Integer, primary_key=True) - _name = Column(String) - #FIXME not autoload -# _infos = relationship("_Information", -# collection_class=attribute_mapped_collection('key'), -# cascade="all, delete-orphan") -# _informations = association_proxy("_infos", "value") - _informations = relationship("_Information") - _default = Column(PickleType) - _default_multi = Column(PickleType) - _subdyn = Column(Integer) - _dyn = Column(String) - _opt = Column(Integer) - _master_slaves = Column(Integer) - _choice_values = Column(PickleType) - #_cho_params = relationship('_ChoiceParam', - # collection_class=attribute_mapped_collection('key')) - #_choice_values_params = association_proxy("_cho_params", "params", - # getset_factory=load_choice_parm) - _reqs = relationship("_Require", collection_class=list) - _requires = association_proxy("_reqs", "requires", getset_factory=load_requires) - _multi = Column(Integer) - ###### - _callback = Column(PickleType) - _call_params = relationship('_CallbackParam', - collection_class=attribute_mapped_collection('key')) - _callback_params = association_proxy("_call_params", "params", - getset_factory=load_callback_parm) - _validator = Column(PickleType) - _val_params = relationship('_ValidatorParam', - collection_class=attribute_mapped_collection('key')) - _validator_params = association_proxy("_val_params", "params", - getset_factory=load_validator_parm) - ###### - #FIXME not autoload - _props = relationship("_PropertyOption", collection_class=set) - _properties = association_proxy("_props", "name") - _calc_props = relationship("_CalcProperties", collection_class=set) - _calc_properties = association_proxy("_calc_props", "name") - _warnings_only = Column(Boolean) - _allow_empty_list = Column(Boolean) - _readonly = Column(Boolean, default=False) - _consistencies = relationship('_Consistency', secondary=consistency_table, - backref=backref('options', - enable_typechecks=False)) - _stated = Column(Boolean) - _type = Column(String(50)) - __mapper_args__ = { - 'polymorphic_identity': 'optionsql', - 'polymorphic_on': _type - } - _extra = Column(PickleType) - #FIXME devrait etre une table - _group_type = Column(String) - _is_build_cache = Column(Boolean, default=False) - - def __init__(self, name, multi, warnings_only, doc, extra, calc_properties, - requires, properties, allow_empty_list, opt=undefined, session=None): - self._name = name - if multi is not undefined: - self._multi = multi - if warnings_only is not undefined: - self._warnings_only = warnings_only - if allow_empty_list is not undefined: - self._allow_empty_list = allow_empty_list - if doc is not undefined: - self._informations = [_Information(key='doc', value=doc)] - #self._informations = {'doc': doc} - if opt is not undefined: - self._opt = opt.id - if extra is not undefined: - self._extra = extra - if calc_properties is not undefined: - self._calc_properties = calc_properties - if requires is not undefined: - self._requires = requires - if properties is not undefined: - self._properties = properties - session.add(self) - - def getsession(self): - return util.Session() - - def commit(self, session): - session.commit() - del(session) - - def _add_consistency(self, func, all_cons_opts, params): - _Consistency(func, all_cons_opts, params) - - def _set_default_values(self, default, default_multi, is_multi): - self._default = default - if self.impl_is_multi() and default_multi is not None: - err = self._validate(default_multi) - if err: - raise err - self._default_multi = default_multi - - def _get_consistencies(self): - return [(consistency.func, consistency.options, consistency.params) - for consistency in self._consistencies] - - def _get_id(self): - return self.id - - def impl_get_callback(self): - a=self.getsession().query(_Base).filter_by(id=self.id).first() - ret = self._callback - if ret is None: - return (None, {}) - params = self._callback_params - if params is None: - params = {} - return ret, params - - def impl_get_validator(self): - ret = self._validator - if ret is None: - return (None, {}) - return ret, self._validator_params - - def _impl_getsubdyn(self): - session = self.getsession() - return session.query(_Base).filter_by(id=self._subdyn).first() - - def _impl_getopt(self): - session = self.getsession() - return session.query(_Base).filter_by(id=self._opt).first() - - def impl_getname(self): - return self._name - - def impl_getrequires(self): - session = self.getsession() - requires = session.query(_Require).filter_by(requires_id=self.id).all() - for require in requires: - _ret = [] - for req in require.requires: - _ret.append((session.query(_Base).filter_by(id=req.option).first(), - req.expected, - req.action, - req.inverse, - req.transitive, - req.same_action)) - yield(_ret) - - - def impl_getdefault(self): - ret = self._default - if self.impl_is_multi(): - if ret is None: - return [] - return list(ret) - return ret - - def impl_getdefault_multi(self): - if self.impl_is_multi(): - return self._default_multi - - def _get_extra(self, key): - return self._extra[key] - - def _impl_setopt(self, opt): - self._opt = opt.id - - def _impl_setsubdyn(self, subdyn): - session = self.getsession() - self._subdyn = subdyn.id - self.commit(session) - - def _set_readonly(self, has_extra): - session = self.getsession() - opt = session.query(_Base).filter_by(id=self.id).first() - opt._readonly = True - session.commit() - self._readonly = True - - def _set_callback(self, callback, callback_params): - self._callback = callback - if callback_params is not None: - opt._callback_params = callback_params - #session = self.getsession() - #opt = session.query(_Base).filter_by(id=self.id).first() - #opt._callback = callback - #if callback_params is not None: - # opt._callback_params = callback_params - #session.commit() - - def impl_set_choice_values_params(self, values, values_params, session): - self._choice_values = values - if values_params is not None: - for key, params in values_params.items(): - choice = _ChoiceParam(self, key) - session.add(choice) - session.commit() - for param in params: - if isinstance(param, tuple): - if param == (None,): - session.add(_ChoiceParamOption(choice)) - else: - session.add(_ChoiceParamOption(choice, option=param[0], force_permissive=param[1])) - else: - session.add(_ChoiceParamOption(choice, value=param)) - session.commit() - - def impl_get_choice_values_params(self): - session = self.getsession() - params = {} - for param in session.query(_ChoiceParam).filter_by(option=self.id).all(): - _params = [] - for _param in session.query(_ChoiceParamOption).filter_by(choice=param.id).all(): - if _param.value: - _params.append(_param.value) - elif _param.option: - _params.append((session.query(_Base).filter_by(id=_param.option).first(), - _param.force_permissive)) - else: - _params.append((None,)) - params[param.key] = _params - return params - - def _set_validator(self, validator, validator_params): - self._validator = validator - if validator_params is not None: - self._validator_params = validator_params - - def impl_is_readonly(self): - session = self.getsession() - opt = session.query(_Base).filter_by(id=self.id).first() - if opt is None or opt._readonly is None: - return False - return opt._readonly - - def impl_is_multi(self): - return self._multi == 0 or self._multi == 2 - - def impl_is_submulti(self): - return self._multi == 2 - - def impl_allow_empty_list(self): - try: - return self._allow_empty_list - except AttributeError: - return undefined - - def _is_warnings_only(self): - return self._warnings_only - - def impl_get_calc_properties(self): - session = self.getsession() - return session.query(_CalcProperties).filter_by(require=self.id).all() - #try: - # return self._calc_properties - #except AttributeError: - # return frozenset() - - # information - def impl_set_information(self, key, value): - session = self.getsession() - val = session.query(_Information).filter_by( - option=self.id, key=key).first() - if val is None: - session.add(_Information(self, key, value)) - else: - val.value = value - session.commit() - - def impl_get_information(self, key, default=undefined): - """retrieves one information's item - - :param key: the item string (ex: "help") - """ - session = self.getsession() - val = session.query(_Information).filter_by( - option=self.id, key=key).first() - if not val: - if default is not undefined: - return default - raise ValueError(_("information's item not found: {0}").format( - key)) - return val.value - - def _impl_getattributes(self): - slots = set() - mapper = inspect(self) - for column in mapper.attrs: - slots.add(column.key) - return slots - - def impl_getproperties(self): - session = self.getsession() - for prop in session.query(_PropertyOption).filter_by(option=self.id).all(): - yield prop.name - - def _set_master_slaves(self, option): - session = self.getsession() - opt = session.query(_Base).filter_by(id=self.id).first() - opt._master_slaves = option._p_.id - self.commit(session) - - def _get_master_slave(self): - session = self.getsession() - return session.query(StorageMasterSlaves).filter_by(id=self._master_slaves).first() - - -class Cache(SqlAlchemyBase): - __tablename__ = 'cache' - id = Column(Integer, primary_key=True) - path = Column(String, nullable=False, index=True) - descr = Column(Integer, nullable=False, index=True) - parent = Column(Integer, nullable=False, index=True) - option = Column(Integer, nullable=False, index=True) - opt_type = Column(String, nullable=False, index=True) - is_subdyn = Column(Boolean, nullable=False, index=True) - subdyn_path = Column(String) - - def __init__(self, descr, parent, option, path, subdyn_path): - #context - self.descr = descr.id - self.parent = parent.id - self.option = option.id - self.path = path - self.opt_type = option.__class__.__name__ - if subdyn_path: - self.is_subdyn = True - self.subdyn_path = subdyn_path - else: - self.is_subdyn = False - self.subdyn_path = None - - -class StorageOptionDescription(object): - def impl_already_build_caches(self): - cache = self._is_build_cache - if cache is None: - cache = False - return cache - - def impl_get_opt_by_path(self, path): - session = self.getsession() - ret = session.query(Cache).filter_by(descr=self.id, path=path).first() - if ret is None: - raise AttributeError(_('no option for path {0}').format(path)) - return session.query(_Base).filter_by(id=ret.option).first() - - def impl_get_path_by_opt(self, opt): - session = self.getsession() - ret = session.query(Cache).filter_by(descr=self.id, - option=opt.id).first() - if ret is None: - ret = session.query(Cache).filter_by(descr=self.id).first() - if ret is None: - raise ConfigError(_('use impl_get_path_by_opt only with root OptionDescription')) - raise AttributeError(_('no option {0} found').format(opt)) - return ret.path - - def impl_get_group_type(self): - return getattr(groups, self._group_type) - - def impl_build_cache_option(self, descr=None, _currpath=None, - subdyn_path=None, session=None): - if self.impl_is_readonly() or (_currpath is None and getattr(self, '_cache_paths', None) is not None): - # cache already set - return - if descr is None: - save = True - descr = self - _currpath = [] - session = self.getsession() - else: - save = False - for option in self._impl_getchildren(dyn=False): - attr = option.impl_getname() - if isinstance(option, StorageOptionDescription): - sub = subdyn_path - if option.impl_is_dynoptiondescription(): - sub = '.'.join(_currpath) - session.add(Cache(descr, self, option, - str('.'.join(_currpath + [attr])), - sub)) - _currpath.append(attr) - option.impl_build_cache_option(descr, - _currpath, - sub, session) - _currpath.pop() - else: - if subdyn_path: - subdyn_path = '.'.join(_currpath) - session.add(Cache(descr, self, option, - str('.'.join(_currpath + [attr])), - subdyn_path)) - if save: - self._is_build_cache = True - self.commit(session) - - def impl_get_options_paths(self, bytype, byname, _subpath, only_first, - context): - def _build_ret_opt(opt, option, suffix, name): - subdyn_path = opt.subdyn_path - dynpaths = opt.path[len(subdyn_path):].split('.') - - path = subdyn_path - dot = False - for dynpath in dynpaths: - if dot: - path += '.' - path += dynpath + suffix - dot = True - _opt = option._impl_to_dyn(name + suffix, path) - return (path, _opt) - - session = self.getsession() - sqlquery = session.query(Cache).filter_by(descr=self.id) - if bytype is None: - sqlquery = sqlquery.filter(and_(not_( - Cache.opt_type == 'OptionDescription'), - not_(Cache.opt_type == 'DynOptionDescription'))) - else: - sqlquery = sqlquery.filter_by(opt_type=bytype.__name__) - - query = '' - or_query = '' - if _subpath is not None: - query += _subpath + '.%' - #if byname is not None: - # or_query = query + byname - # query += '%.' + byname - if query != '': - filter_query = Cache.path.like(query) - if or_query != '': - filter_query = or_(Cache.path == or_query, filter_query) - sqlquery = sqlquery.filter(filter_query) - #if only_first: - # opt = sqlquery.first() - # if opt is None: - # return tuple() - # option = util.session.query(_Base).filter_by(id=opt.option).first() - # return ((opt.path, option),) - #else: - ret = [] - for opt in sqlquery.all(): - option = session.query(_Base).filter_by(id=opt.option).first() - if opt.is_subdyn: - name = option.impl_getname() - if byname is not None: - if byname.startswith(name): - found = False - dynoption = option._impl_getsubdyn() - for suffix in dynoption._impl_get_suffixes( - context): - if byname == name + suffix: - found = True - break - if not found: - continue - ret_opt = _build_ret_opt(opt, option, suffix, name) - else: - ret_opt = _build_ret_opt(opt, option, suffix, name) - else: - if not only_first: - ret_opt = [] - dynoption = option._impl_getsubdyn() - for suffix in dynoption._impl_get_suffixes(context): - val = _build_ret_opt(opt, option, suffix, name) - if only_first: - ret_opt = val - else: - ret_opt.append(val) - else: - if byname is not None and byname != option.impl_getname(): - continue - ret_opt = (opt.path, option) - if only_first: - return ret_opt - if isinstance(ret_opt, list): - if ret_opt != []: - ret.extend(ret_opt) - else: - ret.append(ret_opt) - return ret - - def _add_children(self, child_names, children): - session = self.getsession() - for child in children: - session.add(_Parent(self, child)) - self.commit(session) - - def _impl_st_getchildren(self, context, only_dyn=False): - session = self.getsession() - if only_dyn is False or context is undefined: - for child in session.query(_Parent).filter_by( - parent_id=self.id).all(): - yield(session.query(_Base).filter_by(id=child.child_id - ).first()) - else: - descr = context.cfgimpl_get_description().id - for child in session.query(Cache).filter_by(descr=descr, - parent=self.id - ).all(): - yield(session.query(_Base).filter_by(id=child.option).first()) - - def _getattr(self, name, suffix=undefined, context=undefined, dyn=True): - error = False - if suffix is not undefined: - try: - if undefined in [suffix, context]: # pragma: optional cover - raise ConfigError(_("suffix and context needed if " - "it's a dyn option")) - if name.endswith(suffix): - session = self.getsession() - oname = name[:-len(suffix)] - #child = self._children[1][self._children[0].index(oname)] - child = session.query(_Parent).filter_by( - parent_id=self.id, child_name=oname).first() - if child is None: - error = True - else: - opt = session.query(_Base).filter_by( - id=child.child_id).first() - return self._impl_get_dynchild(opt, suffix) - else: - error = True - except ValueError: # pragma: optional cover - error = True - else: - session = self.getsession() - child = session.query(_Parent).filter_by(parent_id=self.id, - child_name=name - ).first() - if child is None: - child = self._impl_search_dynchild(name, context=context) - if child != []: - return child - error = True - if error is False: - return session.query(_Base).filter_by(id=child.child_id - ).first() - if error: - raise AttributeError(_('unknown Option {0} in OptionDescription {1}' - '').format(name, self.impl_getname())) - - def _get_force_store_value(self): - #only option in current tree - session = self.getsession() - current_ids = tuple(chain(*session.query(Cache.option).filter_by( - descr=self.id).all())) - for prop in session.query(_PropertyOption).filter( - _PropertyOption.option.in_(current_ids), - _PropertyOption.name == 'force_store_value').all(): - opt = session.query(_Base).filter_by(id=prop.option).first() - path = self.impl_get_path_by_opt(opt) - yield (opt, path) - - -class StorageBase(_Base): - @declared_attr - def __mapper_args__(self): - return {'polymorphic_identity': self.__name__.lower()} - - -class _Slave(SqlAlchemyBase): - __tablename__ = 'slaves' - id = Column(Integer, primary_key=True) - master_id = Column(Integer, index=True, nullable=False) - slave_id = Column(Integer) - - def __init__(self, master, slave): - self.master_id = master.id - self.slave_id = slave.id - - -class StorageMasterSlaves(SqlAlchemyBase): - __tablename__ = 'masterslaves2' - id = Column(Integer, primary_key=True) - master = Column(Integer) - - def __init__(self, master, slaves): - session = util.Session() - self.master = master.id - session.add(self) - session.commit() - for slave in slaves: - sl = _Slave(self, slave) - session.add(sl) - session.commit() - - def _sm_getslaves(self): - session = util.Session() - for slave in session.query(_Slave).filter_by(master_id=self.master).all(): - yield(session.query(_Base).filter_by(id=slave.slave_id).first()) - - def _sm_getmaster(self): - session = util.Session() - return session.query(_Base).filter_by(id=self.master).first()