better hidden/display support
This commit is contained in:
@@ -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()
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user