refactor valid_enum

This commit is contained in:
Emmanuel Garette 2020-10-03 21:43:50 +02:00
parent 8f7fa59333
commit b55b8f4d9d
59 changed files with 182 additions and 166 deletions

View File

@ -56,9 +56,6 @@ KEY_TYPE = {'variable': 'symlink',
'URLOption': 'web_address',
'FilenameOption': 'filename'}
TYPE_PARAM_CHECK = ('string', 'python', 'variable')
TYPE_PARAM_CONDITION = ('string', 'python', 'number', 'variable')
TYPE_PARAM_FILL = ('string', 'number', 'variable')
CONVERSION = {'number': int}
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
@ -476,9 +473,11 @@ class VariableAnnotator:
check.name = 'valid_enum'
check.target = path
check.namespace = namespace
param = self.objectspace.param()
param.text = str(FORCE_CHOICE[variable.type])
check.param = [param]
check.param = []
for value in FORCE_CHOICE[variable.type]:
param = self.objectspace.param()
param.text = value
check.param.append(param)
if not hasattr(self.objectspace.space, 'constraints'):
self.objectspace.space.constraints = self.objectspace.constraints()
self.objectspace.space.constraints.namespace = namespace
@ -627,8 +626,6 @@ class ConstraintAnnotator:
if hasattr(check, 'param'):
param_option_indexes = []
for idx, param in enumerate(check.param):
if param.type not in TYPE_PARAM_CHECK:
raise DictConsistencyError(_('cannot use {} type as a param in check for {}').format(param.type, check.target))
if param.type == 'variable' and not self.objectspace.paths.path_is_defined(param.text):
if param.optional is True:
param_option_indexes.append(idx)
@ -661,55 +658,50 @@ class ConstraintAnnotator:
remove_indexes = []
for idx, check in enumerate(self.objectspace.space.constraints.check):
if check.name == 'valid_enum':
if len(check.param) != 1:
raise DictConsistencyError(_(f'cannot set more than one param for valid_enum for variable {check.target}'))
param = check.param[0]
if check.target in self.valid_enums:
raise DictConsistencyError(_(f'valid_enum already set for {check.target}'))
if param.type not in ['string', 'python', 'number']:
raise DictConsistencyError(_(f'unknown type {param.type} for param in valid_enum for {check.target}'))
if not hasattr(check, 'param'):
raise DictConsistencyError(_(f'param is mandatory for a valid_enum of variable {check.target}'))
variable = self.objectspace.paths.get_variable_obj(check.target)
values = self.load_params_in_validenum(param,
variable.name,
variable.type,
)
self.valid_enums[check.target] = {'type': param.type,
'values': values}
values = self.load_params_in_valid_enum(check.param,
variable.name,
variable.type,
)
self._set_valid_enum(variable,
values,
variable.type,
check.target
)
remove_indexes.append(idx)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx]
def load_params_in_validenum(self,
param,
variable_name,
variable_type,
):
if not hasattr(param, 'text') and (param.type == 'python' or param.type == 'number'):
raise DictConsistencyError(_(f"All '{param.type}' variables shall be set in order to calculate valid_enum for variable {variable_name}"))
if variable_type == 'string' and param.type == 'number':
raise DictConsistencyError(_(f'Unconsistency valid_enum type ({param.type}), for variable {variable_name}'))
if param.type == 'python':
try:
values = eval(param.text, {'eosfunc': self.eosfunc, '__builtins__': {'range': range, 'str': str}})
except NameError:
raise DictConsistencyError(_('The function {} is unknown').format(param.text))
else:
try:
values = literal_eval(param.text)
except ValueError:
raise DictConsistencyError(_(f'Cannot load {param.text} in valid_enum'))
if not isinstance(values, list):
raise DictConsistencyError(_('Function {} shall return a list').format(param.text))
for value in values:
if variable_type == 'string' and not isinstance(value, str):
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a string'))
if variable_type == 'number' and not isinstance(value, int):
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a number'))
def load_params_in_valid_enum(self,
params,
variable_name,
variable_type,
):
has_variable = None
values = []
for param in params:
if param.type == 'variable':
if has_variable is not None:
raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
has_variable = True
variable = self.objectspace.paths.get_variable_obj(param.text)
if not variable.multi:
raise DictConsistencyError(_(f'only multi "variable" parameter is allowed for valid_enum of variable {variable_name}'))
values = param.text
else:
if has_variable:
raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
if not hasattr(param, 'text'):
if param.type == 'number':
raise DictConsistencyError(_(f'value is mandatory for valid_enum of variable {variable_name}'))
values.append(None)
else:
values.append(param.text)
return values
def check_change_warning(self):
@ -736,9 +728,6 @@ class ConstraintAnnotator:
def check_params_target(self):
for condition in self.objectspace.space.constraints.condition:
for param in condition.param:
if param.type not in TYPE_PARAM_CONDITION:
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a condition'))
if not hasattr(condition, 'target'):
raise DictConsistencyError(_('target is mandatory in condition'))
for target in condition.target:
@ -919,40 +908,52 @@ class ConstraintAnnotator:
variable.property.append(prop)
del self.objectspace.space.constraints.condition
def _set_valid_enum(self, variable, values, type_):
def _set_valid_enum(self, variable, values, type_, target):
# value for choice's variable is mandatory
variable.mandatory = True
# build choice
variable.choice = []
choices = []
for value in values:
if isinstance(values, str):
choice = self.objectspace.choice()
try:
choice.name = CONVERSION.get(type_, str)(value)
except:
raise DictConsistencyError(_(f'unable to change type of a valid_enum entry "{value}" is not a valid "{type_}" for "{variable.name}"'))
if choice.name == '':
choice.name = None
choices.append(choice.name)
choice.type = type_
choice.type = 'calculation'
choice.name = values
variable.choice.append(choice)
else:
self.valid_enums[target] = {'type': type_,
'values': values,
}
choices = []
for value in values:
choice = self.objectspace.choice()
try:
if value is not None:
choice.name = CONVERSION.get(type_, str)(value)
else:
choice.name = value
except:
raise DictConsistencyError(_(f'unable to change type of a valid_enum entry "{value}" is not a valid "{type_}" for "{variable.name}"'))
if choice.name == '':
choice.name = None
choices.append(choice.name)
choice.type = type_
variable.choice.append(choice)
# check value or set first choice value has default value
if hasattr(variable, 'value'):
for value in variable.value:
value.type = type_
try:
cvalue = CONVERSION.get(type_, str)(value.name)
except:
raise DictConsistencyError(_(f'unable to change type of value "{value}" is not a valid "{type_}" for "{variable.name}"'))
if cvalue not in choices:
raise DictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices))
else:
new_value = self.objectspace.value()
new_value.name = choices[0]
new_value.type = type_
variable.value = [new_value]
if not variable.choice:
raise DictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name))
# check value or set first choice value has default value
if hasattr(variable, 'value'):
for value in variable.value:
value.type = type_
try:
cvalue = CONVERSION.get(type_, str)(value.name)
except:
raise DictConsistencyError(_(f'unable to change type of value "{value}" is not a valid "{type_}" for "{variable.name}"'))
if cvalue not in choices:
raise DictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices))
else:
new_value = self.objectspace.value()
new_value.name = values[0]
new_value.type = type_
variable.value = [new_value]
variable.type = 'choice'
def convert_check(self):
@ -1030,8 +1031,6 @@ class ConstraintAnnotator:
if hasattr(fill, 'param'):
param_to_delete = []
for fill_idx, param in enumerate(fill.param):
if param.type not in TYPE_PARAM_FILL:
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a fill/auto'))
if param.type != 'string' and not hasattr(param, 'text'):
raise DictConsistencyError(_(f"All '{param.type}' variables shall have a value in order to calculate {fill.target}"))
if param.type == 'variable':

