to_dict improvment and add display_name parameter to change impl_get_display_name function

This commit is contained in:
Emmanuel Garette 2019-07-26 08:54:01 +02:00
parent 35ef218c9c
commit 34d71901d0
9 changed files with 167 additions and 99 deletions

View File

@ -565,7 +565,7 @@ def test_mandatory_warnings_validate_empty():
cfg = Config(descr)
cfg.option('str').value.set('')
cfg.property.read_only()
assert list(cfg.value.mandatory()) == ['str', 'str1', 'str3', 'unicode1']
assert list(cfg.value.mandatory()) == ['str', 'str1', 'str3']
def test_mandatory_warnings_requires():

View File

@ -180,10 +180,17 @@ def test_consistency_not_equal_many_opts(config_type):
#
cfg.option('a').value.set(1)
raises(ValueError, "cfg.option('b').value.set(1)")
assert cfg.option('b').value.get() == None
#
cfg.option('b').value.set(2)
raises(ValueError, "cfg.option('f').value.set(2)")
assert cfg.option('f').value.get() is None
assert cfg.option('a').value.get() == 1
assert cfg.option('b').value.get() == 2
raises(ValueError, "cfg.option('f').value.set(1)")
assert cfg.option('f').value.get() is None
assert cfg.option('a').value.get() == 1
assert cfg.option('b').value.get() == 2
#
cfg.option('d').value.set(3)
raises(ValueError, "cfg.option('f').value.set(3)")

View File

@ -1,4 +1,4 @@
from .autopath import do_autopath
1rom .autopath import do_autopath
do_autopath()
from .config import config_type, get_config
@ -127,6 +127,14 @@ def test_validator(config_type):
cfg.option('opt2').value.set('val')
assert len(w) == 1
assert str(w[0].message) == msg
with warnings.catch_warnings(record=True) as w:
cfg.option('opt2').value.get()
assert len(w) == 1
assert str(w[0].message) == msg
with warnings.catch_warnings(record=True) as w:
cfg.option('opt2').value.get()
assert len(w) == 1
assert str(w[0].message) == msg
def test_validator_params(config_type):

View File

