From 007a22ca94ff3e3b4f1db07fa3e888665411d423 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 13 Nov 2017 22:45:53 +0100 Subject: [PATCH] refactor and better test in api --- test/api/test_multi.py | 4 +- test/api/test_owner.py | 145 +++++++++++--------- tiramisu/api.py | 10 +- tiramisu/config.py | 191 ++++++++++++--------------- tiramisu/option/baseoption.py | 18 +-- tiramisu/option/option.py | 41 +++--- tiramisu/option/optiondescription.py | 189 ++++++++++++++++---------- tiramisu/setting.py | 92 +++++++------ tiramisu/value.py | 64 ++++----- 9 files changed, 406 insertions(+), 348 deletions(-) diff --git a/test/api/test_multi.py b/test/api/test_multi.py index d45b6e5..6379e1d 100644 --- a/test/api/test_multi.py +++ b/test/api/test_multi.py @@ -210,8 +210,8 @@ def test_callback_submulti_list_list(): def test_values_with_master_and_slaves_submulti(): - ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) - netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=submulti) interface1 = MasterSlaves('f_ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) conf = OptionDescription('conf', '', [interface1]) cfg = Config(conf) diff --git a/test/api/test_owner.py b/test/api/test_owner.py index 679e864..e168642 100644 --- a/test/api/test_owner.py +++ b/test/api/test_owner.py @@ -2,6 +2,7 @@ """ import pytest from py.test import raises +import weakref from .autopath import do_autopath do_autopath() from tiramisu import Config, StrOption, OptionDescription, MasterSlaves, DynOptionDescription, \ @@ -55,7 +56,10 @@ def autocheck_owner_without_value(api, path, **kwargs): isslave = False # check if owner is a string "default" if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False): - assert api.option(path).owner.get() == 'default' + if not isslave: + assert api.option(path).owner.get() == 'default' + else: + assert api.option(path, 0).owner.get() == 'default' else: if not isslave: raises(PropertiesOptionError, "api.option(path).owner.get()") @@ -76,7 +80,10 @@ def autocheck_owner_without_value(api, path, **kwargs): # check if default owner raises(APIError, "api.unrestraint.option(path).owner.isdefault()") if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False): - assert api.option(path).owner.isdefault() + if not isslave: + assert api.option(path).owner.isdefault() + else: + assert api.option(path, 0).owner.isdefault() else: raises(PropertiesOptionError, "api.option(path).owner.isdefault()") if not kwargs.get('propertyerror', False): @@ -141,7 +148,6 @@ def autocheck_value(api, path, **kwargs): api.option(path).value.set([first_value[0]]) elif isslave: api.option(path, 0).value.set(first_value[0]) - raise Exception('pouet') else: api.option(path).value.set(first_value) else: @@ -321,6 +327,14 @@ def autocheck_value2(*args, **kwargs): autocheck_value(*args, **kwargs) +@autocheck +def autocheck_display(api, path, **kwargs): + """re set value + """ + #FIXME utile ? + print(api.config) + + @autocheck def autocheck_property(api, path, **kwargs): """get property from path @@ -756,6 +770,7 @@ def check_all(api, path, multi, **kwargs): text = u' {} launch tests for {}'.format(ICON, path) if multi: text += u' as a multi' + text += u', kwargs: {}'.format(kwargs) print(text) for func in autocheck_registers: if DISPLAY: @@ -764,6 +779,7 @@ def check_all(api, path, multi, **kwargs): def make_api(options, multi): + weakrefs = [] def make_option(path, option_infos): #FIXME option_type = 'str' @@ -779,7 +795,9 @@ def make_api(options, multi): if multi: kwargs['multi'] = True tiramisu_option = OPTIONS_TYPE[option_type]['option'] - return tiramisu_option(*args, **kwargs) + obj = tiramisu_option(*args, **kwargs) + weakrefs.append(weakref.ref(obj)) + return obj def make_optiondescriptions(path, collected): infos = collected.get('properties', {}) @@ -809,7 +827,9 @@ def make_api(options, multi): options.append(option) if properties != []: kwargs['properties'] = tuple(properties) - return optiondescription(path, "{}'s optiondescription".format(path), options, **kwargs) + obj = optiondescription(path, "{}'s optiondescription".format(path), options, **kwargs) + weakrefs.append(weakref.ref(obj)) + return obj collect_options = {} for path, option in options.items(): @@ -826,16 +846,20 @@ def make_api(options, multi): rootod = make_optiondescriptions('root', collect_options) if rootod is None: - return - cfg = Config(rootod) - return getapi(cfg) + return None, None + cfg = Config(rootod, session_id='conftest') + weakrefs.append(weakref.ref(cfg)) + api = getapi(cfg) + weakrefs.append(weakref.ref(api)) + return api, weakrefs DICT_PATHS = [ #test a config without optiondescription OrderedDict([('first', {}), ('second', {'second': {'disabled': True}}), - ('third', {'third': {'hidden': True}})]), + ('third', {'third': {'hidden': True}}) + ]), #test a config with an optiondescription OrderedDict([('subod.first', {}), ('subod.second', {'second': {'disabled': True}}), @@ -864,8 +888,22 @@ DICT_PATHS = [ ('subodval2.thirdval2', None)]), #test a config with dynoption subdir OrderedDict([('subod.subsubod.first', {'subsubod': {'dyn': True}}), - ('subod.subsubod.second', {'second': {'disabled': True}}), - ('subod.subsubod.third', {'third': {'hidden': True}}), + ('subod.subsubod.second', {'subsubod': {'dyn': True}, 'second': {'disabled': True}}), + ('subod.subsubod.third', {'subsubod': {'dyn': True}, 'third': {'hidden': True}}), + ('subod.subsubodval1.firstval1', None), + ('subod.subsubodval1.secondval1', None), + ('subod.subsubodval1.thirdval1', None), + ('subod.subsubodval2.firstval2', None), + ('subod.subsubodval2.secondval2', None), + ('subod.subsubodval2.thirdval2', None)]), + #test a config with hidden subsubod + OrderedDict([('subod.subsubod.first', {'subsubod': {'hidden': True}}), + ('subod.subsubod.second', {'subsubod': {'hidden': True}}), + ('subod.subsubod.third', {'subsubod': {'hidden': True}})]), + #test a config with hidden dyn subsubod + OrderedDict([('subod.subsubod.first', {'subsubod': {'dyn': True, 'hidden': True}}), + ('subod.subsubod.second', {'subsubod': {'dyn': True, 'hidden': True}}), + ('subod.subsubod.third', {'subsubod': {'dyn': True, 'hidden': True}}), ('subod.subsubodval1.firstval1', None), ('subod.subsubodval1.secondval1', None), ('subod.subsubodval1.thirdval1', None), @@ -883,63 +921,40 @@ def paths(request): def test_options(paths): + def get_kwargs_option(options, kwargs, od=False): + if options.get('hidden', False) is True: + kwargs['permissive'] = True + if not od: + kwargs.setdefault('extra_properties', []).append('hidden') + if options.get('disabled', False) is True: + kwargs['propertyerror'] = True + if not od: + kwargs.setdefault('extra_properties', []).append('disabled') + + def get_kwargs(path): + kwargs = {} + spath = path.split('.') + get_kwargs_option(paths[path].get(spath[-1], {}), kwargs) + if len(spath) > 1: + get_kwargs_option(paths[path].get(spath[-2], {}), kwargs, od=True) + return kwargs + lpaths = list(paths.keys()) for multi in (False, True): - api = make_api(paths, multi) + api, weakrefs = make_api(paths, multi) if api is None: continue if len(lpaths) == 9: - check_all(api, lpaths[3], multi) - check_all(api, lpaths[4], multi, propertyerror=True, extra_properties=['disabled']) - check_all(api, lpaths[5], multi, permissive=True, extra_properties=['hidden']) - check_all(api, lpaths[6], multi) - check_all(api, lpaths[7], multi, propertyerror=True, extra_properties=['disabled']) - check_all(api, lpaths[8], multi, permissive=True, extra_properties=['hidden']) + check_all(api, lpaths[3], multi, **get_kwargs(lpaths[0])) + check_all(api, lpaths[4], multi, **get_kwargs(lpaths[1])) + check_all(api, lpaths[5], multi, **get_kwargs(lpaths[2])) + check_all(api, lpaths[6], multi, **get_kwargs(lpaths[0])) + check_all(api, lpaths[7], multi, **get_kwargs(lpaths[1])) + check_all(api, lpaths[8], multi, **get_kwargs(lpaths[2])) else: - check_all(api, lpaths[0], multi) - check_all(api, lpaths[1], multi, propertyerror=True, extra_properties=['disabled']) - check_all(api, lpaths[2], multi, permissive=True, extra_properties=['hidden']) - - -DICT_PATHS2 = [ - OrderedDict([('subod.subsubod.first', {'subsubod': {'hidden': True}}), - ('subod.subsubod.second', {}), - ('subod.subsubod.third', {})]), - OrderedDict([('subod.subsubod.first', {'subsubod': {'dyn': True, 'hidden': True}}), - ('subod.subsubod.second', {}), - ('subod.subsubod.third', {}), - ('subod.subsubodval1.firstval1', None), - ('subod.subsubodval1.secondval1', None), - ('subod.subsubodval1.thirdval1', None), - ('subod.subsubodval2.firstval2', None), - ('subod.subsubodval2.secondval2', None), - ('subod.subsubodval2.thirdval2', None)]) -] - - -@pytest.fixture(scope="function", params=DICT_PATHS2) -def paths2(request): - if DISPLAY: - print(u'\n{} {}: {}'.format(ICON, request.function.__name__, request.param)) - return request.param - - -def test_tree_od_permissive(paths2): - """permissive when optiondescription is hidden - """ - lpaths = list(paths2.keys()) - for multi in (False, True): - api = make_api(paths2, multi) - if api is None: - continue - if len(lpaths) == 9: - check_all(api, lpaths[3], multi, permissive=True) - check_all(api, lpaths[4], multi, permissive=True) - check_all(api, lpaths[5], multi, permissive=True) - check_all(api, lpaths[6], multi, permissive=True) - check_all(api, lpaths[7], multi, permissive=True) - check_all(api, lpaths[8], multi, permissive=True) - else: - check_all(api, lpaths[0], multi, permissive=True) - check_all(api, lpaths[1], multi, permissive=True) - check_all(api, lpaths[2], multi, permissive=True) + check_all(api, lpaths[0], multi, **get_kwargs(lpaths[0])) + check_all(api, lpaths[1], multi, **get_kwargs(lpaths[1])) + check_all(api, lpaths[2], multi, **get_kwargs(lpaths[2])) + del(api) + for wr in weakrefs: + assert wr() is None diff --git a/tiramisu/api.py b/tiramisu/api.py index 1781eb9..7747b91 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -15,7 +15,7 @@ # along with this program. If not, see . # ____________________________________________________________ from inspect import ismethod, getdoc -from .error import APIError +from .error import APIError, PropertiesOptionError from .i18n import _ from .setting import owners, undefined try: @@ -377,7 +377,7 @@ class TiramisuAPI(object): self.force_permissive = force_permissive self.force_unrestraint = force_unrestraint settings = self.config.cfgimpl_get_settings() - #FIXME ? +# #FIXME ? self.config.read_write() settings.setpermissive(('hidden',)) #/FIXME ? @@ -386,8 +386,12 @@ class TiramisuAPI(object): validate = not self.force_unrestraint settings = self.config.cfgimpl_get_settings() setting_properties = settings.get_global_properties() + if validate: + s_properties = setting_properties + else: + s_properties = None opt = self.config.unwrap_from_path(path, - setting_properties=setting_properties, + setting_properties=s_properties, validate=validate, validate_properties=validate, force_permissive=self.force_permissive, diff --git a/tiramisu/config.py b/tiramisu/config.py index 75fdbfa..debbe9b 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -87,6 +87,28 @@ class SubConfig(object): validate=validate, force_permissive=force_permissive) + 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) + resetted_opts |= tresetted_opts + for option in opt._get_dependencies(self): + if option in resetted_opts: + continue + self.reset_one_option_cache(values, settings, resetted_opts, option(), only) + del(option) + def cfgimpl_reset_cache(self, only_expired=False, only=('values', 'properties', 'permissives', 'settings'), @@ -98,27 +120,6 @@ class SubConfig(object): :param only_expired: if True reset only expired cached values :type only_expired: boolean """ - def reset_one_option_cache(opt, resetted_opts): - 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) - resetted_opts |= tresetted_opts - for option in opt._get_dependencies(self): - if option in resetted_opts: - continue - reset_one_option_cache(option, resetted_opts) - def reset_expired_cache(): # reset cache for expired cache value ony @@ -153,7 +154,11 @@ class SubConfig(object): if not None in (opt, path): if opt not in resetted_opts: - reset_one_option_cache(opt, resetted_opts) + self.reset_one_option_cache(values, + settings, + resetted_opts, + opt, + only) elif only_expired: reset_expired_cache() @@ -164,15 +169,13 @@ class SubConfig(object): path, setting_properties, validate_properties=True, - force_permissive=False, - returns_raise=False): + force_permissive=False): """:returns: tuple (config, name)""" path = path.split('.') for step in path[:-1]: self = self.getattr(step, force_permissive=force_permissive, validate_properties=validate_properties, - returns_raise=returns_raise, setting_properties=setting_properties) if isinstance(self, Exception): return self, None @@ -182,13 +185,16 @@ class SubConfig(object): def __iter__(self, force_permissive=False): """Pythonesque way of parsing group's ordered options. iteration only on Options (not OptionDescriptions)""" + setting_properties = self.cfgimpl_get_context().cfgimpl_get_settings( + )._getproperties(read_write=True) for child in self.cfgimpl_get_description()._impl_getchildren( context=self._cfgimpl_get_context()): if not child.impl_is_optiondescription(): try: name = child.impl_getname() yield name, self.getattr(name, - force_permissive=force_permissive) + force_permissive=force_permissive, + setting_properties=setting_properties) except GeneratorExit: # pragma: optional cover if sys.version_info[0] < 3: raise StopIteration @@ -224,15 +230,19 @@ class SubConfig(object): if group_type is not None and not isinstance(group_type, groups.GroupType): # pragma: optional cover raise TypeError(_("unknown group_type: {0}").format(group_type)) + context = self._cfgimpl_get_context() + setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True) for child in self.cfgimpl_get_description()._impl_getchildren( - context=self._cfgimpl_get_context()): + context=context): if child.impl_is_optiondescription(): try: if group_type is None or (group_type is not None and child.impl_get_group_type() == group_type): name = child.impl_getname() - yield name, self.getattr(name, force_permissive=force_permissive) + yield name, self.getattr(name, + force_permissive=force_permissive, + setting_properties=setting_properties) except GeneratorExit: # pragma: optional cover if sys.version_info[0] < 3: raise StopIteration @@ -391,7 +401,7 @@ class SubConfig(object): raise ret def __getattr__(self, name): - setting_properties = self.cfgimpl_get_context().cfgimpl_get_settings().getcontextproperties() + setting_properties = self.cfgimpl_get_context().cfgimpl_get_settings().get_global_properties() return self.getattr(name, setting_properties) def _get_subpath(self, name): @@ -408,7 +418,6 @@ class SubConfig(object): validate=True, validate_properties=True, index=None, - returns_raise=False, returns_option=False): """ attribute notation mechanism for accessing the value of an option @@ -423,7 +432,6 @@ class SubConfig(object): homeconfig, name = self.cfgimpl_get_home_by_path(name, force_permissive=force_permissive, validate_properties=validate_properties, - returns_raise=returns_raise, setting_properties=setting_properties) if isinstance(homeconfig, Exception): cfg = homeconfig @@ -433,8 +441,7 @@ class SubConfig(object): validate=validate, validate_properties=validate_properties, setting_properties=setting_properties, - index=index, - returns_raise=returns_raise) + index=index) else: option = self.cfgimpl_get_description().__getattr__(name, context=context) @@ -454,8 +461,7 @@ class SubConfig(object): validate_properties=validate_properties, force_permissive=force_permissive, setting_properties=setting_properties, - index=index, - returns_raise=True) + index=index) elif option.impl_is_optiondescription(): if setting_properties: props = self.cfgimpl_get_settings().validate_properties(option, @@ -464,11 +470,6 @@ class SubConfig(object): path=subpath, force_permissive=force_permissive, setting_properties=setting_properties) - if props: - if returns_raise: - return props - else: - raise props if returns_option is True: return option return SubConfig(option, @@ -482,11 +483,6 @@ class SubConfig(object): ret = self.cfgimpl_get_description().impl_validate(context, force_permissive, setting_properties) - if ret: - if returns_raise: - return ret - else: - raise ret cfg = self.cfgimpl_get_values().get_cached_value(option, path=subpath, validate=validate, @@ -494,8 +490,6 @@ class SubConfig(object): force_permissive=force_permissive, setting_properties=setting_properties, index=index) - if not returns_raise and isinstance(cfg, Exception): - raise cfg if returns_option is True: return option else: @@ -549,14 +543,12 @@ class SubConfig(object): def _filter_by_value(): if byvalue is undefined: return True - value = self.getattr(path, force_permissive=force_permissive, - setting_properties=setting_properties, - returns_raise=True) - if isinstance(value, Exception): - if isinstance(value, PropertiesOptionError): - return False - raise value # pragma: no cover - elif isinstance(value, list): + try: + value = self.getattr(path, force_permissive=force_permissive, + setting_properties=setting_properties) + except PropertiesOptionError: + return False + if isinstance(value, list): return byvalue in value else: return value == byvalue @@ -581,15 +573,12 @@ class SubConfig(object): continue #remove option with propertyerror, ... if byvalue is undefined and check_properties: - value = self.getattr(path, - force_permissive=force_permissive, - setting_properties=setting_properties, - returns_raise=True) - if isinstance(value, Exception): - if isinstance(value, PropertiesOptionError): - continue - else: - raise value # pragma: no cover + try: + value = self.getattr(path, + force_permissive=force_permissive, + setting_properties=setting_properties) + except PropertiesOptionError: + continue if type_ == 'value': retval = value elif type_ == 'path': @@ -707,20 +696,19 @@ class SubConfig(object): def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten, setting_properties, force_permissive=False, fullpath=False): - value = self.getattr(path, - force_permissive=force_permissive, - setting_properties=setting_properties, - returns_raise=True) - if isinstance(value, Exception): - if not isinstance(value, PropertiesOptionError): # pragma: no cover - raise value + try: + value = self.getattr(path, + force_permissive=force_permissive, + setting_properties=setting_properties) + except PropertiesOptionError as err: + pass else: if opt.impl_is_optiondescription(): - pathsvalues += value.make_dict(flatten, - _currpath + path.split('.'), - force_permissive=force_permissive, - setting_properties=setting_properties, - fullpath=fullpath) + pathsvalues += value.make_dict(flatten, + _currpath + path.split('.'), + force_permissive=force_permissive, + setting_properties=setting_properties, + fullpath=fullpath) else: if flatten: name = opt.impl_getname() @@ -792,34 +780,32 @@ class _CommonConfig(SubConfig): force_permissive=force_permissive, validate_properties=validate_properties, setting_properties=setting_properties) + if isinstance(self, Exception): + return self + option = self.cfgimpl_get_description().__getattr__(path, + context=self._cfgimpl_get_context()) if not validate_properties: - return self.cfgimpl_get_description().__getattr__(path, - context=self._cfgimpl_get_context()) + return option else: - option = self.cfgimpl_get_description().__getattr__(path, - context=self._cfgimpl_get_context()) - if not validate_properties: + 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 return option 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 - return option - else: - return self.getattr(path, - validate=validate, - force_permissive=force_permissive, - index=index, - setting_properties=setting_properties, - validate_properties=validate_properties, - returns_option=True) + return self.getattr(path, + validate=validate, + force_permissive=force_permissive, + index=index, + setting_properties=setting_properties, + validate_properties=validate_properties, + returns_option=True) def cfgimpl_get_path(self, dyn=True): return None @@ -1037,7 +1023,6 @@ class GroupConfig(_CommonConfig): force_permissive=False, validate=True, index=None, - returns_raise=False, validate_properties=True, returns_option=False): for child in self._impl_children: @@ -1047,7 +1032,6 @@ class GroupConfig(_CommonConfig): validate, index=index, setting_properties=setting_properties, - returns_raise=returns_raise, validate_properties=validate_properties, returns_option=False) @@ -1117,8 +1101,7 @@ class MetaConfig(GroupConfig): continue if force_dont_change_value: child_value = child.getattr(path, - setting_properties=setting_properties, - returns_raise=True) + setting_properties=setting_properties) if isinstance(child_value, Exception): ret.append(child_value) elif value != child_value: diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index b070070..10b1b0e 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -21,6 +21,7 @@ import re from types import FunctionType import sys +import weakref from ..i18n import _ from ..setting import undefined @@ -164,7 +165,7 @@ class Base(object): _setattr(self, '_calc_properties', calc_properties) if requires is not undefined: _setattr(self, '_requires', requires) - if properties is not undefined: + if properties: _setattr(self, '_properties', properties) def _build_validator_params(self, validator, validator_params): @@ -213,14 +214,15 @@ class Base(object): def _get_dependencies(self, context): if context: od = context.cfgimpl_get_description() + ret = set(getattr(self, '_dependencies', STATIC_TUPLE)) if context and hasattr(od, '_dependencies'): - return set(od._dependencies) | set(getattr(self, '_dependencies', STATIC_TUPLE)) + return set(od._dependencies) | ret else: - return getattr(self, '_dependencies', STATIC_TUPLE) + return ret def _add_dependency(self, option): - options = set(self._get_dependencies(None)) - options.add(option) + options = self._get_dependencies(None) + options.add(weakref.ref(option)) self._dependencies = tuple(options) def impl_set_callback(self, callback, callback_params=None, _init=False): @@ -260,7 +262,7 @@ class Base(object): return not isinstance(getattr(self, '_informations', dict()), dict) def impl_getproperties(self): - return self._properties + return getattr(self, '_properties', STATIC_TUPLE) def _set_readonly(self): if not self.impl_is_readonly(): @@ -277,7 +279,7 @@ class Base(object): _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())])) def _impl_setsubdyn(self, subdyn): - self._subdyn = subdyn + self._subdyn = weakref.ref(subdyn) def impl_getrequires(self): return getattr(self, '_requires', STATIC_TUPLE) @@ -599,7 +601,7 @@ class SymLinkOption(OnlyOption): return True def impl_getproperties(self): - return self._impl_getopt()._properties + return self._impl_getopt().impl_getproperties() def impl_get_callback(self): return self._impl_getopt().impl_get_callback() diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 6b81fca..5c13d13 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -194,21 +194,20 @@ class Option(OnlyOption): _index = None else: _index = index - opt_value = context.getattr(path, validate=False, - index=_index, - force_permissive=True, - returns_raise=True) - if isinstance(opt_value, Exception): - if isinstance(opt_value, PropertiesOptionError): - if debug: # pragma: no cover - log.debug('propertyerror in _launch_consistency: {0}'.format(opt_value)) - if transitive: - opt_value.set_orig_opt(option) - return opt_value - else: - opt_value = None - else: # pragma: no cover - return opt_value + try: + opt_value = context.getattr(path, validate=False, + index=_index, + force_permissive=True) + except PropertiesOptionError as err: + if debug: # pragma: no cover + log.debug('propertyerror in _launch_consistency: {0}'.format(err)) + if transitive: + err.set_orig_opt(option) + raise err + else: + opt_value = None + else: # pragma: no cover + return opt_value elif index is None: opt_value = opt.impl_getdefault() else: @@ -472,7 +471,10 @@ class Option(OnlyOption): return False def impl_get_master_slaves(self): - return getattr(self, '_master_slaves', None) + masterslave = getattr(self, '_master_slaves', None) + if masterslave is None: + return masterslave + return masterslave() def impl_getdoc(self): "accesses the Option's doc" @@ -480,7 +482,7 @@ class Option(OnlyOption): def _valid_consistencies(self, other_opts, init=True, func=None): if self._is_subdyn(): - dynod = self._subdyn + dynod = self._subdyn() else: dynod = None if self.impl_is_submulti(): @@ -495,10 +497,11 @@ class Option(OnlyOption): if dynod is None: raise ConfigError(_('almost one option in consistency is ' 'in a dynoptiondescription but not all')) - if dynod != opt._subdyn: + subod = opt._subdyn() + if dynod != subod: raise ConfigError(_('option in consistency must be in same' ' dynoptiondescription')) - dynod = opt._subdyn + dynod = subod elif dynod is not None: raise ConfigError(_('almost one option in consistency is in a ' 'dynoptiondescription but not all')) diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index c899f25..e7f82a5 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -20,6 +20,7 @@ # ____________________________________________________________ from copy import copy import re +import weakref from ..i18n import _ @@ -72,8 +73,6 @@ class CacheOptionDescription(BaseOption): cache_option, force_store_values, _dependencies) else: - if option.impl_is_master_slaves('master'): - option._add_dependency(option.impl_get_master_slaves()) option._set_readonly() is_multi = option.impl_is_multi() if not option._is_symlinkoption() and 'force_store_value' in option.impl_getproperties(): @@ -207,10 +206,17 @@ class CacheOptionDescription(BaseOption): class OptionDescriptionWalk(CacheOptionDescription): __slots__ = ('_children',) - def impl_get_options_paths(self, bytype, byname, _subpath, only_first, context): + def impl_get_options_paths(self, + bytype, + byname, + _subpath, + only_first, + context): find_results = [] - def _rebuild_dynpath(path, suffix, dynopt): + def _rebuild_dynpath(path, + suffix, + dynopt): found = False spath = path.split('.') for length in xrange(1, len(spath)): @@ -226,19 +232,22 @@ class OptionDescriptionWalk(CacheOptionDescription): subpath = subpath + '.' + spath[slength] + suffix return subpath - def _filter_by_name(path, option): + 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( + subdyn = option._subdyn() + for suffix in 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) + path = _rebuild_dynpath(path, + suffix, + subdyn) + option = option._impl_to_dyn(name + suffix, + path) break if not found: return False @@ -248,8 +257,10 @@ class OptionDescriptionWalk(CacheOptionDescription): find_results.append((path, option)) return True - def _filter_by_type(path, option): - if isinstance(option, bytype): + 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: @@ -257,7 +268,8 @@ class OptionDescriptionWalk(CacheOptionDescription): name = option.impl_getname() for suffix in option._subdyn._impl_get_suffixes( context): - spath = _rebuild_dynpath(path, suffix, + spath = _rebuild_dynpath(path, + suffix, option._subdyn) find_results.append((spath, option._impl_to_dyn( name + suffix, spath))) @@ -485,9 +497,11 @@ class OptionDescription(OptionDescriptionWalk): def __getstate__(self): raise NotImplementedError() - def _impl_get_suffixes(self, context): + def _impl_get_suffixes(self, + context): callback, callback_params = self.impl_get_callback() - values = carry_out_calculation(self, context=context, + values = carry_out_calculation(self, + context=context, callback=callback, callback_params=callback_params) if len(values) > len(set(values)): @@ -497,15 +511,28 @@ class OptionDescription(OptionDescriptionWalk): raise ValueError(_("invalid suffix: {0} for option").format(val)) return values - def impl_validate_value(self, option, value, context): + def impl_validate_value(self, + option, + value, + context): pass class DynOptionDescription(OptionDescription): - def __init__(self, name, doc, children, requires=None, properties=None, - callback=None, callback_params=None): - super(DynOptionDescription, self).__init__(name, doc, children, - requires, properties) + def __init__(self, + name, + doc, + children, + requires=None, + properties=None, + callback=None, + callback_params=None): + + super(DynOptionDescription, self).__init__(name, + doc, + children, + requires, + properties) for child in children: if isinstance(child, OptionDescription): if child.impl_get_group_type() != groups.master: @@ -517,9 +544,12 @@ class DynOptionDescription(OptionDescription): raise ConfigError(_('cannot set symlinkoption in a ' 'dynoptiondescription')) child._impl_setsubdyn(self) - self.impl_set_callback(callback, callback_params) + self.impl_set_callback(callback, + callback_params) - def _validate_callback(self, callback, callback_params): + def _validate_callback(self, + callback, + callback_params): if callback is None: raise ConfigError(_('callback is mandatory for dynoptiondescription')) @@ -532,15 +562,22 @@ class SynDynOptionDescription(object): self._name = name self._suffix = suffix - def __getattr__(self, name, context=undefined): + def __getattr__(self, + name, + context=undefined): if name in dir(self._opt): - return getattr(self._opt, name) - return self._opt._getattr(name, suffix=self._suffix, context=context) + return getattr(self._opt, + name) + return self._opt._getattr(name, + suffix=self._suffix, + context=context) def impl_getname(self): return self._name - def _impl_getchildren(self, dyn=True, context=undefined): + def _impl_getchildren(self, + dyn=True, + context=undefined): children = [] for child in self._opt._impl_getchildren(): yield(self._opt._impl_get_dynchild(child, self._suffix)) @@ -608,7 +645,7 @@ class MasterSlaves(OptionDescription): "not refered a slave's ones")) #everything is ok, store references for child in children: - child._master_slaves = self + child._master_slaves = weakref.ref(self) master._add_dependency(self) def is_master(self, opt): @@ -643,7 +680,7 @@ class MasterSlaves(OptionDescription): c_opt = opt._opt else: c_opt = opt - return c_opt in self._children[1] + return child in self._children[1] def reset(self, opt, @@ -714,10 +751,16 @@ class MasterSlaves(OptionDescription): setting_properties, check_frozen) else: - return self._getslave(values, opt, path, validate, - force_permissive, trusted_cached_properties, - validate_properties, setting_properties, - self_properties, index, + return self._getslave(values, + opt, + path, + validate, + force_permissive, + trusted_cached_properties, + validate_properties, + setting_properties, + self_properties, + index, check_frozen) def _getmaster(self, @@ -768,17 +811,19 @@ class MasterSlaves(OptionDescription): master = self.getmaster(opt) context = values._getcontext() masterp = master.impl_getpath(context) - mastervalue = values.get_cached_value(master, path=masterp, validate=validate, - force_permissive=force_permissive, - validate_properties=validate_properties, - self_properties=self_properties, - from_masterslave=True, - setting_properties=setting_properties, - check_frozen=check_frozen) - if isinstance(mastervalue, Exception): - if isinstance(mastervalue, PropertiesOptionError): - mastervalue.set_orig_opt(opt) - return mastervalue + try: + mastervalue = values.get_cached_value(master, + path=masterp, + validate=validate, + force_permissive=force_permissive, + validate_properties=validate_properties, + self_properties=self_properties, + from_masterslave=True, + setting_properties=setting_properties, + check_frozen=check_frozen) + except PropertiesOptionError as mastervalue: + mastervalue.set_orig_opt(opt) + raise mastervalue masterlen = len(mastervalue) #self._master_is_meta = values._is_meta(master, masterp, force_permissive=force_permissive) multi = list() # values._get_multi(opt, path) @@ -797,27 +842,27 @@ class MasterSlaves(OptionDescription): else: indexes = [index] for idx in indexes: - value = values.get_cached_value(opt, path, validate, - force_permissive, - trusted_cached_properties, - validate_properties, - index=idx, - # not self_properties, - # depends to index - #self_properties=self_properties, - setting_properties=setting_properties, - from_masterslave=True, - check_frozen=check_frozen) - if isinstance(value, Exception): - if isinstance(value, PropertiesOptionError): - err = value - if index is None: - multi.append(value) - else: - multi = value + try: + value = values.get_cached_value(opt, + path, + validate, + force_permissive, + trusted_cached_properties, + validate_properties, + index=idx, + # not self_properties, + # depends to index + #self_properties=self_properties, + setting_properties=setting_properties, + from_masterslave=True, + check_frozen=check_frozen) + except PropertiesOptionError as perr: + err = perr + if index is None: + multi.append(err) else: - return value - elif index is None: + multi = err + if index is None: multi.append(value) else: multi = value @@ -861,14 +906,16 @@ class MasterSlaves(OptionDescription): master = self.getmaster(None) if masterp is None: masterp = master.impl_getpath(values._getcontext()) - value = self.getitem(values, - master, - masterp, - validate, - force_permissive, - None, - True, - setting_properties=setting_properties) + value = self._getmaster(values, + master, + masterp, + validate, + force_permissive, + validate, + undefined, + None, + setting_properties, + False) if isinstance(value, Exception): return value return len(value) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index e68d866..88a1d0c 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -575,24 +575,29 @@ class Settings(object): else: opt_type = 'option' if 'frozen' in properties: - return PropertiesOptionError(_('cannot change the value for ' - 'option "{0}" this option is' - ' frozen').format( - opt_or_descr.impl_getname()), - props, self, datas, opt_type) + 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') - return 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) + 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): """ @@ -728,37 +733,36 @@ class Settings(object): idx = index else: idx = None - value = context.getattr(reqpath, force_permissive=True, - _setting_properties=setting_properties, - index=idx, returns_raise=True) - if isinstance(value, Exception): - if isinstance(value, PropertiesOptionError): - 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]) - if not set(value.proptype) - set(all_properties): - continue - properties = value.proptype - if same_action and action not in properties: # pragma: optional cover - if len(properties) == 1: - prop_msg = _('property') - else: - prop_msg = _('properties') - raise RequirementError(_('cannot access to option "{0}" because ' - 'required option "{1}" has {2} {3}' - '').format(opt.impl_get_display_name(), - option.impl_get_display_name(), - prop_msg, - display_list(properties))) - orig_value = value - # transitive action, force expected - value = expected[0] - inverse = False - else: # pragma: no cover - raise value + try: + value = context.getattr(reqpath, + 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]) + if not set(err.proptype) - set(all_properties): + continue + properties = err.proptype + if same_action and action not in properties: # pragma: optional cover + if len(properties) == 1: + prop_msg = _('property') + else: + prop_msg = _('properties') + raise RequirementError(_('cannot access to option "{0}" because ' + 'required option "{1}" has {2} {3}' + '').format(opt.impl_get_display_name(), + option.impl_get_display_name(), + prop_msg, + display_list(properties))) + orig_value = err + # transitive action, force expected + value = expected[0] + inverse = False else: orig_value = value if (not inverse and value in expected or diff --git a/tiramisu/value.py b/tiramisu/value.py index 5b4d807..5773409 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -93,15 +93,15 @@ class Values(object): meta = self._getcontext().cfgimpl_get_meta() if meta is not None: # retrieved value from meta config - value = meta.cfgimpl_get_values().get_cached_value(opt, - path, - index=index, - _orig_context=_orig_context) - if isinstance(value, Exception): + try: + value = meta.cfgimpl_get_values().get_cached_value(opt, + path, + index=index, + _orig_context=_orig_context) + except PropertiesOptionError: # if properties error, return an other default value - if not isinstance(value, PropertiesOptionError): # pragma: no cover - # unexpected error, should not happened - raise value + # unexpected error, should not happened + pass else: return value @@ -476,28 +476,26 @@ class Values(object): context = self._getcontext() setting = context.cfgimpl_get_settings() config_error = None - value = self._getvalue(opt, - path, - self_properties, - index, - validate, - _orig_context) - if isinstance(value, Exception): + try: + value = self._getvalue(opt, + path, + self_properties, + index, + validate, + _orig_context) + except ConfigError as value: value_error = True - if isinstance(value, ConfigError): - # 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: # pragma: no cover - raise value + # 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: @@ -807,6 +805,7 @@ class Values(object): setting_properties = context.cfgimpl_get_settings()._getproperties() setting_properties.update(['mandatory', 'empty']) def _is_properties_option(err, path): + #FIXME hum ... if not isinstance(err, Exception): pass elif isinstance(err, PropertiesOptionError): @@ -871,6 +870,7 @@ class Values(object): context.cfgimpl_reset_cache() for path in context.cfgimpl_get_description().impl_getpaths( include_groups=True): - err = context.getattr(path, returns_raise=True) - if isinstance(err, Exception) and not isinstance(err, PropertiesOptionError): # pragma: no cover - raise err + try: + err = context.getattr(path) + except PropertiesOptionError as err: + pass