From 119ca8504113d8a2b31b8f9f97ea1ae04674c5b7 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 20 Nov 2017 17:01:36 +0100 Subject: [PATCH] refactor --- test/api/test_owner.py | 168 +++-- tiramisu/api.py | 50 +- tiramisu/config.py | 140 ++-- tiramisu/error.py | 2 +- tiramisu/option/baseoption.py | 12 +- tiramisu/option/option.py | 164 ++-- tiramisu/option/optiondescription.py | 19 +- tiramisu/setting.py | 764 ++++++++----------- tiramisu/storage/dictionary/setting.py | 2 +- tiramisu/storage/dictionary/value.py | 23 +- tiramisu/value.py | 996 ++++++++++++------------- 11 files changed, 1141 insertions(+), 1199 deletions(-) diff --git a/test/api/test_owner.py b/test/api/test_owner.py index 428db6a..c300bd6 100644 --- a/test/api/test_owner.py +++ b/test/api/test_owner.py @@ -220,16 +220,12 @@ def _autocheck_set_value(api, path, **kwargs): submulti_ = api.unrestraint.option(path).option.issubmulti() ismaster = api.unrestraint.option(path).option.ismaster() isslave = api.unrestraint.option(path).option.isslave() -# empty_value = _getdefault(api, path, multi, isslave, submulti_) if not multi: first_value = FIRST_VALUE -# second_value = SECOND_VALUE elif submulti_ is False: first_value = LIST_FIRST_VALUE -# second_value = LIST_SECOND_VALUE else: first_value = SUBLIST_FIRST_VALUE -# second_value = SUBLIST_SECOND_VALUE # for slave should have an index and good length # for master must append, not set @@ -509,7 +505,7 @@ def _getproperties(multi, isslave, kwargs): if extra_properties: properties.extend(extra_properties) default_props.extend(extra_properties) - return default_props, tuple(properties) + return default_props, frozenset(properties) def _check_default_properties(api, path, kwargs, props_permissive, props): @@ -540,17 +536,6 @@ def _autocheck_property(api, path, **kwargs): multi = api.unrestraint.option(path).option.ismulti() isslave = api.unrestraint.option(path).option.isslave() - # define properties - properties = copy(PROPERTIES_LIST) - if multi and not isslave: - default_props = ['empty'] - properties.append('empty') - else: - default_props = [] - extra_properties = kwargs.get('extra_properties') - if extra_properties: - properties.extend(extra_properties) - default_props.extend(extra_properties) default_props, properties = _getproperties(multi, isslave, kwargs) _check_default_properties(api, path, kwargs, default_props, default_props) @@ -863,7 +848,7 @@ def autocheck_permissive(api, path, **kwargs): -def check_all(cfg, path, meta, multi, default, default_multi, **kwargs): +def check_all(cfg, path, meta, multi, default, default_multi, require, consistency, **kwargs): if DISPLAY: text = u' {} launch tests for {}'.format(ICON, path) if multi is True: @@ -882,22 +867,37 @@ def check_all(cfg, path, meta, multi, default, default_multi, **kwargs): if api.unrestraint.option(path).option.isslave(): master_path = path.rsplit('.', 1)[0] + '.master' api.option(master_path).value.set(LIST_SECOND_VALUE) - for func in autocheck_registers: - api = getapi(cfg.duplicate()) - if DISPLAY: - print(u' {} {}'.format(ICON, func.__name__)) - try: - func(api, path, **kwargs) - except Exception as err: - msg = u'error in function {} for {}'.format(func.__name__, path) - if multi is True: - msg += u' as a multi' - elif multi is submulti: - msg += u' as a submulti' - if multi is True: - msg += u' with default value' - print(u'{}: {}'.format(msg, kwargs)) - raise err + if not require: + requires = [False] + else: + requires = [False, True] + for req in requires: + for func in autocheck_registers: + api = getapi(cfg.duplicate()) + #FIXME devrait etre dans la config ca ... + api.read_write() + ckwargs = copy(kwargs) + if req: + api.option('extraoptrequire').value.set('value') + if 'permissive' in ckwargs and not 'permissive_od' in ckwargs or \ + 'propertyerror' in ckwargs and not 'propertyerror_od' in ckwargs: + for to_del in ['permissive', 'propertyerror', 'extra_properties']: + if to_del in ckwargs: + del ckwargs[to_del] + if DISPLAY: + print(u' {} {}'.format(ICON, func.__name__)) + try: + func(api, path, **ckwargs) + except Exception as err: + msg = u'error in function {} for {}'.format(func.__name__, path) + if multi is True: + msg += u' as a multi' + elif multi is submulti: + msg += u' as a submulti' + if multi is True: + msg += u' with default value' + print(u'{}: {}'.format(msg, ckwargs)) + raise err def check_deref(weakrefs): @@ -907,23 +907,32 @@ def check_deref(weakrefs): assert wrf() is None -def make_conf(options, meta, multi, default, default_multi): +def make_conf(options, meta, multi, default, default_multi, require, consistency): weakrefs = [] + dyn = [] + goptions = [] def make_option(path, option_infos): #FIXME option_type = 'str' option_properties = [] + option_requires = [] isslave = False if option_infos is not None: for prop in PROPERTIES: if option_infos.get(prop, False) is True: - option_properties.append(prop) + if not require: + option_properties.append(prop) + else: + option_requires.append({'option': goptions[0], 'expected': None, + 'action': prop}) isslave = option_infos.get('slave', False) args = [path, "{}'s option".format(path)] kwargs = {} if option_properties != []: kwargs['properties'] = tuple(option_properties) - if multi: + if option_requires != []: + kwargs['requires'] = option_requires + if multi and path is not 'extraoptrequire': kwargs['multi'] = multi if default and not submulti: if multi is False: @@ -933,7 +942,7 @@ def make_conf(options, meta, multi, default, default_multi): else: value = SUBLIST_EMPTY_VALUE kwargs['default'] = value - if default_multi: + if default_multi and path is not 'extraoptrequire': if multi is not submulti: value = SECOND_VALUE else: @@ -942,6 +951,12 @@ def make_conf(options, meta, multi, default, default_multi): tiramisu_option = OPTIONS_TYPE[option_type]['option'] obj = tiramisu_option(*args, **kwargs) + if not 'extraopt' in path and consistency: + if require: + gopt = goptions[1] + else: + gopt = goptions[0] + obj.impl_add_consistency('not_equal', gopt, warnings_only=True) weakrefs.append(weakref.ref(obj)) return obj @@ -961,6 +976,7 @@ def make_conf(options, meta, multi, default, default_multi): if infos.get('dyn', False) is True: optiondescription = DynOptionDescription kwargs['callback'] = return_list + dyn.append(path) options = [] if 'options' in collected: options.extend(collected['options']) @@ -978,7 +994,22 @@ def make_conf(options, meta, multi, default, default_multi): return obj collect_options = {} - for path, option in options.items(): + if require or consistency: + noptions = OrderedDict() + if require: + noptions['extraoptrequire'] = {} + if consistency: + subpath = list(options.keys())[0] + if '.' in subpath: + subpath = subpath.rsplit('.', 1)[0] + '.' + else: + subpath = '' + noptions[subpath + 'extraoptconsistency'] = {} + noptions.update(options) + else: + noptions = options + + for path, option in noptions.items(): if option is None: continue local_collect_options = collect_options @@ -987,8 +1018,9 @@ def make_conf(options, meta, multi, default, default_multi): local_collect_options = local_collect_options[optiondescription] local_collect_options['properties'].update(option.get(optiondescription, {})) option_name = path.split('.')[-1] - path = '.'.join(path.split('.')[:-1]) - local_collect_options.setdefault('options', []).append(make_option(option_name, option.get(option_name))) + obj = make_option(option_name, option.get(option_name)) + goptions.append(obj) + local_collect_options.setdefault('options', []).append(obj) rootod = make_optiondescriptions('root', collect_options) if rootod is None: @@ -998,7 +1030,8 @@ def make_conf(options, meta, multi, default, default_multi): if meta: cfg = MetaConfig([cfg], session_id='metatest') weakrefs.append(weakref.ref(cfg)) - return cfg, weakrefs + del goptions + return cfg, weakrefs, dyn DICT_PATHS = [ @@ -1091,24 +1124,35 @@ def test_options(paths): return kwargs lpaths = list(paths.keys()) - for meta in (False, True): - for default_multi in (False, True): - for default in (False, True): - for multi in (False, True, submulti): - if multi is False and default_multi: - continue - cfg, weakrefs = make_conf(paths, meta, multi, default, default_multi) - if cfg is None: - continue - if len(lpaths) == 9: - check_all(cfg, lpaths[3], meta, multi, default, default_multi, **get_kwargs(lpaths[0])) - check_all(cfg, lpaths[4], meta, multi, default, default_multi, **get_kwargs(lpaths[1])) - check_all(cfg, lpaths[5], meta, multi, default, default_multi, **get_kwargs(lpaths[2])) - check_all(cfg, lpaths[6], meta, multi, default, default_multi, **get_kwargs(lpaths[0])) - check_all(cfg, lpaths[7], meta, multi, default, default_multi, **get_kwargs(lpaths[1])) - check_all(cfg, lpaths[8], meta, multi, default, default_multi, **get_kwargs(lpaths[2])) - else: - for lpath in lpaths: - check_all(cfg, lpath, meta, multi, default, default_multi, **get_kwargs(lpath)) - del cfg - check_deref(weakrefs) + meta = False + #for meta in (False, True): + for consistency in (False, True): + for require in (False, True): + for default_multi in (False, True): + for default in (False, True): + for multi in (False, True, submulti): + if multi is submulti and consistency: + continue + if multi is False and default_multi: + continue + cfg, weakrefs, dyn = make_conf(paths, meta, multi, default, default_multi, require, consistency) + if cfg is None: + continue + if dyn: + cnt = 0 + idx = 0 + for index, lpath in enumerate(lpaths): + if paths[lpath]: + cnt += 1 + else: + check_all(cfg, lpaths[index], meta, multi, default, + default_multi, require, consistency, **get_kwargs(lpaths[idx])) + idx += 1 + if idx == cnt: + idx = 0 + else: + for lpath in lpaths: + check_all(cfg, lpath, meta, multi, default, + default_multi, require, consistency, **get_kwargs(lpath)) + del cfg + check_deref(weakrefs) diff --git a/tiramisu/api.py b/tiramisu/api.py index b848a10..8e7222a 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -145,6 +145,7 @@ class TiramisuOptionOwner(CommonTiramisuOption): setting_properties, force_permissive, force_unrestraint): + super(TiramisuOptionOwner, self).__init__(opt, path, index, @@ -158,12 +159,16 @@ class TiramisuOptionOwner(CommonTiramisuOption): def get(self): """get owner for a specified option""" return self.values.getowner(self.opt, + self.path, + self.setting_properties, index=self.index, force_permissive=self.force_permissive) def isdefault(self): """is option has defaut value""" return self.values.is_default_owner(self.opt, + self.path, + self.setting_properties, index=self.index, force_permissive=self.force_permissive) @@ -174,10 +179,9 @@ class TiramisuOptionOwner(CommonTiramisuOption): except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) - self.values.setowner(self.opt, + self.values.setowner(self.path, obj_owner, - self.index, - force_permissive=self.force_permissive) + self.index) class TiramisuOptionProperty(CommonTiramisuOption): @@ -207,14 +211,14 @@ class TiramisuOptionProperty(CommonTiramisuOption): self._test_slave_index() return self.settings.getproperties(self.opt, self.path, - index=self.index, - obj=False) + self.setting_properties, + index=self.index) def set(self, properties): """set properties for a specified option""" - self.settings.setproperties(set(properties), - self.opt, - self.path) + self.settings.setproperties(self.opt, + self.path, + properties) def reset(self): """reset all personalised properties @@ -247,16 +251,17 @@ class TiramisuOptionPermissive(CommonTiramisuOption): def get(self): """get permissive value for a specified path""" - return self.settings.getpermissive(self.setting_properties, self.path) + return self.settings.getpermissive(self.path) def set(self, permissive): - self.settings.setpermissive(permissive, opt=self.opt, path=self.path) + self.settings.setpermissive(self.opt, + self.path, + permissive) - #def reset(self, path): - # """reset all personalised properties - # """ - # self._get_obj_by_path(path) - # self.settings.reset(_path=path) + def reset(self, path): + """reset all personalised permissive + """ + self.set(tuple()) class TiramisuOptionValue(CommonTiramisuOption): @@ -409,11 +414,6 @@ class TiramisuAPI(object): self.config = config self.force_permissive = force_permissive self.force_unrestraint = force_unrestraint - settings = self.config.cfgimpl_get_settings() -# #FIXME ? - self.config.read_write() - settings.setpermissive(('hidden',)) - #/FIXME ? def option(self, path, index=None): validate = not self.force_unrestraint @@ -464,6 +464,16 @@ class TiramisuAPI(object): txt.append(module(None, None).help) return '\n'.join(txt) + def read_only(self): + self.config.read_write() + + def read_write(self): + settings = self.config.cfgimpl_get_settings() + self.config.read_write() +# #FIXME ? + settings.set_context_permissive(frozenset(['hidden'])) + #/FIXME ? + def getapi(config): """instanciate TiramisuAPI diff --git a/tiramisu/config.py b/tiramisu/config.py index bedb881..0d0acd6 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -90,31 +90,39 @@ class SubConfig(object): def cfgimpl_get_length(self): return self._impl_length - def reset_one_option_cache(self, values, settings, resetted_opts, opt, only): - if 'values' in only: - tresetted_opts = copy(resetted_opts) - opt.reset_cache(opt, values, 'values', tresetted_opts) - - if 'settings' in only: - tresetted_opts = copy(resetted_opts) - opt.reset_cache(opt, settings, 'settings', tresetted_opts) - else: - if 'properties' in only: - tresetted_opts = copy(resetted_opts) - opt.reset_cache(opt, settings, 'properties', tresetted_opts) - if 'permissives' in only: - tresetted_opts = copy(resetted_opts) - opt.reset_cache(opt, settings, 'permissives', tresetted_opts) + def reset_one_option_cache(self, + values, + settings, + resetted_opts, + opt, + path): + tresetted_opts = copy(resetted_opts) + opt.reset_cache(opt, + path, + values, + 'values', + tresetted_opts) + tresetted_opts = copy(resetted_opts) + opt.reset_cache(opt, + path, + settings, + 'settings', + tresetted_opts) resetted_opts |= tresetted_opts - for option in opt._get_dependencies(self): + for woption in opt._get_dependencies(self): + option = woption() if option in resetted_opts: continue - self.reset_one_option_cache(values, settings, resetted_opts, option(), only) - del(option) + option_path = opt.impl_getpath(self) + self.reset_one_option_cache(values, + settings, + resetted_opts, + option, + option_path) + del option def cfgimpl_reset_cache(self, only_expired=False, - only=('values', 'properties', 'permissives', 'settings'), opt=None, path=None, resetted_opts=None): @@ -127,33 +135,19 @@ class SubConfig(object): def reset_expired_cache(): # reset cache for expired cache value ony datetime = int(time()) - if 'values' in only: - values._p_.reset_expired_cache(datetime) - if 'settings' in only or 'properties' in only: - settings._p_.reset_expired_cache(datetime) - if 'settings' in only or 'permissives' in only: - settings._pp_.reset_expired_cache(datetime) + values._p_.reset_expired_cache(datetime) + settings._p_.reset_expired_cache(datetime) def reset_all_cache(): - if 'values' in only: - values._p_.reset_all_cache() - if 'settings' in only: - settings._p_.reset_all_cache() - settings._pp_.reset_all_cache() - else: - if 'properties' in only: - settings._p_.reset_all_cache() - if 'permissives' in only: - settings._pp_.reset_all_cache() + values._p_.reset_all_cache() + settings._p_.reset_all_cache() if resetted_opts is None: resetted_opts = set() context = self._cfgimpl_get_context() - if 'values' in only: - values = context.cfgimpl_get_values() - if 'settings' in only or 'properties' in only or 'permissives' in only: - settings = context.cfgimpl_get_settings() + values = context.cfgimpl_get_values() + settings = context.cfgimpl_get_settings() if not None in (opt, path): if opt not in resetted_opts: @@ -161,7 +155,7 @@ class SubConfig(object): settings, resetted_opts, opt, - only) + path) elif only_expired: reset_expired_cache() @@ -308,7 +302,6 @@ class SubConfig(object): name, value, force_permissive=False, - not_raises=False, index=None, setting_properties=undefined, _commit=True): @@ -319,7 +312,7 @@ class SubConfig(object): value) context = self._cfgimpl_get_context() if setting_properties is undefined: - setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True) + setting_properties = context.cfgimpl_get_settings().get_context_properties() if '.' in name: # pragma: optional cover self, name = self.cfgimpl_get_home_by_path(name, force_permissive=force_permissive, @@ -332,21 +325,17 @@ class SubConfig(object): not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover raise TypeError(_("can't assign to a SymlinkOption")) else: - msg = self.cfgimpl_get_description().impl_validate_value(child, value, self) - if msg is not None: - if not_raises: - return msg - else: - raise msg + self.cfgimpl_get_description().impl_validate_value(child, + value, + self) subpath = self._get_subpath(name) - return self.cfgimpl_get_values().setitem(child, - value, - subpath, - force_permissive, - not_raises, - index, - setting_properties, - _commit) + return self.cfgimpl_get_values().setvalue(child, + value, + subpath, + force_permissive, + index, + setting_properties, + _commit) def __delattr__(self, name): self.delattr(name) @@ -467,12 +456,12 @@ class SubConfig(object): index=index) elif option.impl_is_optiondescription(): if setting_properties: - props = self.cfgimpl_get_settings().validate_properties(option, - True, - False, - path=subpath, - force_permissive=force_permissive, - setting_properties=setting_properties) + self.cfgimpl_get_settings().validate_properties(option, + True, + False, + path=subpath, + force_permissive=force_permissive, + setting_properties=setting_properties) if returns_option is True: return option return SubConfig(option, @@ -618,7 +607,7 @@ class SubConfig(object): fullpath=False): """exports the whole config into a `dict`, for example: - >>> print cfg.make_dict() + >>> print(cfg.make_dict()) {'od2.var4': None, 'od2.var5': None, 'od2.var6': None} @@ -626,13 +615,13 @@ class SubConfig(object): :param flatten: returns a dict(name=value) instead of a dict(path=value) :: - >>> print cfg.make_dict(flatten=True) + >>> print(cfg.make_dict(flatten=True)) {'var5': None, 'var4': None, 'var6': None} :param withoption: returns the options that are present in the very same `OptionDescription` than the `withoption` itself:: - >>> print cfg.make_dict(withoption='var1') + >>> print(cfg.make_dict(withoption='var1')) {'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value', @@ -643,8 +632,8 @@ class SubConfig(object): :param withvalue: returns the options that have the value `withvalue` :: - >>> print c.make_dict(withoption='var1', - withvalue=u'value') + >>> print(c.make_dict(withoption='var1', + withvalue=u'value')) {'od2.var4': None, 'od2.var5': None, 'od2.var6': None, @@ -798,14 +787,12 @@ class _CommonConfig(SubConfig): else: if index is None and option.impl_is_master_slaves('slave'): subpath = self._get_subpath(path) - props = self.cfgimpl_get_settings().validate_properties(option, - True, - False, - path=subpath, - force_permissive=force_permissive, - setting_properties=setting_properties) - if props: - raise props + self.cfgimpl_get_settings().validate_properties(option, + True, + False, + path=subpath, + force_permissive=force_permissive, + setting_properties=setting_properties) return option else: return self.getattr(path, @@ -975,19 +962,16 @@ class GroupConfig(_CommonConfig): def cfgimpl_reset_cache(self, only_expired=False, - only=('values', 'settings'), opt=None, path=None, resetted_opts=set()): if isinstance(self, MetaConfig): super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, - only=only, opt=opt, path=path, resetted_opts=copy(resetted_opts)) for child in self._impl_children: child.cfgimpl_reset_cache(only_expired=only_expired, - only=only, opt=opt, path=path, resetted_opts=copy(resetted_opts)) diff --git a/tiramisu/error.py b/tiramisu/error.py index 46b5cdf..0b138ae 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -155,7 +155,7 @@ class ValueWarning(UserWarning): # pragma: optional cover ... >>> w[0].message.opt == s True - >>> print str(w[0].message) + >>> print(str(w[0].message)) invalid value val for option s: pouet """ def __init__(self, msg, opt): diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 10b1b0e..6001672 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -32,7 +32,7 @@ if sys.version_info[0] >= 3: # pragma: no cover else: from inspect import getargspec -STATIC_TUPLE = tuple() +STATIC_TUPLE = frozenset() submulti = 2 @@ -145,6 +145,8 @@ class Base(object): requires = undefined if properties is None: properties = tuple() + if is_multi and 'empty' not in properties: + properties = tuple(list(properties) + ['empty']) if not isinstance(properties, tuple): raise TypeError(_('invalid properties type {0} for {1},' ' must be a tuple').format( @@ -406,11 +408,15 @@ class BaseOption(Base): name = name.encode('utf8') return name - def reset_cache(self, opt, obj, type_, resetted_opts): + def reset_cache(self, + opt, + path, + obj, + type_, + resetted_opts): if opt in resetted_opts: return if not type_ == 'values' or not opt.impl_is_optiondescription(): - path = opt.impl_getpath(obj._getcontext()) if type_ != 'permissives': obj._p_.delcache(path) if type_ in ['settings', 'permissives']: diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 5c13d13..aa0535f 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -21,6 +21,7 @@ # ____________________________________________________________ import warnings import sys +import weakref from .baseoption import OnlyOption, submulti, DynSymLinkOption, validate_callback, STATIC_TUPLE from ..i18n import _ @@ -146,9 +147,17 @@ class Option(OnlyOption): def impl_is_multi(self): return getattr(self, '_multi', 1) != 1 - def _launch_consistency(self, current_opt, func, option, value, context, - index, submulti_index, opts, warnings_only, - transitive): + def _launch_consistency(self, + current_opt, + func, + option, + value, + context, + index, + opts, + warnings_only, + transitive, + setting_properties): """Launch consistency now :param func: function name, this name should start with _cons_ @@ -174,7 +183,8 @@ class Option(OnlyOption): all_cons_vals = [] all_cons_opts = [] val_consistencies = True - for opt in opts: + for wopt in opts: + opt = wopt() if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \ option == opt: # option is current option @@ -195,7 +205,9 @@ class Option(OnlyOption): else: _index = index try: - opt_value = context.getattr(path, validate=False, + opt_value = context.getattr(path, + setting_properties, + validate=False, index=_index, force_permissive=True) except PropertiesOptionError as err: @@ -254,7 +266,6 @@ class Option(OnlyOption): context=undefined, validate=True, force_index=None, - force_submulti_index=None, current_opt=undefined, is_multi=None, display_error=True, @@ -270,9 +281,6 @@ class Option(OnlyOption): :param force_index: if multi, value has to be a list not if force_index is not None :type force_index: integer - :param force_submulti_index: if submulti, value has to be a list - not if force_submulti_index is not None - :type force_submulti_index: integer """ if not validate: return @@ -287,10 +295,12 @@ class Option(OnlyOption): if display_error and self.impl_is_unique() and len(set(value)) != len(value): for idx, val in enumerate(value): if val in value[idx+1:]: - return ValueError(_('invalid value "{}", this value is already in "{}"').format( - val, self.impl_get_display_name())) + return ValueError(_('invalid value "{}", this value is already in "{}"' + '').format(val, + self.impl_get_display_name())) - def calculation_validator(val, _index): + def calculation_validator(val, + _index): validator, validator_params = self.impl_get_validator() if validator is not None: if validator_params != {}: @@ -316,9 +326,10 @@ class Option(OnlyOption): if isinstance(value, Exception): return value - def do_validation(_value, _index, submulti_index): + def do_validation(_value, + _index): if _value is None: - error = warning = None + error = None else: if display_error: # option validation @@ -327,35 +338,41 @@ class Option(OnlyOption): current_opt) if err: if debug: # pragma: no cover - log.debug('do_validation: value: {0}, index: {1}, ' - 'submulti_index: {2}'.format(_value, - _index, - submulti_index), + log.debug('do_validation: value: {0}, index: {1}:' + ' {2}'.format(_value, + _index), exc_info=True) err_msg = '{0}'.format(err) if err_msg: msg = _('"{0}" is an invalid {1} for "{2}", {3}' - '').format(_value, self._display_name, + '').format(_value, + self._display_name, self.impl_get_display_name(), err_msg) else: msg = _('"{0}" is an invalid {1} for "{2}"' - '').format(_value, self._display_name, + '').format(_value, + self._display_name, self.impl_get_display_name()) return ValueError(msg) error = None 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) + error = calculation_validator(_value, + _index) if not error: - error = self._second_level_validation(_value, 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 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) + 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), ValueWarning, self.__class__.__name__, 0) @@ -367,9 +384,9 @@ class Option(OnlyOption): _value, context, _index, - submulti_index, display_warnings, - display_error) + display_error, + setting_properties) if isinstance(ret, ValueError): error = ret elif ret: @@ -390,22 +407,22 @@ class Option(OnlyOption): is_multi = self.impl_is_multi() if not is_multi: - return do_validation(value, None, None) + return do_validation(value, None) elif force_index is not None: - if self.impl_is_submulti() and force_submulti_index is None: + if self.impl_is_submulti(): err = _is_not_unique(value) if err: return err if not isinstance(value, list): return ValueError(_('invalid value "{0}" for "{1}" which' ' must be a list').format( - value, self.impl_get_display_name())) + value, self.impl_get_display_name())) for idx, val in enumerate(value): if isinstance(val, list): # pragma: no cover return ValueError(_('invalid value "{}" for "{}" ' 'which must not be a list').format(val, - self.impl_get_display_name())) - err = do_validation(val, force_index, idx) + self.impl_get_display_name())) + err = do_validation(val, force_index) if err: return err else: @@ -419,12 +436,12 @@ class Option(OnlyOption): return ValueError(_('invalid value "{}", this value is already' ' in "{}"').format(value, self.impl_get_display_name())) - return do_validation(value, force_index, force_submulti_index) + return do_validation(value, force_index) elif not isinstance(value, list): return ValueError(_('invalid value "{0}" for "{1}" which ' 'must be a list').format(value, self.impl_getname())) - elif self.impl_is_submulti() and force_submulti_index is None: + elif self.impl_is_submulti(): for idx, val in enumerate(value): err = _is_not_unique(val) if err: @@ -434,8 +451,9 @@ class Option(OnlyOption): 'which must be a list of list' '').format(val, self.impl_getname())) - for slave_idx, slave_val in enumerate(val): - err = do_validation(slave_val, idx, slave_idx) + for slave_val in val: + err = do_validation(slave_val, + idx) if err: return err else: @@ -443,16 +461,17 @@ class Option(OnlyOption): if err: return err for idx, val in enumerate(value): - err = do_validation(val, idx, force_submulti_index) + err = do_validation(val, + idx) if err: return err return self._valid_consistency(current_opt, None, context, None, - None, display_warnings, - display_error) + display_error, + setting_properties) def impl_is_dynsymlinkoption(self): return False @@ -480,7 +499,10 @@ class Option(OnlyOption): "accesses the Option's doc" return self.impl_get_information('doc') - def _valid_consistencies(self, other_opts, init=True, func=None): + def _valid_consistencies(self, + other_opts, + init=True, + func=None): if self._is_subdyn(): dynod = self._subdyn() else: @@ -488,7 +510,11 @@ class Option(OnlyOption): if self.impl_is_submulti(): raise ConfigError(_('cannot add consistency with submulti option')) is_multi = self.impl_is_multi() - for opt in other_opts: + for wopt in other_opts: + if isinstance(wopt, weakref.ReferenceType): + opt = wopt() + else: + opt = wopt if opt.impl_is_submulti(): raise ConfigError(_('cannot add consistency with submulti option')) if not isinstance(opt, Option): @@ -515,7 +541,10 @@ class Option(OnlyOption): if func != 'not_equal': opt._has_dependency = True - def impl_add_consistency(self, func, *other_opts, **params): + def impl_add_consistency(self, + func, + *other_opts, + **params): """Add consistency means that value will be validate with other_opts option's values. @@ -525,39 +554,51 @@ class Option(OnlyOption): :type other_opts: `list` of `tiramisu.option.Option` :param params: extra params (warnings_only and transitive are allowed) """ - if self.impl_is_readonly(): + if self.impl_is_readonly(): raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is" " read-only").format( self.__class__.__name__, self.impl_getname())) - self._valid_consistencies(other_opts, func=func) + self._valid_consistencies(other_opts, + func=func) func = '_cons_{0}'.format(func) if func not in dir(self): raise ConfigError(_('consistency {0} not available for this option').format(func)) - all_cons_opts = tuple([self] + list(other_opts)) + options = [weakref.ref(self)] + for option in other_opts: + options.append(weakref.ref(option)) + all_cons_opts = tuple(options) unknown_params = set(params.keys()) - set(['warnings_only', 'transitive']) if unknown_params != set(): raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params)) - self._add_consistency(func, all_cons_opts, params) + self._add_consistency(func, + all_cons_opts, + params) #validate default value when add consistency err = self.impl_validate(self.impl_getdefault()) if err: self._del_consistency() raise err - if func in ALLOWED_CONST_LIST: - for opt in all_cons_opts: - if getattr(opt, '_unique', undefined) == undefined: - opt._unique = True if func != '_cons_not_equal': #consistency could generate warnings or errors self._has_dependency = True - for opt in all_cons_opts: + for wopt in all_cons_opts: + opt = wopt() + if func in ALLOWED_CONST_LIST: + if getattr(opt, '_unique', undefined) == undefined: + opt._unique = True if opt != self: self._add_dependency(opt) opt._add_dependency(self) - def _valid_consistency(self, option, value, context, index, submulti_idx, - display_warnings, display_error): + def _valid_consistency(self, + option, + value, + context, + index, + display_warnings, + display_error, + setting_properties): if context is not undefined: descr = context.cfgimpl_get_description() if descr._cache_consistencies is None: @@ -586,10 +627,16 @@ class Option(OnlyOption): opts.append(opt._impl_to_dyn(name, path)) else: opts = all_cons_opts - err = opts[0]._launch_consistency(self, func, option, value, - context, index, submulti_idx, - opts, warnings_only, - transitive) + err = opts[0]()._launch_consistency(self, + func, + option, + value, + context, + index, + opts, + warnings_only, + transitive, + setting_properties) if err: return err @@ -680,7 +727,10 @@ class Option(OnlyOption): #____________________________________________________________ # consistency - def _add_consistency(self, func, all_cons_opts, params): + def _add_consistency(self, + func, + all_cons_opts, + params): cons = (func, all_cons_opts, params) consistencies = getattr(self, '_consistencies', None) if consistencies is None: diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index e7f82a5..917782c 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -125,8 +125,8 @@ class CacheOptionDescription(BaseOption): 'must be in same master/slaves for {1}').format( require_opt.impl_getname(), option.impl_getname())) else: - raise ValueError(_('malformed requirements option {0} ' - 'must not be a multi for {1}').format( + raise ValueError(_('malformed requirements option "{0}" ' + 'must not be a multi for "{1}"').format( require_opt.impl_getname(), option.impl_getname())) if init: if len(cache_option) != len(set(cache_option)): @@ -137,7 +137,7 @@ class CacheOptionDescription(BaseOption): if _consistencies != {}: self._cache_consistencies = {} for opt, cons in _consistencies.items(): - if opt 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())) @@ -437,12 +437,12 @@ class OptionDescription(OptionDescriptionWalk): for child in valid_child: if child == old: # pragma: optional cover raise ConflictError(_('duplicate option name: ' - '{0}').format(child)) + '"{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')) + raise ConflictError(_('the option\'s name "{}" start as ' + 'the dynoptiondescription\'s name "{}"').format(child, dynopt)) old = child _setattr = object.__setattr__ _setattr(self, '_children', (tuple(child_names), tuple(children))) @@ -623,7 +623,7 @@ class MasterSlaves(OptionDescription): name)) slaves.append(child) child._add_dependency(self) - for child in children: + for idx, child in enumerate(children): if child._is_symlinkoption(): # pragma: optional cover raise ValueError(_("master group {0} shall not have " "a symlinkoption").format(self.impl_getname())) @@ -635,6 +635,11 @@ class MasterSlaves(OptionDescription): "in group {1}" ": this option is not a multi" "").format(child.impl_getname(), self.impl_getname())) + # no empty property for save + if idx != 0: + properties = list(child._properties) + properties.remove('empty') + child._properties = tuple(properties) callback, callback_params = master.impl_get_callback() if callback is not None and callback_params != {}: for callbacks in callback_params.values(): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 900349e..ffff3f3 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -106,7 +106,8 @@ rw_append = set(['frozen', 'disabled', 'validator', 'hidden']) rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty']) -forbidden_set_properties = set(['force_store_value']) +forbidden_set_properties = frozenset(['force_store_value']) +forbidden_set_permissives = frozenset(['frozen', 'force_default_on_freeze']) log = getLogger('tiramisu') @@ -124,15 +125,16 @@ class _NameSpace(object): when attribute is added, we cannot delete it """ - def __setattr__(self, name, value): - if name in self.__dict__: # pragma: optional cover + def __setattr__(self, + name, + value): + if name in self.__dict__: raise ConstError(_("can't rebind {0}").format(name)) self.__dict__[name] = value - def __delattr__(self, name): # pragma: optional cover - if name in self.__dict__: - raise ConstError(_("can't unbind {0}").format(name)) - raise ValueError(name) + def __delattr__(self, + name): + raise ConstError(_("can't unbind {0}").format(name)) class GroupModule(_NameSpace): @@ -168,69 +170,44 @@ class OwnerModule(_NameSpace): """groups that are default (typically 'default')""" pass - -class MultiTypeModule(_NameSpace): - "namespace for the master/slaves" - class MultiType(str): - pass - - class DefaultMultiType(MultiType): - pass - - class MasterMultiType(MultiType): - pass - - class SlaveMultiType(MultiType): - pass - - -# ____________________________________________________________ -def populate_groups(): - """populates the available groups in the appropriate namespaces - - groups.default - default group set when creating a new optiondescription - - groups.master - master group is a special optiondescription, all suboptions should be - multi option and all values should have same length, to find master's - option, the optiondescription's name should be same than de master's - option - - groups.family - example of group, no special behavior with this group's type - """ - groups.default = groups.DefaultGroupType('default') - groups.master = groups.MasterGroupType('master') - groups.family = groups.GroupType('family') - - -def populate_owners(): - """populates the available owners in the appropriate namespaces - - default - is the config owner after init time - - user - is the generic is the generic owner - """ - setattr(owners, 'default', owners.DefaultOwner('default')) - setattr(owners, 'user', owners.Owner('user')) - setattr(owners, 'forced', owners.Owner('forced')) - - def addowner(name): + def addowner(self, name): """ :param name: the name of the new owner """ setattr(owners, name, owners.Owner(name)) - setattr(owners, 'addowner', addowner) + # ____________________________________________________________ -# populate groups and owners with default attributes +# populate groups groups = GroupModule() -populate_groups() +"""groups.default + default group set when creating a new optiondescription""" +groups.default = groups.DefaultGroupType('default') + +"""groups.master + master group is a special optiondescription, all suboptions should be + multi option and all values should have same length, to find master's + option, the optiondescription's name should be same than de master's + option""" +groups.master = groups.MasterGroupType('master') + +""" groups.family + example of group, no special behavior with this group's type""" +groups.family = groups.GroupType('family') + + +# ____________________________________________________________ +# populate owners with default attributes owners = OwnerModule() -populate_owners() +"""default + is the config owner after init time""" +owners.default = owners.DefaultOwner('default') +"""user + is the generic is the generic owner""" +owners.user = owners.Owner('user') +"""forced + special owner when value is forced""" +owners.forced = owners.Owner('forced') # ____________________________________________________________ @@ -241,73 +218,6 @@ class Undefined(object): undefined = Undefined() -# ____________________________________________________________ -class Property(object): - "a property is responsible of the option's value access rules" - __slots__ = ('_setting', '_properties', '_opt', '_path') - - def __init__(self, setting, prop, opt=None, path=None): - self._opt = opt - self._path = path - self._setting = setting - self._properties = prop - - def append(self, propname): - """Appends a property named propname - - :param propname: a predefined or user defined property name - :type propname: string - """ - self._append(propname) - - def _append(self, propname, save=True): - if self._opt is not None and self._opt.impl_getrequires() is not None \ - 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())) - if propname in forbidden_set_properties: - raise ConfigError(_('cannot add those properties: {0}').format(propname)) - self._properties.add(propname) - if save: - self._setting.setproperties(self._properties, self._opt, self._path, force=True) - - def remove(self, propname): - """Removes a property named propname - - :param propname: a predefined or user defined property name - :type propname: string - """ - if propname in self._properties: - self._properties.remove(propname) - self._setting.setproperties(self._properties, self._opt, self._path) - - def extend(self, propnames): - """Extends properties to the existing properties - - :param propnames: an iterable made of property names - :type propnames: iterable of string - """ - for propname in propnames: - self._append(propname, save=False) - self._setting.setproperties(self._properties, self._opt, self._path) - - def reset(self): - """resets the properties (does not **clear** the properties, - default properties are still present) - """ - self._setting.reset(_path=self._path) - - def __contains__(self, propname): - return propname in self._properties - - def __repr__(self): - return str(list(self._properties)) - - def get(self): - return tuple(self._properties) - - #____________________________________________________________ class Settings(object): "``config.Config()``'s configuration options settings" @@ -341,321 +251,89 @@ class Settings(object): return context #____________________________________________________________ - # properties methods - def __contains__(self, propname): - "enables the pythonic 'in' syntaxic sugar" - return propname in self._getproperties(read_write=False) + # get properties and permissive methods - def __repr__(self): - return str(list(self._getproperties(read_write=False))) - - def __getitem__(self, opt): - path = opt.impl_getpath(self._getcontext()) - return self.getproperties(opt, path) + def get_context_properties(self): + ntime = int(time()) + if self._p_.hascache(None, + None): + is_cached, props = self._p_.getcache(None, + ntime, + None) + else: + is_cached = False + if not is_cached or 'cache' not in props: + meta = self._getcontext().cfgimpl_get_meta() + if meta is None: + props = self._p_.getproperties(None, + default_properties) + else: + props = meta.cfgimpl_get_settings().get_context_properties() + if 'cache' in props: + if 'expire' in props: + ntime = ntime + expires_time + else: + ntime = None + self._p_.setcache(None, props, ntime, None) + return props def getproperties(self, opt, path, - setting_properties=undefined, + setting_properties, index=None, - obj=True): - """get properties for a specified option - """ - properties = self._getproperties(opt, - path, - index=index, - setting_properties=setting_properties) - if obj: - return Property(self, properties, opt, path) - return properties - - def get_context_properties(self): - return self._getproperties() - - def __setitem__(self, opt, value): # pragma: optional cover - raise ValueError(_('you should only append/remove properties')) - - def reset(self, opt=None, _path=None, all_properties=False): - if all_properties and (_path or opt): # pragma: optional cover - raise ValueError(_('opt and all_properties must not be set ' - 'together in reset')) - if all_properties: - self._p_.reset_all_properties() - else: - if opt is not None and _path is None: - _path = opt.impl_getpath(self._getcontext()) - self._p_.delproperties(_path) - self._getcontext().cfgimpl_reset_cache(opt=opt, path=_path, only=('settings', 'values')) - - def _getproperties(self, - opt=None, - path=None, - setting_properties=undefined, - read_write=True, - apply_requires=True, - index=None): + apply_requires=True): """ """ - if opt is None: - ntime = int(time()) - if self._p_.hascache(path, index): - is_cached, props = self._p_.getcache(path, ntime, None) - else: - is_cached = False - if not is_cached or 'cache' not in props: - meta = self._getcontext().cfgimpl_get_meta() - if meta is None: - props = self._p_.getproperties(path, default_properties) - else: - props = meta.cfgimpl_get_settings()._getproperties() - if 'cache' in props: - if 'expire' in props: - ntime = ntime + expires_time - else: - ntime = None - self._p_.setcache(path, props, ntime, None) - else: - if path is None: # pragma: optional cover - raise ValueError(_('if opt is not None, path should not be' - ' None in _getproperties')) - if setting_properties is undefined: - setting_properties = self._getproperties(read_write=False) - is_cached = False + is_cached = False - if apply_requires: - if 'cache' in setting_properties and 'expire' in setting_properties: - ntime = int(time()) - else: - ntime = None - if 'cache' in setting_properties and self._p_.hascache(path, index): - is_cached, props = self._p_.getcache(path, ntime, index) - if not is_cached: - props = self._p_.getproperties(path, opt.impl_getproperties()) - if not opt.impl_is_optiondescription() and opt.impl_is_multi() and \ - not opt.impl_is_master_slaves('slave'): - props.add('empty') - if apply_requires: - requires = self.apply_requires(opt, path, setting_properties, index, False) - if requires != set([]): - props = copy(props) - props |= requires - if 'cache' in setting_properties: - if 'expire' in setting_properties: - ntime = ntime + expires_time - self._p_.setcache(path, props, ntime, index) - if read_write: - props = copy(props) - return props - - def append(self, propname): - "puts property propname in the Config's properties attribute" - props = self._p_.getproperties(None, default_properties) - if propname not in props: - props.add(propname) - self.setproperties(props, None, None) - - def remove(self, propname): - "deletes property propname in the Config's properties attribute" - props = self._p_.getproperties(None, default_properties) - if propname in props: - props.remove(propname) - self.setproperties(props, None, None) - - def extend(self, propnames): - for propname in propnames: - self.append(propname) - - def _setproperties(self, properties, opt, path, force=False): - """just for compatibility - """ - self.setproperties(properties, opt, path, force) - - def setproperties(self, properties, opt, path, force=False): - """save properties for specified path - (never save properties if same has option properties) - """ - if self._getcontext().cfgimpl_get_meta() is not None: - raise ConfigError(_('cannot change global property with metaconfig')) - if not force: - forbidden_properties = forbidden_set_properties & properties - if forbidden_properties: - raise ConfigError(_('cannot add those properties: {0}').format( - ' '.join(forbidden_properties))) - self._p_.setproperties(path, properties) - #values too because of slave values could have a PropertiesOptionError has value - self._getcontext().cfgimpl_reset_cache(opt=opt, path=path, only=('values', 'properties',)) - - def getpermissive(self, setting_properties, path=None): - if 'cache' in setting_properties and 'expire' in setting_properties: - ntime = int(time()) - else: - ntime = None - if 'cache' in setting_properties and self._pp_.hascache(path, None): - is_cached, perm = self._pp_.getcache(path, ntime, None) - else: - is_cached = False - if not is_cached: - if path is not None: - perm = self._pp_.getpermissive(path) - else: - perm = self._pp_.getpermissive() - if 'cache' in setting_properties: - if 'expire' in setting_properties: - ntime = ntime + expires_time - self._pp_.setcache(path, perm, ntime, None) - return perm - - #____________________________________________________________ - def validate_properties(self, opt_or_descr, is_descr, check_frozen, path, - value=None, force_permissive=False, - setting_properties=undefined, - self_properties=undefined, - index=None, debug=False): - """ - validation upon the properties related to `opt_or_descr` - - :param opt_or_descr: an option or an option description object - :param force_permissive: behaves as if the permissive property - was present - :param is_descr: we have to know if we are in an option description, - just because the mandatory property - doesn't exist here - - :param check_frozen: in the validation process, an option is to be modified, - the behavior can be different - (typically with the `frozen` property) - """ - # opt properties - if setting_properties is undefined: - setting_properties = self._getproperties(read_write=False) - if self_properties is not undefined: - properties = copy(self_properties) - else: - properties = self._getproperties(opt_or_descr, path, - setting_properties=setting_properties, - index=index) - # calc properties - properties &= setting_properties - if not is_descr: - #mandatory - if 'mandatory' in properties and \ - not self._getcontext().cfgimpl_get_values()._isempty( - opt_or_descr, value, index=index): - properties.remove('mandatory') - elif 'empty' in properties and \ - 'empty' in setting_properties and \ - self._getcontext().cfgimpl_get_values()._isempty( - opt_or_descr, value, force_allow_empty_list=True, index=index): - properties.add('mandatory') - # should return 'frozen' only when tried to modify a value - if check_frozen and 'everything_frozen' in setting_properties: - properties.add('frozen') - elif 'frozen' in properties and not check_frozen: - properties.remove('frozen') - if 'empty' in properties: - properties.remove('empty') - - # remove permissive properties - if properties != frozenset(): - # remove opt permissive - # permissive affect option's permission with or without permissive - # global property - properties -= self.getpermissive(setting_properties, path) - # remove global permissive if need - if force_permissive is True or 'permissive' in setting_properties: - properties -= self.getpermissive(setting_properties) - - # at this point an option should not remain in properties - if properties != frozenset(): - props = list(properties) - datas = {'opt': opt_or_descr, 'path': path, 'setting_properties': setting_properties, - 'index': index, 'debug': True} - if is_descr: - opt_type = 'optiondescription' - else: - opt_type = 'option' - if 'frozen' in properties: - raise PropertiesOptionError(_('cannot change the value for ' - 'option "{0}" this option is' - ' frozen').format( - opt_or_descr.impl_getname()), - props, - self, - datas, - opt_type) - else: - if len(props) == 1: - prop_msg = _('property') - else: - prop_msg = _('properties') - raise PropertiesOptionError(_('cannot access to {0} "{1}" ' - 'because has {2} {3}' - '').format(opt_type, - opt_or_descr.impl_get_display_name(), - prop_msg, - display_list(props)), - props, - self, - datas, - opt_type) - - def setpermissive(self, permissive, opt=None, path=None): - """ - enables us to put the permissives in the storage - - :param path: the option's path - :param type: str - :param opt: if an option object is set, the path is extracted. - it is better (faster) to set the path parameter - instead of passing a :class:`tiramisu.option.Option()` object. - """ - if opt is not None and path is None: - path = opt.impl_getpath(self._getcontext()) - if not isinstance(permissive, tuple): # pragma: optional cover - raise TypeError(_('permissive must be a tuple')) - self._pp_.setpermissive(path, permissive) - setting_properties = self._getproperties(read_write=False) - self._getcontext().cfgimpl_reset_cache(opt=opt, path=path, only=('properties', 'values')) - if 'cache' in setting_properties: - if 'expire' in setting_properties: - ntime = int(time()) + expires_time + if apply_requires: + if 'cache' in setting_properties and 'expire' in setting_properties: + ntime = int(time()) else: ntime = None - self._pp_.setcache(path, set(permissive), ntime, None) + if 'cache' in setting_properties and self._p_.hascache(path, + index): + is_cached, props = self._p_.getcache(path, + ntime, + index) + if not is_cached: + props = self._p_.getproperties(path, + opt.impl_getproperties()) + if apply_requires: + requires = self.apply_requires(opt, + path, + setting_properties, + index, + False) + #FIXME devrait etre un frozenset! + if requires != set([]): + props = copy(props) + props |= requires - #____________________________________________________________ - def setowner(self, owner): - ":param owner: sets the default value for owner at the Config level" - if not isinstance(owner, owners.Owner): # pragma: optional cover - raise TypeError(_("invalid generic owner {0}").format(str(owner))) - self._owner = owner - #FIXME qu'est ce qui se passe si pas de owner ?? + props -= self.getpermissive(path) + if apply_requires and 'cache' in setting_properties: + if 'expire' in setting_properties: + ntime = ntime + expires_time + self._p_.setcache(path, + props, + ntime, + index) + return props - def getowner(self): - return self._owner + def get_context_permissive(self): + return self.getpermissive(None) - #____________________________________________________________ - def _read(self, remove, append): - props = self._p_.getproperties(None, default_properties) - modified = False - if remove & props != set([]): - props = props - remove - modified = True - if append & props != append: - props = props | append - modified = True - if modified: - self.setproperties(props, None, None) + def getpermissive(self, + path): + return self._pp_.getpermissive(path) - def read_only(self): - "convenience method to freeze, hide and disable" - self._read(ro_remove, ro_append) - - def read_write(self): - "convenience method to freeze, hide and disable" - self._read(rw_remove, rw_append) - - def apply_requires(self, opt, path, setting_properties, index, debug): + def apply_requires(self, + opt, + path, + setting_properties, + index, + debug): """carries out the jit (just in time) requirements between options a requirement is a tuple of this form that comes from the option's @@ -735,16 +413,16 @@ class Settings(object): idx = None try: value = context.getattr(reqpath, + setting_properties, force_permissive=True, - _setting_properties=setting_properties, index=idx) except PropertiesOptionError as err: if not transitive: if all_properties is None: all_properties = [] - for requires in opt.impl_getrequires(): - for require in requires: - all_properties.append(require[1]) + for requires_ in opt.impl_getrequires(): + for require_ in requires_: + all_properties.append(require_[1]) if not set(err.proptype) - set(all_properties): continue properties = err.proptype @@ -794,8 +472,228 @@ class Settings(object): break return calc_properties + #____________________________________________________________ + # set methods + def set_context_properties(self, properties): + self.setproperties(None, None, properties) + + def setproperties(self, + opt, + path, + properties): +# force=False): + """save properties for specified path + (never save properties if same has option properties) + """ + if self._getcontext().cfgimpl_get_meta() is not None: + raise ConfigError(_('cannot change global property with metaconfig')) + #if not force: + forbidden_properties = forbidden_set_properties & properties + if forbidden_properties: + raise ConfigError(_('cannot add those properties: {0}').format( + ' '.join(forbidden_properties))) + if not isinstance(properties, frozenset): + raise TypeError(_('properties must be a frozenset')) + self._p_.setproperties(path, + properties) + #values too because of slave values could have a PropertiesOptionError has value + self._getcontext().cfgimpl_reset_cache(opt=opt, + path=path) + + def set_context_permissive(self, permissive): + self.setpermissive(None, None, permissive) + + def setpermissive(self, + opt, + path, + permissives): + """ + enables us to put the permissives in the storage + + :param path: the option's path + :param type: str + :param opt: if an option object is set, the path is extracted. + it is better (faster) to set the path parameter + instead of passing a :class:`tiramisu.option.Option()` object. + """ + if not isinstance(permissives, frozenset): + raise TypeError(_('permissive must be a frozenset')) + forbidden_permissives = forbidden_set_permissives & permissives + if forbidden_permissives: + raise ConfigError(_('cannot add those permissives: {0}').format( + ' '.join(forbidden_permissives))) + self._pp_.setpermissive(path, permissives) + self._getcontext().cfgimpl_reset_cache(opt=opt, + path=path) + + #____________________________________________________________ + # reset methods + + def reset(self, opt=None, _path=None, all_properties=False): + if all_properties and (_path or opt): # pragma: optional cover + raise ValueError(_('opt and all_properties must not be set ' + 'together in reset')) + if all_properties: + self._p_.reset_all_properties() + else: + if opt is not None and _path is None: + _path = opt.impl_getpath(self._getcontext()) + self._p_.delproperties(_path) + self._getcontext().cfgimpl_reset_cache(opt=opt, + path=_path) + + #____________________________________________________________ + # validate properties + + def validate_properties(self, + opt_or_descr, + is_descr, + check_frozen, + path, + value=None, + force_permissive=False, + setting_properties=undefined, + self_properties=undefined, + index=None, + debug=False): + """ + validation upon the properties related to `opt_or_descr` + + :param opt_or_descr: an option or an option description object + :param force_permissive: behaves as if the permissive property + was present + :param is_descr: we have to know if we are in an option description, + just because the mandatory property + doesn't exist here + + :param check_frozen: in the validation process, an option is to be modified, + the behavior can be different + (typically with the `frozen` property) + """ + # opt properties + if self_properties is not undefined: + if not isinstance(self_properties, frozenset): + raise Exception('pouet') + properties = self_properties + else: + properties = self.getproperties(opt_or_descr, + path, + setting_properties=setting_properties, + index=index) + # calc properties + properties &= setting_properties + if not is_descr: + #mandatory + if 'mandatory' in properties and \ + not self._getcontext().cfgimpl_get_values().isempty(opt_or_descr, + value, + index=index): + properties.remove('mandatory') + elif 'empty' in properties and \ + 'empty' in setting_properties and \ + self._getcontext().cfgimpl_get_values().isempty(opt_or_descr, + value, + force_allow_empty_list=True, + index=index): + properties.add('mandatory') + # should return 'frozen' only when tried to modify a value + if check_frozen and 'everything_frozen' in setting_properties: + properties.add('frozen') + elif 'frozen' in properties and not check_frozen: + properties.remove('frozen') + if 'empty' in properties: + properties.remove('empty') + + # remove permissive properties + if properties != frozenset() and (force_permissive is True or + 'permissive' in setting_properties): + # remove global permissive if need + properties -= self.get_context_permissive() + + # at this point an option should not remain in properties + if properties != frozenset(): + props = list(properties) + datas = {'opt': opt_or_descr, + 'path': path, + 'setting_properties': setting_properties, + 'index': index, + 'debug': True} + if is_descr: + opt_type = 'optiondescription' + else: + opt_type = 'option' + if 'frozen' in properties: + raise PropertiesOptionError(_('cannot change the value for ' + 'option "{0}" this option is' + ' frozen').format( + opt_or_descr.impl_getname()), + props, + self, + datas, + opt_type) + else: + if len(props) == 1: + prop_msg = _('property') + else: + prop_msg = _('properties') + raise PropertiesOptionError(_('cannot access to {0} "{1}" ' + 'because has {2} {3}' + '').format(opt_type, + opt_or_descr.impl_get_display_name(), + prop_msg, + display_list(props)), + props, + self, + datas, + opt_type) + + #____________________________________________________________ + # read only/read write + + def _read(self, + remove, + append): + props = self._p_.getproperties(None, + default_properties) + modified = False + if remove & props != set([]): + props = props - remove + modified = True + if append & props != append: + props = props | append + modified = True + if modified: + self.set_context_properties(frozenset(props)) + + def read_only(self): + "convenience method to freeze, hide and disable" + self._read(ro_remove, + ro_append) + + def read_write(self): + "convenience method to freeze, hide and disable" + self._read(rw_remove, + rw_append) + + #____________________________________________________________ + # get modified properties/permissives + def get_modified_properties(self): return self._p_.get_modified_properties() def get_modified_permissives(self): return self._pp_.get_modified_permissives() + + #____________________________________________________________ + # default owner methods + + def setowner(self, + owner): + ":param owner: sets the default value for owner at the Config level" + if not isinstance(owner, + owners.Owner): # pragma: optional cover + raise TypeError(_("invalid generic owner {0}").format(str(owner))) + self._owner = owner + + def getowner(self): + return self._owner diff --git a/tiramisu/storage/dictionary/setting.py b/tiramisu/storage/dictionary/setting.py index ede22ee..2310d61 100644 --- a/tiramisu/storage/dictionary/setting.py +++ b/tiramisu/storage/dictionary/setting.py @@ -34,7 +34,7 @@ class Properties(Cache): self._properties[path] = properties def getproperties(self, path, default_properties): - return self._properties.get(path, set(default_properties)) + return self._properties.get(path, frozenset(default_properties)) def reset_all_properties(self): self._properties.clear() diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index 92df7ce..a0bfea5 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -195,7 +195,11 @@ class Values(Cache): return 0 return max(self._values[1][idx]) + 1 - def getowner(self, path, default, index=None, only_default=False, + def getowner(self, + path, + default, + index=None, + only_default=False, with_value=False): """get owner for a path return: owner object @@ -207,19 +211,28 @@ class Values(Cache): else: owner = default else: - owner = self._getvalue(path, 3, index) + owner = self._getvalue(path, + 3, + index) if owner is undefined: owner = default else: - owner = self._getvalue(path, 3, index) + owner = self._getvalue(path, + 3, + index) if owner is undefined: owner = default if with_value: - return owner, self._getvalue(path, 2, index) + return owner, self._getvalue(path, + 2, + index) else: return owner - def _getvalue(self, path, nb, index): + def _getvalue(self, + path, + nb, + index): """ _values == ((path1, path2), ((idx1_1, idx1_2), None), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2)) """ diff --git a/tiramisu/value.py b/tiramisu/value.py index 5773409..b4bda24 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -43,6 +43,9 @@ class Values(object): # the storage type is dictionary or sqlite3 self._p_ = storage + #______________________________________________________________________ + # get context + def _getcontext(self): """context could be None, we need to test it context is None only if all reference to `Config` object is deleted @@ -54,6 +57,198 @@ class Values(object): raise ConfigError(_('the context does not exist anymore')) return context + #______________________________________________________________________ + # get value + + def get_cached_value(self, + opt, + path=None, + validate=True, + force_permissive=False, + trusted_cached_properties=True, + validate_properties=True, + setting_properties=undefined, + self_properties=undefined, + index=None, + check_frozen=False, + display_warnings=True, + _orig_context=undefined): + context = self._getcontext() + settings = context.cfgimpl_get_settings() + if path is None: + path = opt.impl_getpath(context) + ntime = None + if self_properties is undefined: + self_properties = settings.getproperties(opt, + path, + setting_properties=setting_properties, + index=index) + if 'cache' in setting_properties and self._p_.hascache(path, index) and \ + _orig_context is undefined: + if 'expire' in setting_properties: + ntime = int(time()) + is_cached, value = self._p_.getcache(path, + ntime, + None) + if index: + value = value[index] + if is_cached: + if not trusted_cached_properties: + # revalidate properties (because of not default properties) + settings.validate_properties(opt, + False, + False, + value=value, + path=path, + force_permissive=force_permissive, + setting_properties=setting_properties, + self_properties=self_properties, + index=index) + return value + if _orig_context is not undefined: + _context = _orig_context + else: + _context = context + val = self.get_validated_value(opt, + path, + validate, + force_permissive, + validate_properties, + setting_properties, + self_properties, + index=index, + check_frozen=check_frozen, + display_warnings=display_warnings, + _orig_context=_context) + if index is None and 'cache' in setting_properties and \ + validate and validate_properties and force_permissive is False \ + and trusted_cached_properties is True and _orig_context is undefined: + if 'expire' in setting_properties: + if ntime is None: + ntime = int(time()) + ntime = ntime + expires_time + self._p_.setcache(path, val, ntime, None) + return val + + def get_validated_value(self, + opt, + path, + validate, + force_permissive, + validate_properties, + setting_properties, + self_properties, + index=None, + check_frozen=False, + display_warnings=True, + _orig_context=undefined): + """same has getitem but don't touch the cache + index is None for slave value, if value returned is not a list, just return [] + """ + context = self._getcontext() + setting = context.cfgimpl_get_settings() + config_error = None + try: + value = self.getvalue(opt, + path, + self_properties, + index, + validate, + _orig_context) + except ConfigError as value: + value_error = True + # For calculating properties, we need value (ie for mandatory + # value). + # If value is calculating with a PropertiesOptionError's option + # getvalue raise a ConfigError. + # We can not raise ConfigError if this option should raise + # PropertiesOptionError too. So we get config_error and raise + # ConfigError if properties did not raise. + config_error = value + # value is not set, for 'undefined' (cannot set None because of + # mandatory property) + value = undefined + else: + value_error = False + if validate: + err = opt.impl_validate(value, + context, + 'validator' in setting_properties, + force_index=index, + display_error=True, + display_warnings=False, + setting_properties=setting_properties) + if err: + config_error = err + value = None + + if validate_properties: + if config_error is not None: + # should not raise PropertiesOptionError if option is + # mandatory + val_props = undefined + else: + val_props = value + setting.validate_properties(opt, + False, + check_frozen, + value=val_props, + path=path, + force_permissive=force_permissive, + setting_properties=setting_properties, + self_properties=self_properties, + index=index) + if not value_error and validate and display_warnings: + opt.impl_validate(value, + context, + 'validator' in setting_properties, + force_index=index, + display_error=False, + display_warnings=display_warnings, + setting_properties=setting_properties) + if config_error is not None: + return config_error + return value + + def getvalue(self, + opt, + path, + self_properties, + index, + validate, + _orig_context): + """actually retrieves the value + + :param opt: the `option.Option()` object + :returns: the option's value (or the default value if not set) + """ + force_default = 'frozen' in self_properties and \ + 'force_default_on_freeze' in self_properties + # not default value + if index is None or not opt.impl_is_master_slaves('slave'): + _index = None + else: + _index = index + owner, value = self._p_.getowner(path, + owners.default, + only_default=True, + index=_index, + with_value=True) + is_default = owner == owners.default + if not is_default and not force_default: + if index is not None and not opt.impl_is_master_slaves('slave'): + if len(value) > index: + return value[index] + #value is smaller than expected + #so return default value + else: + return value + return self._getdefaultvalue(opt, + path, + index, + validate, + _orig_context) + def getdefaultvalue(self, opt, path, @@ -84,8 +279,7 @@ class Values(object): def _reset_cache(): # calculated value could be a new value, so reset cache _orig_context.cfgimpl_reset_cache(opt=opt, - path=path, - only=('values', 'properties')) + path=path) #FIXME with_meta should be calculated here... with_meta = True @@ -170,60 +364,253 @@ class Values(object): value = opt.impl_getdefault_multi() return value - def _getvalue(self, - opt, - path, - self_properties, - index, - validate, - _orig_context): - """actually retrieves the value - - :param opt: the `option.Option()` object - :returns: the option's value (or the default value if not set) - """ - force_default = 'frozen' in self_properties and \ - 'force_default_on_freeze' in self_properties - # not default value - if index is None or not opt.impl_is_master_slaves('slave'): - _index = None + def isempty(self, + opt, + value, + force_allow_empty_list=False, + index=None): + "convenience method to know if an option is empty" + if value is undefined: + return False else: - _index = index - owner, value = self._p_.getowner(path, - owners.default, - only_default=True, - index=_index, - with_value=True) - is_default = owner == owners.default - if not is_default and not force_default: - if index is not None and not opt.impl_is_master_slaves('slave'): - if len(value) > index: - return value[index] - #value is smaller than expected - #so return default value + empty = opt._empty + if index in [None, undefined] and opt.impl_is_multi(): + if force_allow_empty_list: + allow_empty_list = True + else: + allow_empty_list = opt.impl_allow_empty_list() + if allow_empty_list is undefined: + if opt.impl_is_master_slaves('slave'): + allow_empty_list = True + else: + allow_empty_list = False + isempty = value is None or (not allow_empty_list and value == []) or \ + None in value or empty in value else: - return value - return self._getdefaultvalue(opt, - path, - index, - validate, - _orig_context) + isempty = value is None or value == empty + return isempty def get_modified_values(self): return self._p_.get_modified_values() - def __contains__(self, opt): - """ - implements the 'in' keyword syntax in order provide a pythonic way - to kow if an option have a value + #______________________________________________________________________ + # set value - :param opt: the `option.Option()` object - """ - path = opt.impl_getpath(self._getcontext()) - return self._contains(path) + def setvalue(self, + opt, + value, + path, + force_permissive, + index, + setting_properties, + _commit): - def _contains(self, path): - return self._p_.hasvalue(path) + context = self._getcontext() + owner = context.cfgimpl_get_settings().getowner() + if 'validator' in setting_properties: + if opt._has_consistencies(): + # set value to a fake config when option has dependency + # validation will be complet in this case (consistency, ...) + tested_context = context._gen_fake_values() + tested_values = tested_context.cfgimpl_get_values() + tested_values._setvalue(opt, + path, + value, + index=index, + owner=owner) + else: + tested_context = context + tested_values = self + tested_values.validate_setitem(opt, + value, + path, + force_permissive, + setting_properties, + index) + + self._setvalue(opt, + path, + value, + owner, + index=index, + commit=_commit) + + def validate_setitem(self, + opt, + value, + path, + force_permissive, + setting_properties, + index): + + context = self._getcontext() + # First validate properties with this value + context.cfgimpl_get_settings().validate_properties(opt, + False, + True, + value=value, + path=path, + force_permissive=force_permissive, + setting_properties=setting_properties, + index=index) + # Value must be valid for option + opt.impl_validate(value, + context, + display_warnings=False, + force_index=index, + setting_properties=setting_properties) + # No error found so emit warnings + opt.impl_validate(value, + context, + display_error=False, + force_index=index, + setting_properties=setting_properties) + + def _setvalue(self, + opt, + path, + value, + owner, + index=None, + commit=True): + + self._getcontext().cfgimpl_reset_cache(opt=opt, + path=path) + if isinstance(value, list): + # copy + value = list(value) + self._p_.setvalue(path, + value, + owner, + index, + commit) + + def _is_meta(self, + opt, + path, + setting_properties, + force_permissive=False): + context = self._getcontext() + if context.cfgimpl_get_meta() is None: + return False + return self.is_default_owner(opt, + path, + setting_properties, + validate_meta=False, + index=None, + force_permissive=force_permissive) + + #______________________________________________________________________ + # owner + + def getowner(self, + opt, + path, + setting_properties, + index=None, + force_permissive=False): + """ + retrieves the option's owner + + :param opt: the `option.Option` object + :param force_permissive: behaves as if the permissive property + was present + :returns: a `setting.owners.Owner` object + """ + if opt._is_symlinkoption() and \ + not isinstance(opt, DynSymLinkOption): + opt = opt._impl_getopt() + return self._getowner(opt, + path, + setting_properties, + index=index, + force_permissive=force_permissive) + + def _getowner(self, + opt, + path, + setting_properties, + force_permissive=False, + validate_meta=undefined, + self_properties=undefined, + only_default=False, + index=None): + """get owner of an option + """ + if not isinstance(opt, Option) and not isinstance(opt, + DynSymLinkOption): + raise ConfigError(_('owner only avalaible for an option')) + context = self._getcontext() + if self_properties is undefined: + self_properties = context.cfgimpl_get_settings().getproperties(opt, + path, + setting_properties) + if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties: + return owners.default + owner = self._p_.getowner(path, + owners.default, + only_default=only_default, + index=index) + if validate_meta is undefined: + if opt.impl_is_master_slaves('slave'): + master = opt.impl_get_master_slaves().getmaster(opt) + masterp = master.impl_getpath(context) + validate_meta = self._is_meta(master, + masterp, + setting_properties, + force_permissive=force_permissive) + else: + validate_meta = True + if validate_meta and owner is owners.default: + meta = context.cfgimpl_get_meta() + if meta is not None: + owner = meta.cfgimpl_get_values()._getowner(opt, + path, + setting_properties, + force_permissive=force_permissive, + self_properties=self_properties, + only_default=only_default, + index=index) + return owner + + def setowner(self, + path, + owner, + index=None): + """ + sets a owner to an option + + :param opt: the `option.Option` object + :param owner: a valid owner, that is a `setting.owners.Owner` object + """ + if not isinstance(owner, owners.Owner): + raise TypeError(_("invalid generic owner {0}").format(str(owner))) + + if not self._p_.hasvalue(path): + raise ConfigError(_('no value for {0} cannot change owner to {1}' + '').format(path, owner)) + self._p_.setowner(path, owner, index=index) + + def is_default_owner(self, + opt, + path, + setting_properties, + validate_meta=True, + self_properties=undefined, + index=None, + force_permissive=False): + owner = self._getowner(opt, + path, + setting_properties, + validate_meta=validate_meta, + self_properties=self_properties, + only_default=True, + index=index, + force_permissive=force_permissive) + return owner == owners.default + + #______________________________________________________________________ + # reset def reset(self, opt, @@ -235,7 +622,7 @@ class Values(object): context = self._getcontext() setting = context.cfgimpl_get_settings() - hasvalue = self._contains(path) + hasvalue = self._p_.hasvalue(path) if validate and hasvalue and 'validator' in setting_properties: fake_context = context._gen_fake_values() @@ -258,19 +645,15 @@ class Values(object): _commit=_commit, force_permissive=force_permissive) if hasvalue: - if 'force_store_value' in setting._getproperties(opt=opt, - path=path, - setting_properties=setting_properties, - read_write=False, - apply_requires=False): + if 'force_store_value' in setting.getproperties(opt, + path, + setting_properties, + apply_requires=False): value = self._getdefaultvalue(opt, path, - True, - undefined, + None, validate, context) - if isinstance(value, Exception): # pragma: no cover - raise value self._setvalue(opt, path, value, @@ -281,8 +664,7 @@ class Values(object): self._p_.resetvalue(path, _commit) context.cfgimpl_reset_cache(opt=opt, - path=path, - only=('values', 'properties')) + path=path) def reset_slave(self, opt, @@ -301,14 +683,12 @@ class Values(object): index, setting_properties, validate=False) - ret = fake_value.get_cached_value(opt, - path, - index=index, - setting_properties=setting_properties, - check_frozen=True, - force_permissive=force_permissive) - if isinstance(ret, Exception): - raise ret + fake_value.get_cached_value(opt, + path, + index=index, + setting_properties=setting_properties, + check_frozen=True, + force_permissive=force_permissive) self._p_.resetvalue_index(path, index) def reset_master(self, @@ -325,457 +705,21 @@ class Values(object): check_frozen=True, force_permissive=force_permissive) current_value.pop(index) - ret = self.setitem(opt, - current_value, - path, - force_permissive=force_permissive, - not_raises=True, - index=None, - setting_properties=setting_properties, - _commit=True) - if ret: - return ret + self.setvalue(opt, + current_value, + path, + force_permissive=force_permissive, + index=None, + setting_properties=setting_properties, + _commit=True) subconfig.cfgimpl_get_description().pop(opt, path, self, index) - def _isempty(self, - opt, - value, - force_allow_empty_list=False, - index=None): - "convenience method to know if an option is empty" - if value is undefined: - return False - else: - empty = opt._empty - if index in [None, undefined] and opt.impl_is_multi(): - if force_allow_empty_list: - allow_empty_list = True - else: - allow_empty_list = opt.impl_allow_empty_list() - if allow_empty_list is undefined: - if opt.impl_is_master_slaves('slave'): - allow_empty_list = True - else: - allow_empty_list = False - isempty = value is None or (not allow_empty_list and value == []) or \ - None in value or empty in value - else: - isempty = value is None or value == empty - return isempty - - def __getitem__(self, opt): - "enables us to use the pythonic dictionary-like access to values" - return self.get_cached_value(opt) - - def get_cached_value(self, - opt, - path=None, - validate=True, - force_permissive=False, - trusted_cached_properties=True, - validate_properties=True, - setting_properties=undefined, - self_properties=undefined, - index=None, - check_frozen=False, - display_warnings=True, - _orig_context=undefined): - context = self._getcontext() - settings = context.cfgimpl_get_settings() - if path is None: - path = opt.impl_getpath(context) - ntime = None - if setting_properties is undefined: - setting_properties = settings._getproperties(read_write=False) - if self_properties is undefined: - self_properties = settings._getproperties(opt, - path, - read_write=False, - setting_properties=setting_properties, - index=index) - if 'cache' in setting_properties and self._p_.hascache(path, index) and \ - _orig_context is undefined: - if 'expire' in setting_properties: - ntime = int(time()) - is_cached, value = self._p_.getcache(path, ntime, None) - if index: - value = value[index] - if is_cached: - #if opt.impl_is_multi() and not isinstance(value, Multi) and index is None: - # value = Multi(value, self.context, opt, path) - if not trusted_cached_properties: - # revalidate properties (because of not default properties) - props = settings.validate_properties(opt, - False, - False, - value=value, - path=path, - force_permissive=force_permissive, - setting_properties=setting_properties, - self_properties=self_properties, - index=index) - if props: - return props - return value - #if not from_masterslave and opt.impl_is_master_slaves(): - # val = opt.impl_get_master_slaves().getitem(self, opt, path, - # validate, - # force_permissive, - # trusted_cached_properties, - # validate_properties, - # setting_properties=setting_properties, - # index=index, - # self_properties=self_properties, - # check_frozen=check_frozen) - #else: - if _orig_context is not undefined: - _context = _orig_context - else: - _context = context - val = self._get_validated_value(opt, - path, - validate, - force_permissive, - validate_properties, - setting_properties, - self_properties, - index=index, - check_frozen=check_frozen, - display_warnings=display_warnings, - _orig_context=_context) - if isinstance(val, Exception): - return val - if index is None and 'cache' in setting_properties and \ - validate and validate_properties and force_permissive is False \ - and trusted_cached_properties is True and _orig_context is undefined: - if 'expire' in setting_properties: - if ntime is None: - ntime = int(time()) - ntime = ntime + expires_time - self._p_.setcache(path, val, ntime, None) - return val - - def _get_validated_value(self, - opt, - path, - validate, - force_permissive, - validate_properties, - setting_properties, - self_properties, - index=None, - check_frozen=False, - display_warnings=True, - _orig_context=undefined): - """same has getitem but don't touch the cache - index is None for slave value, if value returned is not a list, just return [] - """ - context = self._getcontext() - setting = context.cfgimpl_get_settings() - config_error = None - try: - value = self._getvalue(opt, - path, - self_properties, - index, - validate, - _orig_context) - except ConfigError as value: - value_error = True - # For calculating properties, we need value (ie for mandatory - # value). - # If value is calculating with a PropertiesOptionError's option - # _getvalue raise a ConfigError. - # We can not raise ConfigError if this option should raise - # PropertiesOptionError too. So we get config_error and raise - # ConfigError if properties did not raise. - config_error = value - # value is not set, for 'undefined' (cannot set None because of - # mandatory property) - value = undefined - else: - value_error = False - if validate: - err = opt.impl_validate(value, - context, - 'validator' in setting_properties, - force_index=index, - display_error=True, - display_warnings=False, - setting_properties=setting_properties) - if err: - config_error = err - value = None - - if validate_properties: - if config_error is not None: - # should not raise PropertiesOptionError if option is - # mandatory - val_props = undefined - else: - val_props = value - props = setting.validate_properties(opt, - False, - check_frozen, - value=val_props, - path=path, - force_permissive=force_permissive, - setting_properties=setting_properties, - self_properties=self_properties, - index=index) - if props: - return props - if not value_error and validate and display_warnings: - opt.impl_validate(value, - context, - 'validator' in setting_properties, - force_index=index, - display_error=False, - display_warnings=display_warnings, - setting_properties=setting_properties) - if config_error is not None: - return config_error - return value - - def setitem(self, - opt, - value, - path, - force_permissive, - not_raises, - index, - setting_properties, - _commit): - - context = self._getcontext() - owner = context.cfgimpl_get_settings().getowner() - if 'validator' in setting_properties: - if opt._has_consistencies(): - # set value to a fake config when option has dependency - # validation will be complet in this case (consistency, ...) - tested_context = context._gen_fake_values() - tested_values = tested_context.cfgimpl_get_values() - tested_values._setvalue(opt, - path, - value, - index=index, - owner=owner) - else: - tested_context = context - tested_values = self - props = tested_values.validate_setitem(opt, - value, - path, - force_permissive, - setting_properties, - index) - if props: - if not not_raises: - raise props - return props - - self._setvalue(opt, - path, - value, - owner, - index=index, - commit=_commit) - - def validate_setitem(self, - opt, - value, - path, - force_permissive, - setting_properties, - index): - - context = self._getcontext() - # First validate properties with this value - props = context.cfgimpl_get_settings().validate_properties(opt, - False, - True, - value=value, - path=path, - force_permissive=force_permissive, - setting_properties=setting_properties, - index=index) - if props: - return props - # Value must be valid for option - err = opt.impl_validate(value, - context, - display_warnings=False, - force_index=index, - setting_properties=setting_properties) - if err: - return err - # No error found so emit warnings - opt.impl_validate(value, - context, - display_error=False, - force_index=index, - setting_properties=setting_properties) - - def _setvalue(self, - opt, - path, - value, - owner, - index=None, - commit=True): - - self._getcontext().cfgimpl_reset_cache(opt=opt, - path=path, - only=('values', 'properties')) - if isinstance(value, list): - # copy - value = list(value) - self._p_.setvalue(path, - value, - owner, - index, - commit) - - def _is_meta(self, opt, path, force_permissive=False): - context = self._getcontext() - if context.cfgimpl_get_meta() is None: - return False - setting = context.cfgimpl_get_settings() - self_properties = setting._getproperties(opt, path, read_write=False) - return self.is_default_owner(opt, path=path, validate_properties=True, - validate_meta=False, index=None, - force_permissive=force_permissive) - - def getowner(self, opt, index=None, force_permissive=False): - """ - retrieves the option's owner - - :param opt: the `option.Option` object - :param force_permissive: behaves as if the permissive property - was present - :returns: a `setting.owners.Owner` object - """ - if opt._is_symlinkoption() and \ - not isinstance(opt, DynSymLinkOption): - opt = opt._impl_getopt() - path = opt.impl_getpath(self._getcontext()) - return self._getowner(opt, - path, - index=index, - force_permissive=force_permissive) - - def _getowner(self, - opt, - path, - validate_properties=True, - force_permissive=False, - validate_meta=undefined, - self_properties=undefined, - only_default=False, - index=None): - """get owner of an option - """ - if not isinstance(opt, Option) and not isinstance(opt, - DynSymLinkOption): - raise ConfigError(_('owner only avalaible for an option')) - context = self._getcontext() - if self_properties is undefined: - self_properties = context.cfgimpl_get_settings()._getproperties( - opt, path, read_write=False) - if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties: - return owners.default - if validate_properties: - value = self.get_cached_value(opt, - path=path, - force_permissive=force_permissive, - self_properties=self_properties, - index=index) - if isinstance(value, Exception): - raise value - - owner = self._p_.getowner(path, owners.default, only_default=only_default, index=index) - if validate_meta is undefined: - if opt.impl_is_master_slaves('slave'): - master = opt.impl_get_master_slaves().getmaster(opt) - masterp = master.impl_getpath(context) - validate_meta = self._is_meta(master, masterp) - else: - validate_meta = True - if validate_meta and owner is owners.default: - meta = context.cfgimpl_get_meta() - if meta is not None: - owner = meta.cfgimpl_get_values()._getowner(opt, - path, - validate_properties=validate_properties, - force_permissive=force_permissive, - self_properties=self_properties, - only_default=only_default, - index=index) - return owner - - def setowner(self, opt, owner, index=None, force_permissive=False): - """ - sets a owner to an option - - :param opt: the `option.Option` object - :param owner: a valid owner, that is a `setting.owners.Owner` object - """ - if not isinstance(owner, owners.Owner): - raise TypeError(_("invalid generic owner {0}").format(str(owner))) - - path = opt.impl_getpath(self._getcontext()) - props = self._getcontext().cfgimpl_get_settings().validate_properties(opt, - False, - True, - path, - index=index, - force_permissive=force_permissive) - if props: - raise props - if not self._p_.hasvalue(path): - raise ConfigError(_('no value for {0} cannot change owner to {1}' - '').format(path, owner)) - self._p_.setowner(path, owner, index=index) - - def is_default_owner(self, opt, path=None, validate_properties=True, - validate_meta=True, index=None, - force_permissive=False): - """ - :param config: *must* be only the **parent** config - (not the toplevel config) - :return: boolean - """ - if path is None: - path = opt.impl_getpath(self._getcontext()) - return self._is_default_owner(opt, - path, - validate_properties=validate_properties, - validate_meta=validate_meta, - index=index, - force_permissive=force_permissive) - - def _is_default_owner(self, - opt, - path, - validate_properties=True, - validate_meta=True, - self_properties=undefined, - index=None, - force_permissive=False): - owner = self._getowner(opt, - path, - validate_properties=validate_properties, - validate_meta=validate_meta, - self_properties=self_properties, - only_default=True, - index=index, - force_permissive=force_permissive) - return owner == owners.default - + #______________________________________________________________________ # information + def set_information(self, key, value): """updates the information's attribute @@ -794,6 +738,9 @@ class Values(object): def del_information(self, key, raises=True): self._p_.del_information(key, raises) + #______________________________________________________________________ + # mandatory warnings + def mandatory_warnings(self, force_permissive=True): """convenience function to trace Options that are mandatory and where no value has been set @@ -802,7 +749,7 @@ class Values(object): """ context = self._getcontext() settings = context.cfgimpl_get_settings() - setting_properties = context.cfgimpl_get_settings()._getproperties() + setting_properties = context.cfgimpl_get_settings().get_context_properties() setting_properties.update(['mandatory', 'empty']) def _is_properties_option(err, path): #FIXME hum ... @@ -834,9 +781,9 @@ class Values(object): if opt._is_symlinkoption() and \ not isinstance(opt, DynSymLinkOption): continue - self_properties = settings._getproperties(opt, path, - read_write=False, - setting_properties=setting_properties) + self_properties = settings.getproperties(opt, + path, + setting_properties=setting_properties) if 'mandatory' in self_properties or 'empty' in self_properties: err = self.get_cached_value(opt, path=path, trusted_cached_properties=False, @@ -859,18 +806,3 @@ class Values(object): descr = context.cfgimpl_get_description() for path in _mandatory_warnings(descr): yield path - - def force_cache(self): - """parse all option to force data in cache - """ - context = self.context() - if not 'cache' in context.cfgimpl_get_settings(): - raise ConfigError(_('can force cache only if cache ' - 'is actived in config')) - context.cfgimpl_reset_cache() - for path in context.cfgimpl_get_description().impl_getpaths( - include_groups=True): - try: - err = context.getattr(path) - except PropertiesOptionError as err: - pass