View File

@ -141,7 +141,7 @@
<!ATTLIST group description CDATA #IMPLIED>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param type (string|variable|number|python) "string">
<!ATTLIST param type (string|number|variable|information) "string">
<!ATTLIST param name CDATA #IMPLIED>
<!ATTLIST param notraisepropertyerror (True|False) "False">
<!ATTLIST param optional (True|False) "False">

View File

@ -252,7 +252,7 @@ class Common:
if not self.attrib[key]:
continue
value = "frozenset({" + self.attrib[key] + "})"
elif key in ['default', 'multi', 'suffixes', 'validators']:
elif key in ['default', 'multi', 'suffixes', 'validators', 'values']:
value = self.attrib[key]
elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
value = "'" + value.replace("'", "\\\'") + "'"
@ -368,9 +368,16 @@ class Variable(Common):
elif tag == 'check':
self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
elif tag == 'choice':
choices.append(child.name)
if child.type == 'calculation':
value = self.storage.get(child.name).get()
choices = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
else:
choices.append(child.name)
if choices:
self.attrib['values'] = tuple(choices)
if isinstance(choices, list):
self.attrib['values'] = str(tuple(choices))
else:
self.attrib['values'] = choices
if self.attrib['default'] == []:
del self.attrib['default']
elif not self.attrib['multi'] and isinstance(self.attrib['default'], list):