@ -19,7 +19,7 @@ from time import time
from typing import List, Set, Any, Optional, Callable, Union, Dict
from .error import APIError, ConfigError, LeadershipError, PropertiesOptionError
from .error import APIError, ConfigError, LeadershipError, PropertiesOptionError, ValueErrorWarning
from .i18n import _
from .setting import ConfigBag, OptionBag, owners, groups, Undefined, undefined, \
FORBIDDEN_SET_PROPERTIES, SPECIAL_PROPERTIES, EXPIRATION_TIME
@ -530,6 +530,17 @@ class _TiramisuOptionValueOption:
else:
return values.getdefaultvalue(self._option_bag)
def valid(self):
try:
with warnings.catch_warnings(record=True) as warns:
self.get()
for warn in warns:
if isinstance(warns.message, ValueErrorWarning):
return False
except ValueError:
return False
return True
class _TiramisuOptionValueLeader:
def pop(self, index):
@ -739,52 +750,54 @@ class _TiramisuOptionDescription(_TiramisuOption):
def _filter(self,
opt,
subconfig):
if self._config_bag.properties:
name = opt.impl_getname()
path = subconfig._get_subpath(name)
option_bag = OptionBag()
option_bag.set_option(opt,
path,
None,
self._config_bag)
if opt.impl_is_optiondescription():
self._subconfig.get_subconfig(option_bag)
else:
subconfig.getattr(name,
option_bag)
subconfig,
config_bag):
option_bag = OptionBag()
option_bag.set_option(opt,
opt.impl_getpath(),
None,
config_bag)
if opt.impl_is_optiondescription():
config_bag.context.cfgimpl_get_settings().validate_properties(option_bag)
return subconfig.get_subconfig(option_bag)
subconfig.getattr(opt.impl_getname(),
option_bag)
def list(self,
type='option',
group_type=None):
"""List options in an optiondescription (only for optiondescription)"""
"""List options (by default list only option)"""
assert type in ('all', 'option', 'optiondescription'), _('unknown list type {}').format(type)
assert group_type is None or isinstance(group_type, groups.GroupType), \
_("unknown group_type: {0}").format(group_type)
config_bag = self._config_bag
if config_bag.properties and 'warnings' in config_bag.properties:
config_bag = config_bag.copy()
config_bag.remove_warnings()
option = self._get_option()
name = option.impl_getname()
path = self._subconfig._get_subpath(name)
option_bag = OptionBag()
option_bag.set_option(option,
path,
option.impl_getpath(),
None,
self._config_bag)
config_bag)
subconfig = self._subconfig.get_subconfig(option_bag)
for opt in option.get_children(self._config_bag):
for opt in option.get_children(config_bag):
try:
self._filter(opt,
subconfig)
subconfig,
config_bag)
except PropertiesOptionError:
continue
if opt.impl_is_optiondescription():
if type == 'option' or (type == 'optiondescription' and group_type and \
opt.impl_get_group_type() != group_type):
if type == 'option' or (type == 'optiondescription' and \
group_type and opt.impl_get_group_type() != group_type):
continue
elif type == 'optiondescription':
continue
name = opt.impl_getname()
path = opt.impl_getpath()
yield TiramisuOption(name,
subconfig._get_subpath(name),
path,
None,
subconfig,
self._config_bag)
@ -792,9 +805,10 @@ class _TiramisuOptionDescription(_TiramisuOption):
def dict(self,
clearable: str="all",
remotable: str="minimum",
form: List=[]) -> Dict:
form: List=[],
force: bool=False) -> Dict:
"""convert config and option to tiramisu format"""
if self._tiramisu_dict is None:
if force or self._tiramisu_dict is None:
option = self._get_option()
name = option.impl_getname()
root = self._subconfig._get_subpath(name)
@ -822,9 +836,13 @@ class TiramisuOption(CommonTiramisuOption):
subconfig: Union[None, KernelConfig, SubConfig]=None,
config_bag: Optional[ConfigBag]=None) -> None:
if subconfig:
# not for groupconfig
if '.' in name:
subconfig, name = config_bag.context.cfgimpl_get_home_by_path(path,
config_bag)
option = subconfig.cfgimpl_get_description().get_child(name,
config_bag,
subconfig.cfgimpl_get_path())
config_bag,
subconfig.cfgimpl_get_path())
if option.impl_is_optiondescription():
return _TiramisuOptionDescription(name=name,
path=path,
@ -1219,13 +1237,12 @@ class TiramisuContextOption(TiramisuContext):
continue
if opt.impl_is_optiondescription():
if recursive:
for toption in self._walk(opt,
recursive,
type_,
group_type,
config_bag,
subsubconfig):
yield toption
yield from self._walk(opt,
recursive,
type_,
group_type,
config_bag,
subsubconfig)
if type_ == 'option' or (type_ == 'optiondescription' and \
group_type and opt.impl_get_group_type() != group_type):
continue
@ -1252,20 +1269,20 @@ class TiramisuContextOption(TiramisuContext):
config_bag = config_bag.copy()
config_bag.remove_warnings()
option = config_bag.context.cfgimpl_get_description()
for toption in self._walk(option,
recursive,
type,
group_type,
config_bag,
config_bag.context):
yield toption
yield from self._walk(option,
recursive,
type,
group_type,
config_bag,
config_bag.context)
def dict(self,
clearable="all",
remotable="minimum",
form=[]):
form=[],
force=False):
"""convert config and option to tiramisu format"""
if self._tiramisu_dict is None:
if force or self._tiramisu_dict is None:
self._tiramisu_dict = TiramisuDict(Config(self._config_bag.context),
root=None,
clearable=clearable,
@ -1499,14 +1516,16 @@ class Config(TiramisuAPI):
descr: OptionDescription,
session_id: str=None,
persistent: bool=False,
storage=None) -> None:
storage=None,
display_name=None) -> None:
if isinstance(descr, KernelConfig):
config = descr
else:
config = KernelConfig(descr,
session_id=session_id,
persistent=persistent,
storage=storage)
storage=storage,
display_name=display_name)
super().__init__(config)
@ -1516,7 +1535,8 @@ class MetaConfig(TiramisuAPI):
children,
session_id: Union[str, None]=None,
persistent: bool=False,
optiondescription: Optional[OptionDescription]=None) -> None:
optiondescription: Optional[OptionDescription]=None,
display_name=None) -> None:
if isinstance(children, KernelMetaConfig):
config = children
else:
@ -1530,7 +1550,8 @@ class MetaConfig(TiramisuAPI):
config = KernelMetaConfig(_children,
session_id=session_id,
persistent=persistent,
optiondescription=optiondescription)
optiondescription=optiondescription,
display_name=display_name)
super().__init__(config)
@ -1540,7 +1561,8 @@ class MixConfig(TiramisuAPI):
optiondescription: OptionDescription,
children: List[Config],
session_id: Optional[str]=None,
persistent: bool=False) -> None:
persistent: bool=False,
display_name: Callable=None) -> None:
if isinstance(children, KernelMixConfig):
config = children
else:
@ -1554,7 +1576,8 @@ class MixConfig(TiramisuAPI):
config = KernelMixConfig(optiondescription,
_children,
session_id=session_id,
persistent=persistent)
persistent=persistent,
display_name=display_name)
super().__init__(config)

