rougail/src/rougail/annotator/check.py

231 lines
11 KiB
Python
Raw Normal View History

2021-01-17 18:00:29 +01:00
"""Annotate check
"""
from importlib.machinery import SourceFileLoader
from typing import List, Any
from .variable import CONVERT_OPTION
from ..i18n import _
from ..error import DictConsistencyError
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
class CheckAnnotator:
"""Annotate check
"""
def __init__(self,
objectspace,
eosfunc_file,
):
if not hasattr(objectspace.space, 'constraints') or \
not hasattr(objectspace.space.constraints, 'check'):
return
self.objectspace = objectspace
eosfunc = SourceFileLoader('eosfunc', eosfunc_file).load_module()
self.functions = dir(eosfunc)
self.functions.extend(INTERNAL_FUNCTIONS)
self.check_check()
self.check_valid_enum()
self.check_change_warning()
self.convert_check()
def check_check(self):
"""valid and manage <check>
"""
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:
2021-01-17 18:04:49 +01:00
check.target = self.objectspace.paths.get_variable(check.target)
2021-01-17 18:00:29 +01:00
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)
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
2021-01-17 18:04:49 +01:00
param.text = self.objectspace.paths.get_variable(param.text)
2021-01-17 18:00:29 +01:00
param_option_indexes.sort(reverse=True)
for idx in param_option_indexes:
check.param.pop(idx)
if 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
"""
remove_indexes = []
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]
remove_indexes.append(idx)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx]
def _set_valid_enum(self,
variable,
check,
) -> List[Any]:
# value for choice's variable is mandatory
variable.mandatory = True
# build choice
variable.choice = []
variable_type = variable.type
variable.type = 'choice'
has_variable = False
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)
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)
msg = _(f'only multi "variable" parameter is allowed for valid_enum '
f'of variable "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 6)
param_type = 'calculation'
value = param.text
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)
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
values.append(value)
choice = self.objectspace.choice(variable.xmlfiles)
choice.name = value
choice.type = param_type
variable.choice.append(choice)
if has_variable:
return None
self.objectspace.valid_enums[check.target.path] = {'type': variable_type,
'values': values,
'xmlfiles': check.xmlfiles,
}
return values
def check_change_warning(self):
"""convert level to "warnings_only"
"""
for check in self.objectspace.space.constraints.check:
check.warnings_only = check.level == 'warning'
check.level = None
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)
else:
if not hasattr(check.target, 'check'):
check.target.check = []
check.target.check.append(check)