From 3d49f2fe8cacd56275c2115f9d48157d300ad4a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 11 Nov 2020 16:24:06 +0100 Subject: [PATCH 1/3] add xmlfiles in various error message --- src/rougail/annotator.py | 57 ++++++----- src/rougail/objspace.py | 95 +++++++++++++------ .../dictionaries/80check_unknown/00-base.xml | 27 ++++++ .../dictionaries/80check_unknown/__init__.py | 0 .../80check_unknown_var/00-base.xml | 26 +++++ .../80check_unknown_var/__init__.py | 0 6 files changed, 150 insertions(+), 55 deletions(-) create mode 100644 tests/dictionaries/80check_unknown/00-base.xml create mode 100644 tests/dictionaries/80check_unknown/__init__.py create mode 100644 tests/dictionaries/80check_unknown_var/00-base.xml create mode 100644 tests/dictionaries/80check_unknown_var/__init__.py diff --git a/src/rougail/annotator.py b/src/rougail/annotator.py index 94f96e7b..c914acba 100644 --- a/src/rougail/annotator.py +++ b/src/rougail/annotator.py @@ -38,7 +38,7 @@ modes = mode_factory() # 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', - 'level', 'remove_fill') + 'level', 'remove_fill', 'xmlfiles') ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group') FORCE_CHOICE = {'oui/non': ['oui', 'non'], @@ -125,7 +125,7 @@ class GroupAnnotator: # if variable.hidden: # leader_is_hidden = True else: - leader_space = self.objectspace.Leadership() + leader_space = self.objectspace.Leadership(variable.xmlfiles) leader_is_hidden = self.manage_leader(leader_space, leader_family_name, leader_name, @@ -233,7 +233,7 @@ class ServiceAnnotator: families = {} for idx, service_name in enumerate(self.objectspace.space.services.service.keys()): service = self.objectspace.space.services.service[service_name] - new_service = self.objectspace.service() + new_service = self.objectspace.service(service.xmlfiles) for elttype, values in vars(service).items(): if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES: setattr(new_service, elttype, values) @@ -242,6 +242,7 @@ class ServiceAnnotator: path = '.'.join(['services', service_name, eltname]) family = self.gen_family(eltname, path, + service.xmlfiles, ) if isinstance(values, dict): values = list(values.values()) @@ -258,8 +259,9 @@ class ServiceAnnotator: def gen_family(self, name, path, + xmlfiles ): - family = self.objectspace.family() + family = self.objectspace.family(xmlfiles) family.name = normalize_family(name) family.doc = name family.mode = None @@ -307,7 +309,10 @@ class ServiceAnnotator: if not self.objectspace.paths.family_is_defined(subpath): break idx += 1 - family = self.gen_family(c_name, subpath) + family = self.gen_family(c_name, + subpath, + elt.xmlfiles, + ) family.variable = [] listname = '{}list'.format(name) activate_path = '.'.join([subpath, 'activate']) @@ -345,7 +350,7 @@ class ServiceAnnotator: elt, path, ): - variable = self.objectspace.variable() + variable = self.objectspace.variable(elt.xmlfiles) variable.name = normalize_family(key) variable.mode = None if key == 'name': @@ -370,7 +375,7 @@ class ServiceAnnotator: variable.multi = None else: variable.doc = key - val = self.objectspace.value() + val = self.objectspace.value(elt.xmlfiles) val.type = type_ val.name = value variable.value = [val] @@ -469,17 +474,17 @@ class VariableAnnotator: path, ): if variable.type in FORCE_CHOICE: - check = self.objectspace.check() + check = self.objectspace.check(variable.xmlfiles) check.name = 'valid_enum' check.target = path check.namespace = namespace check.param = [] for value in FORCE_CHOICE[variable.type]: - param = self.objectspace.param() + param = self.objectspace.param(variable.xmlfiles) 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 = self.objectspace.constraints(variable.xmlfiles) self.objectspace.space.constraints.namespace = namespace if not hasattr(self.objectspace.space.constraints, 'check'): self.objectspace.space.constraints.check = [] @@ -542,14 +547,14 @@ class VariableAnnotator: def convert_auto_freeze(self): # pylint: disable=C0111 def _convert_auto_freeze(variable, namespace): if variable.auto_freeze: - new_condition = self.objectspace.condition() + new_condition = self.objectspace.condition(variable.xmlfiles) new_condition.name = 'auto_hidden_if_not_in' new_condition.namespace = namespace new_condition.source = FREEZE_AUTOFREEZE_VARIABLE - new_param = self.objectspace.param() + new_param = self.objectspace.param(variable.xmlfiles) new_param.text = 'oui' new_condition.param = [new_param] - new_target = self.objectspace.target() + new_target = self.objectspace.target(variable.xmlfiles) new_target.type = 'variable' path = variable.namespace + '.' + normalize_family(family.name) + '.' + variable.name new_target.name = path @@ -583,7 +588,8 @@ class VariableAnnotator: subpath = self.objectspace.paths.get_variable_path(separator.name, separator.namespace, ) - raise DictConsistencyError(_('{} already has a separator').format(subpath)) + xmlfiles = self.objectspace.display_xmlfiles(separator.xmlfiles) + raise DictConsistencyError(_(f'{subpath} already has a separator in {xmlfiles}')) option.separator = separator.text del family.separators @@ -622,7 +628,8 @@ class ConstraintAnnotator: remove_indexes = [] for check_idx, check in enumerate(self.objectspace.space.constraints.check): if not check.name in self.functions: - raise DictConsistencyError(_('cannot find check function {}').format(check.name)) + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'cannot find check function "{check.name}" in {xmlfiles}')) if hasattr(check, 'param'): param_option_indexes = [] for idx, param in enumerate(check.param): @@ -630,7 +637,8 @@ class ConstraintAnnotator: if param.optional is True: param_option_indexes.append(idx) else: - raise DictConsistencyError(_(f'unknown param {param.text} in check')) + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'cannot find check param "{param.text}" in {xmlfiles}')) if param.type != 'variable': param.notraisepropertyerror = None param_option_indexes = list(set(param_option_indexes)) @@ -766,7 +774,7 @@ class ConstraintAnnotator: for listvar in listvars: variable = self.objectspace.paths.get_variable_obj(listvar) type_ = 'variable' - new_target = self.objectspace.target() + new_target = self.objectspace.target(variable.xmlfiles) new_target.type = type_ new_target.name = listvar new_target.index = target.index @@ -891,7 +899,7 @@ class ConstraintAnnotator: if hasattr(leader_or_variable, actions[0]) and getattr(leader_or_variable, actions[0]) is True: continue for idx, action in enumerate(actions): - prop = self.objectspace.property_() + prop = self.objectspace.property_(leader_or_variable.xmlfiles) prop.type = 'calculation' prop.inverse = inverse prop.source = condition.source @@ -924,7 +932,7 @@ class ConstraintAnnotator: } choices = [] for value in values: - choice = self.objectspace.choice() + choice = self.objectspace.choice(variable.xmlfiles) try: if value is not None: choice.name = CONVERSION.get(type_, str)(value) @@ -948,7 +956,7 @@ class ConstraintAnnotator: 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 = self.objectspace.value(variable.xmlfiles) new_value.name = choices[0] new_value.type = type_ variable.value = [new_value] @@ -973,7 +981,7 @@ class ConstraintAnnotator: else: raise DictConsistencyError(_(f'unknown parameter {param.text} in check "valid_entier" for variable {check.target}')) else: - check_ = self.objectspace.check() + check_ = self.objectspace.check(variable.xmlfiles) if name == 'valid_differ': name = 'valid_not_equal' elif name == 'valid_network_netmask': @@ -1026,7 +1034,8 @@ class ConstraintAnnotator: ) if suffix is not None: raise DictConsistencyError(_(f'Cannot add fill function to "{fill.target}" only with the suffix "{suffix}"')) - value = self.objectspace.value() + variable = self.objectspace.paths.get_variable_obj(fill.target) + value = self.objectspace.value(variable.xmlfiles) value.type = 'calculation' value.name = fill.name if hasattr(fill, 'param'): @@ -1058,7 +1067,6 @@ class ConstraintAnnotator: for param_idx in param_to_delete: fill.param.pop(param_idx) value.param = fill.param - variable = self.objectspace.paths.get_variable_obj(fill.target) variable.value = [value] del self.objectspace.space.constraints.fill @@ -1066,6 +1074,7 @@ class ConstraintAnnotator: if hasattr(self.objectspace.space.constraints, 'index'): del self.objectspace.space.constraints.index del self.objectspace.space.constraints.namespace + del self.objectspace.space.constraints.xmlfiles if vars(self.objectspace.space.constraints): raise Exception('constraints again?') del self.objectspace.space.constraints @@ -1126,7 +1135,7 @@ class FamilyAnnotator: # if the variable is mandatory and doesn't have any value # then the variable's mode is set to 'basic' if not hasattr(variable, 'value') and variable.type == 'boolean': - new_value = self.objectspace.value() + new_value = self.objectspace.value(variable.xmlfiles) new_value.name = True new_value.type = 'boolean' variable.value = [new_value] diff --git a/src/rougail/objspace.py b/src/rougail/objspace.py index 37b82308..30756532 100644 --- a/src/rougail/objspace.py +++ b/src/rougail/objspace.py @@ -59,7 +59,10 @@ CONVERT_EXPORT = {'Leadership': 'leader', # _____________________________________________________________________________ # special types definitions for the Object Space's internal representation class RootCreoleObject: - "" + def __init__(self, xmlfiles): + if not isinstance(xmlfiles, list): + xmlfiles = [xmlfiles] + self.xmlfiles = xmlfiles class CreoleObjSpace: @@ -157,12 +160,14 @@ class CreoleObjSpace: self.fill_removed = [] self.check_removed = [] self.condition_removed = [] - self.xml_parse_document(document, + self.xml_parse_document(xmlfile, + document, self.space, namespace, ) def xml_parse_document(self, + xmlfile, document, space, namespace, @@ -179,7 +184,7 @@ class CreoleObjSpace: continue if child.tag == 'family': if child.attrib['name'] in family_names: - raise DictConsistencyError(_('Family {} is set several times').format(child.attrib['name'])) + raise DictConsistencyError(_(f'Family "{child.attrib["name"]}" is set several times in "{xmlfile}"')) family_names.append(child.attrib['name']) if child.tag == 'variables': child.attrib['name'] = namespace @@ -188,16 +193,18 @@ class CreoleObjSpace: continue # variable objects creation try: - variableobj = self.generate_variableobj(child, - space, - namespace, - ) + variableobj = self.generate_variableobj(xmlfile, + child, + space, + namespace, + ) except SpaceObjShallNotBeUpdated: continue self.set_text_to_obj(child, variableobj, ) - self.set_xml_attributes_to_obj(child, + self.set_xml_attributes_to_obj(xmlfile, + child, variableobj, ) self.variableobj_tree_visitor(child, @@ -215,26 +222,29 @@ class CreoleObjSpace: child, ) if list(child) != []: - self.xml_parse_document(child, + self.xml_parse_document(xmlfile, + child, variableobj, namespace, ) def generate_variableobj(self, - child, - space, - namespace, - ): + xmlfile, + child, + space, + namespace, + ): """ instanciates or creates Creole Object Subspace objects """ - variableobj = getattr(self, child.tag)() + variableobj = getattr(self, child.tag)(xmlfile) if isinstance(variableobj, self.Redefinable): - variableobj = self.create_or_update_redefinable_object(child.attrib, - space, - child, - namespace, - ) + variableobj = self.create_or_update_redefinable_object(xmlfile, + child.attrib, + space, + child, + namespace, + ) elif isinstance(variableobj, self.Atom) and child.tag in vars(space): # instanciates an object from the CreoleObjSpace's builtins types # example : child.tag = constraints -> a self.Constraints() object is created @@ -248,6 +258,7 @@ class CreoleObjSpace: return variableobj def create_or_update_redefinable_object(self, + xmlfile, subspace, space, child, @@ -281,15 +292,17 @@ class CreoleObjSpace: name = child.text else: name = subspace['name'] - if self.is_already_exists(name, - space, - child, - namespace, - ): + existed_var = self.is_already_exists(name, + space, + child, + namespace, + ) + if existed_var: default_redefine = child.tag in FORCE_REDEFINABLES redefine = self.convert_boolean(subspace.get('redefine', default_redefine)) exists = self.convert_boolean(subspace.get('exists', True)) if redefine is True: + existed_var.xmlfiles.append(xmlfile) return self.translate_in_space(name, space, child, @@ -297,12 +310,21 @@ class CreoleObjSpace: ) elif exists is False: raise SpaceObjShallNotBeUpdated() - raise DictConsistencyError(_(f'Already present in another XML file, {name} cannot be re-created')) + xmlfiles = self.display_xmlfiles(existed_var.xmlfiles) + raise DictConsistencyError(_(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", already defined in {xmlfiles}')) redefine = self.convert_boolean(subspace.get('redefine', False)) exists = self.convert_boolean(subspace.get('exists', False)) if redefine is False or exists is True: - return getattr(self, child.tag)() - raise DictConsistencyError(_(f'Redefined object: {name} does not exist yet')) + return getattr(self, child.tag)(xmlfile) + raise DictConsistencyError(_(f'Redefined object in "{xmlfile}": "{name}" does not exist yet')) + + def display_xmlfiles(self, + xmlfiles: list, + ) -> str: + if len(xmlfiles) == 1: + return '"' + xmlfiles[0] + '"' + else: + return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"' def create_tree_structure(self, space, @@ -329,16 +351,25 @@ class CreoleObjSpace: raise OperationError(_("Creole object {} " "has a wrong type").format(type(variableobj))) - def is_already_exists(self, name, space, child, namespace): + def is_already_exists(self, + name: str, + space: str, + child, + namespace: str, + ): if isinstance(space, self.family): # pylint: disable=E1101 if namespace != Config['variable_namespace']: name = space.path + '.' + name - return self.paths.path_is_defined(name) + if self.paths.path_is_defined(name): + return self.paths.get_variable_obj(name) + return if child.tag == 'family': norm_name = normalize_family(name) else: norm_name = name - return norm_name in getattr(space, child.tag, {}) + children = getattr(space, child.tag, {}) + if norm_name in children: + return children[norm_name] def convert_boolean(self, value): # pylint: disable=R0201 """Boolean coercion. The Creole XML may contain srings like `True` or `False` @@ -451,6 +482,7 @@ class CreoleObjSpace: variableobj.text = text def set_xml_attributes_to_obj(self, + xmlfile, child, variableobj, ): @@ -463,7 +495,8 @@ class CreoleObjSpace: # UNREDEFINABLE concerns only 'variable' node so we can fix name # to child.attrib['name'] name = child.attrib['name'] - raise DictConsistencyError(_(f'cannot redefine attribute {attr} for variable {name}')) + xmlfiles = self.display_xmlfiles(variableobj.xmlfiles[:-1]) + raise DictConsistencyError(_(f'cannot redefine attribute "{attr}" for variable "{name}" in "{xmlfile}", already defined in {xmlfiles}')) if attr in self.booleans_attributs: val = self.convert_boolean(val) if not (attr == 'name' and getattr(variableobj, 'name', None) != None): diff --git a/tests/dictionaries/80check_unknown/00-base.xml b/tests/dictionaries/80check_unknown/00-base.xml new file mode 100644 index 00000000..9d604427 --- /dev/null +++ b/tests/dictionaries/80check_unknown/00-base.xml @@ -0,0 +1,27 @@ + + + + + + + + + b + + + + + + + + + 0 + 100 + + + + + + + diff --git a/tests/dictionaries/80check_unknown/__init__.py b/tests/dictionaries/80check_unknown/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80check_unknown_var/00-base.xml b/tests/dictionaries/80check_unknown_var/00-base.xml new file mode 100644 index 00000000..afa32329 --- /dev/null +++ b/tests/dictionaries/80check_unknown_var/00-base.xml @@ -0,0 +1,26 @@ + + + + + + + + + b + + + + + + + + int3 + + + + + + + + diff --git a/tests/dictionaries/80check_unknown_var/__init__.py b/tests/dictionaries/80check_unknown_var/__init__.py new file mode 100644 index 00000000..e69de29b From 5a45885ed824e55cffae3d02057fd63fd3bbc4e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 11 Nov 2020 16:59:44 +0100 Subject: [PATCH 2/3] family and var with same name --- src/rougail/path.py | 29 ++++++++++--------- .../01base_var_name_same_family/00-base.xml | 12 ++++++++ .../01base_var_name_same_family/__init__.py | 0 .../makedict/base.json | 1 + .../tiramisu/__init__.py | 0 .../tiramisu/base.py | 14 +++++++++ .../60extra_name_family/00-base.xml | 25 ++++++++++++++++ .../60extra_name_family/__init__.py | 0 .../extra_dirs/extra/00-base.xml | 23 +++++++++++++++ .../60extra_name_family/makedict/base.json | 1 + .../60extra_name_family/tiramisu/__init__.py | 0 .../60extra_name_family/tiramisu/base.py | 20 +++++++++++++ 12 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 tests/dictionaries/01base_var_name_same_family/00-base.xml create mode 100644 tests/dictionaries/01base_var_name_same_family/__init__.py create mode 100644 tests/dictionaries/01base_var_name_same_family/makedict/base.json create mode 100644 tests/dictionaries/01base_var_name_same_family/tiramisu/__init__.py create mode 100644 tests/dictionaries/01base_var_name_same_family/tiramisu/base.py create mode 100644 tests/dictionaries/60extra_name_family/00-base.xml create mode 100644 tests/dictionaries/60extra_name_family/__init__.py create mode 100644 tests/dictionaries/60extra_name_family/extra_dirs/extra/00-base.xml create mode 100644 tests/dictionaries/60extra_name_family/makedict/base.json create mode 100644 tests/dictionaries/60extra_name_family/tiramisu/__init__.py create mode 100644 tests/dictionaries/60extra_name_family/tiramisu/base.py diff --git a/src/rougail/path.py b/src/rougail/path.py index 09f7ff48..16123fab 100644 --- a/src/rougail/path.py +++ b/src/rougail/path.py @@ -13,7 +13,8 @@ class Path: def __init__(self): self.variables = {} self.families = {} - self.full_paths = {} + self.full_paths_families = {} + self.full_paths_variables = {} # Family def add_family(self, @@ -23,7 +24,7 @@ class Path: ) -> str: # pylint: disable=C0111 if '.' not in name and namespace == Config['variable_namespace']: full_name = '.'.join([namespace, name]) - self.full_paths[name] = full_name + self.full_paths_families[name] = full_name else: full_name = name if full_name in self.families and self.families[full_name]['variableobj'] != variableobj: @@ -41,8 +42,8 @@ class Path: check_name=False, allow_dot=True, ) - if '.' not in name and current_namespace == Config['variable_namespace'] and name in self.full_paths: - name = self.full_paths[name] + if '.' not in name and current_namespace == Config['variable_namespace'] and name in self.full_paths_families: + name = self.full_paths_families[name] if current_namespace is None: # pragma: no cover raise OperationError('current_namespace must not be None') dico = self.families[name] @@ -55,8 +56,8 @@ class Path: def get_family_obj(self, name: str, ) -> 'Family': # pylint: disable=C0111 - if '.' not in name and name in self.full_paths: - name = self.full_paths[name] + if '.' not in name and name in self.full_paths_families: + name = self.full_paths_families[name] if name not in self.families: raise DictConsistencyError(_('unknown family {}').format(name)) dico = self.families[name] @@ -65,7 +66,7 @@ class Path: def family_is_defined(self, name: str, ) -> str: # pylint: disable=C0111 - if '.' not in name and name not in self.families and name in self.full_paths: + if '.' not in name and name not in self.families and name in self.full_paths_families: return True return name in self.families @@ -88,7 +89,7 @@ class Path: dico['variableobj'], ) if namespace == Config['variable_namespace']: - self.full_paths[name] = new_path + self.full_paths_variables[name] = new_path else: name = new_path dico = self._get_variable(name) @@ -110,7 +111,7 @@ class Path: ) -> str: # pylint: disable=C0111 if '.' not in name: full_name = '.'.join([namespace, family, name]) - self.full_paths[name] = full_name + self.full_paths_variables[name] = full_name else: full_name = name if namespace == Config['variable_namespace']: @@ -176,7 +177,7 @@ class Path: def path_is_defined(self, name: str, ) -> str: # pylint: disable=C0111 - if '.' not in name and name not in self.variables and name in self.full_paths: + if '.' not in name and name not in self.variables and name in self.full_paths_variables: return True return name in self.variables @@ -186,8 +187,8 @@ class Path: ) -> str: if name not in self.variables: if name not in self.variables: - if '.' not in name and name in self.full_paths: - name = self.full_paths[name] + if '.' not in name and name in self.full_paths_variables: + name = self.full_paths_variables[name] if name not in self.variables: for var_name, variable in self.variables.items(): if variable['is_dynamic'] and name.startswith(var_name): @@ -195,9 +196,9 @@ class Path: raise Exception('This option is dynamic, should use "with_suffix" attribute') return variable, name[len(var_name):] if '.' not in name: - for var_name, path in self.full_paths.items(): + for var_name, path in self.full_paths_variables.items(): if name.startswith(var_name): - variable = self.variables[self.full_paths[var_name]] + variable = self.variables[self.full_paths_variables[var_name]] if variable['is_dynamic']: if not with_suffix: raise Exception('This option is dynamic, should use "with_suffix" attribute') diff --git a/tests/dictionaries/01base_var_name_same_family/00-base.xml b/tests/dictionaries/01base_var_name_same_family/00-base.xml new file mode 100644 index 00000000..eb4bf06f --- /dev/null +++ b/tests/dictionaries/01base_var_name_same_family/00-base.xml @@ -0,0 +1,12 @@ + + + + + + non + + + + + diff --git a/tests/dictionaries/01base_var_name_same_family/__init__.py b/tests/dictionaries/01base_var_name_same_family/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_var_name_same_family/makedict/base.json b/tests/dictionaries/01base_var_name_same_family/makedict/base.json new file mode 100644 index 00000000..949887bb --- /dev/null +++ b/tests/dictionaries/01base_var_name_same_family/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.general": "non"} diff --git a/tests/dictionaries/01base_var_name_same_family/tiramisu/__init__.py b/tests/dictionaries/01base_var_name_same_family/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_var_name_same_family/tiramisu/base.py b/tests/dictionaries/01base_var_name_same_family/tiramisu/base.py new file mode 100644 index 00000000..e5a1d6e8 --- /dev/null +++ b/tests/dictionaries/01base_var_name_same_family/tiramisu/base.py @@ -0,0 +1,14 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='general', doc='description', 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]) diff --git a/tests/dictionaries/60extra_name_family/00-base.xml b/tests/dictionaries/60extra_name_family/00-base.xml new file mode 100644 index 00000000..7304014f --- /dev/null +++ b/tests/dictionaries/60extra_name_family/00-base.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/dictionaries/60extra_name_family/__init__.py b/tests/dictionaries/60extra_name_family/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/60extra_name_family/extra_dirs/extra/00-base.xml b/tests/dictionaries/60extra_name_family/extra_dirs/extra/00-base.xml new file mode 100644 index 00000000..651c2125 --- /dev/null +++ b/tests/dictionaries/60extra_name_family/extra_dirs/extra/00-base.xml @@ -0,0 +1,23 @@ + + + + + + + Exportation de la base de ejabberd + + + + pre + + + + + + non + activer_ejabberd + none + daily + + + diff --git a/tests/dictionaries/60extra_name_family/makedict/base.json b/tests/dictionaries/60extra_name_family/makedict/base.json new file mode 100644 index 00000000..eb7df63a --- /dev/null +++ b/tests/dictionaries/60extra_name_family/makedict/base.json @@ -0,0 +1 @@ +{"rougail.extra.mode_conteneur_actif": "non", "rougail.extra.activer_ejabberd": "non", "extra.ejabberd.description": "Exportation de la base de ejabberd", "extra.ejabberd.day": null, "extra.ejabberd.mode": "pre"} diff --git a/tests/dictionaries/60extra_name_family/tiramisu/__init__.py b/tests/dictionaries/60extra_name_family/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/60extra_name_family/tiramisu/base.py b/tests/dictionaries/60extra_name_family/tiramisu/base.py new file mode 100644 index 00000000..3ea4851e --- /dev/null +++ b/tests/dictionaries/60extra_name_family/tiramisu/base.py @@ -0,0 +1,20 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non')) +option_4 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='activer_ejabberd', doc='No change', multi=False, default='non', values=('oui', 'non')) +option_2 = OptionDescription(name='extra', doc='extra', properties=frozenset({'normal'}), children=[option_3, option_4]) +option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2]) +option_7 = StrOption(properties=frozenset({'mandatory', 'normal'}), name='description', doc='description', multi=False, default='Exportation de la base de ejabberd') +option_8 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='day', doc='day', multi=False, default=Calculation(func.calc_multi_condition, Params((ParamValue("non")), kwargs={'condition_1': ParamOption(option_4, notraisepropertyerror=True, todict=False), 'match': ParamValue("none"), 'mismatch': ParamValue("daily")})), values=('none', 'daily', 'weekly', 'monthly')) +option_9 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode', doc='mode', multi=False, default='pre', values=('pre', 'post')) +option_6 = OptionDescription(name='ejabberd', doc='ejabberd', properties=frozenset({'normal'}), children=[option_7, option_8, option_9]) +option_5 = OptionDescription(name='extra', doc='extra', children=[option_6]) +option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_5]) From a835e5a017e6518686a51d3d33900bfc0572bd79 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 14 Nov 2020 08:02:38 +0100 Subject: [PATCH 3/3] valid_in_network should works with CIDR --- src/rougail/annotator.py | 16 ++++++---- src/rougail/xmlreflector.py | 2 +- .../10check_valid_in_network/00-base.xml | 29 +++++++++++++++++++ .../10check_valid_in_network/__init__.py | 0 .../makedict/base.json | 1 + .../tiramisu/__init__.py | 0 .../10check_valid_in_network/tiramisu/base.py | 17 +++++++++++ .../10check_valid_in_network_cidr/00-base.xml | 27 +++++++++++++++++ .../10check_valid_in_network_cidr/__init__.py | 0 .../makedict/base.json | 1 + .../tiramisu/__init__.py | 0 .../tiramisu/base.py | 16 ++++++++++ tests/eosfunc/test.py | 2 +- 13 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 tests/dictionaries/10check_valid_in_network/00-base.xml create mode 100644 tests/dictionaries/10check_valid_in_network/__init__.py create mode 100644 tests/dictionaries/10check_valid_in_network/makedict/base.json create mode 100644 tests/dictionaries/10check_valid_in_network/tiramisu/__init__.py create mode 100644 tests/dictionaries/10check_valid_in_network/tiramisu/base.py create mode 100644 tests/dictionaries/10check_valid_in_network_cidr/00-base.xml create mode 100644 tests/dictionaries/10check_valid_in_network_cidr/__init__.py create mode 100644 tests/dictionaries/10check_valid_in_network_cidr/makedict/base.json create mode 100644 tests/dictionaries/10check_valid_in_network_cidr/tiramisu/__init__.py create mode 100644 tests/dictionaries/10check_valid_in_network_cidr/tiramisu/base.py diff --git a/src/rougail/annotator.py b/src/rougail/annotator.py index c914acba..e3334c11 100644 --- a/src/rougail/annotator.py +++ b/src/rougail/annotator.py @@ -987,20 +987,24 @@ class ConstraintAnnotator: elif name == 'valid_network_netmask': params_len = 1 if len(check.param) != params_len: - raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}')) elif name == 'valid_ipnetmask': params_len = 1 if len(check.param) != params_len: - raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}')) name = 'valid_ip_netmask' elif name == 'valid_broadcast': params_len = 2 if len(check.param) != params_len: - raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}')) elif name == 'valid_in_network': - params_len = 2 - if len(check.param) != params_len: - raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) + if len(check.param) not in (1, 2): + params_len = 2 + xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles) + raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}')) check_.name = name check_.warnings_only = check.warnings_only if hasattr(check, 'param'): diff --git a/src/rougail/xmlreflector.py b/src/rougail/xmlreflector.py index 81cc5d75..a2ebcc90 100644 --- a/src/rougail/xmlreflector.py +++ b/src/rougail/xmlreflector.py @@ -37,7 +37,7 @@ class XMLReflector(object): # document = parse(BytesIO(xmlfile), XMLParser(remove_blank_text=True)) document = parse(xmlfile) if not self.dtd.validate(document): - raise DictConsistencyError(_("not a valid xml file: {}").format(xmlfile)) + raise DictConsistencyError(_(f'"{xmlfile}" not a valid xml file: {self.dtd.error_log.filter_from_errors()[0]}')) return document.getroot() def load_xml_from_folders(self, xmlfolders): diff --git a/tests/dictionaries/10check_valid_in_network/00-base.xml b/tests/dictionaries/10check_valid_in_network/00-base.xml new file mode 100644 index 00000000..43cc2898 --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network/00-base.xml @@ -0,0 +1,29 @@ + + + + + + + + + oui + + + + + + + + + + + adresse_ip_eth0 + adresse_netmask_eth0 + + + + + + + diff --git a/tests/dictionaries/10check_valid_in_network/__init__.py b/tests/dictionaries/10check_valid_in_network/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10check_valid_in_network/makedict/base.json b/tests/dictionaries/10check_valid_in_network/makedict/base.json new file mode 100644 index 00000000..b82b5b43 --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif": "oui", "rougail.general.adresse_ip_eth0": null, "rougail.general.adresse_netmask_eth0": null, "rougail.general.adresse_ip": null} diff --git a/tests/dictionaries/10check_valid_in_network/tiramisu/__init__.py b/tests/dictionaries/10check_valid_in_network/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10check_valid_in_network/tiramisu/base.py b/tests/dictionaries/10check_valid_in_network/tiramisu/base.py new file mode 100644 index 00000000..c5e87cc1 --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network/tiramisu/base.py @@ -0,0 +1,17 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='oui', values=('oui', 'non')) +option_4 = IPOption(private_only=True, warnings_only=True, properties=frozenset({'basic', 'mandatory'}), name='adresse_ip_eth0', doc='Adresse IP de la carte', multi=False) +option_5 = NetmaskOption(properties=frozenset({'basic', 'mandatory'}), name='adresse_netmask_eth0', doc='Masque de sous réseau de la carte', multi=False) +option_6 = IPOption(private_only=True, warnings_only=True, properties=frozenset({'basic', 'mandatory'}), validators=[Calculation(func.valid_in_network, Params((ParamSelfOption(), ParamOption(option_4, notraisepropertyerror=False, todict=False), ParamOption(option_5, notraisepropertyerror=False, todict=False)), kwargs={}), warnings_only=True)], name='adresse_ip', doc='IP', multi=False) +option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'basic'}), children=[option_3, option_4, option_5, option_6]) +option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2]) +option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1]) diff --git a/tests/dictionaries/10check_valid_in_network_cidr/00-base.xml b/tests/dictionaries/10check_valid_in_network_cidr/00-base.xml new file mode 100644 index 00000000..f11b495b --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network_cidr/00-base.xml @@ -0,0 +1,27 @@ + + + + + + + + + oui + + + + + + + + + + adresse_ip_eth0 + + + + + + + diff --git a/tests/dictionaries/10check_valid_in_network_cidr/__init__.py b/tests/dictionaries/10check_valid_in_network_cidr/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10check_valid_in_network_cidr/makedict/base.json b/tests/dictionaries/10check_valid_in_network_cidr/makedict/base.json new file mode 100644 index 00000000..3d0e2f80 --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network_cidr/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif": "oui", "rougail.general.adresse_ip_eth0": null, "rougail.general.adresse_ip": null} diff --git a/tests/dictionaries/10check_valid_in_network_cidr/tiramisu/__init__.py b/tests/dictionaries/10check_valid_in_network_cidr/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10check_valid_in_network_cidr/tiramisu/base.py b/tests/dictionaries/10check_valid_in_network_cidr/tiramisu/base.py new file mode 100644 index 00000000..c994c75d --- /dev/null +++ b/tests/dictionaries/10check_valid_in_network_cidr/tiramisu/base.py @@ -0,0 +1,16 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='oui', values=('oui', 'non')) +option_4 = IPOption(cidr=True, properties=frozenset({'basic', 'mandatory'}), name='adresse_ip_eth0', doc='Adresse IP de la carte', multi=False) +option_5 = IPOption(private_only=True, warnings_only=True, properties=frozenset({'basic', 'mandatory'}), validators=[Calculation(func.valid_in_network, Params((ParamSelfOption(), ParamOption(option_4, notraisepropertyerror=False, todict=False)), kwargs={}), warnings_only=True)], name='adresse_ip', doc='IP', multi=False) +option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'basic'}), children=[option_3, option_4, option_5]) +option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2]) +option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1]) diff --git a/tests/eosfunc/test.py b/tests/eosfunc/test.py index d258ac45..ec3de625 100644 --- a/tests/eosfunc/test.py +++ b/tests/eosfunc/test.py @@ -1,4 +1,4 @@ -from tiramisu import valid_not_equal, valid_ip_netmask, calc_value +from tiramisu import valid_not_equal, valid_ip_netmask, calc_value, valid_in_network def calc_val(*args, **kwargs): if len(args) > 0: