This commit is contained in:
Emmanuel Garette 2017-11-20 17:01:36 +01:00
parent 5d1be8a11a
commit 119ca85041
11 changed files with 1141 additions and 1199 deletions

View file

@ -220,16 +220,12 @@ def _autocheck_set_value(api, path, **kwargs):
submulti_ = api.unrestraint.option(path).option.issubmulti() submulti_ = api.unrestraint.option(path).option.issubmulti()
ismaster = api.unrestraint.option(path).option.ismaster() ismaster = api.unrestraint.option(path).option.ismaster()
isslave = api.unrestraint.option(path).option.isslave() isslave = api.unrestraint.option(path).option.isslave()
# empty_value = _getdefault(api, path, multi, isslave, submulti_)
if not multi: if not multi:
first_value = FIRST_VALUE first_value = FIRST_VALUE
# second_value = SECOND_VALUE
elif submulti_ is False: elif submulti_ is False:
first_value = LIST_FIRST_VALUE first_value = LIST_FIRST_VALUE
# second_value = LIST_SECOND_VALUE
else: else:
first_value = SUBLIST_FIRST_VALUE first_value = SUBLIST_FIRST_VALUE
# second_value = SUBLIST_SECOND_VALUE
# for slave should have an index and good length # for slave should have an index and good length
# for master must append, not set # for master must append, not set
@ -509,7 +505,7 @@ def _getproperties(multi, isslave, kwargs):
if extra_properties: if extra_properties:
properties.extend(extra_properties) properties.extend(extra_properties)
default_props.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): 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() multi = api.unrestraint.option(path).option.ismulti()
isslave = api.unrestraint.option(path).option.isslave() 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) default_props, properties = _getproperties(multi, isslave, kwargs)
_check_default_properties(api, path, kwargs, default_props, default_props) _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: if DISPLAY:
text = u' {} launch tests for {}'.format(ICON, path) text = u' {} launch tests for {}'.format(ICON, path)
if multi is True: 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(): if api.unrestraint.option(path).option.isslave():
master_path = path.rsplit('.', 1)[0] + '.master' master_path = path.rsplit('.', 1)[0] + '.master'
api.option(master_path).value.set(LIST_SECOND_VALUE) api.option(master_path).value.set(LIST_SECOND_VALUE)
for func in autocheck_registers: if not require:
api = getapi(cfg.duplicate()) requires = [False]
if DISPLAY: else:
print(u' {} {}'.format(ICON, func.__name__)) requires = [False, True]
try: for req in requires:
func(api, path, **kwargs) for func in autocheck_registers:
except Exception as err: api = getapi(cfg.duplicate())
msg = u'error in function {} for {}'.format(func.__name__, path) #FIXME devrait etre dans la config ca ...
if multi is True: api.read_write()
msg += u' as a multi' ckwargs = copy(kwargs)
elif multi is submulti: if req:
msg += u' as a submulti' api.option('extraoptrequire').value.set('value')
if multi is True: if 'permissive' in ckwargs and not 'permissive_od' in ckwargs or \
msg += u' with default value' 'propertyerror' in ckwargs and not 'propertyerror_od' in ckwargs:
print(u'{}: {}'.format(msg, kwargs)) for to_del in ['permissive', 'propertyerror', 'extra_properties']:
raise err 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): def check_deref(weakrefs):
@ -907,23 +907,32 @@ def check_deref(weakrefs):
assert wrf() is None 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 = [] weakrefs = []
dyn = []
goptions = []
def make_option(path, option_infos): def make_option(path, option_infos):
#FIXME #FIXME
option_type = 'str' option_type = 'str'
option_properties = [] option_properties = []
option_requires = []
isslave = False isslave = False
if option_infos is not None: if option_infos is not None:
for prop in PROPERTIES: for prop in PROPERTIES:
if option_infos.get(prop, False) is True: 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) isslave = option_infos.get('slave', False)
args = [path, "{}'s option".format(path)] args = [path, "{}'s option".format(path)]
kwargs = {} kwargs = {}
if option_properties != []: if option_properties != []:
kwargs['properties'] = tuple(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 kwargs['multi'] = multi
if default and not submulti: if default and not submulti:
if multi is False: if multi is False:
@ -933,7 +942,7 @@ def make_conf(options, meta, multi, default, default_multi):
else: else:
value = SUBLIST_EMPTY_VALUE value = SUBLIST_EMPTY_VALUE
kwargs['default'] = value kwargs['default'] = value
if default_multi: if default_multi and path is not 'extraoptrequire':
if multi is not submulti: if multi is not submulti:
value = SECOND_VALUE value = SECOND_VALUE
else: else:
@ -942,6 +951,12 @@ def make_conf(options, meta, multi, default, default_multi):
tiramisu_option = OPTIONS_TYPE[option_type]['option'] tiramisu_option = OPTIONS_TYPE[option_type]['option']
obj = tiramisu_option(*args, **kwargs) 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)) weakrefs.append(weakref.ref(obj))
return obj return obj
@ -961,6 +976,7 @@ def make_conf(options, meta, multi, default, default_multi):
if infos.get('dyn', False) is True: if infos.get('dyn', False) is True:
optiondescription = DynOptionDescription optiondescription = DynOptionDescription
kwargs['callback'] = return_list kwargs['callback'] = return_list
dyn.append(path)
options = [] options = []
if 'options' in collected: if 'options' in collected:
options.extend(collected['options']) options.extend(collected['options'])
@ -978,7 +994,22 @@ def make_conf(options, meta, multi, default, default_multi):
return obj return obj
collect_options = {} 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: if option is None:
continue continue
local_collect_options = collect_options 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 = local_collect_options[optiondescription]
local_collect_options['properties'].update(option.get(optiondescription, {})) local_collect_options['properties'].update(option.get(optiondescription, {}))
option_name = path.split('.')[-1] option_name = path.split('.')[-1]
path = '.'.join(path.split('.')[:-1]) obj = make_option(option_name, option.get(option_name))
local_collect_options.setdefault('options', []).append(make_option(option_name, option.get(option_name))) goptions.append(obj)
local_collect_options.setdefault('options', []).append(obj)
rootod = make_optiondescriptions('root', collect_options) rootod = make_optiondescriptions('root', collect_options)
if rootod is None: if rootod is None:
@ -998,7 +1030,8 @@ def make_conf(options, meta, multi, default, default_multi):
if meta: if meta:
cfg = MetaConfig([cfg], session_id='metatest') cfg = MetaConfig([cfg], session_id='metatest')
weakrefs.append(weakref.ref(cfg)) weakrefs.append(weakref.ref(cfg))
return cfg, weakrefs del goptions
return cfg, weakrefs, dyn
DICT_PATHS = [ DICT_PATHS = [
@ -1091,24 +1124,35 @@ def test_options(paths):
return kwargs return kwargs
lpaths = list(paths.keys()) lpaths = list(paths.keys())
for meta in (False, True): meta = False
for default_multi in (False, True): #for meta in (False, True):
for default in (False, True): for consistency in (False, True):
for multi in (False, True, submulti): for require in (False, True):
if multi is False and default_multi: for default_multi in (False, True):
continue for default in (False, True):
cfg, weakrefs = make_conf(paths, meta, multi, default, default_multi) for multi in (False, True, submulti):
if cfg is None: if multi is submulti and consistency:
continue continue
if len(lpaths) == 9: if multi is False and default_multi:
check_all(cfg, lpaths[3], meta, multi, default, default_multi, **get_kwargs(lpaths[0])) continue
check_all(cfg, lpaths[4], meta, multi, default, default_multi, **get_kwargs(lpaths[1])) cfg, weakrefs, dyn = make_conf(paths, meta, multi, default, default_multi, require, consistency)
check_all(cfg, lpaths[5], meta, multi, default, default_multi, **get_kwargs(lpaths[2])) if cfg is None:
check_all(cfg, lpaths[6], meta, multi, default, default_multi, **get_kwargs(lpaths[0])) continue
check_all(cfg, lpaths[7], meta, multi, default, default_multi, **get_kwargs(lpaths[1])) if dyn:
check_all(cfg, lpaths[8], meta, multi, default, default_multi, **get_kwargs(lpaths[2])) cnt = 0
else: idx = 0
for lpath in lpaths: for index, lpath in enumerate(lpaths):
check_all(cfg, lpath, meta, multi, default, default_multi, **get_kwargs(lpath)) if paths[lpath]:
del cfg cnt += 1
check_deref(weakrefs) 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)

View file

@ -145,6 +145,7 @@ class TiramisuOptionOwner(CommonTiramisuOption):
setting_properties, setting_properties,
force_permissive, force_permissive,
force_unrestraint): force_unrestraint):
super(TiramisuOptionOwner, self).__init__(opt, super(TiramisuOptionOwner, self).__init__(opt,
path, path,
index, index,
@ -158,12 +159,16 @@ class TiramisuOptionOwner(CommonTiramisuOption):
def get(self): def get(self):
"""get owner for a specified option""" """get owner for a specified option"""
return self.values.getowner(self.opt, return self.values.getowner(self.opt,
self.path,
self.setting_properties,
index=self.index, index=self.index,
force_permissive=self.force_permissive) force_permissive=self.force_permissive)
def isdefault(self): def isdefault(self):
"""is option has defaut value""" """is option has defaut value"""
return self.values.is_default_owner(self.opt, return self.values.is_default_owner(self.opt,
self.path,
self.setting_properties,
index=self.index, index=self.index,
force_permissive=self.force_permissive) force_permissive=self.force_permissive)
@ -174,10 +179,9 @@ class TiramisuOptionOwner(CommonTiramisuOption):
except AttributeError: except AttributeError:
owners.addowner(owner) owners.addowner(owner)
obj_owner = getattr(owners, owner) obj_owner = getattr(owners, owner)
self.values.setowner(self.opt, self.values.setowner(self.path,
obj_owner, obj_owner,
self.index, self.index)
force_permissive=self.force_permissive)
class TiramisuOptionProperty(CommonTiramisuOption): class TiramisuOptionProperty(CommonTiramisuOption):
@ -207,14 +211,14 @@ class TiramisuOptionProperty(CommonTiramisuOption):
self._test_slave_index() self._test_slave_index()
return self.settings.getproperties(self.opt, return self.settings.getproperties(self.opt,
self.path, self.path,
index=self.index, self.setting_properties,
obj=False) index=self.index)
def set(self, properties): def set(self, properties):
"""set properties for a specified option""" """set properties for a specified option"""
self.settings.setproperties(set(properties), self.settings.setproperties(self.opt,
self.opt, self.path,
self.path) properties)
def reset(self): def reset(self):
"""reset all personalised properties """reset all personalised properties
@ -247,16 +251,17 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
def get(self): def get(self):
"""get permissive value for a specified path""" """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): 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): def reset(self, path):
# """reset all personalised properties """reset all personalised permissive
# """ """
# self._get_obj_by_path(path) self.set(tuple())
# self.settings.reset(_path=path)
class TiramisuOptionValue(CommonTiramisuOption): class TiramisuOptionValue(CommonTiramisuOption):
@ -409,11 +414,6 @@ class TiramisuAPI(object):
self.config = config self.config = config
self.force_permissive = force_permissive self.force_permissive = force_permissive
self.force_unrestraint = force_unrestraint 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): def option(self, path, index=None):
validate = not self.force_unrestraint validate = not self.force_unrestraint
@ -464,6 +464,16 @@ class TiramisuAPI(object):
txt.append(module(None, None).help) txt.append(module(None, None).help)
return '\n'.join(txt) 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): def getapi(config):
"""instanciate TiramisuAPI """instanciate TiramisuAPI

View file

@ -90,31 +90,39 @@ class SubConfig(object):
def cfgimpl_get_length(self): def cfgimpl_get_length(self):
return self._impl_length return self._impl_length
def reset_one_option_cache(self, values, settings, resetted_opts, opt, only): def reset_one_option_cache(self,
if 'values' in only: values,
tresetted_opts = copy(resetted_opts) settings,
opt.reset_cache(opt, values, 'values', tresetted_opts) resetted_opts,
opt,
if 'settings' in only: path):
tresetted_opts = copy(resetted_opts) tresetted_opts = copy(resetted_opts)
opt.reset_cache(opt, settings, 'settings', tresetted_opts) opt.reset_cache(opt,
else: path,
if 'properties' in only: values,
tresetted_opts = copy(resetted_opts) 'values',
opt.reset_cache(opt, settings, 'properties', tresetted_opts) tresetted_opts)
if 'permissives' in only: tresetted_opts = copy(resetted_opts)
tresetted_opts = copy(resetted_opts) opt.reset_cache(opt,
opt.reset_cache(opt, settings, 'permissives', tresetted_opts) path,
settings,
'settings',
tresetted_opts)
resetted_opts |= 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: if option in resetted_opts:
continue continue
self.reset_one_option_cache(values, settings, resetted_opts, option(), only) option_path = opt.impl_getpath(self)
del(option) self.reset_one_option_cache(values,
settings,
resetted_opts,
option,
option_path)
del option
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, only_expired=False,
only=('values', 'properties', 'permissives', 'settings'),
opt=None, opt=None,
path=None, path=None,
resetted_opts=None): resetted_opts=None):
@ -127,33 +135,19 @@ class SubConfig(object):
def reset_expired_cache(): def reset_expired_cache():
# reset cache for expired cache value ony # reset cache for expired cache value ony
datetime = int(time()) datetime = int(time())
if 'values' in only: values._p_.reset_expired_cache(datetime)
values._p_.reset_expired_cache(datetime) settings._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)
def reset_all_cache(): def reset_all_cache():
if 'values' in only: values._p_.reset_all_cache()
values._p_.reset_all_cache() settings._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()
if resetted_opts is None: if resetted_opts is None:
resetted_opts = set() resetted_opts = set()
context = self._cfgimpl_get_context() context = self._cfgimpl_get_context()
if 'values' in only: values = context.cfgimpl_get_values()
values = context.cfgimpl_get_values() settings = context.cfgimpl_get_settings()
if 'settings' in only or 'properties' in only or 'permissives' in only:
settings = context.cfgimpl_get_settings()
if not None in (opt, path): if not None in (opt, path):
if opt not in resetted_opts: if opt not in resetted_opts:
@ -161,7 +155,7 @@ class SubConfig(object):
settings, settings,
resetted_opts, resetted_opts,
opt, opt,
only) path)
elif only_expired: elif only_expired:
reset_expired_cache() reset_expired_cache()
@ -308,7 +302,6 @@ class SubConfig(object):
name, name,
value, value,
force_permissive=False, force_permissive=False,
not_raises=False,
index=None, index=None,
setting_properties=undefined, setting_properties=undefined,
_commit=True): _commit=True):
@ -319,7 +312,7 @@ class SubConfig(object):
value) value)
context = self._cfgimpl_get_context() context = self._cfgimpl_get_context()
if setting_properties is undefined: 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 if '.' in name: # pragma: optional cover
self, name = self.cfgimpl_get_home_by_path(name, self, name = self.cfgimpl_get_home_by_path(name,
force_permissive=force_permissive, force_permissive=force_permissive,
@ -332,21 +325,17 @@ class SubConfig(object):
not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover
raise TypeError(_("can't assign to a SymlinkOption")) raise TypeError(_("can't assign to a SymlinkOption"))
else: else:
msg = self.cfgimpl_get_description().impl_validate_value(child, value, self) self.cfgimpl_get_description().impl_validate_value(child,
if msg is not None: value,
if not_raises: self)
return msg
else:
raise msg
subpath = self._get_subpath(name) subpath = self._get_subpath(name)
return self.cfgimpl_get_values().setitem(child, return self.cfgimpl_get_values().setvalue(child,
value, value,
subpath, subpath,
force_permissive, force_permissive,
not_raises, index,
index, setting_properties,
setting_properties, _commit)
_commit)
def __delattr__(self, name): def __delattr__(self, name):
self.delattr(name) self.delattr(name)
@ -467,12 +456,12 @@ class SubConfig(object):
index=index) index=index)
elif option.impl_is_optiondescription(): elif option.impl_is_optiondescription():
if setting_properties: if setting_properties:
props = self.cfgimpl_get_settings().validate_properties(option, self.cfgimpl_get_settings().validate_properties(option,
True, True,
False, False,
path=subpath, path=subpath,
force_permissive=force_permissive, force_permissive=force_permissive,
setting_properties=setting_properties) setting_properties=setting_properties)
if returns_option is True: if returns_option is True:
return option return option
return SubConfig(option, return SubConfig(option,
@ -618,7 +607,7 @@ class SubConfig(object):
fullpath=False): fullpath=False):
"""exports the whole config into a `dict`, for example: """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} {'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) :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} {'var5': None, 'var4': None, 'var6': None}
:param withoption: returns the options that are present in the very same :param withoption: returns the options that are present in the very same
`OptionDescription` than the `withoption` itself:: `OptionDescription` than the `withoption` itself::
>>> print cfg.make_dict(withoption='var1') >>> print(cfg.make_dict(withoption='var1'))
{'od2.var4': None, 'od2.var5': None, {'od2.var4': None, 'od2.var5': None,
'od2.var6': None, 'od2.var6': None,
'od2.var1': u'value', 'od2.var1': u'value',
@ -643,8 +632,8 @@ class SubConfig(object):
:param withvalue: returns the options that have the value `withvalue` :param withvalue: returns the options that have the value `withvalue`
:: ::
>>> print c.make_dict(withoption='var1', >>> print(c.make_dict(withoption='var1',
withvalue=u'value') withvalue=u'value'))
{'od2.var4': None, {'od2.var4': None,
'od2.var5': None, 'od2.var5': None,
'od2.var6': None, 'od2.var6': None,
@ -798,14 +787,12 @@ class _CommonConfig(SubConfig):
else: else:
if index is None and option.impl_is_master_slaves('slave'): if index is None and option.impl_is_master_slaves('slave'):
subpath = self._get_subpath(path) subpath = self._get_subpath(path)
props = self.cfgimpl_get_settings().validate_properties(option, self.cfgimpl_get_settings().validate_properties(option,
True, True,
False, False,
path=subpath, path=subpath,
force_permissive=force_permissive, force_permissive=force_permissive,
setting_properties=setting_properties) setting_properties=setting_properties)
if props:
raise props
return option return option
else: else:
return self.getattr(path, return self.getattr(path,
@ -975,19 +962,16 @@ class GroupConfig(_CommonConfig):
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, only_expired=False,
only=('values', 'settings'),
opt=None, opt=None,
path=None, path=None,
resetted_opts=set()): resetted_opts=set()):
if isinstance(self, MetaConfig): if isinstance(self, MetaConfig):
super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired,
only=only,
opt=opt, opt=opt,
path=path, path=path,
resetted_opts=copy(resetted_opts)) resetted_opts=copy(resetted_opts))
for child in self._impl_children: for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, child.cfgimpl_reset_cache(only_expired=only_expired,
only=only,
opt=opt, opt=opt,
path=path, path=path,
resetted_opts=copy(resetted_opts)) resetted_opts=copy(resetted_opts))

View file

@ -155,7 +155,7 @@ class ValueWarning(UserWarning): # pragma: optional cover
... ...
>>> w[0].message.opt == s >>> w[0].message.opt == s
True True
>>> print str(w[0].message) >>> print(str(w[0].message))
invalid value val for option s: pouet invalid value val for option s: pouet
""" """
def __init__(self, msg, opt): def __init__(self, msg, opt):

View file

@ -32,7 +32,7 @@ if sys.version_info[0] >= 3: # pragma: no cover
else: else:
from inspect import getargspec from inspect import getargspec
STATIC_TUPLE = tuple() STATIC_TUPLE = frozenset()
submulti = 2 submulti = 2
@ -145,6 +145,8 @@ class Base(object):
requires = undefined requires = undefined
if properties is None: if properties is None:
properties = tuple() properties = tuple()
if is_multi and 'empty' not in properties:
properties = tuple(list(properties) + ['empty'])
if not isinstance(properties, tuple): if not isinstance(properties, tuple):
raise TypeError(_('invalid properties type {0} for {1},' raise TypeError(_('invalid properties type {0} for {1},'
' must be a tuple').format( ' must be a tuple').format(
@ -406,11 +408,15 @@ class BaseOption(Base):
name = name.encode('utf8') name = name.encode('utf8')
return name 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: if opt in resetted_opts:
return return
if not type_ == 'values' or not opt.impl_is_optiondescription(): if not type_ == 'values' or not opt.impl_is_optiondescription():
path = opt.impl_getpath(obj._getcontext())
if type_ != 'permissives': if type_ != 'permissives':
obj._p_.delcache(path) obj._p_.delcache(path)
if type_ in ['settings', 'permissives']: if type_ in ['settings', 'permissives']:

View file

@ -21,6 +21,7 @@
# ____________________________________________________________ # ____________________________________________________________
import warnings import warnings
import sys import sys
import weakref
from .baseoption import OnlyOption, submulti, DynSymLinkOption, validate_callback, STATIC_TUPLE from .baseoption import OnlyOption, submulti, DynSymLinkOption, validate_callback, STATIC_TUPLE
from ..i18n import _ from ..i18n import _
@ -146,9 +147,17 @@ class Option(OnlyOption):
def impl_is_multi(self): def impl_is_multi(self):
return getattr(self, '_multi', 1) != 1 return getattr(self, '_multi', 1) != 1
def _launch_consistency(self, current_opt, func, option, value, context, def _launch_consistency(self,
index, submulti_index, opts, warnings_only, current_opt,
transitive): func,
option,
value,
context,
index,
opts,
warnings_only,
transitive,
setting_properties):
"""Launch consistency now """Launch consistency now
:param func: function name, this name should start with _cons_ :param func: function name, this name should start with _cons_
@ -174,7 +183,8 @@ class Option(OnlyOption):
all_cons_vals = [] all_cons_vals = []
all_cons_opts = [] all_cons_opts = []
val_consistencies = True val_consistencies = True
for opt in opts: for wopt in opts:
opt = wopt()
if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \ if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
option == opt: option == opt:
# option is current option # option is current option
@ -195,7 +205,9 @@ class Option(OnlyOption):
else: else:
_index = index _index = index
try: try:
opt_value = context.getattr(path, validate=False, opt_value = context.getattr(path,
setting_properties,
validate=False,
index=_index, index=_index,
force_permissive=True) force_permissive=True)
except PropertiesOptionError as err: except PropertiesOptionError as err:
@ -254,7 +266,6 @@ class Option(OnlyOption):
context=undefined, context=undefined,
validate=True, validate=True,
force_index=None, force_index=None,
force_submulti_index=None,
current_opt=undefined, current_opt=undefined,
is_multi=None, is_multi=None,
display_error=True, display_error=True,
@ -270,9 +281,6 @@ class Option(OnlyOption):
:param force_index: if multi, value has to be a list :param force_index: if multi, value has to be a list
not if force_index is not None not if force_index is not None
:type force_index: integer :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: if not validate:
return return
@ -287,10 +295,12 @@ class Option(OnlyOption):
if display_error and self.impl_is_unique() and len(set(value)) != len(value): if display_error and self.impl_is_unique() and len(set(value)) != len(value):
for idx, val in enumerate(value): for idx, val in enumerate(value):
if val in value[idx+1:]: if val in value[idx+1:]:
return ValueError(_('invalid value "{}", this value is already in "{}"').format( return ValueError(_('invalid value "{}", this value is already in "{}"'
val, self.impl_get_display_name())) '').format(val,
self.impl_get_display_name()))
def calculation_validator(val, _index): def calculation_validator(val,
_index):
validator, validator_params = self.impl_get_validator() validator, validator_params = self.impl_get_validator()
if validator is not None: if validator is not None:
if validator_params != {}: if validator_params != {}:
@ -316,9 +326,10 @@ class Option(OnlyOption):
if isinstance(value, Exception): if isinstance(value, Exception):
return value return value
def do_validation(_value, _index, submulti_index): def do_validation(_value,
_index):
if _value is None: if _value is None:
error = warning = None error = None
else: else:
if display_error: if display_error:
# option validation # option validation
@ -327,35 +338,41 @@ class Option(OnlyOption):
current_opt) current_opt)
if err: if err:
if debug: # pragma: no cover if debug: # pragma: no cover
log.debug('do_validation: value: {0}, index: {1}, ' log.debug('do_validation: value: {0}, index: {1}:'
'submulti_index: {2}'.format(_value, ' {2}'.format(_value,
_index, _index),
submulti_index),
exc_info=True) exc_info=True)
err_msg = '{0}'.format(err) err_msg = '{0}'.format(err)
if err_msg: if err_msg:
msg = _('"{0}" is an invalid {1} for "{2}", {3}' 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) self.impl_get_display_name(), err_msg)
else: else:
msg = _('"{0}" is an invalid {1} for "{2}"' msg = _('"{0}" is an invalid {1} for "{2}"'
'').format(_value, self._display_name, '').format(_value,
self._display_name,
self.impl_get_display_name()) self.impl_get_display_name())
return ValueError(msg) return ValueError(msg)
error = None error = None
is_warnings_only = getattr(self, '_warnings_only', False) is_warnings_only = getattr(self, '_warnings_only', False)
if ((display_error and not is_warnings_only) or if ((display_error and not is_warnings_only) or
(display_warnings and is_warnings_only)): (display_warnings and is_warnings_only)):
error = calculation_validator(_value, _index) error = calculation_validator(_value,
_index)
if not error: if not error:
error = self._second_level_validation(_value, is_warnings_only) error = self._second_level_validation(_value,
is_warnings_only)
if error: if error:
if debug: # pragma: no cover if debug: # pragma: no cover
log.debug(_('do_validation for {0}: error in value').format( log.debug(_('do_validation for {0}: error in value').format(
self.impl_getname()), exc_info=True) self.impl_getname()), exc_info=True)
if is_warnings_only: if is_warnings_only:
msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format( msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}'
_value, self._display_name, self.impl_get_display_name(), error) '').format(_value,
self._display_name,
self.impl_get_display_name(),
error)
warnings.warn_explicit(ValueWarning(msg, self), warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning, ValueWarning,
self.__class__.__name__, 0) self.__class__.__name__, 0)
@ -367,9 +384,9 @@ class Option(OnlyOption):
_value, _value,
context, context,
_index, _index,
submulti_index,
display_warnings, display_warnings,
display_error) display_error,
setting_properties)
if isinstance(ret, ValueError): if isinstance(ret, ValueError):
error = ret error = ret
elif ret: elif ret:
@ -390,22 +407,22 @@ class Option(OnlyOption):
is_multi = self.impl_is_multi() is_multi = self.impl_is_multi()
if not is_multi: if not is_multi:
return do_validation(value, None, None) return do_validation(value, None)
elif force_index is not 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) err = _is_not_unique(value)
if err: if err:
return err return err
if not isinstance(value, list): if not isinstance(value, list):
return ValueError(_('invalid value "{0}" for "{1}" which' return ValueError(_('invalid value "{0}" for "{1}" which'
' must be a list').format( ' must be a list').format(
value, self.impl_get_display_name())) value, self.impl_get_display_name()))
for idx, val in enumerate(value): for idx, val in enumerate(value):
if isinstance(val, list): # pragma: no cover if isinstance(val, list): # pragma: no cover
return ValueError(_('invalid value "{}" for "{}" ' return ValueError(_('invalid value "{}" for "{}" '
'which must not be a list').format(val, 'which must not be a list').format(val,
self.impl_get_display_name())) self.impl_get_display_name()))
err = do_validation(val, force_index, idx) err = do_validation(val, force_index)
if err: if err:
return err return err
else: else:
@ -419,12 +436,12 @@ class Option(OnlyOption):
return ValueError(_('invalid value "{}", this value is already' return ValueError(_('invalid value "{}", this value is already'
' in "{}"').format(value, ' in "{}"').format(value,
self.impl_get_display_name())) 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): elif not isinstance(value, list):
return ValueError(_('invalid value "{0}" for "{1}" which ' return ValueError(_('invalid value "{0}" for "{1}" which '
'must be a list').format(value, 'must be a list').format(value,
self.impl_getname())) 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): for idx, val in enumerate(value):
err = _is_not_unique(val) err = _is_not_unique(val)
if err: if err:
@ -434,8 +451,9 @@ class Option(OnlyOption):
'which must be a list of list' 'which must be a list of list'
'').format(val, '').format(val,
self.impl_getname())) self.impl_getname()))
for slave_idx, slave_val in enumerate(val): for slave_val in val:
err = do_validation(slave_val, idx, slave_idx) err = do_validation(slave_val,
idx)
if err: if err:
return err return err
else: else:
@ -443,16 +461,17 @@ class Option(OnlyOption):
if err: if err:
return err return err
for idx, val in enumerate(value): for idx, val in enumerate(value):
err = do_validation(val, idx, force_submulti_index) err = do_validation(val,
idx)
if err: if err:
return err return err
return self._valid_consistency(current_opt, return self._valid_consistency(current_opt,
None, None,
context, context,
None, None,
None,
display_warnings, display_warnings,
display_error) display_error,
setting_properties)
def impl_is_dynsymlinkoption(self): def impl_is_dynsymlinkoption(self):
return False return False
@ -480,7 +499,10 @@ class Option(OnlyOption):
"accesses the Option's doc" "accesses the Option's doc"
return self.impl_get_information('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(): if self._is_subdyn():
dynod = self._subdyn() dynod = self._subdyn()
else: else:
@ -488,7 +510,11 @@ class Option(OnlyOption):
if self.impl_is_submulti(): if self.impl_is_submulti():
raise ConfigError(_('cannot add consistency with submulti option')) raise ConfigError(_('cannot add consistency with submulti option'))
is_multi = self.impl_is_multi() 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(): if opt.impl_is_submulti():
raise ConfigError(_('cannot add consistency with submulti option')) raise ConfigError(_('cannot add consistency with submulti option'))
if not isinstance(opt, Option): if not isinstance(opt, Option):
@ -515,7 +541,10 @@ class Option(OnlyOption):
if func != 'not_equal': if func != 'not_equal':
opt._has_dependency = True 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 """Add consistency means that value will be validate with other_opts
option's values. option's values.
@ -525,39 +554,51 @@ class Option(OnlyOption):
:type other_opts: `list` of `tiramisu.option.Option` :type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (warnings_only and transitive are allowed) :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" raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
" read-only").format( " read-only").format(
self.__class__.__name__, self.__class__.__name__,
self.impl_getname())) self.impl_getname()))
self._valid_consistencies(other_opts, func=func) self._valid_consistencies(other_opts,
func=func)
func = '_cons_{0}'.format(func) func = '_cons_{0}'.format(func)
if func not in dir(self): if func not in dir(self):
raise ConfigError(_('consistency {0} not available for this option').format(func)) 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']) unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
if unknown_params != set(): if unknown_params != set():
raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params)) 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 #validate default value when add consistency
err = self.impl_validate(self.impl_getdefault()) err = self.impl_validate(self.impl_getdefault())
if err: if err:
self._del_consistency() self._del_consistency()
raise err 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': if func != '_cons_not_equal':
#consistency could generate warnings or errors #consistency could generate warnings or errors
self._has_dependency = True 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: if opt != self:
self._add_dependency(opt) self._add_dependency(opt)
opt._add_dependency(self) opt._add_dependency(self)
def _valid_consistency(self, option, value, context, index, submulti_idx, def _valid_consistency(self,
display_warnings, display_error): option,
value,
context,
index,
display_warnings,
display_error,
setting_properties):
if context is not undefined: if context is not undefined:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
if descr._cache_consistencies is None: if descr._cache_consistencies is None:
@ -586,10 +627,16 @@ class Option(OnlyOption):
opts.append(opt._impl_to_dyn(name, path)) opts.append(opt._impl_to_dyn(name, path))
else: else:
opts = all_cons_opts opts = all_cons_opts
err = opts[0]._launch_consistency(self, func, option, value, err = opts[0]()._launch_consistency(self,
context, index, submulti_idx, func,
opts, warnings_only, option,
transitive) value,
context,
index,
opts,
warnings_only,
transitive,
setting_properties)
if err: if err:
return err return err
@ -680,7 +727,10 @@ class Option(OnlyOption):
#____________________________________________________________ #____________________________________________________________
# consistency # 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) cons = (func, all_cons_opts, params)
consistencies = getattr(self, '_consistencies', None) consistencies = getattr(self, '_consistencies', None)
if consistencies is None: if consistencies is None:

View file

@ -125,8 +125,8 @@ class CacheOptionDescription(BaseOption):
'must be in same master/slaves for {1}').format( 'must be in same master/slaves for {1}').format(
require_opt.impl_getname(), option.impl_getname())) require_opt.impl_getname(), option.impl_getname()))
else: else:
raise ValueError(_('malformed requirements option {0} ' raise ValueError(_('malformed requirements option "{0}" '
'must not be a multi for {1}').format( 'must not be a multi for "{1}"').format(
require_opt.impl_getname(), option.impl_getname())) require_opt.impl_getname(), option.impl_getname()))
if init: if init:
if len(cache_option) != len(set(cache_option)): if len(cache_option) != len(set(cache_option)):
@ -137,7 +137,7 @@ class CacheOptionDescription(BaseOption):
if _consistencies != {}: if _consistencies != {}:
self._cache_consistencies = {} self._cache_consistencies = {}
for opt, cons in _consistencies.items(): 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} ' raise ConfigError(_('consistency with option {0} '
'which is not in Config').format( 'which is not in Config').format(
opt.impl_getname())) opt.impl_getname()))
@ -437,12 +437,12 @@ class OptionDescription(OptionDescriptionWalk):
for child in valid_child: for child in valid_child:
if child == old: # pragma: optional cover if child == old: # pragma: optional cover
raise ConflictError(_('duplicate option name: ' raise ConflictError(_('duplicate option name: '
'{0}').format(child)) '"{0}"').format(child))
if dynopt_names: if dynopt_names:
for dynopt in dynopt_names: for dynopt in dynopt_names:
if child != dynopt and child.startswith(dynopt): if child != dynopt and child.startswith(dynopt):
raise ConflictError(_('option must not start as ' raise ConflictError(_('the option\'s name "{}" start as '
'dynoptiondescription')) 'the dynoptiondescription\'s name "{}"').format(child, dynopt))
old = child old = child
_setattr = object.__setattr__ _setattr = object.__setattr__
_setattr(self, '_children', (tuple(child_names), tuple(children))) _setattr(self, '_children', (tuple(child_names), tuple(children)))
@ -623,7 +623,7 @@ class MasterSlaves(OptionDescription):
name)) name))
slaves.append(child) slaves.append(child)
child._add_dependency(self) child._add_dependency(self)
for child in children: for idx, child in enumerate(children):
if child._is_symlinkoption(): # pragma: optional cover if child._is_symlinkoption(): # pragma: optional cover
raise ValueError(_("master group {0} shall not have " raise ValueError(_("master group {0} shall not have "
"a symlinkoption").format(self.impl_getname())) "a symlinkoption").format(self.impl_getname()))
@ -635,6 +635,11 @@ class MasterSlaves(OptionDescription):
"in group {1}" "in group {1}"
": this option is not a multi" ": this option is not a multi"
"").format(child.impl_getname(), self.impl_getname())) "").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() callback, callback_params = master.impl_get_callback()
if callback is not None and callback_params != {}: if callback is not None and callback_params != {}:
for callbacks in callback_params.values(): for callbacks in callback_params.values():

