better hidden/display support

This commit is contained in:
2019-07-04 20:43:47 +02:00
parent 83f05197fb
commit fb1286e50e
14 changed files with 1489 additions and 1195 deletions

View File

@@ -278,6 +278,11 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
option = self._option_bag.option
return option.impl_get_callback()
def validator(self):
"""Get validator for an option (not for optiondescription)"""
option = self._option_bag.option
return option.impl_get_validator()
def pattern(self) -> str:
option = self._option_bag.option
type = option.get_type()
@@ -374,7 +379,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
only_raises=False):
"""Get properties for an option"""
option = self._option_bag.option
self._test_follower_index()
#self._test_follower_index()
if not only_raises:
return self._option_bag.properties
# do not check cache properties/permissives which are not save (unrestraint, ...)
@@ -677,7 +682,6 @@ class _TiramisuOption(CommonTiramisu):
if subfunc in self._registers:
subconfig = self._subconfig
if subconfig:
option_bag = self._option_bag
option = self._get_option()
if option.impl_is_optiondescription() and subfunc == 'option':
config = self._get_config()

View File

@@ -43,7 +43,7 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
if config_bag is undefined:
return undefined
if isinstance(callbk, ParamContext):
#Not an option, set full context
# Not an option, set full context
return config_bag.context.duplicate(force_values=get_default_values_storages(),
force_settings=get_default_settings_storages())
opt = callbk.option

View File

