"""Annotate check 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 typing import List, Any from .target import TargetAnnotator from .param import ParamAnnotator from ..utils import load_modules from ..i18n import _ from ..error import DictConsistencyError, display_xmlfiles INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier'] class CheckAnnotator(TargetAnnotator, ParamAnnotator): """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 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 valid_type_validation(self, obj, ) -> None: variable_type = None if obj.name == 'valid_enum': for target in obj.target: if variable_type and target.name.type != variable_type: raise Exception('pfff') variable_type = target.name.type return variable_type def check_check(self): # pylint: disable=R0912 """valid and manage """ remove_indexes = [] for check_idx, check in enumerate(self.objectspace.space.constraints.check): if not check.name in self.functions: 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] 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 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: 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.values = [] variable.ori_type = variable.type variable.type = 'choice' has_variable = False values = [] for param in check.param: if has_variable: msg = _(f'only one "variable" parameter is allowed for valid_enum ' f'of variable "{variable.name}"') raise DictConsistencyError(msg, 5, param.xmlfiles) param_type = variable.ori_type if param.type == 'variable': has_variable = True if param.optional is True: msg = _(f'optional parameter in valid_enum for variable "{variable.name}" ' 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}"') raise DictConsistencyError(msg, 6, param.xmlfiles) param_type = 'calculation' values.append(param.text) choice = self.objectspace.choice(variable.xmlfiles) choice.name = param.text choice.type = param_type variable.values.append(choice) if has_variable: return None for target in check.target: self.objectspace.valid_enums[target.name.path] = {'type': variable.ori_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" """ for check in self.objectspace.space.constraints.check: check.warnings_only = check.level == 'warning' check.level = None def convert_valid_entier(self) -> None: """valid and manage """ 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 """ for check in self.objectspace.space.constraints.check: for target in check.target: if not hasattr(target.name, 'validators'): target.name.validators = [] target.name.validators.append(check)