same target and param in check|condition|fill

This commit is contained in:
2021-02-08 17:46:58 +01:00
parent 0100c692b2
commit 6f05c22a3d
230 changed files with 1054 additions and 648 deletions

View File

@ -27,14 +27,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from typing import List, Any
from .variable import CONVERT_OPTION
from .target import TargetAnnotator
from .param import ParamAnnotator
from ..utils import load_modules
from ..i18n import _
from ..error import DictConsistencyError
from ..error import DictConsistencyError, display_xmlfiles
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
class CheckAnnotator:
class CheckAnnotator(TargetAnnotator, ParamAnnotator):
"""Annotate check
"""
def __init__(self,
@ -45,12 +47,19 @@ class CheckAnnotator:
not hasattr(objectspace.space.constraints, 'check'):
return
self.objectspace = objectspace
self.let_none = True
self.only_variable = True
self.functions = dir(load_modules(eosfunc_file))
self.functions.extend(INTERNAL_FUNCTIONS)
self.target_is_uniq = False
self.convert_target(self.objectspace.space.constraints.check)
self.convert_param(self.objectspace.space.constraints.check)
self.check_check()
self.check_valid_enum()
self.check_change_warning()
self.convert_valid_entier()
self.convert_check()
del objectspace.space.constraints.check
def check_check(self): # pylint: disable=R0912
"""valid and manage <check>
@ -58,56 +67,14 @@ class CheckAnnotator:
remove_indexes = []
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
if not check.name in self.functions:
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'cannot find check function "{check.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 1)
check_name = check.target
# let's replace the target by the an object
try:
check.target = self.objectspace.paths.get_variable(check.target)
except DictConsistencyError as err:
if err.errno == 36:
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'the target "{check.target}" in check cannot be a dynamic '
f'variable in {xmlfiles}')
raise DictConsistencyError(msg, 22) from err
raise err
check.is_in_leadership = self.objectspace.paths.is_in_leadership(check_name)
if not hasattr(check, 'param'):
continue
param_option_indexes = []
for idx, param in enumerate(check.param):
if param.type == 'variable':
if not self.objectspace.paths.path_is_defined(param.text):
if not param.optional:
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'cannot find check param "{param.text}" in {xmlfiles}')
raise DictConsistencyError(msg, 2)
param_option_indexes.append(idx)
else:
# let's replace params by the path
param.text = self.objectspace.paths.get_variable(param.text)
param_option_indexes.sort(reverse=True)
for idx in param_option_indexes:
check.param.pop(idx)
if check.param == []:
msg = _(f'cannot find check function "{check.name}"')
raise DictConsistencyError(msg, 1, check.xmlfiles)
if hasattr(check, 'param') and check.param == []:
remove_indexes.append(check_idx)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx]
@staticmethod
def check_valid_enum_value(variable,
values,
) -> None:
"""check that values in valid_enum are valid
"""
for value in variable.value:
if value.name not in values:
msg = _(f'value "{value.name}" of variable "{variable.name}" is not in list '
f'of all expected values ({values})')
raise DictConsistencyError(msg, 15)
def check_valid_enum(self):
"""verify valid_enum
"""
@ -115,32 +82,30 @@ class CheckAnnotator:
for idx, check in enumerate(self.objectspace.space.constraints.check):
if check.name != 'valid_enum':
continue
if check.target.path in self.objectspace.valid_enums:
check_xmlfiles = self.objectspace.valid_enums[check.target.path]['xmlfiles']
old_xmlfiles = self.objectspace.display_xmlfiles(check_xmlfiles)
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'valid_enum define in {xmlfiles} but already set in {old_xmlfiles} '
f'for "{check.target.name}", did you forget remove_check?')
raise DictConsistencyError(msg, 3)
if not hasattr(check, 'param'):
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'param is mandatory for a valid_enum of variable "{check.target.name}" '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 4)
variable_type = check.target.type
values = self._set_valid_enum(check.target,
check,
)
if values:
if hasattr(check.target, 'value'):
# check value
self.check_valid_enum_value(check.target, values)
else:
# no value, set the first choice has default value
new_value = self.objectspace.value(check.xmlfiles)
new_value.name = values[0]
new_value.type = variable_type
check.target.value = [new_value]
for target in check.target:
if target.name.path in self.objectspace.valid_enums:
check_xmlfiles = display_xmlfiles(self.objectspace.valid_enums\
[target.name.path]['xmlfiles'])
msg = _(f'valid_enum already set in {check_xmlfiles} '
f'for "{target.name.name}", you may have forget remove_check')
raise DictConsistencyError(msg, 3, check.xmlfiles)
if not hasattr(check, 'param'):
msg = _(f'param is mandatory for a valid_enum of variable "{target.name.name}"')
raise DictConsistencyError(msg, 4, check.xmlfiles)
variable_type = target.name.type
values = self._set_valid_enum(target.name,
check,
)
if values:
if hasattr(target.name, 'value'):
# check value
self.check_valid_enum_value(target.name, values)
else:
# no value, set the first choice has default value
new_value = self.objectspace.value(check.xmlfiles)
new_value.name = values[0]
new_value.type = variable_type
target.name.value = [new_value]
remove_indexes.append(idx)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
@ -161,47 +126,36 @@ class CheckAnnotator:
values = []
for param in check.param:
if has_variable:
xmlfiles = self.objectspace.display_xmlfiles(param.xmlfiles)
msg = _(f'only one "variable" parameter is allowed for valid_enum '
f'of variable "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 5)
f'of variable "{variable.name}"')
raise DictConsistencyError(msg, 5, param.xmlfiles)
param_type = variable_type
if param.type == 'variable':
has_variable = True
if param.optional is True:
xmlfiles = self.objectspace.display_xmlfiles(param.xmlfiles)
msg = _(f'optional parameter in valid_enum for variable "{variable.name}" '
f'is not allowed in {xmlfiles}')
raise DictConsistencyError(msg, 14)
param_variable = param.text
if not param_variable.multi:
xmlfiles = self.objectspace.display_xmlfiles(param.xmlfiles)
f'is not allowed')
raise DictConsistencyError(msg, 14, param.xmlfiles)
if not param.text.multi:
msg = _(f'only multi "variable" parameter is allowed for valid_enum '
f'of variable "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 6)
f'of variable "{variable.name}"')
raise DictConsistencyError(msg, 6, param.xmlfiles)
param_type = 'calculation'
value = param.text
elif param.type == 'nil':
value = None
else:
if 'type' in vars(param) and variable_type != param.type:
xmlfiles = self.objectspace.display_xmlfiles(param.xmlfiles)
msg = _(f'parameter in valid_enum has incompatible type "{param.type}" '
f'with type of the variable "{variable.name}" ("{variable_type}") '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 7)
f'with type of the variable "{variable.name}" ("{variable_type}")')
raise DictConsistencyError(msg, 7, param.xmlfiles)
if hasattr(param, 'text'):
try:
value = CONVERT_OPTION[variable_type].get('func', str)(param.text)
except ValueError as err:
msg = _(f'unable to change type of a valid_enum entry "{param.text}" '
f'is not a valid "{variable_type}" for "{variable.name}"')
raise DictConsistencyError(msg, 13) from err
else:
if param.type == 'number':
xmlfiles = self.objectspace.display_xmlfiles(param.xmlfiles)
msg = _('param type is number, so value is mandatory for valid_enum '
f'of variable "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 8)
value = None
raise DictConsistencyError(msg, 13, variable.xmlfiles) from err
values.append(value)
choice = self.objectspace.choice(variable.xmlfiles)
choice.name = value
@ -211,12 +165,25 @@ class CheckAnnotator:
if has_variable:
return None
self.objectspace.valid_enums[check.target.path] = {'type': variable_type,
'values': values,
'xmlfiles': check.xmlfiles,
}
for target in check.target:
self.objectspace.valid_enums[target.name.path] = {'type': variable_type,
'values': values,
'xmlfiles': check.xmlfiles,
}
return values
@staticmethod
def check_valid_enum_value(variable,
values,
) -> None:
"""check that values in valid_enum are valid
"""
for value in variable.value:
if value.name not in values:
msg = _(f'value "{value.name}" of variable "{variable.name}" is not in list '
f'of all expected values ({values})')
raise DictConsistencyError(msg, 15, value.xmlfiles)
def check_change_warning(self):
"""convert level to "warnings_only"
"""
@ -224,30 +191,38 @@ class CheckAnnotator:
check.warnings_only = check.level == 'warning'
check.level = None
def convert_valid_entier(self) -> None:
"""valid and manage <check>
"""
remove_indexes = []
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
if not check.name == 'valid_entier':
continue
remove_indexes.append(check_idx)
if not hasattr(check, 'param'):
msg = _(f'{check.name} must have, at least, 1 param')
raise DictConsistencyError(msg, 17, check.xmlfiles)
for param in check.param:
if param.type != 'number':
msg = _(f'param in "valid_entier" must be an "integer", not "{param.type}"')
raise DictConsistencyError(msg, 18, check.xmlfiles)
for target in check.target:
if param.name == 'mini':
target.name.min_number = int(param.text)
elif param.name == 'maxi':
target.name.max_number = int(param.text)
else:
msg = _(f'unknown parameter "{param.name}" in check "valid_entier"')
raise DictConsistencyError(msg, 19, check.xmlfiles)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx]
def convert_check(self) -> None:
"""valid and manage <check>
"""
for check in self.objectspace.space.constraints.check:
if check.name == 'valid_entier':
if not hasattr(check, 'param'):
msg = _(f'{check.name} must have, at least, 1 param')
raise DictConsistencyError(msg, 17)
for param in check.param:
if param.type != 'number':
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'param in "valid_entier" must be an "integer", not "{param.type}"'
f' in {xmlfiles}')
raise DictConsistencyError(msg, 18)
if param.name == 'mini':
check.target.min_number = int(param.text)
elif param.name == 'maxi':
check.target.max_number = int(param.text)
else:
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
msg = _(f'unknown parameter "{param.name}" in check "valid_entier" '
f'for variable "{check.target.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 19)
continue
if not hasattr(check.target, 'validators'):
check.target.validators = []
check.target.validators.append(check)
for target in check.target:
if not hasattr(target.name, 'validators'):
target.name.validators = []
target.name.validators.append(check)

View File

@ -31,10 +31,13 @@ from ..i18n import _
from ..error import DictConsistencyError
from ..config import Config
from .target import TargetAnnotator
from .param import ParamAnnotator
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
class ConditionAnnotator:
class ConditionAnnotator(TargetAnnotator, ParamAnnotator):
"""Annotate condition
"""
def __init__(self,
@ -47,7 +50,11 @@ class ConditionAnnotator:
if not hasattr(objectspace.space, 'constraints') or \
not hasattr(self.objectspace.space.constraints, 'condition'):
return
self.convert_condition_target()
self.target_is_uniq = False
self.only_variable = False
self.convert_target(self.objectspace.space.constraints.condition)
self.convert_param(self.objectspace.space.constraints.condition)
self.check_source_target()
self.check_condition_fallback()
self.convert_xxxlist()
self.convert_condition_source()
@ -59,29 +66,6 @@ class ConditionAnnotator:
"""convert auto_freeze
only if FREEZE_AUTOFREEZE_VARIABLE == 'oui' this variable is frozen
"""
def _convert_auto_freeze(variable):
if not variable.auto_freeze:
return
if variable.namespace != Config['variable_namespace']:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}" in {xmlfiles}')
raise DictConsistencyError(msg, 49)
new_condition = self.objectspace.condition(variable.xmlfiles)
new_condition.name = 'auto_frozen_if_not_in'
new_condition.namespace = variable.namespace
new_condition.source = FREEZE_AUTOFREEZE_VARIABLE
new_param = self.objectspace.param(variable.xmlfiles)
new_param.text = 'oui'
new_condition.param = [new_param]
new_target = self.objectspace.target(variable.xmlfiles)
new_target.type = 'variable'
new_target.name = variable.name
new_condition.target = [new_target]
if not hasattr(self.objectspace.space, 'constraints'):
self.objectspace.space.constraints = self.objectspace.constraints(variable.xmlfiles)
if not hasattr(self.objectspace.space.constraints, 'condition'):
self.objectspace.space.constraints.condition = []
self.objectspace.space.constraints.condition.append(new_condition)
for variables in self.objectspace.space.variables.values():
for family in variables.family.values():
if not hasattr(family, 'variable'):
@ -89,50 +73,45 @@ class ConditionAnnotator:
for variable in family.variable.values():
if isinstance(variable, self.objectspace.leadership):
for follower in variable.variable:
_convert_auto_freeze(follower)
self._convert_auto_freeze(follower)
else:
_convert_auto_freeze(variable)
self._convert_auto_freeze(variable)
def convert_condition_target(self):
"""verify and manage target in condition
def _convert_auto_freeze(self,
variable: 'self.objectspace.variable',
) -> None:
if not variable.auto_freeze:
return
if variable.namespace != Config['variable_namespace']:
msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
raise DictConsistencyError(msg, 49, variable.xmlfiles)
new_condition = self.objectspace.condition(variable.xmlfiles)
new_condition.name = 'auto_frozen_if_not_in'
new_condition.namespace = variable.namespace
new_condition.source = FREEZE_AUTOFREEZE_VARIABLE
new_param = self.objectspace.param(variable.xmlfiles)
new_param.text = 'oui'
new_condition.param = [new_param]
new_target = self.objectspace.target(variable.xmlfiles)
new_target.type = 'variable'
new_target.name = variable.name
new_condition.target = [new_target]
if not hasattr(self.objectspace.space, 'constraints'):
self.objectspace.space.constraints = self.objectspace.constraints(variable.xmlfiles)
if not hasattr(self.objectspace.space.constraints, 'condition'):
self.objectspace.space.constraints.condition = []
self.objectspace.space.constraints.condition.append(new_condition)
def check_source_target(self):
"""verify that source != target in condition
"""
for condition in self.objectspace.space.constraints.condition:
if not hasattr(condition, 'target'):
xmlfiles = self.objectspace.display_xmlfiles(condition.xmlfiles)
msg = _(f'target is mandatory in a condition for source "{condition.source}" '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 9)
remove_targets = []
for index, target in enumerate(condition.target):
try:
if target.type == 'variable':
if condition.source == target.name:
msg = _('target name and source name must be different: '
f'{condition.source}')
raise DictConsistencyError(msg, 11)
target.name = self.objectspace.paths.get_variable(target.name)
elif target.type == 'family':
target.name = self.objectspace.paths.get_family(target.name,
condition.namespace,
)
elif target.type.endswith('list') and \
condition.name not in ['disabled_if_in', 'disabled_if_not_in']:
xmlfiles = self.objectspace.display_xmlfiles(target.xmlfiles)
msg = _(f'target "{target.type}" not allow in condition "{condition.name}" '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 10)
except DictConsistencyError as err:
if err.errno != 42:
raise err
# for optional variable
if not target.optional:
xmlfiles = self.objectspace.display_xmlfiles(condition.xmlfiles)
msg = f'cannot found target "{target.name}" in the condition in {xmlfiles}'
raise DictConsistencyError(_(msg), 12) from err
remove_targets.append(index)
remove_targets.sort(reverse=True)
for index in remove_targets:
condition.target.pop(index)
for target in condition.target:
if target.type == 'variable' and \
condition.source in [target.name.name, target.name.path]:
msg = _('target name and source name must be different: '
f'{condition.source}')
raise DictConsistencyError(msg, 11, condition.xmlfiles)
def check_condition_fallback(self):
"""a condition with a fallback **and** the source variable doesn't exist
@ -140,17 +119,16 @@ class ConditionAnnotator:
remove_conditions = []
for idx, condition in enumerate(self.objectspace.space.constraints.condition):
# fallback
if condition.fallback is True and \
not self.objectspace.paths.path_is_defined(condition.source):
apply_action = False
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
apply_action = not condition.force_condition_on_fallback
else:
apply_action = condition.force_inverse_condition_on_fallback
remove_conditions.append(idx)
if apply_action:
self.force_actions_to_variable(condition)
remove_conditions = list(set(remove_conditions))
if condition.fallback is False or \
self.objectspace.paths.path_is_defined(condition.source):
continue
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
apply_action = not condition.force_condition_on_fallback
else:
apply_action = condition.force_inverse_condition_on_fallback
remove_conditions.append(idx)
if apply_action:
self.force_actions_to_variable(condition)
remove_conditions.sort(reverse=True)
for idx in remove_conditions:
self.objectspace.space.constraints.condition.pop(idx)
@ -234,7 +212,6 @@ class ConditionAnnotator:
if target.name in self.force_service_value:
listvar.default = self.force_service_value[target.name]
continue
value = condition.name != 'disabled_if_in'
if listvar.path in fills:
fill = fills[listvar.path]
or_needed = True
@ -246,7 +223,9 @@ class ConditionAnnotator:
fill.index += 1
else:
fill = self.objectspace.fill(target.xmlfiles)
fill.target = listvar.path
new_target = self.objectspace.target(target.xmlfiles)
new_target.name = listvar.path
fill.target = [new_target]
fill.name = 'calc_value'
fill.namespace = 'services'
fill.index = 0
@ -284,7 +263,7 @@ class ConditionAnnotator:
fill.param.append(param5)
if or_needed:
param6 = self.objectspace.param(target.xmlfiles)
param6.name = f'condition_operator'
param6.name = 'condition_operator'
param6.text = 'OR'
fill.param.append(param6)
@ -296,15 +275,12 @@ class ConditionAnnotator:
condition.source = self.objectspace.paths.get_variable(condition.source)
except DictConsistencyError as err:
if err.errno == 36:
xmlfiles = self.objectspace.display_xmlfiles(condition.xmlfiles)
msg = _(f'the source "{condition.source}" in condition cannot be a dynamic '
f'variable in {xmlfiles}')
raise DictConsistencyError(msg, 20) from err
f'variable')
raise DictConsistencyError(msg, 20, condition.xmlfiles) from err
if err.errno == 42:
xmlfiles = self.objectspace.display_xmlfiles(condition.xmlfiles)
msg = _(f'the source "{condition.source}" in condition is an unknown variable '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 23) from err
msg = _(f'the source "{condition.source}" in condition is an unknown variable')
raise DictConsistencyError(msg, 23, condition.xmlfiles) from err
raise err from err # pragma: no cover
def check_choice_option_condition(self):

View File

@ -138,13 +138,11 @@ class FamilyAnnotator:
leader_mode = None
for follower in leadership.variable:
if follower.auto_save is True:
xmlfiles = self.objectspace.display_xmlfiles(leadership.xmlfiles)
msg = _(f'leader/followers "{follower.name}" could not be auto_save in {xmlfiles}')
raise DictConsistencyError(msg, 29)
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
raise DictConsistencyError(msg, 29, leadership.xmlfiles)
if follower.auto_freeze is True:
xmlfiles = self.objectspace.display_xmlfiles(leadership.xmlfiles)
msg = f'leader/followers "{follower.name}" could not be auto_freeze in {xmlfiles}'
raise DictConsistencyError(_(msg), 30)
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
raise DictConsistencyError(_(msg), 30, leadership.xmlfiles)
self._change_variabe_mode(follower,
family_mode,
is_follower,
@ -169,10 +167,9 @@ class FamilyAnnotator:
family.suffixes = self.objectspace.paths.get_variable(family.dynamic)
del family.dynamic
if not family.suffixes.multi:
xmlfiles = self.objectspace.display_xmlfiles(family.xmlfiles)
msg = _(f'dynamic family "{family.name}" must be linked '
f'to multi variable in {xmlfiles}')
raise DictConsistencyError(msg, 16)
f'to multi variable')
raise DictConsistencyError(msg, 16, family.xmlfiles)
def convert_help(self):
"""Convert variable help

View File

@ -28,8 +28,11 @@ from ..utils import load_modules
from ..i18n import _
from ..error import DictConsistencyError
from .target import TargetAnnotator
from .param import ParamAnnotator
class FillAnnotator:
class FillAnnotator(TargetAnnotator, ParamAnnotator):
"""Fill annotator
"""
def __init__(self,
@ -41,97 +44,32 @@ class FillAnnotator:
not hasattr(self.objectspace.space.constraints, 'fill'):
return
self.functions = dir(load_modules(eosfunc_file))
self.convert_fill()
self.target_is_uniq = True
self.only_variable = True
self.convert_target(self.objectspace.space.constraints.fill)
self.convert_param(self.objectspace.space.constraints.fill)
self.fill_to_value()
del self.objectspace.space.constraints.fill
def convert_fill(self) -> None:
def fill_to_value(self) -> None:
"""valid and manage <fill>
"""
targets = []
for fill in self.objectspace.space.constraints.fill:
# test if it's redefined calculation
if fill.target in targets:
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _(f'A fill already exists for the target of "{fill.target}" created '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 24)
targets.append(fill.target)
for target in fill.target:
# test if the function exists
if fill.name not in self.functions:
msg = _(f'cannot find fill function "{fill.name}"')
raise DictConsistencyError(msg, 25, fill.xmlfiles)
# test if the function exists
if fill.name not in self.functions:
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _(f'cannot find fill function "{fill.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 25)
# create an object value
value = self.objectspace.value(fill.xmlfiles)
value.type = 'calculation'
value.name = fill.name
if target.name.namespace == 'services':
target.name.default = value
else:
target.name.value = [value]
# let's replace the target by the path
fill.target, suffix = self.objectspace.paths.get_variable_path(fill.target,
fill.namespace,
)
if suffix is not None:
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _(f'Cannot add fill function to "{fill.target}" only '
f'for the suffix "{suffix}" in {xmlfiles}')
raise DictConsistencyError(msg, 26)
# get the target variable
variable = self.objectspace.paths.get_variable(fill.target)
# create an object value
value = self.objectspace.value(fill.xmlfiles)
value.type = 'calculation'
value.name = fill.name
if variable.namespace == 'services':
variable.default = value
else:
variable.value = [value]
# manage params
if not hasattr(fill, 'param'):
continue
self.convert_fill_param(fill)
if fill.param:
value.param = fill.param
def convert_fill_param(self,
fill: "self.objectspace.fill",
) -> None:
""" valid and convert fill's param
"""
param_to_delete = []
for param_idx, param in enumerate(fill.param):
if param.type == 'string' and not hasattr(param, 'text'):
param.text = None
if param.type == 'suffix':
if hasattr(param, 'text'):
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _(f'"{param.type}" variables must not have a value in order '
f'to calculate "{fill.target}" in {xmlfiles}')
raise DictConsistencyError(msg, 28)
if not self.objectspace.paths.variable_is_dynamic(fill.target):
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _('Cannot set suffix target to the none dynamic variable '
f'"{fill.target}" in {xmlfiles}')
raise DictConsistencyError(msg, 53)
elif not hasattr(param, 'text'):
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
msg = _(f'All "{param.type}" variables must have a value in order '
f'to calculate "{fill.target}" in {xmlfiles}')
raise DictConsistencyError(msg, 27)
if param.type == 'variable':
try:
path, suffix = self.objectspace.paths.get_variable_path(param.text,
fill.namespace,
)
param.text = self.objectspace.paths.get_variable(path)
if suffix:
param.suffix = suffix
family_path = self.objectspace.paths.get_variable_family_path(path)
param.family = self.objectspace.paths.get_family(family_path,
param.text.namespace,
)
except DictConsistencyError as err:
if err.errno != 42 or not param.optional:
raise err
param_to_delete.append(param_idx)
param_to_delete.sort(reverse=True)
for param_idx in param_to_delete:
fill.param.pop(param_idx)
# manage params
if hasattr(fill, 'param') and fill.param:
value.param = fill.param

View File

@ -83,11 +83,9 @@ class GroupAnnotator:
# no more follower
break
else:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
joined = '", "'.join(follower_names)
msg = _(f'when parsing leadership, we espect to find those followers "{joined}" '
f'in {xmlfiles}')
raise DictConsistencyError(msg, 31)
msg = _(f'when parsing leadership, we espect to find those followers "{joined}"')
raise DictConsistencyError(msg, 31, variable.xmlfiles)
del self.objectspace.space.constraints.group
def manage_leader(self,
@ -98,9 +96,8 @@ class GroupAnnotator:
"""manage leader's variable
"""
if variable.multi is not True:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _(f'the variable "{variable.name}" in a group must be multi in {xmlfiles}')
raise DictConsistencyError(msg, 32)
msg = _(f'the variable "{variable.name}" in a group must be multi')
raise DictConsistencyError(msg, 32, variable.xmlfiles)
if hasattr(group, 'name'):
leadership_name = group.name
else:
@ -145,10 +142,9 @@ class GroupAnnotator:
"""manage follower
"""
if variable.name != follower_name:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _('when parsing leadership, we expect to find the follower '
f'"{follower_name}" but we found "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 33)
f'"{follower_name}" but we found "{variable.name}"')
raise DictConsistencyError(msg, 33, variable.xmlfiles)
self.objectspace.paths.set_leader(variable.namespace,
leader_family_name,
leader_space.name,

View File

@ -0,0 +1,74 @@
"""Param annotator
Created by:
EOLE (http://eole.orion.education.fr)
Copyright (C) 2005-2018
Forked by:
Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from ..i18n import _
from ..error import DictConsistencyError
class ParamAnnotator:
def convert_param(self, objects) -> None:
""" valid and convert param
"""
for obj in objects:
if not hasattr(obj, 'param'):
continue
param_to_delete = []
for param_idx, param in enumerate(obj.param):
if param.type == 'suffix':
if hasattr(param, 'text'):
msg = _(f'"{param.type}" parameter must not have a value')
raise DictConsistencyError(msg, 28, obj.xmlfiles)
for target in obj.target:
if not self.objectspace.paths.variable_is_dynamic(target.name.path):
msg = _(f'"suffix" parameter cannot be set with target "{target.name}"'
f' which is not a dynamic variable')
raise DictConsistencyError(msg, 53, obj.xmlfiles)
elif not hasattr(param, 'text'):
if not param.type == 'nil':
msg = _(f'"{param.type}" parameter must have a value')
raise DictConsistencyError(msg, 27, obj.xmlfiles)
elif param.type == 'nil':
msg = _(f'"{param.type}" parameter must not have a value')
raise DictConsistencyError(msg, 40, obj.xmlfiles)
if param.type == 'variable':
try:
path, suffix = self.objectspace.paths.get_variable_path(param.text,
obj.namespace,
)
param.text = self.objectspace.paths.get_variable(path)
if suffix:
param.suffix = suffix
family_path = self.objectspace.paths.get_variable_family_path(path)
param.family = self.objectspace.paths.get_family(family_path,
param.text.namespace,
)
except DictConsistencyError as err:
if err.errno != 42 or not param.optional:
raise err
param_to_delete.append(param_idx)
param_to_delete.sort(reverse=True)
for param_idx in param_to_delete:
obj.param.pop(param_idx)

View File

@ -71,10 +71,9 @@ class PropertyAnnotator:
if 'force_store_value' in variable.properties and \
'force_default_on_freeze' in variable.properties: # pragma: no cover
# should not appened
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _('cannot have auto_freeze or auto_store with the hidden '
f'variable "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 50)
f'variable "{variable.name}"')
raise DictConsistencyError(msg, 50, variable.xmlfiles)
if not variable.properties:
del variable.properties

View File

@ -33,7 +33,7 @@ from ..error import DictConsistencyError
# a CreoleObjSpace's attribute has some annotations
# that shall not be present in the exported (flatened) XML
ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace',
'remove_condition', 'path', 'instance_mode', 'index', 'is_in_leadership',
'remove_condition', 'path', 'instance_mode', 'index',
'level', 'remove_fill', 'xmlfiles', 'type', 'reflector_name',
'reflector_object', 'manage')
@ -252,7 +252,6 @@ class ServiceAnnotator:
if not hasattr(file_, 'source'):
file_.source = basename(file_.name)
elif not hasattr(file_, 'source'):
xmlfiles = self.objectspace.display_xmlfiles(file_.xmlfiles)
msg = _(f'attribute "source" is mandatory for the file "{file_.name}" '
f'"({service_name})" in {xmlfiles}')
raise DictConsistencyError(msg, 34)
f'"({service_name})"')
raise DictConsistencyError(msg, 34, file_.xmlfiles)

View File

@ -0,0 +1,82 @@
"""Target annotator
Created by:
EOLE (http://eole.orion.education.fr)
Copyright (C) 2005-2018
Forked by:
Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from ..i18n import _
from ..error import DictConsistencyError
class TargetAnnotator:
def convert_target(self,
objects,
) -> None:
""" valid and convert param
"""
targets = []
for obj in objects:
if not hasattr(obj, 'target'):
msg = _('target is mandatory')
raise DictConsistencyError(msg, 9, obj.xmlfiles)
remove_targets = []
for index, target in enumerate(obj.target):
# test if it's redefined calculation
if self.target_is_uniq and target.name in targets:
msg = _(f'A fill already exists for the target of "{target.name}" created')
raise DictConsistencyError(msg, 24, obj.xmlfiles)
targets.append(target.name)
# let's replace the target by the path
try:
if target.type == 'variable':
path, suffix = self.objectspace.paths.get_variable_path(target.name,
obj.namespace,
)
target.name = self.objectspace.paths.get_variable(path)
if suffix:
msg = _(f'target to {target.name.path} with suffix is not allowed')
raise DictConsistencyError(msg, 35, obj.xmlfiles)
elif self.only_variable:
msg = _(f'target to "{target.name}" with param type "{target.type}" '
f'is not allowed')
raise DictConsistencyError(msg, 2, obj.xmlfiles)
if target.type == 'family':
target.name = self.objectspace.paths.get_family(target.name,
obj.namespace,
)
elif target.type.endswith('list') and \
obj.name not in ['disabled_if_in', 'disabled_if_not_in']:
msg = _(f'target "{target.type}" not allow')
raise DictConsistencyError(msg, 10, target.xmlfiles)
except DictConsistencyError as err:
if err.errno != 42:
raise err
# for optional variable
if not target.optional:
msg = f'cannot found target "{target.name}"'
raise DictConsistencyError(_(msg), 12, target.xmlfiles) from err
remove_targets.append(index)
remove_targets.sort(reverse=True)
for index in remove_targets:
obj.target.pop(index)

View File

@ -147,7 +147,9 @@ class VariableAnnotator: # pylint: disable=R0903
self.objectspace.space.constraints.check = []
check = self.objectspace.check(variable.xmlfiles)
check.name = 'valid_enum'
check.target = variable.path
target = self.objectspace.target(variable.xmlfiles)
target.name = variable.path
check.target = [target]
check.namespace = namespace
check.param = []
for value in FORCE_CHOICE[variable.type]:

View File

@ -112,16 +112,14 @@
<!ELEMENT constraints ((fill* | check* | condition* | group*)*)>
<!ELEMENT fill (param*)>
<!ELEMENT fill ((target|param)+)>
<!ATTLIST fill name CDATA #REQUIRED>
<!ATTLIST fill target CDATA #REQUIRED>
<!ELEMENT check (param*)>
<!ELEMENT check ((target|param)+)>
<!ATTLIST check name CDATA #REQUIRED>
<!ATTLIST check target CDATA #REQUIRED>
<!ATTLIST check level (error|warning) "error">
<!ELEMENT condition ((target | param)+ )>
<!ELEMENT condition ((target|param)+)>
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
<!ATTLIST condition source CDATA #REQUIRED>
<!ATTLIST condition fallback (True|False) "False">
@ -129,7 +127,7 @@
<!ATTLIST condition force_inverse_condition_on_fallback (True|False) "False">
<!ELEMENT param (#PCDATA)>
<!ATTLIST param type (string|number|variable|information|suffix) "string">
<!ATTLIST param type (string|number|nil|variable|information|suffix) "string">
<!ATTLIST param name CDATA #IMPLIED>
<!ATTLIST param propertyerror (True|False) "True">
<!ATTLIST param optional (True|False) "False">

View File

@ -24,6 +24,17 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from .i18n import _
def display_xmlfiles(xmlfiles: list) -> str:
"""The function format xmlfiles informations to generate errors
"""
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
class ConfigError(Exception):
"""Standard error for templating
"""
@ -54,6 +65,8 @@ class DictConsistencyError(Exception):
"""It's not only that the Creole XML is valid against the Creole DTD
it's that it is not consistent.
"""
def __init__(self, msg, errno):
def __init__(self, msg, errno, xmlfiles):
if xmlfiles:
msg = _(f'{msg} in {display_xmlfiles(xmlfiles)}')
super().__init__(msg)
self.errno = errno

View File

@ -149,14 +149,6 @@ class RougailObjSpace:
name = name[:-1]
return name
@staticmethod
def display_xmlfiles(xmlfiles: list) -> str:
"""The function format xmlfiles informations to generate errors
"""
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
def xml_parse_document(self,
xmlfile,
document,
@ -166,8 +158,8 @@ class RougailObjSpace:
"""Parses a Rougail XML file and populates the RougailObjSpace
"""
if namespace in ['services', Config['variable_namespace']]:
msg = _(f'Namespace name "{namespace}" is not allowed in "{xmlfile}"')
raise DictConsistencyError(msg, 21)
msg = _(f'Namespace name "{namespace}" is not allowed')
raise DictConsistencyError(msg, 21, xmlfile)
if not namespace:
namespace = Config['variable_namespace']
redefine_variables = []
@ -193,8 +185,8 @@ class RougailObjSpace:
continue
if child.tag == 'family':
if child.attrib['name'] in family_names:
msg = _(f'Family "{child.attrib["name"]}" is set several times in "{xmlfile}"')
raise DictConsistencyError(msg, 44)
msg = _(f'Family "{child.attrib["name"]}" is set several times')
raise DictConsistencyError(msg, 44, xmlfile)
family_names.append(child.attrib['name'])
try:
# variable objects creation
@ -309,10 +301,9 @@ class RougailObjSpace:
exists = self.convert_boolean(subspace.get('exists', True))
if exists is False:
raise SpaceObjShallNotBeUpdated()
xmlfiles = self.display_xmlfiles(existed_var.xmlfiles)
msg = _(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", '
f'already defined in {xmlfiles}')
raise DictConsistencyError(msg, 45)
f'already defined')
raise DictConsistencyError(msg, 45, existed_var.xmlfiles)
# object deos not exists
exists = self.convert_boolean(subspace.get('exists', False))
if exists is True:
@ -321,8 +312,8 @@ class RougailObjSpace:
redefine = self.convert_boolean(subspace.get('redefine', False))
if redefine is True:
# cannot redefine an inexistant object
msg = _(f'Redefined object in "{xmlfile}": "{name}" does not exist yet')
raise DictConsistencyError(msg, 46)
msg = _(f'Redefined object: "{name}" does not exist yet')
raise DictConsistencyError(msg, 46, xmlfile)
if child.tag not in vars(space):
setattr(space, child.tag, {})
return getattr(self, child.tag)(xmlfile, name)
@ -344,10 +335,9 @@ class RougailObjSpace:
return None
old_family_name = self.paths.get_variable_family_path(name)
if space.path != old_family_name:
xmlfiles = self.display_xmlfiles(space.xmlfiles)
msg = _(f'Variable was previously create in family "{old_family_name}", '
f'now it is in "{space.path}" in {xmlfiles}')
raise DictConsistencyError(msg, 47)
f'now it is in "{space.path}"')
raise DictConsistencyError(msg, 47, space.xmlfiles)
return self.paths.get_variable(name)
# it's not a family
children = getattr(space, child.tag, {})
@ -392,10 +382,9 @@ class RougailObjSpace:
del variableobj.value
for attr, val in child.attrib.items():
if redefine and attr in UNREDEFINABLE:
xmlfiles = self.display_xmlfiles(variableobj.xmlfiles[:-1])
msg = _(f'cannot redefine attribute "{attr}" for variable "{child.attrib["name"]}"'
f' in "{xmlfile}", already defined in {xmlfiles}')
raise DictConsistencyError(msg, 48)
f' in "{xmlfile}", already defined')
raise DictConsistencyError(msg, 48, variableobj.xmlfiles[:-1])
if attr in self.booleans_attributs:
val = self.convert_boolean(val)
if attr == 'name' and getattr(variableobj, 'name', None):
@ -417,16 +406,19 @@ class RougailObjSpace:
self.remove_condition(variableobj.name)
if child.attrib.get('remove_fill', False):
self.remove_fill(variableobj.name)
if child.tag == 'fill' and child.attrib['target'] in redefine_variables:
self.remove_fill(child.attrib['target'])
if child.tag == 'fill':
for target in child:
if target.tag == 'target' and target.text in redefine_variables:
self.remove_fill(target.text)
def remove_check(self, name):
"""Remove a check with a specified target
"""
remove_checks = []
for idx, check in enumerate(self.space.constraints.check): # pylint: disable=E1101
if check.target == name:
remove_checks.append(idx)
for target in check.target:
if target.name == name:
remove_checks.append(idx)
remove_checks.sort(reverse=True)
for idx in remove_checks:
self.space.constraints.check.pop(idx) # pylint: disable=E1101
@ -451,8 +443,9 @@ class RougailObjSpace:
"""
remove_fills = []
for idx, fill in enumerate(self.space.constraints.fill): # pylint: disable=E1101
if fill.target == name:
remove_fills.append(idx)
for target in fill.target:
if target.name == name:
remove_fills.append(idx)
remove_fills.sort(reverse=True)
for idx in remove_fills:
self.space.constraints.fill.pop(idx) # pylint: disable=E1101

View File

@ -56,11 +56,11 @@ class Path:
else:
if '.' not in name: # pragma: no cover
msg = _(f'Variable "{name}" in namespace "{namespace}" must have dot')
raise DictConsistencyError(msg, 39)
raise DictConsistencyError(msg, 39, variableobj.xmlfiles)
full_name = name
if full_name in self.families and \
self.families[full_name]['variableobj'] != variableobj: # pragma: no cover
raise DictConsistencyError(_(f'Duplicate family name "{name}"'), 37)
raise DictConsistencyError(_(f'Duplicate family name "{name}"'), 37, variableobj.xmlfiles)
self.families[full_name] = dict(name=name,
namespace=namespace,
variableobj=variableobj,
@ -90,13 +90,13 @@ class Path:
if name not in self.families and name in self.full_paths_families:
name = self.full_paths_families[name]
if name not in self.families:
raise DictConsistencyError(_('unknown option {}').format(name), 42)
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
dico = self.families[name]
if current_namespace not in [Config['variable_namespace'], 'services'] and \
current_namespace != dico['namespace']:
msg = _(f'A family located in the "{dico["namespace"]}" namespace '
f'shall not be used in the "{current_namespace}" namespace')
raise DictConsistencyError(msg, 38)
raise DictConsistencyError(msg, 38, [])
return dico['variableobj']
# Leadership
@ -119,11 +119,6 @@ class Path:
if namespace == Config['variable_namespace']:
self.full_paths_variables[name] = new_path
def is_in_leadership(self, name):
"""Is the variable is in a leadership
"""
return self._get_variable(name)['leader'] is not None
def is_leader(self, path): # pylint: disable=C0111
"""Is the variable is a leader
"""
@ -164,7 +159,7 @@ class Path:
"""
variable, suffix = self._get_variable(name, with_suffix=True)
if suffix:
raise DictConsistencyError(_(f"{name} is a dynamic variable"), 36)
raise DictConsistencyError(_(f"{name} is a dynamic variable"), 36, [])
return variable['variableobj']
def get_variable_family_path(self,
@ -188,7 +183,7 @@ class Path:
current_namespace != namespace:
msg = _(f'A variable located in the "{namespace}" namespace shall not be used '
f'in the "{current_namespace}" namespace')
raise DictConsistencyError(msg, 41)
raise DictConsistencyError(msg, 41, [])
return dico['variableobj'].path, suffix
def path_is_defined(self,
@ -222,7 +217,7 @@ class Path:
if variable['is_dynamic']:
return variable, name[len(var_name):]
if name not in self.variables:
raise DictConsistencyError(_('unknown option {}').format(name), 42)
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
if with_suffix:
return self.variables[name], None
return self.variables[name]

View File

@ -203,21 +203,31 @@ class Common:
) -> None:
"""Change properties to string
"""
properties = [self.convert_str(property_) for property_ in values if isinstance(property_, str)]
properties = [self.convert_str(property_) for property_ in values
if isinstance(property_, str)]
calc_properties = [self.calc_properties(property_) for property_ in values \
if isinstance(property_, self.objectspace.property_)]
return 'frozenset({' + ', '.join(sorted(properties) + calc_properties) + '})'
@staticmethod
def calc_properties(child) -> str:
def calc_properties(self,
child,
) -> str:
"""Populate properties
"""
option_name = child.source.reflector_object.get()
kwargs = (f"'condition': ParamOption({option_name}, todict=True), "
f"'expected': ParamValue('{child.expected}')")
kwargs = f"'condition': ParamOption({option_name}, todict=True), "
if child.expected is None or isinstance(child.expected, int):
kwargs += f"'expected': ParamValue({child.expected})"
elif isinstance(child.expected, str):
val = self.convert_str(child.expected)
kwargs += f"'expected': ParamValue({val})"
else:
val = child.expected.reflector_object.get()
kwargs += f"'expected': ParamOption({val})"
if child.inverse:
kwargs += ", 'reverse_condition': ParamValue(True)"
return f"Calculation(func.calc_value, Params(ParamValue('{child.name}'), kwargs={{{kwargs}}}))"
return (f"Calculation(func.calc_value, Params(ParamValue('{child.name}'), "
f"kwargs={{{kwargs}}}))")
def populate_informations(self):
"""Populate Tiramisu's informations
@ -313,6 +323,8 @@ class Variable(Common):
return f"ParamValue({value})"
if param.type in ['number', 'boolean']:
return f'ParamValue({param.text})'
if param.type == 'nil':
return 'ParamValue(None)'
if param.type == 'variable':
return self.build_param(param, function)
if param.type == 'information':
@ -323,7 +335,7 @@ class Variable(Common):
@staticmethod
def build_param(param,
function: str,
function: str, # pylint: disable=W0613
) -> str:
"""build variable parameters
"""

View File

@ -62,11 +62,11 @@ class XMLReflector:
try:
document = parse(xmlfile)
except XMLSyntaxError as err:
raise DictConsistencyError(_(f'{xmlfile} is not an XML file: {err}'), 52) from err
raise DictConsistencyError(_(f'not an XML file: {err}'), 52, xmlfile) from err
if not self.dtd.validate(document):
dtd_error = self.dtd.error_log.filter_from_errors()[0]
msg = _(f'"{xmlfile}" not a valid XML file: {dtd_error}')
raise DictConsistencyError(msg, 43)
msg = _(f'not a valid XML file: {dtd_error}')
raise DictConsistencyError(msg, 43, xmlfile)
return document.getroot()
@staticmethod