From 4c011ee5518b8c31e8e7f929a68a1ee1b0cbca7f Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 19 Jan 2022 18:24:00 +0100 Subject: [PATCH] detect infinite loop --- src/rougail/annotator/check.py | 2 ++ src/rougail/annotator/service.py | 1 + src/rougail/tiramisureflector.py | 28 +++++++++++++-------- tests/dictionaries/80check_self/00-base.xml | 18 +++++++++++++ tests/dictionaries/80check_self/__init__.py | 0 tests/dictionaries/80check_self/errno_80 | 0 tests/test_1_flattener.py | 4 ++- tests/test_2_makedict.py | 4 ++- 8 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 tests/dictionaries/80check_self/00-base.xml create mode 100644 tests/dictionaries/80check_self/__init__.py create mode 100644 tests/dictionaries/80check_self/errno_80 diff --git a/src/rougail/annotator/check.py b/src/rougail/annotator/check.py index 187c6646..2d41d78c 100644 --- a/src/rougail/annotator/check.py +++ b/src/rougail/annotator/check.py @@ -99,6 +99,8 @@ class Annotator(TargetAnnotator, ParamAnnotator): if param.type != 'number': msg = _(f'param in "valid_entier" must be an "integer", not "{param.type}"') raise DictConsistencyError(msg, 18, check.xmlfiles) + if not hasattr(param, 'name'): + continue for target in check.target: if param.name == 'mini': target.name.min_number = int(param.text) diff --git a/src/rougail/annotator/service.py b/src/rougail/annotator/service.py index 62e76e04..ebec54c9 100644 --- a/src/rougail/annotator/service.py +++ b/src/rougail/annotator/service.py @@ -115,6 +115,7 @@ class Annotator: if not hasattr(service, 'information'): service.information = self.objectspace.information(service.xmlfiles) setattr(service.information, elttype, values) + service.path = '.'.join(['services', service.name]) manage = self._generate_element('boolean', None, None, diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py index 041200e5..033c5db4 100644 --- a/src/rougail/tiramisureflector.py +++ b/src/rougail/tiramisureflector.py @@ -188,7 +188,7 @@ class TiramisuReflector: def get_text(self): """Get text """ - self.baseelt.reflector_object.get() # pylint: disable=E1101 + self.baseelt.reflector_object.get([]) # pylint: disable=E1101 return '\n'.join(self.text) @@ -207,9 +207,15 @@ class Common: self.elt.reflector_object = self self.object_type = None - def get(self): + def get(self, calls): """Get tiramisu's object """ + self_calls = calls.copy() + if self.elt.path in self_calls: + msg = f'"{self.elt.path}" will make an infinite loop' + raise DictConsistencyError(msg, 80, self.elt.xmlfiles) + self_calls.append(self.elt.path) + self.calls = self_calls if self.option_name is None: self.option_name = self.elt.reflector_name self.populate_attrib() @@ -261,7 +267,7 @@ class Common: ) -> str: """Populate properties """ - option_name = child.source.reflector_object.get() + option_name = child.source.reflector_object.get(self.calls) kwargs = (f"'condition': ParamOption({option_name}, todict=True, notraisepropertyerror=True), " f"'expected': {self.populate_param(child.expected)}") if child.inverse: @@ -311,16 +317,16 @@ class Common: return 'ParamIndex()' raise Exception(_(f'unknown type {param.type}')) # pragma: no cover - @staticmethod - def build_option_param(param, + def build_option_param(self, + param, ) -> str: """build variable parameters """ - option_name = param.text.reflector_object.get() + option_name = param.text.reflector_object.get(self.calls) params = [f'{option_name}'] if hasattr(param, 'suffix'): param_type = 'ParamDynOption' - family = param.family.reflector_object.get() + family = param.family.reflector_object.get(self.calls) params.extend([f"'{param.suffix}'", f'{family}']) else: param_type = 'ParamOption' @@ -348,11 +354,11 @@ class Variable(Common): keys: dict, ): if hasattr(self.elt, 'opt'): - keys['opt'] = self.elt.opt.reflector_object.get() + keys['opt'] = self.elt.opt.reflector_object.get(self.calls) if hasattr(self.elt, 'choice'): values = self.elt.choice if values[0].type == 'variable': - value = values[0].name.reflector_object.get() + value = values[0].name.reflector_object.get(self.calls) keys['values'] = f"Calculation(func.calc_value, Params((ParamOption({value}))))" elif values[0].type == 'function': keys['values'] = self.calculation_value(values[0], []) @@ -441,6 +447,6 @@ class Family(Common): keys: list, ) -> None: if hasattr(self.elt, 'suffixes'): - dyn = self.elt.suffixes.reflector_object.get() + dyn = self.elt.suffixes.reflector_object.get(self.calls) keys['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}, notraisepropertyerror=True))))" - keys['children'] = '[' + ', '.join([child.get() for child in self.children]) + ']' + keys['children'] = '[' + ', '.join([child.get(self.calls) for child in self.children]) + ']' diff --git a/tests/dictionaries/80check_self/00-base.xml b/tests/dictionaries/80check_self/00-base.xml new file mode 100644 index 00000000..3089c557 --- /dev/null +++ b/tests/dictionaries/80check_self/00-base.xml @@ -0,0 +1,18 @@ + + + + + + b + + + + + + + 0 + int + int + + + diff --git a/tests/dictionaries/80check_self/__init__.py b/tests/dictionaries/80check_self/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80check_self/errno_80 b/tests/dictionaries/80check_self/errno_80 new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_1_flattener.py b/tests/test_1_flattener.py index d096ee85..ef8ab1b4 100644 --- a/tests/test_1_flattener.py +++ b/tests/test_1_flattener.py @@ -2,9 +2,11 @@ from lxml import etree from os import getcwd from os.path import isfile, join, isdir from pytest import fixture, raises -from os import listdir +from os import listdir, environ from json import load +environ['TIRAMISU_LOCALE'] = 'en' + from rougail import RougailConvert, RougailConfig from rougail.error import DictConsistencyError diff --git a/tests/test_2_makedict.py b/tests/test_2_makedict.py index 9a042630..7ffbb90a 100644 --- a/tests/test_2_makedict.py +++ b/tests/test_2_makedict.py @@ -1,9 +1,11 @@ from lxml import etree from os.path import isfile, join, isdir from pytest import fixture, mark -from os import listdir, mkdir +from os import listdir, mkdir, environ from json import dump, load, dumps, loads +environ['TIRAMISU_LOCALE'] = 'en' + from tiramisu import Config from tiramisu.error import PropertiesOptionError