View File

@ -58,4 +58,4 @@ def device_type(*args, **kwargs):
def calc_list(*args, **kwargs):
return []
return list(args)

View File

@ -20,7 +20,9 @@
<constraints>
<check name="valid_enum" target="condition">
<param>['tous', 'authentifié', 'aucun']</param>
<param>tous</param>
<param>authentifié</param>
<param>aucun</param>
</check>
<condition name="hidden_if_in" source="condition">
<param>tous</param>

View File

@ -19,7 +19,9 @@
<constraints>
<check name="valid_enum" target="condition">
<param>['tous', 'authentifié', 'aucun']</param>
<param>tous</param>
<param>authentifié</param>
<param>aucun</param>
</check>
<condition name='hidden_if_in' source='condition'>
<param>oui</param>

View File

@ -19,7 +19,10 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a', 'b', 'c', 'é']</param>
<param>a</param>
<param>b</param>
<param>c</param>
<param>é</param>
</check>
</constraints>

View File

@ -19,7 +19,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>

View File

@ -22,10 +22,14 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
<check name="valid_enum" target="enumvar2">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>

View File

@ -14,7 +14,8 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a','c']</param>
<param>a</param>
<param>c</param>
</check>
</constraints>

View File

@ -1,25 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services/>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="string" description="No change">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
<constraints>
<check name="valid_enum" target="mode_conteneur_actif">
<param type="python">eosfunc.list_files('/notexists', default=['oui', 'non'])</param>
</check>
</constraints>
<help/>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -1 +0,0 @@
{"rougail.general.mode_conteneur_actif": "non"}

View File

@ -1,8 +0,0 @@
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
import imp
func = imp.load_source('func', 'tests/flattener_dicos/../eosfunc/test.py')
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -17,7 +17,9 @@
<constraints>
<check name="valid_enum" target="follower1">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
<group leader="leader">
<follower>follower1</follower>

View File

@ -15,7 +15,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>
</rougail>

View File

@ -15,7 +15,9 @@
<constraints>
<check name="valid_enum" target="multi">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>

View File

@ -19,7 +19,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>['a','b','']</param>
<param>a</param>
<param>b</param>
<param/>
</check>
</constraints>

View File

@ -11,7 +11,7 @@
</family>
<family name="enumfam" mode="expert">
<variable name="enumvar" type="string" description="multi">
<value>c</value>
<value>b</value>
</variable>
</family>
<separators/>
@ -19,7 +19,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param type="python">eosfunc.calc_multi_val(['pouet'])</param>
<param>a</param>
<param>b</param>
<param></param>
</check>
</constraints>

View File

@ -1 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "rougail.enumfam.enumvar": "test"}
{"rougail.general.mode_conteneur_actif": "non", "rougail.enumfam.enumvar": "b"}

View File

@ -4,7 +4,7 @@ import imp
func = imp.load_source('func', 'tests/flattener_dicos/../eosfunc/test.py')
option_3 = ChoiceOption(properties=frozenset({'expert', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'expert'}), children=[option_3])
option_5 = ChoiceOption(properties=frozenset({'expert', 'mandatory'}), name='enumvar', doc='multi', multi=False, default='test', values=('test',))
option_5 = ChoiceOption(properties=frozenset({'expert', 'mandatory'}), name='enumvar', doc='multi', multi=False, default='b', values=('a', 'b', None))
option_5.impl_set_information("help", "bla bla bla")
option_4 = OptionDescription(name='enumfam', doc='enumfam', properties=frozenset({'expert'}), children=[option_5])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2, option_4])

View File

@ -17,7 +17,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>[1, 2, 3]</param>
<param>1</param>
<param>2</param>
<param>3</param>
</check>
</constraints>

View File

@ -19,7 +19,9 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param>[1, 2, 3]</param>
<param>1</param>
<param>2</param>
<param>3</param>
</check>
</constraints>

View File

@ -14,7 +14,9 @@
<constraints>
<check name="valid_enum" target="mode_conteneur_actif">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>

View File