View File

@ -615,7 +615,7 @@ class _CommonConfig(SubConfig):
def _impl_build_all_caches(self):
descr = self.cfgimpl_get_description()
if not descr.impl_already_build_caches():
descr._build_cache()
descr._build_cache(display_name=self._display_name)
config_bag = ConfigBag(context=self)
descr.impl_build_force_store_values(config_bag)
@ -652,7 +652,8 @@ class _CommonConfig(SubConfig):
fake_config = KernelConfig(self._impl_descr,
persistent=False,
force_values=get_default_values_storages(),
force_settings=self.cfgimpl_get_settings())
force_settings=self.cfgimpl_get_settings(),
display_name=self._display_name)
fake_config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation())
return fake_config
@ -673,7 +674,8 @@ class _CommonConfig(SubConfig):
force_values=force_values,
force_settings=force_settings,
persistent=persistent,
storage=storage)
storage=storage,
display_name=self._display_name)
else:
if session_id is None and metaconfig_prefix is not None:
session_id = metaconfig_prefix + self.impl_getname()
@ -682,7 +684,8 @@ class _CommonConfig(SubConfig):
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent,
storage=storage)
storage=storage,
display_name=self._display_name)
duplicated_config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation())
properties = self.cfgimpl_get_settings()._p_.exportation()
duplicated_config.cfgimpl_get_settings()._p_.importation(properties)
@ -714,7 +717,8 @@ class _CommonConfig(SubConfig):
class KernelConfig(_CommonConfig):
"main configuration management entry"
__slots__ = ('__weakref__',
'_impl_name')
'_impl_name',
'_display_name')
impl_type = 'config'
def __init__(self,
@ -723,6 +727,7 @@ class KernelConfig(_CommonConfig):
persistent=False,
force_values=None,
force_settings=None,
display_name=None,
_duplicate=False,
storage=None):
""" Configuration option management class
@ -738,6 +743,7 @@ class KernelConfig(_CommonConfig):
:type persistent: `boolean`
"""
self._impl_meta = None
self._display_name = display_name
if isinstance(descr, Leadership):
raise ConfigError(_('cannot set leadership object has root optiondescription'))
if isinstance(descr, DynOptionDescription):
@ -976,7 +982,7 @@ class KernelGroupConfig(_CommonConfig):
class KernelMixConfig(KernelGroupConfig):
__slots__ = tuple()
__slots__ = ('_display_name',)
impl_type = 'mix'
def __init__(self,
@ -985,8 +991,10 @@ class KernelMixConfig(KernelGroupConfig):
session_id=None,
persistent=False,
storage=None,
display_name=None,
_duplicate=False):
# FIXME _duplicate
self._display_name = display_name
for child in children:
if not isinstance(child, _CommonConfig):
try:
@ -1210,8 +1218,10 @@ class KernelMetaConfig(KernelMixConfig):
persistent=False,
optiondescription=None,
storage=None,
display_name=None,
_duplicate=False):
descr = None
self._display_name = display_name
if optiondescription is not None:
if not _duplicate:
new_children = []
@ -1221,7 +1231,8 @@ class KernelMetaConfig(KernelMixConfig):
'not {}').format(child_session_id)
new_children.append(KernelConfig(optiondescription,
persistent=persistent,
session_id=child_session_id))
session_id=child_session_id,
display_name=self._display_name))
children = new_children
descr = optiondescription
for child in children:
@ -1259,17 +1270,20 @@ class KernelMetaConfig(KernelMixConfig):
if type_ == 'config':
config = KernelConfig(self._impl_descr,
session_id=session_id,
persistent=persistent)
persistent=persistent,
display_name=self._display_name)
elif type_ == 'metaconfig':
config = KernelMetaConfig([],
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent)
persistent=persistent,
display_name=self._display_name)
elif type_ == 'mixconfig':
config = KernelMixConfig(children=[],
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent)
persistent=persistent,
display_name=self._display_name)
# Copy context properties/permissives
if new:
config.cfgimpl_get_settings().set_context_properties(self.cfgimpl_get_settings().get_context_properties(), config)

