update todict support and tests
This commit is contained in:
@@ -257,7 +257,16 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
|
||||
def defaultmulti(self):
|
||||
"""Get default value when added a value for a multi option (not for optiondescription)"""
|
||||
option = self._option_bag.option
|
||||
return option.impl_getdefault_multi()
|
||||
ret = option.impl_getdefault_multi()
|
||||
if ret is None and option.impl_is_multi() and option.impl_has_callback() and not self.isfollower():
|
||||
callback, callback_params = option.impl_get_callback()
|
||||
values = self._option_bag.config_bag.context.cfgimpl_get_values()
|
||||
value = values.carry_out_calculation(self._option_bag,
|
||||
callback,
|
||||
callback_params)
|
||||
if not isinstance(value, list):
|
||||
ret = value
|
||||
return ret
|
||||
|
||||
def consistencies(self):
|
||||
"""Get consistencies for an option (not for optiondescription)"""
|
||||
@@ -556,6 +565,11 @@ class _TiramisuOptionValueChoiceOption:
|
||||
option = self._option_bag.option
|
||||
return option.impl_get_values(self._option_bag)
|
||||
|
||||
def callbacks(self):
|
||||
"""Get callbacks for a values"""
|
||||
option = self._option_bag.option
|
||||
return option.get_callback()
|
||||
|
||||
|
||||
class _TiramisuOptionValueOptionDescription:
|
||||
|
||||
|
@@ -78,7 +78,7 @@ class PropertiesOptionError(AttributeError):
|
||||
self.proptype = proptype
|
||||
self._settings = settings
|
||||
self.msg = None
|
||||
super(PropertiesOptionError, self).__init__(None)
|
||||
super().__init__(None)
|
||||
|
||||
def set_orig_opt(self, opt):
|
||||
self._orig_opt = opt
|
||||
@@ -168,6 +168,7 @@ class _CommonError:
|
||||
self.val = val
|
||||
self.display_type = display_type
|
||||
self.opt = weakref.ref(opt)
|
||||
self.name = opt.impl_get_display_name()
|
||||
self.err_msg = err_msg
|
||||
self.index = index
|
||||
super().__init__(self.err_msg)
|
||||
@@ -178,7 +179,7 @@ class _CommonError:
|
||||
except AttributeError:
|
||||
self.prefix = self.tmpl.format(self.val,
|
||||
self.display_type,
|
||||
self.opt().impl_get_display_name())
|
||||
self.name)
|
||||
msg = self.prefix
|
||||
if self.err_msg:
|
||||
if msg:
|
||||
|
@@ -75,7 +75,7 @@ msgstr "group_type inconnu: {0}"
|
||||
|
||||
#: tiramisu/api.py:753 tiramisu/api.py:1208
|
||||
msgid "please use .dict() before .updates()"
|
||||
msgstr "faire .dico() avant .updates()"
|
||||
msgstr "faire .dict() avant .updates()"
|
||||
|
||||
#: tiramisu/api.py:1000
|
||||
msgid "properties must be a set"
|
||||
|
@@ -81,25 +81,34 @@ class ChoiceOption(Option):
|
||||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def get_callback(self):
|
||||
values = self._choice_values
|
||||
if isinstance(values, FunctionType):
|
||||
return (values, getattr(self, '_choice_values_params', {}))
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
def impl_get_values(self,
|
||||
option_bag,
|
||||
current_opt=undefined):
|
||||
if current_opt is undefined:
|
||||
current_opt = self
|
||||
values = self._choice_values
|
||||
if isinstance(values, FunctionType):
|
||||
values, values_params = self.get_callback()
|
||||
if values is not None:
|
||||
if option_bag is undefined:
|
||||
values = undefined
|
||||
else:
|
||||
values = carry_out_calculation(current_opt,
|
||||
callback=values,
|
||||
callback_params=getattr(self, '_choice_values_params', {}),
|
||||
callback_params=values_params,
|
||||
index=None,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=[])
|
||||
if values is not undefined and not isinstance(values, list):
|
||||
raise ConfigError(_('calculated values for {0} is not a list'
|
||||
'').format(self.impl_getname()))
|
||||
else:
|
||||
values = self._choice_values
|
||||
return values
|
||||
|
||||
|
||||
|
@@ -359,10 +359,10 @@ class Option(BaseOption):
|
||||
'{0}'.format(err),
|
||||
err_index)
|
||||
warnings.warn_explicit(ValueErrorWarning(val,
|
||||
self._display_name,
|
||||
option_bag.ori_option,
|
||||
'{0}'.format(err),
|
||||
err_index),
|
||||
self._display_name,
|
||||
option_bag.ori_option,
|
||||
'{0}'.format(err),
|
||||
err_index),
|
||||
ValueErrorWarning,
|
||||
self.__class__.__name__, 0)
|
||||
|
||||
@@ -707,9 +707,8 @@ class Option(BaseOption):
|
||||
for opt_ in [opts[idx_inf], opts[idx_inf + idx_sup + 1]]:
|
||||
if opt_ == current_opt:
|
||||
is_current = True
|
||||
else:
|
||||
if opt_ not in equal:
|
||||
equal.append(opt_)
|
||||
elif opt_ not in equal:
|
||||
equal.append(opt_)
|
||||
if equal:
|
||||
if is_current:
|
||||
if warnings_only:
|
||||
|
@@ -406,7 +406,8 @@ class Settings(object):
|
||||
|
||||
def getproperties(self,
|
||||
option_bag,
|
||||
apply_requires=True):
|
||||
apply_requires=True,
|
||||
search_properties=None):
|
||||
"""
|
||||
"""
|
||||
opt = option_bag.option
|
||||
@@ -432,7 +433,8 @@ class Settings(object):
|
||||
opt.impl_getproperties())
|
||||
if apply_requires:
|
||||
props |= self.apply_requires(option_bag,
|
||||
False)
|
||||
False,
|
||||
search_properties=search_properties)
|
||||
props -= self.getpermissives(opt,
|
||||
path)
|
||||
if apply_requires:
|
||||
@@ -457,7 +459,8 @@ class Settings(object):
|
||||
|
||||
def apply_requires(self,
|
||||
option_bag,
|
||||
readable):
|
||||
readable,
|
||||
search_properties=None):
|
||||
"""carries out the jit (just in time) requirements between options
|
||||
|
||||
a requirement is a tuple of this form that comes from the option's
|
||||
@@ -517,6 +520,8 @@ class Settings(object):
|
||||
for requires in current_requires:
|
||||
for require in requires:
|
||||
exps, action, inverse, transitive, same_action, operator = require
|
||||
#if search_properties and action not in search_properties:
|
||||
# continue
|
||||
breaked = False
|
||||
for option, expected in exps:
|
||||
if not isinstance(option, tuple):
|
||||
|
@@ -65,6 +65,14 @@ class Values(Cache):
|
||||
# follower
|
||||
self._values[nb].append([value])
|
||||
|
||||
def _add_new_value(self, index, nb, value):
|
||||
if index is None or nb == 0:
|
||||
# not follower or path
|
||||
self._values[nb].append(value)
|
||||
else:
|
||||
# follower
|
||||
self._values[nb].append([value])
|
||||
|
||||
# value
|
||||
def setvalue(self,
|
||||
path,
|
||||
|
@@ -3,8 +3,9 @@
|
||||
import warnings
|
||||
import sys
|
||||
from copy import copy
|
||||
from collections import OrderedDict
|
||||
from .error import ValueWarning, ValueErrorWarning, PropertiesOptionError
|
||||
from itertools import chain
|
||||
from .error import ValueWarning, ValueErrorWarning, PropertiesOptionError, ConfigError
|
||||
from .setting import undefined
|
||||
from . import SynDynOption, RegexpOption, ChoiceOption, ParamContext, ParamOption
|
||||
from .i18n import _
|
||||
|
||||
@@ -57,23 +58,21 @@ class Callbacks(object):
|
||||
|
||||
def process_properties(self, form):
|
||||
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
|
||||
if childapi.option.isfollower():
|
||||
self.tiramisu_web.set_remotable(path, form, childapi)
|
||||
continue
|
||||
has_option = False
|
||||
if callback_params is not None:
|
||||
for callback_param in callback_params.args:
|
||||
for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
|
||||
if isinstance(callback_param, ParamContext):
|
||||
raise ValueError(_('context is not supported from now for {}').format(path))
|
||||
if isinstance(callback_param, ParamOption):
|
||||
has_option = True
|
||||
if callback.__name__ != 'tiramisu_copy' or 'expire' in childapi.option.properties():
|
||||
if self.remotable == 'none':
|
||||
raise ValueError(_('option {} only works when remotable is not "none"').format(path))
|
||||
form[callback_param.option.impl_getpath()]['remote'] = True
|
||||
remote = True
|
||||
if not has_option and form.get(path, {}).get('remote') == False:
|
||||
self.tiramisu_web.set_remotable(callback_param.option.impl_getpath(), form)
|
||||
if not has_option and form.get(path, {}).get('remote', False) == False:
|
||||
if 'expire' in childapi.option.properties():
|
||||
if self.remotable == 'none':
|
||||
raise ValueError(_('option {} only works when remotable is not "none"').format(path))
|
||||
form.setdefault(path, {})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form, childapi)
|
||||
elif childapi.owner.isdefault():
|
||||
# get calculated value and set clearable
|
||||
schema[path]['value'] = childapi.value.get()
|
||||
@@ -83,7 +82,7 @@ class Callbacks(object):
|
||||
def manage_callbacks(self, form):
|
||||
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
|
||||
if callback_params is not None:
|
||||
for callback_param in callback_params.args:
|
||||
for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
|
||||
if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
|
||||
opt_path = callback_param.option.impl_getpath()
|
||||
if form.get(opt_path, {}).get('remote') is not True:
|
||||
@@ -98,41 +97,39 @@ class Callbacks(object):
|
||||
|
||||
class Consistencies(object):
|
||||
def __init__(self, tiramisu_web):
|
||||
self.not_equal = []
|
||||
self.options = {}
|
||||
self.not_equal = {}
|
||||
self.tiramisu_web = tiramisu_web
|
||||
|
||||
def add(self, path, childapi):
|
||||
child = childapi.option.get()
|
||||
if isinstance(child, SynDynOption):
|
||||
child = child._impl_getopt()
|
||||
self.options[child] = path
|
||||
def add(self, path, childapi, form):
|
||||
if not childapi.option.isoptiondescription():
|
||||
for consistency in childapi.option.consistencies():
|
||||
cons_id, func, all_cons_opts, params = consistency
|
||||
if func == '_cons_not_equal':
|
||||
options = []
|
||||
if func == '_cons_not_equal' and params.get('transitive', True) is True:
|
||||
options_path = []
|
||||
for option in all_cons_opts:
|
||||
options_path.append(option()._path)
|
||||
for idx, option in enumerate(all_cons_opts):
|
||||
option = option()
|
||||
options.append(option)
|
||||
# FIXME transitive
|
||||
self.not_equal.append((options, params.get('warnings_only')))
|
||||
paths = options_path.copy()
|
||||
paths.pop(idx)
|
||||
warnings_only = params.get('warnings_only') or getattr(option, '_warnings_only', False)
|
||||
self.not_equal.setdefault(option._path, {}).setdefault(warnings_only, []).extend(paths)
|
||||
else:
|
||||
for option in all_cons_opts:
|
||||
self.tiramisu_web.set_remotable(option()._path, form)
|
||||
|
||||
def process(self, form):
|
||||
for not_equal, warnings_only in self.not_equal:
|
||||
not_equal_option = []
|
||||
for option in not_equal:
|
||||
not_equal_option.append(self.options[option])
|
||||
for idx, path in enumerate(not_equal_option):
|
||||
if form.get(path, {}).get('remote') is True:
|
||||
continue
|
||||
options = copy(not_equal_option)
|
||||
options.pop(idx)
|
||||
form.setdefault(path, {}).setdefault('not_equal',
|
||||
{'options': []})
|
||||
form[path]['not_equal']['options'].extend(options)
|
||||
if warnings_only or getattr(option, '_warnings_only', False):
|
||||
form[path]['not_equal']['warnings'] = True
|
||||
for path in self.not_equal:
|
||||
for warnings_only in self.not_equal[path]:
|
||||
options = self.not_equal[path][warnings_only]
|
||||
if path not in form:
|
||||
form[path] = {}
|
||||
if 'not_equal' not in form[path]:
|
||||
form[path]['not_equal'] = []
|
||||
obj = {'options': options}
|
||||
if warnings_only:
|
||||
obj['warnings'] = True
|
||||
form[path]['not_equal'].append(obj)
|
||||
|
||||
|
||||
class Requires(object):
|
||||
@@ -140,8 +137,6 @@ class Requires(object):
|
||||
self.requires = {}
|
||||
self.options = {}
|
||||
self.tiramisu_web = tiramisu_web
|
||||
self.config = tiramisu_web.config
|
||||
self.remotable = tiramisu_web.remotable
|
||||
|
||||
def manage_requires(self,
|
||||
childapi,
|
||||
@@ -153,37 +148,36 @@ class Requires(object):
|
||||
for require in requires:
|
||||
options, action, inverse, \
|
||||
transitive, same_action, operator = require
|
||||
if transitive is False:
|
||||
if transitive is False or same_action is False or operator == 'and':
|
||||
# transitive to "False" not supported yet for a requirement
|
||||
if self.remotable == 'none':
|
||||
raise ValueError('require set for {} but remotable is "none"'
|
||||
''.format(path))
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
return
|
||||
if same_action is False:
|
||||
# same_action to "False" not supported yet for a requirement
|
||||
if self.remotable == 'none':
|
||||
raise ValueError('require set for {} but remotable is "none"'
|
||||
''.format(path))
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
return
|
||||
if operator == 'and':
|
||||
# operator "and" not supported yet for a requirement
|
||||
if self.remotable == 'none':
|
||||
raise ValueError('require set for {} but remotable is "none"'
|
||||
''.format(path))
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form, childapi)
|
||||
return
|
||||
for option, expected in options:
|
||||
option_path = self.options.get(option)
|
||||
if option_path is not None and action in action_hide:
|
||||
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:
|
||||
if self.remotable == 'none':
|
||||
raise ValueError('require set for {} but remotable is "none"'
|
||||
''.format(path))
|
||||
form.setdefault(option_path, {'key': option_path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(option_path, form)
|
||||
if inverse:
|
||||
act = 'show'
|
||||
inv_act = 'hide'
|
||||
@@ -196,25 +190,9 @@ class Requires(object):
|
||||
)['expected'].setdefault(exp,
|
||||
{}).setdefault(act,
|
||||
[]).append(option_path)
|
||||
if isinstance(option, ChoiceOption):
|
||||
choice_obj = self.config.unrestraint.option(option_path)
|
||||
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)
|
||||
self.requires[path].setdefault('default', {}).setdefault(inv_act, []).append(option_path)
|
||||
else:
|
||||
if self.remotable == 'none':
|
||||
raise ValueError('require set for {} but remotable est "none"'
|
||||
''.format(path))
|
||||
form.setdefault(option_path, {'key': option_path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(option_path, form)
|
||||
|
||||
def add(self, path, childapi, form):
|
||||
#collect id of all options
|
||||
@@ -231,7 +209,7 @@ class Requires(object):
|
||||
current_action)
|
||||
|
||||
def is_remote(self, path, form):
|
||||
if self.remotable == 'all':
|
||||
if self.tiramisu_web.remotable == 'all':
|
||||
return True
|
||||
else:
|
||||
return form.get(path) and form[path].get('remote', False)
|
||||
@@ -244,7 +222,7 @@ class Requires(object):
|
||||
if 'default' in values:
|
||||
for option in values['default'].get('show', []):
|
||||
if path == option:
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form)
|
||||
if not self.is_remote(option, form):
|
||||
dependencies.setdefault(option,
|
||||
{'default': {}, 'expected': {}}
|
||||
@@ -253,7 +231,7 @@ class Requires(object):
|
||||
dependencies[option]['default']['show'].append(path)
|
||||
for option in values['default'].get('hide', []):
|
||||
if path == option:
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form)
|
||||
if not self.is_remote(option, form):
|
||||
dependencies.setdefault(option,
|
||||
{'default': {}, 'expected': {}}
|
||||
@@ -265,7 +243,7 @@ class Requires(object):
|
||||
expected = ''
|
||||
for option in actions.get('show', []):
|
||||
if path == option:
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form)
|
||||
if not self.is_remote(option, form):
|
||||
dependencies.setdefault(option,
|
||||
{'expected': {}}
|
||||
@@ -275,7 +253,7 @@ class Requires(object):
|
||||
dependencies[option]['expected'][expected]['show'].append(path)
|
||||
for option in actions.get('hide', []):
|
||||
if path == option:
|
||||
form.setdefault(path, {'key': path})['remote'] = True
|
||||
self.tiramisu_web.set_remotable(path, form)
|
||||
if not self.is_remote(option, form):
|
||||
dependencies.setdefault(option,
|
||||
{'expected': {}}
|
||||
@@ -336,6 +314,18 @@ class TiramisuDict:
|
||||
path = root + '.' + childname
|
||||
yield path, childapi
|
||||
|
||||
def set_remotable(self, path, form, childapi=None):
|
||||
if self.remotable == 'none':
|
||||
raise ValueError(_('option {} only works when remotable is not "none"').format(path))
|
||||
form.setdefault(path, {})['remote'] = True
|
||||
if childapi is None:
|
||||
childapi = self.config.unrestraint.option(path)
|
||||
if childapi.option.isfollower():
|
||||
parent_path = path.rsplit('.', 1)[0]
|
||||
parent = self.config.unrestraint.option(parent_path)
|
||||
leader = next(parent.list())
|
||||
form.setdefault(leader.option.path(), {})['remote'] = True
|
||||
|
||||
def walk(self,
|
||||
root,
|
||||
subchildapi,
|
||||
@@ -345,6 +335,7 @@ class TiramisuDict:
|
||||
order,
|
||||
updates_status,
|
||||
init=False):
|
||||
error = None
|
||||
if init:
|
||||
if form is not None:
|
||||
self.requires = Requires(self)
|
||||
@@ -352,113 +343,120 @@ class TiramisuDict:
|
||||
self.callbacks = Callbacks(self)
|
||||
else:
|
||||
init = False
|
||||
if subchildapi is None:
|
||||
if root is None:
|
||||
subchildapi = self.config.unrestraint.option
|
||||
try:
|
||||
if subchildapi is None:
|
||||
if root is None:
|
||||
subchildapi = self.config.unrestraint.option
|
||||
else:
|
||||
subchildapi = self.config.unrestraint.option(root)
|
||||
isleadership = False
|
||||
else:
|
||||
subchildapi = self.config.unrestraint.option(root)
|
||||
isleadership = False
|
||||
else:
|
||||
isleadership = subchildapi.option.isleadership()
|
||||
leader_len = None
|
||||
for path, childapi in self.get_list(root, subchildapi):
|
||||
if isleadership and leader_len is None:
|
||||
leader_len = childapi.value.len()
|
||||
props_no_requires = set(childapi.option.properties())
|
||||
if form is not None:
|
||||
self.requires.add(path,
|
||||
childapi,
|
||||
form)
|
||||
self.consistencies.add(path,
|
||||
childapi)
|
||||
self.callbacks.add(path,
|
||||
childapi,
|
||||
schema,
|
||||
'force_store_value' in props_no_requires)
|
||||
childapi_option = childapi.option
|
||||
if model is not None and childapi.option.isoptiondescription() or not childapi_option.issymlinkoption():
|
||||
self.gen_model(model,
|
||||
childapi,
|
||||
path,
|
||||
leader_len,
|
||||
props_no_requires,
|
||||
updates_status)
|
||||
if order is not None:
|
||||
order.append(path)
|
||||
if childapi.option.isoptiondescription():
|
||||
web_type = 'optiondescription'
|
||||
if childapi_option.isleadership():
|
||||
type_ = 'array'
|
||||
else:
|
||||
type_ = 'object'
|
||||
if schema is not None:
|
||||
schema[path] = {'properties': OrderedDict(),
|
||||
'type': type_}
|
||||
subschema = schema[path]['properties']
|
||||
else:
|
||||
subschema = schema
|
||||
self.walk(path,
|
||||
childapi,
|
||||
subschema,
|
||||
model,
|
||||
form,
|
||||
order,
|
||||
updates_status)
|
||||
else:
|
||||
child = childapi_option.get()
|
||||
childtype = child.__class__.__name__
|
||||
if childtype == 'SynDynOption':
|
||||
childtype = child._impl_getopt().__class__.__name__
|
||||
if childapi_option.issymlinkoption():
|
||||
web_type = 'symlink'
|
||||
else:
|
||||
web_type = childapi_option.type()
|
||||
value = childapi.option.default()
|
||||
if value not in [[], None]:
|
||||
has_value = True
|
||||
else:
|
||||
value = None
|
||||
has_value = False
|
||||
|
||||
is_multi = childapi_option.ismulti()
|
||||
if is_multi:
|
||||
default = childapi_option.defaultmulti()
|
||||
if default not in [None, []]:
|
||||
has_value = True
|
||||
else:
|
||||
default = None
|
||||
else:
|
||||
default = None
|
||||
|
||||
if schema is not None:
|
||||
self.gen_schema(schema,
|
||||
childapi,
|
||||
childapi_option,
|
||||
path,
|
||||
props_no_requires,
|
||||
value,
|
||||
default,
|
||||
is_multi,
|
||||
web_type)
|
||||
isleadership = subchildapi.option.isleadership()
|
||||
leader_len = None
|
||||
for path, childapi in self.get_list(root, subchildapi):
|
||||
if isleadership and leader_len is None:
|
||||
leader_len = childapi.value.len()
|
||||
one_is_remote = False
|
||||
props_no_requires = set(childapi.option.properties())
|
||||
if form is not None:
|
||||
self.gen_form(form,
|
||||
web_type,
|
||||
path,
|
||||
child,
|
||||
childapi_option,
|
||||
childtype,
|
||||
has_value)
|
||||
if schema is not None:
|
||||
if web_type != 'symlink':
|
||||
schema[path]['title'] = childapi_option.doc()
|
||||
self.add_help(schema[path],
|
||||
childapi)
|
||||
self.requires.add(path,
|
||||
childapi,
|
||||
form)
|
||||
self.consistencies.add(path,
|
||||
childapi,
|
||||
form)
|
||||
self.callbacks.add(path,
|
||||
childapi,
|
||||
schema,
|
||||
'force_store_value' in props_no_requires)
|
||||
childapi_option = childapi.option
|
||||
if model is not None and childapi.option.isoptiondescription() or not childapi_option.issymlinkoption():
|
||||
self.gen_model(model,
|
||||
childapi,
|
||||
path,
|
||||
leader_len,
|
||||
props_no_requires,
|
||||
updates_status)
|
||||
if order is not None:
|
||||
order.append(path)
|
||||
if childapi.option.isoptiondescription():
|
||||
web_type = 'optiondescription'
|
||||
if childapi_option.isleadership():
|
||||
type_ = 'array'
|
||||
else:
|
||||
type_ = 'object'
|
||||
if schema is not None:
|
||||
schema[path] = {'properties': {},
|
||||
'type': type_}
|
||||
subschema = schema[path]['properties']
|
||||
else:
|
||||
subschema = schema
|
||||
self.walk(path,
|
||||
childapi,
|
||||
subschema,
|
||||
model,
|
||||
form,
|
||||
order,
|
||||
updates_status)
|
||||
else:
|
||||
child = childapi_option.get()
|
||||
childtype = child.__class__.__name__
|
||||
if childtype == 'SynDynOption':
|
||||
childtype = child._impl_getopt().__class__.__name__
|
||||
if childapi_option.issymlinkoption():
|
||||
web_type = 'symlink'
|
||||
else:
|
||||
web_type = childapi_option.type()
|
||||
value = childapi.option.default()
|
||||
if value == []:
|
||||
value = None
|
||||
|
||||
is_multi = childapi_option.ismulti()
|
||||
if is_multi:
|
||||
defaultmulti = childapi_option.defaultmulti()
|
||||
if defaultmulti == []:
|
||||
defaultmulti = None
|
||||
else:
|
||||
defaultmulti = None
|
||||
|
||||
if schema is not None:
|
||||
self.gen_schema(schema,
|
||||
childapi,
|
||||
childapi_option,
|
||||
path,
|
||||
props_no_requires,
|
||||
value,
|
||||
defaultmulti,
|
||||
is_multi,
|
||||
web_type,
|
||||
form)
|
||||
if form is not None:
|
||||
self.gen_form(form,
|
||||
web_type,
|
||||
path,
|
||||
child,
|
||||
childapi_option,
|
||||
childtype)
|
||||
if schema is not None:
|
||||
if web_type != 'symlink':
|
||||
schema[path]['title'] = childapi_option.doc()
|
||||
self.add_help(schema[path],
|
||||
childapi)
|
||||
except Exception as err:
|
||||
if not init:
|
||||
raise err
|
||||
error = err
|
||||
if init and form is not None:
|
||||
self.callbacks.process(form)
|
||||
self.requires.process(form)
|
||||
self.consistencies.process(form)
|
||||
del self.requires
|
||||
del self.consistencies
|
||||
del self.callbacks
|
||||
if error:
|
||||
msg = str(error)
|
||||
del error
|
||||
raise ConfigError(_('unable to transform tiramisu object to dict: {}').format(msg))
|
||||
|
||||
|
||||
def gen_schema(self,
|
||||
@@ -468,9 +466,10 @@ class TiramisuDict:
|
||||
path,
|
||||
props_no_requires,
|
||||
value,
|
||||
default,
|
||||
defaultmulti,
|
||||
is_multi,
|
||||
web_type):
|
||||
web_type,
|
||||
form):
|
||||
schema[path] = {'type': web_type}
|
||||
if childapi_option.issymlinkoption():
|
||||
schema[path]['opt_path'] = childapi_option.get().impl_getopt().impl_getpath()
|
||||
@@ -478,8 +477,8 @@ class TiramisuDict:
|
||||
if value is not None:
|
||||
schema[path]['value'] = value
|
||||
|
||||
if default is not None:
|
||||
schema[path]['default'] = default
|
||||
if defaultmulti is not None:
|
||||
schema[path]['defaultmulti'] = defaultmulti
|
||||
|
||||
if is_multi:
|
||||
schema[path]['isMulti'] = is_multi
|
||||
@@ -491,6 +490,12 @@ class TiramisuDict:
|
||||
schema[path]['autoFreeze'] = True
|
||||
|
||||
if web_type == 'choice':
|
||||
values, values_params = childapi.value.callbacks()
|
||||
if values_params:
|
||||
for values_param in chain(values_params.args, values_params.kwargs.values()):
|
||||
if isinstance(values_param, ParamOption):
|
||||
self.set_remotable(path, form, childapi)
|
||||
return
|
||||
schema[path]['enum'] = self.get_enum(childapi,
|
||||
is_multi,
|
||||
path,
|
||||
@@ -514,26 +519,28 @@ class TiramisuDict:
|
||||
path,
|
||||
child,
|
||||
childapi_option,
|
||||
childtype,
|
||||
has_value):
|
||||
childtype):
|
||||
obj_form = {}
|
||||
if path in form:
|
||||
obj_form.update(form[path])
|
||||
if not childapi_option.issymlinkoption():
|
||||
if self.clearable == 'all':
|
||||
obj_form['clearable'] = True
|
||||
if has_value and self.clearable != 'none':
|
||||
if self.clearable != 'none':
|
||||
obj_form['clearable'] = True
|
||||
if self.remotable == 'all' or childapi_option.has_dependency():
|
||||
obj_form['remote'] = True
|
||||
pattern = childapi_option.pattern()
|
||||
if pattern is not None:
|
||||
obj_form['pattern'] = pattern
|
||||
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 not obj_form.get('remote', False):
|
||||
pattern = childapi_option.pattern()
|
||||
if pattern is not None:
|
||||
obj_form['pattern'] = pattern
|
||||
if childtype == 'PortOption':
|
||||
obj_form['min'] = child.impl_get_extra('_min_value')
|
||||
obj_form['max'] = child.impl_get_extra('_max_value')
|
||||
if childtype == 'FloatOption':
|
||||
obj_form['step'] = 'any'
|
||||
if childtype == 'PortOption':
|
||||
obj_form['min'] = child.impl_get_extra('_min_value')
|
||||
obj_form['max'] = child.impl_get_extra('_max_value')
|
||||
if web_type == 'choice':
|
||||
obj_form['type'] = 'choice'
|
||||
elif web_type in INPUTS:
|
||||
@@ -541,50 +548,67 @@ class TiramisuDict:
|
||||
if obj_form:
|
||||
form[path] = obj_form
|
||||
|
||||
def calc_raises_properties(self, childapi):
|
||||
def calc_raises_properties(self,
|
||||
obj,
|
||||
childapi):
|
||||
old_properties = childapi._option_bag.config_bag.properties
|
||||
del childapi._option_bag.config_bag.properties
|
||||
ret = childapi.option.properties(only_raises=True)
|
||||
if 'permissive' not in childapi._option_bag.config_bag.properties:
|
||||
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):
|
||||
obj['hidden'] = True
|
||||
childapi._option_bag.config_bag.properties = childapi._option_bag.config_bag.properties - {'permissive'}
|
||||
if childapi.option.properties(only_raises=True):
|
||||
obj['display'] = False
|
||||
childapi._option_bag.config_bag.properties = old_properties
|
||||
return ret
|
||||
|
||||
def _gen_model_properties(self,
|
||||
childapi,
|
||||
path,
|
||||
index,
|
||||
props_no_requires):
|
||||
obj = {}
|
||||
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())
|
||||
if self.calc_raises_properties(childapi):
|
||||
obj['display'] = False
|
||||
if not isfollower and childapi.option.ismulti():
|
||||
if 'empty' in props:
|
||||
obj = self.gen_properties(props,
|
||||
isfollower,
|
||||
childapi.option.ismulti())
|
||||
self.calc_raises_properties(obj, childapi)
|
||||
return obj
|
||||
|
||||
def gen_properties(self,
|
||||
properties,
|
||||
isfollower=False,
|
||||
ismulti=False):
|
||||
obj = {}
|
||||
if not isfollower and ismulti:
|
||||
if 'empty' in properties:
|
||||
obj['required'] = True
|
||||
props.remove('empty')
|
||||
if 'mandatory' in props:
|
||||
properties.remove('empty')
|
||||
if 'mandatory' in properties:
|
||||
obj['needs_len'] = True
|
||||
props.remove('mandatory')
|
||||
elif 'mandatory' in props:
|
||||
properties.remove('mandatory')
|
||||
elif 'mandatory' in properties:
|
||||
obj['required'] = True
|
||||
props.remove('mandatory')
|
||||
if 'frozen' in props:
|
||||
properties.remove('mandatory')
|
||||
if 'frozen' in properties:
|
||||
obj['readOnly'] = True
|
||||
props.remove('frozen')
|
||||
if 'hidden' in props:
|
||||
obj['hidden'] = True
|
||||
props.remove('hidden')
|
||||
if 'disabled' in props:
|
||||
obj['hidden'] = True
|
||||
props.remove('disabled')
|
||||
if props:
|
||||
lprops = list(props)
|
||||
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()
|
||||
obj['properties'] = lprops
|
||||
obj['props'] = lprops
|
||||
return obj
|
||||
|
||||
def gen_model(self,
|
||||
@@ -597,14 +621,11 @@ class TiramisuDict:
|
||||
if childapi.option.isoptiondescription():
|
||||
props = set(childapi.property.get())
|
||||
obj = {}
|
||||
if self.calc_raises_properties(childapi):
|
||||
obj['display'] = False
|
||||
self.calc_raises_properties(obj, childapi)
|
||||
if props:
|
||||
lprops = list(props)
|
||||
lprops.sort()
|
||||
obj['properties'] = lprops
|
||||
if 'hidden' in props or 'disabled' in props:
|
||||
obj['hidden'] = True
|
||||
try:
|
||||
self.config.option(path).option.get()
|
||||
except PropertiesOptionError:
|
||||
@@ -646,17 +667,27 @@ class TiramisuDict:
|
||||
obj,
|
||||
index,
|
||||
updates_status):
|
||||
# FIXME unrestraint ...
|
||||
try:
|
||||
nchildapi = self.config.option(path, index=index)
|
||||
with warnings.catch_warnings(record=True) as warns:
|
||||
value = nchildapi.value.get()
|
||||
if path in updates_status and index in updates_status[path]:
|
||||
value = childapi.value.get()
|
||||
self._get_value_with_exception(obj,
|
||||
childapi,
|
||||
warns)
|
||||
except PropertiesOptionError:
|
||||
value = childapi.value.get()
|
||||
warns = []
|
||||
updates_status[path][index])
|
||||
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()
|
||||
self._get_value_with_exception(obj,
|
||||
childapi,
|
||||
warns)
|
||||
except ValueError as err:
|
||||
self._get_value_with_exception(obj,
|
||||
childapi,
|
||||
[err])
|
||||
value = self.config.unrestraint.option(path, index=index).value.get()
|
||||
except PropertiesOptionError as err:
|
||||
value = childapi.value.get()
|
||||
if value is not None and value != []:
|
||||
obj['value'] = value
|
||||
obj['owner'] = childapi.owner.get()
|
||||
@@ -666,9 +697,13 @@ class TiramisuDict:
|
||||
childapi,
|
||||
values):
|
||||
for value in values:
|
||||
if isinstance(value.message, ValueErrorWarning):
|
||||
if isinstance(value, ValueError):
|
||||
obj.setdefault('error', [])
|
||||
obj['error'].append(str(value))
|
||||
obj['invalid'] = True
|
||||
elif isinstance(value.message, ValueErrorWarning):
|
||||
value.message.prefix = ''
|
||||
if childapi.option.isleader():
|
||||
if childapi.option.isfollower():
|
||||
obj.setdefault('invalid', [])
|
||||
obj['invalid'].append({'error': str(value.message),
|
||||
'index': value.message.index})
|
||||
@@ -681,10 +716,19 @@ class TiramisuDict:
|
||||
obj['warnings'].append(str(value.message))
|
||||
obj['hasWarnings'] = True
|
||||
|
||||
def gen_global(self):
|
||||
ret = {}
|
||||
ret['owner'] = self.config.owner.get()
|
||||
ret['properties'] = list(self.config.property.get())
|
||||
ret['properties'].sort()
|
||||
ret['permissives'] = list(self.config.permissive.get())
|
||||
ret['permissives'].sort()
|
||||
return ret
|
||||
|
||||
def get_form(self, form):
|
||||
ret = []
|
||||
buttons = []
|
||||
dict_form = OrderedDict()
|
||||
dict_form = {}
|
||||
for form_ in form:
|
||||
if 'key' in form_:
|
||||
dict_form[form_['key']] = form_
|
||||
@@ -723,7 +767,7 @@ class TiramisuDict:
|
||||
childapi.value.set(value)
|
||||
else:
|
||||
multi = childapi.value.get()
|
||||
if not multi and index == 0:
|
||||
if len(multi) < index + 1:
|
||||
multi.append(value)
|
||||
else:
|
||||
multi[index] = value
|
||||
@@ -744,26 +788,25 @@ class TiramisuDict:
|
||||
if childapi_option.isfollower():
|
||||
childapi = self.config.option(path, index)
|
||||
with warnings.catch_warnings(record=True) as warns:
|
||||
#try:
|
||||
if update['action'] == 'modify':
|
||||
self.mod_value(childapi,
|
||||
path,
|
||||
index,
|
||||
update.get('value'))
|
||||
elif update['action'] == 'delete':
|
||||
self.del_value(childapi,
|
||||
path,
|
||||
index)
|
||||
elif update['action'] == 'add':
|
||||
if childapi_option.ismulti():
|
||||
self.add_value(childapi, path, update['value'])
|
||||
try:
|
||||
if update['action'] == 'modify':
|
||||
self.mod_value(childapi,
|
||||
path,
|
||||
index,
|
||||
update.get('value', undefined))
|
||||
elif update['action'] == 'delete':
|
||||
self.del_value(childapi,
|
||||
path,
|
||||
index)
|
||||
elif update['action'] == 'add':
|
||||
if childapi_option.ismulti():
|
||||
self.add_value(childapi, path, update['value'])
|
||||
else:
|
||||
raise ValueError(_('only multi option can have action "add", but "{}" is not a multi').format(path))
|
||||
else:
|
||||
raise ValueError(_('only multi option can have action "add", but "{}" is not a multi').format(path))
|
||||
else:
|
||||
raise ValueError(_('unknown action'))
|
||||
#except ValueError as err:
|
||||
# updates_status.setdefault(path, {})[index] = err
|
||||
# continue
|
||||
raise ValueError(_('unknown action'))
|
||||
except ValueError as err:
|
||||
updates_status.setdefault(path, {})[index] = [err]
|
||||
if warns != []:
|
||||
updates_status.setdefault(path, {}).setdefault(index, []).extend(warns)
|
||||
return updates_status
|
||||
@@ -797,7 +840,7 @@ class TiramisuDict:
|
||||
updates_status={}):
|
||||
rootpath = self.root
|
||||
if build_schema:
|
||||
schema = OrderedDict()
|
||||
schema = {}
|
||||
else:
|
||||
schema = None
|
||||
if build_model:
|
||||
@@ -809,6 +852,7 @@ class TiramisuDict:
|
||||
buttons = []
|
||||
else:
|
||||
form = None
|
||||
ret = {}
|
||||
self.walk(rootpath,
|
||||
None,
|
||||
schema,
|
||||
@@ -832,6 +876,7 @@ class TiramisuDict:
|
||||
ret['schema'] = schema
|
||||
if build_model:
|
||||
ret['model'] = model
|
||||
ret['global'] = self.gen_global()
|
||||
if build_form:
|
||||
ret['form'] = form
|
||||
ret['version'] = '1.0'
|
||||
|
@@ -77,7 +77,7 @@ class Values(object):
|
||||
check_error=True)
|
||||
# store value in cache
|
||||
validator = 'validator' in option_bag.config_bag.properties
|
||||
if not is_cached or validator:
|
||||
if not option_bag.fromconsistency and (not is_cached or validator):
|
||||
self._p_.setcache(option_bag.path,
|
||||
option_bag.index,
|
||||
value,
|
||||
|
Reference in New Issue
Block a user