@@ -249,7 +249,7 @@ class SubConfig(object):
_commit=True):
option = option_bag.option
if option.impl_is_symlinkoption():
raise TypeError(_("can't delete a SymLinkOption"))
raise ConfigError(_("can't delete a SymLinkOption"))
values = self.cfgimpl_get_values()
if option_bag.index is not None:
values.reset_follower(option_bag,

View File

@@ -85,8 +85,10 @@ class PropertiesOptionError(AttributeError):
def __str__(self):
#this part is a bit slow, so only execute when display
if self.msg:
if self.msg is not None:
return self.msg
if self._settings is None:
return 'error'
req = self._settings.apply_requires(self._option_bag,
True)
#if req != {} or self._orig_opt is not None:

View File

@@ -183,6 +183,7 @@ class ConfigBag:
__slots__ = ('context', # link to the current context
'properties', # properties for current context
'true_properties', # properties for current context
'is_unrestraint',
'permissives', # permissives for current context
'expiration_time' # EXPIRATION_TIME
)
@@ -206,6 +207,8 @@ class ConfigBag:
if key == 'expiration_time':
self.expiration_time = EXPIRATION_TIME
return self.expiration_time
if key == 'is_unrestraint':
return False
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
def remove_warnings(self):
@@ -215,6 +218,7 @@ class ConfigBag:
self.properties = frozenset(self.properties - {'validator'})
def unrestraint(self):
self.is_unrestraint = True
self.true_properties = self.properties
self.properties = frozenset(['cache'])
@@ -437,7 +441,8 @@ class Settings(object):
search_properties=search_properties)
props -= self.getpermissives(opt,
path)
if apply_requires:
#if apply_requires and config_bag.properties == config_bag.true_properties:
if apply_requires and not config_bag.is_unrestraint:
self._p_.setcache(path,
index,
props,
@@ -761,15 +766,23 @@ class Settings(object):
def calc_raises_properties(self,
option_bag,
apply_requires=True):
raises_properties = option_bag.config_bag.properties - SPECIAL_PROPERTIES
# remove global permissive properties
if raises_properties and ('permissive' in raises_properties):
raises_properties -= option_bag.config_bag.permissives
if apply_requires and option_bag.properties_setted:
option_properties = option_bag.properties
else:
option_properties = self.getproperties(option_bag,
apply_requires=apply_requires)
return self._calc_raises_properties(option_bag.config_bag.properties,
option_bag.config_bag.permissives,
option_properties)
def _calc_raises_properties(self,
context_properties,
context_permissives,
option_properties):
raises_properties = context_properties - SPECIAL_PROPERTIES
# remove global permissive properties
if raises_properties and ('permissive' in raises_properties):
raises_properties -= context_permissives
properties = option_properties & raises_properties
# at this point an option should not remain in properties
return properties

View File

@@ -30,8 +30,6 @@ INPUTS = ['string',
'ip',
'domainname']
ACTION_HIDE = ['hidden', 'disabled']
# return always warning (even if same warning is already returned)
warnings.simplefilter("always", ValueWarning)
@@ -137,62 +135,89 @@ class Requires(object):
self.requires = {}
self.options = {}
self.tiramisu_web = tiramisu_web
self.action_hide = self.tiramisu_web.config._config_bag.properties
def manage_requires(self,
childapi,
path,
form,
action_hide,
current_action):
for requires in childapi.option.requires():
len_to_long = len(requires) > 1
if childapi.option.isoptiondescription():
isfollower = False
else:
isfollower = childapi.option.isfollower()
if isfollower:
parent_path = path.rsplit('.', 1)[0]
parent = self.tiramisu_web.config.unrestraint.option(parent_path)
leader = next(parent.list())
self.tiramisu_web.set_remotable(leader.option.path(), form, leader)
for require in requires:
options, action, inverse, \
transitive, same_action, operator = require
if transitive is False or same_action is False or operator == 'and':
# transitive to "False" not supported yet for a requirement
# same_action to "False" not supported yet for a requirement
# operator "and" not supported yet for a requirement
self.tiramisu_web.set_remotable(path, form, childapi)
return
options, action, inverse, transitive, same_action, operator = require
if not len_to_long:
len_to_long = len(options) > 1
for option, expected in options:
option_path = option.impl_getpath()
if action in action_hide:
if isinstance(option, ChoiceOption):
choice_obj = self.tiramisu_web.config.unrestraint.option(option_path)
if choice_obj.value.is_values_callback():
self.tiramisu_web.set_remotable(option_path, form, choice_obj)
return
else:
values = self.tiramisu_web.get_enum(choice_obj,
choice_obj.option.ismulti(),
option_path,
choice_obj.option.properties())
for value in values:
if value not in expected:
self.requires.setdefault(path,
{'expected': {}}
)['expected'].setdefault(value,
{}).setdefault(inv_act,
[]).append(option_path)
if current_action is None:
current_action = action
elif current_action != action:
self.tiramisu_web.set_remotable(option_path, form)
if inverse:
act = 'show'
inv_act = 'hide'
else:
act = 'hide'
inv_act = 'show'
for exp in expected:
self.requires.setdefault(path,
{'expected': {}}
)['expected'].setdefault(exp,
{}).setdefault(act,
[]).append(option_path)
self.requires[path].setdefault('default', {}).setdefault(inv_act, []).append(option_path)
if isinstance(option, tuple):
for option_param in chain(option[1].args, option[1].kwargs.values()):
if isinstance(option_param, ParamOption):
self.tiramisu_web.set_remotable(option_param.option.impl_getpath(), form)
elif len_to_long:
self.tiramisu_web.set_remotable(option.impl_getpath(), form)
else:
self.tiramisu_web.set_remotable(option_path, form)
option_path = option.impl_getpath()
if action in self.action_hide:
if childapi.option.isoptiondescription() or isfollower:
self.tiramisu_web.set_remotable(option_path, form)
continue
require_option = self.tiramisu_web.config.unrestraint.option(option_path)
if transitive is False or same_action is False or operator == 'and':
# transitive to "False" not supported yet for a requirement
# same_action to "False" not supported yet for a requirement
# operator "and" not supported yet for a requirement
self.tiramisu_web.set_remotable(option_path, form, require_option)
if require_option.option.requires():
for reqs in require_option.option.requires():
for req in reqs:
for subopt, subexp in req[0]:
if not isinstance(subopt, tuple):
self.tiramisu_web.set_remotable(subopt.impl_getpath(), form)
if isinstance(option, ChoiceOption):
require_option = self.tiramisu_web.config.unrestraint.option(option_path)
if require_option.value.callbacks():
self.tiramisu_web.set_remotable(option_path, form, require_option)
continue
else:
values = self.tiramisu_web.get_enum(require_option,
require_option.option.ismulti(),
option_path,
require_option.option.properties())
for value in values:
if value not in expected:
self.requires.setdefault(path,
{'expected': {}}
)['expected'].setdefault(value,
{}).setdefault(inv_act,
[]).append(option_path)
if current_action is None:
current_action = action
elif current_action != action:
self.tiramisu_web.set_remotable(option_path, form)
if inverse:
act = 'show'
inv_act = 'hide'
else:
act = 'hide'
inv_act = 'show'
for exp in expected:
self.requires.setdefault(path,
{'expected': {}}
)['expected'].setdefault(exp,
{}).setdefault(act,
[]).append(option_path)
self.requires[path].setdefault('default', {}).setdefault(inv_act, []).append(option_path)
else:
self.tiramisu_web.set_remotable(option_path, form)
def add(self, path, childapi, form):
#collect id of all options
@@ -205,7 +230,6 @@ class Requires(object):
self.manage_requires(childapi,
path,
form,
ACTION_HIDE,
current_action)
def is_remote(self, path, form):
@@ -217,8 +241,6 @@ class Requires(object):
def process(self, form):
dependencies = {}
for path, values in self.requires.items():
if form.get(path, {}).get('remote') is True:
continue
if 'default' in values:
for option in values['default'].get('show', []):
if path == option:
@@ -262,7 +284,7 @@ class Requires(object):
if path not in dependencies[option]['expected'][expected]['hide']:
dependencies[option]['expected'][expected]['hide'].append(path)
for path, dependency in dependencies.items():
form[path]['dependencies'] = dependency
form.setdefault(path, {})['dependencies'] = dependency
class TiramisuDict:
@@ -375,7 +397,6 @@ class TiramisuDict:
childapi,
path,
leader_len,
props_no_requires,
updates_status)
if order is not None:
order.append(path)
@@ -405,6 +426,9 @@ class TiramisuDict:
childtype = child._impl_getopt().__class__.__name__
if childapi_option.issymlinkoption():
web_type = 'symlink'
value = None
defaultmulti = None
is_multi = False
else:
web_type = childapi_option.type()
value = childapi.option.default()
@@ -443,6 +467,8 @@ class TiramisuDict:
self.add_help(schema[path],
childapi)
except Exception as err:
import traceback
traceback.print_exc()
if not init:
raise err
error = err
@@ -524,6 +550,15 @@ class TiramisuDict:
if path in form:
obj_form.update(form[path])
if not childapi_option.issymlinkoption():
if childapi_option.validator() != (None, None):
obj_form['remote'] = True
params = childapi_option.validator()[1]
if params is not None:
for param in chain(params.args, params.kwargs.values()):
if isinstance(param, ParamContext):
raise ValueError(_('context is not supported from now for {}').format(path))
if isinstance(param, ParamOption):
self.set_remotable(param.option.impl_getpath(), form)
if self.clearable == 'all':
obj_form['clearable'] = True
if self.clearable != 'none':
@@ -532,6 +567,8 @@ class TiramisuDict:
obj_form['remote'] = True
if childtype == 'IPOption' and (child.impl_get_extra('_private_only') or not child.impl_get_extra('_allow_reserved') or child.impl_get_extra('_cidr')):
obj_form['remote'] = True
if childtype == 'DateOption':
obj_form['remote'] = True
if not obj_form.get('remote', False):
pattern = childapi_option.pattern()
if pattern is not None:
@@ -553,28 +590,32 @@ class TiramisuDict:
childapi):
old_properties = childapi._option_bag.config_bag.properties
del childapi._option_bag.config_bag.properties
if 'permissive' not in childapi._option_bag.config_bag.properties:
has_permissive = 'permissive' in childapi._option_bag.config_bag.properties
if not has_permissive:
childapi._option_bag.config_bag.properties = childapi._option_bag.config_bag.properties | {'permissive'}
# 'display=False' means cannot access only without permissive option
# 'hidden=True' means cannot access with or without permissive option
if childapi.option.properties(only_raises=True):
properties = childapi.property.get(only_raises=True)
if has_permissive:
properties -= self.config.permissive.get()
properties -= childapi.permissive.get()
if properties:
obj['hidden'] = True
childapi._option_bag.config_bag.properties = childapi._option_bag.config_bag.properties - {'permissive'}
if childapi.option.properties(only_raises=True):
properties = childapi.property.get(only_raises=True)
if has_permissive:
properties -= self.config.permissive.get()
properties -= childapi.permissive.get()
if properties:
obj['display'] = False
childapi._option_bag.config_bag.properties = old_properties
def _gen_model_properties(self,
childapi,
path,
index,
props_no_requires):
index):
isfollower = childapi.option.isfollower()
if index is None and isfollower:
# cannot calculated requires with follower without index
props = props_no_requires
else:
props = set(childapi.property.get())
props = set(childapi.property.get())
obj = self.gen_properties(props,
isfollower,
childapi.option.ismulti())
@@ -589,22 +630,16 @@ class TiramisuDict:
if not isfollower and ismulti:
if 'empty' in properties:
obj['required'] = True
properties.remove('empty')
if 'mandatory' in properties:
obj['needs_len'] = True
properties.remove('mandatory')
elif 'mandatory' in properties:
obj['required'] = True
properties.remove('mandatory')
if 'frozen' in properties:
obj['readOnly'] = True
properties.remove('frozen')
#if 'hidden' in properties:
# obj['hidden'] = True
# properties.remove('hidden')
#if 'disabled' in properties:
# obj['hidden'] = True
# properties.remove('disabled')
if properties:
lprops = list(properties)
lprops.sort()
@@ -616,7 +651,6 @@ class TiramisuDict:
childapi,
path,
leader_len,
props_no_requires,
updates_status):
if childapi.option.isoptiondescription():
props = set(childapi.property.get())
@@ -633,15 +667,13 @@ class TiramisuDict:
else:
obj = self._gen_model_properties(childapi,
path,
None,
props_no_requires)
None)
if childapi.option.isfollower():
for index in range(leader_len):
follower_childapi = self.config.unrestraint.option(path, index)
sobj = self._gen_model_properties(follower_childapi,
path,
index,
props_no_requires)
index)
self._get_model_value(follower_childapi,
path,
sobj,
@@ -657,7 +689,7 @@ class TiramisuDict:
updates_status)
if obj:
if not childapi.option.isoptiondescription() and childapi.option.isfollower():
model.setdefault(path, {})[None] = obj
model.setdefault(path, {})['null'] = obj
else:
model[path] = obj
@@ -675,9 +707,8 @@ class TiramisuDict:
del updates_status[path][index]
else:
try:
nchildapi = self.config.option(path, index=index)
with warnings.catch_warnings(record=True) as warns:
value = nchildapi.value.get()
value = self.config.option(path, index=index).value.get()
self._get_value_with_exception(obj,
childapi,
warns)
@@ -687,6 +718,13 @@ class TiramisuDict:
[err])
value = self.config.unrestraint.option(path, index=index).value.get()
except PropertiesOptionError as err:
config_bag = self.config._config_bag
settings = config_bag.context.cfgimpl_get_settings()
if settings._calc_raises_properties(config_bag.properties,
config_bag.permissives,
set(err.proptype)):
obj['hidden'] = True
obj['display'] = False
value = childapi.value.get()
if value is not None and value != []:
obj['value'] = value
@@ -852,7 +890,6 @@ class TiramisuDict:
buttons = []
else:
form = None
ret = {}
self.walk(rootpath,
None,
schema,