@ -10,8 +10,11 @@
</variable>
</family>
<family name="enumfam" mode="expert">
<variable name="varmulti" type="string" description="multi" multi="True" hidden="True">
<value>test</value>
</variable>
<variable name="enumvar" type="string" description="multi">
<value>c</value>
<value>test</value>
</variable>
</family>
<separators/>
@ -19,8 +22,11 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param type="python">eosfunc.valid_lower('toto')</param>
<param type="variable">varmulti</param>
</check>
<fill name="calc_list" target="varmulti">
<param>test</param>
</fill>
</constraints>
<help>

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "rougail.enumfam.enumvar": "test", "rougail.enumfam.varmulti": ["test"]}

View File

@ -0,0 +1,12 @@
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
import imp
func = imp.load_source('func', 'tests/flattener_dicos/../eosfunc/test.py')
option_3 = ChoiceOption(properties=frozenset({'expert', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'expert'}), children=[option_3])
option_5 = StrOption(properties=frozenset({'expert', 'force_default_on_freeze', 'frozen', 'hidden', 'mandatory'}), name='varmulti', doc='multi', multi=True, default=Calculation(func.calc_list, Params((ParamValue("test")), kwargs={})))
option_6 = ChoiceOption(properties=frozenset({'expert', 'mandatory'}), name='enumvar', doc='multi', multi=False, default='test', values=Calculation(func.calc_value, Params((ParamOption(option_5)))))
option_6.impl_set_information("help", "bla bla bla")
option_4 = OptionDescription(name='enumfam', doc='enumfam', properties=frozenset({'expert'}), children=[option_5, option_6])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2, option_4])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -25,7 +25,8 @@
<constraints>
<check name="valid_enum" target="condition">
<param>['non','statique']</param>
<param>non</param>
<param>statique</param>
</check>
<condition name="disabled_if_not_in" source="condition">
<param>statique</param>

View File

@ -20,7 +20,9 @@
<constraints>
<check name="valid_enum" target="mode_conteneur_actif3">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
<condition name="disabled_if_in" source="mode_conteneur_actif3">
<param>d</param>

View File

@ -12,7 +12,9 @@
<constraints>
<check name="valid_enum" target="mode_conteneur_actif">
<param>['a','b','c']</param>
<param>a</param>
<param>b</param>
<param>c</param>
</check>
</constraints>

View File

@ -12,7 +12,8 @@
<constraints>
<check name="valid_enum" target="mode_conteneur_actif">
<param>['a','b']</param>
<param>a</param>
<param>b</param>
</check>
</constraints>

View File

@ -1,21 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services/>
<variables>
<family name="proxy authentifié">
<variable name="toto1" type="port" description="Port d'écoute du proxy" mode="expert">
</variable>
<variable name="toto2" type="port" description="Port d'écoute du proxy NTLM" mode="expert">
<value>3127</value>
</variable>
</family>
</variables>
<constraints>
<check name="valid_enum" target="toto1">
<param type="python"/>
</check>
</constraints>
</rougail>

View File

@ -10,9 +10,7 @@
</variables>
<constraints>
<check name="valid_enum" target="mode_conteneur_actif">
<param>[]</param>
</check>
<check name="valid_enum" target="mode_conteneur_actif"/>
</constraints>
<help/>

View File

@ -10,6 +10,9 @@
</variable>
</family>
<family name="enumfam" mode="expert">
<variable name="varmulti" type="string" description="multi" hidden="True">
<value>test</value>
</variable>
<variable name="enumvar" type="string" description="multi">
<value>test</value>
</variable>
@ -19,8 +22,11 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param type="python">eosfunc.calc_multi_val(['test'])</param>
<param type="variable">varmulti</param>
</check>
<fill name="calc_value" target="varmulti">
<param>test</param>
</fill>
</constraints>
<help>

View File

@ -10,8 +10,11 @@
</variable>
</family>
<family name="enumfam" mode="expert">
<variable name="varmulti" type="string" description="multi" multi="True" hidden="True">
<value>test</value>
</variable>
<variable name="enumvar" type="string" description="multi">
<value>c</value>
<value>test</value>
</variable>
</family>
<separators/>
@ -19,8 +22,11 @@
<constraints>
<check name="valid_enum" target="enumvar">
<param type="python">toto</param>
<param type="variable">varmulti_unknown</param>
</check>
<fill name="calc_list" target="varmulti">
<param>test</param>
</fill>
</constraints>
<help>