View file

@ -106,7 +106,8 @@ rw_append = set(['frozen', 'disabled', 'validator', 'hidden'])
rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty']) 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') log = getLogger('tiramisu')
@ -124,15 +125,16 @@ class _NameSpace(object):
when attribute is added, we cannot delete it when attribute is added, we cannot delete it
""" """
def __setattr__(self, name, value): def __setattr__(self,
if name in self.__dict__: # pragma: optional cover name,
value):
if name in self.__dict__:
raise ConstError(_("can't rebind {0}").format(name)) raise ConstError(_("can't rebind {0}").format(name))
self.__dict__[name] = value self.__dict__[name] = value
def __delattr__(self, name): # pragma: optional cover def __delattr__(self,
if name in self.__dict__: name):
raise ConstError(_("can't unbind {0}").format(name)) raise ConstError(_("can't unbind {0}").format(name))
raise ValueError(name)
class GroupModule(_NameSpace): class GroupModule(_NameSpace):
@ -168,69 +170,44 @@ class OwnerModule(_NameSpace):
"""groups that are default (typically 'default')""" """groups that are default (typically 'default')"""
pass pass
def addowner(self, name):
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):
""" """
:param name: the name of the new owner :param name: the name of the new owner
""" """
setattr(owners, name, owners.Owner(name)) setattr(owners, name, owners.Owner(name))
setattr(owners, 'addowner', addowner)
# ____________________________________________________________ # ____________________________________________________________
# populate groups and owners with default attributes # populate groups
groups = GroupModule() 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() 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() 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): class Settings(object):
"``config.Config()``'s configuration options settings" "``config.Config()``'s configuration options settings"
@ -341,321 +251,89 @@ class Settings(object):
return context return context
#____________________________________________________________ #____________________________________________________________
# properties methods # get properties and permissive methods
def __contains__(self, propname):
"enables the pythonic 'in' syntaxic sugar"
return propname in self._getproperties(read_write=False)
def __repr__(self): def get_context_properties(self):
return str(list(self._getproperties(read_write=False))) ntime = int(time())
if self._p_.hascache(None,
def __getitem__(self, opt): None):
path = opt.impl_getpath(self._getcontext()) is_cached, props = self._p_.getcache(None,
return self.getproperties(opt, 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(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, def getproperties(self,
opt, opt,
path, path,
setting_properties=undefined, setting_properties,
index=None, index=None,
obj=True): apply_requires=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):
""" """
""" """
if opt is None: is_cached = False
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
if apply_requires: if apply_requires:
if 'cache' in setting_properties and 'expire' in setting_properties: if 'cache' in setting_properties and 'expire' in setting_properties:
ntime = int(time()) 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
else: else:
ntime = None 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
#____________________________________________________________ props -= self.getpermissive(path)
def setowner(self, owner): if apply_requires and 'cache' in setting_properties:
":param owner: sets the default value for owner at the Config level" if 'expire' in setting_properties:
if not isinstance(owner, owners.Owner): # pragma: optional cover ntime = ntime + expires_time
raise TypeError(_("invalid generic owner {0}").format(str(owner))) self._p_.setcache(path,
self._owner = owner props,
#FIXME qu'est ce qui se passe si pas de owner ?? ntime,
index)
return props
def getowner(self): def get_context_permissive(self):
return self._owner return self.getpermissive(None)
#____________________________________________________________ def getpermissive(self,
def _read(self, remove, append): path):
props = self._p_.getproperties(None, default_properties) return self._pp_.getpermissive(path)
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 read_only(self): def apply_requires(self,
"convenience method to freeze, hide and disable" opt,
self._read(ro_remove, ro_append) path,
setting_properties,
def read_write(self): index,
"convenience method to freeze, hide and disable" debug):
self._read(rw_remove, rw_append)
def apply_requires(self, opt, path, setting_properties, index, debug):
"""carries out the jit (just in time) requirements between options """carries out the jit (just in time) requirements between options
a requirement is a tuple of this form that comes from the option's a requirement is a tuple of this form that comes from the option's
@ -735,16 +413,16 @@ class Settings(object):
idx = None idx = None
try: try:
value = context.getattr(reqpath, value = context.getattr(reqpath,
setting_properties,
force_permissive=True, force_permissive=True,
_setting_properties=setting_properties,
index=idx) index=idx)
except PropertiesOptionError as err: except PropertiesOptionError as err:
if not transitive: if not transitive:
if all_properties is None: if all_properties is None:
all_properties = [] all_properties = []
for requires in opt.impl_getrequires(): for requires_ in opt.impl_getrequires():
for require in requires: for require_ in requires_:
all_properties.append(require[1]) all_properties.append(require_[1])
if not set(err.proptype) - set(all_properties): if not set(err.proptype) - set(all_properties):
continue continue
properties = err.proptype properties = err.proptype
@ -794,8 +472,228 @@ class Settings(object):
break break
return calc_properties 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): def get_modified_properties(self):
return self._p_.get_modified_properties() return self._p_.get_modified_properties()
def get_modified_permissives(self): def get_modified_permissives(self):
return self._pp_.get_modified_permissives() 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

View file

@ -34,7 +34,7 @@ class Properties(Cache):
self._properties[path] = properties self._properties[path] = properties
def getproperties(self, path, default_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): def reset_all_properties(self):
self._properties.clear() self._properties.clear()

View file

@ -195,7 +195,11 @@ class Values(Cache):
return 0 return 0
return max(self._values[1][idx]) + 1 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): with_value=False):
"""get owner for a path """get owner for a path
return: owner object return: owner object
@ -207,19 +211,28 @@ class Values(Cache):
else: else:
owner = default owner = default
else: else:
owner = self._getvalue(path, 3, index) owner = self._getvalue(path,
3,
index)
if owner is undefined: if owner is undefined:
owner = default owner = default
else: else:
owner = self._getvalue(path, 3, index) owner = self._getvalue(path,
3,
index)
if owner is undefined: if owner is undefined:
owner = default owner = default
if with_value: if with_value:
return owner, self._getvalue(path, 2, index) return owner, self._getvalue(path,
2,
index)
else: else:
return owner 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)) _values == ((path1, path2), ((idx1_1, idx1_2), None), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
""" """

File diff suppressed because it is too large Load diff