View File

@ -373,7 +373,7 @@ class BaseOption(Base):
in options that have to be set only once, it is of course done in the
__setattr__ method
"""
__slots__ = tuple()
__slots__ = ('_display_name_function',)
def __getstate__(self):
raise NotImplementedError()
@ -407,8 +407,8 @@ class BaseOption(Base):
"to know if a callback has been defined or not"
return self.impl_get_callback()[0] is not None
def impl_get_display_name(self,
dyn_name: Base=None) -> str:
def _impl_get_display_name(self,
dyn_name: Base=None) -> str:
name = self.impl_get_information('doc')
if name is None or name == '':
if dyn_name is not None:
@ -417,6 +417,12 @@ class BaseOption(Base):
name = self.impl_getname()
return name
def impl_get_display_name(self,
dyn_name: Base=None) -> str:
if hasattr(self, '_display_name_function'):
return self._display_name_function(self, dyn_name)
return self._impl_get_display_name(dyn_name)
def reset_cache(self,
path: str,
values: Values,

View File

@ -43,7 +43,8 @@ class CacheOptionDescription(BaseOption):
_consistencies_id=0,
currpath: List[str]=None,
cache_option=None,
force_store_values=None) -> None:
force_store_values=None,
display_name=None) -> None:
"""validate options and set option has readonly option
"""
# _consistencies is None only when we start to build cache
@ -73,7 +74,8 @@ class CacheOptionDescription(BaseOption):
_consistencies_id,
sub_currpath,
cache_option,
force_store_values)
force_store_values,
display_name)
else:
is_multi = option.impl_is_multi()
if not option.impl_is_symlinkoption():
@ -155,6 +157,8 @@ class CacheOptionDescription(BaseOption):
require_opt.impl_getname(), option.impl_getname()))
if option.impl_is_readonly():
raise ConflictError(_('duplicate option: {0}').format(option))
if not self.impl_is_readonly() and display_name:
option._display_name_function = display_name
option._path = subpath
option._set_readonly()
if init:

View File

@ -33,6 +33,7 @@ INPUTS = ['string',
# return always warning (even if same warning is already returned)
warnings.simplefilter("always", ValueWarning)
warnings.simplefilter("always", ValueErrorWarning)
class Callbacks(object):
@ -621,27 +622,33 @@ class TiramisuDict:
props = set(childapi.property.get())
obj = self.gen_properties(props,
isfollower,
childapi.option.ismulti())
childapi.option.ismulti(),
index)
self.calc_raises_properties(obj, childapi)
return obj
def gen_properties(self,
properties,
isfollower=False,
ismulti=False):
isfollower,
ismulti,
index):
obj = {}
if not isfollower and ismulti:
if 'empty' in properties:
obj['required'] = True
if index is None:
obj['required'] = True
properties.remove('empty')
if 'mandatory' in properties:
obj['needs_len'] = True
if index is None:
obj['needs_len'] = True
properties.remove('mandatory')
elif 'mandatory' in properties:
obj['required'] = True
if index is None:
obj['required'] = True
properties.remove('mandatory')
if 'frozen' in properties:
obj['readOnly'] = True
if index is None:
obj['readOnly'] = True
properties.remove('frozen')
if 'hidden' in properties:
properties.remove('hidden')
@ -735,7 +742,7 @@ class TiramisuDict:
value = childapi.value.get()
if value is not None and value != []:
obj['value'] = value
obj['owner'] = childapi.owner.get()
obj['owner'] = childapi.owner.get()
def _get_value_with_exception(self,
obj,
@ -744,22 +751,24 @@ class TiramisuDict:
for value in values:
if isinstance(value, ValueError):
obj.setdefault('error', [])
obj['error'].append(str(value))
obj['invalid'] = True
msg = str(value)
if msg not in obj.get('error', []):
obj['error'].append(msg)
obj['invalid'] = True
elif isinstance(value.message, ValueErrorWarning):
value.message.prefix = ''
if childapi.option.isfollower():
obj.setdefault('invalid', [])
obj['invalid'].append({'error': str(value.message),
'index': value.message.index})
else:
obj.setdefault('error', [])
obj['error'].append(str(value.message))
obj.setdefault('error', [])
msg = str(value.message)
if msg not in obj.get('error', []):
obj['error'].append(msg)
obj['invalid'] = True
else:
value.message.prefix = ''
obj.setdefault('warnings', [])
obj['warnings'].append(str(value.message))
obj['hasWarnings'] = True
msg = str(value.message)
if msg not in obj.get('error', []):
obj['warnings'].append(msg)
obj['hasWarnings'] = True
def gen_global(self):
ret = {}

View File

@ -76,7 +76,8 @@ class Values(object):
option_bag,
check_error=True)
# store value in cache
validator = 'validator' in option_bag.config_bag.properties
properties = option_bag.config_bag.properties
validator = 'validator' in properties and 'demoting_error_warning' not in properties
if not option_bag.fromconsistency and (not is_cached or validator):
self._p_.setcache(option_bag.path,
option_bag.index,
@ -570,13 +571,12 @@ class Values(object):
except PropertiesOptionError as err:
pass
else:
for path in self._mandatory_warnings(context,
config_bag,
option,
currpath + [name],
subsubconfig,
od_config_bag):
yield path
yield from self._mandatory_warnings(context,
config_bag,
option,
currpath + [name],
subsubconfig,
od_config_bag)
elif not option.impl_is_symlinkoption():
# don't verifying symlink
try:
@ -602,11 +602,8 @@ class Values(object):
except PropertiesOptionError as err:
if err.proptype == ['mandatory']:
yield path
except RequirementError:
except (RequirementError, ConfigError):
pass
except ConfigError as err:
#assume that uncalculated value is an empty value
yield path
def mandatory_warnings(self,
config_bag):