diff --git a/doc/README.md b/doc/README.md index ff8a82f4..e4b748a5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -34,3 +34,4 @@ Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fi ## Les templates - Type creole +FIXME ^^ diff --git a/src/rougail/annotator/condition.py b/src/rougail/annotator/condition.py index c3f48826..89ebb2ea 100644 --- a/src/rougail/annotator/condition.py +++ b/src/rougail/annotator/condition.py @@ -29,7 +29,6 @@ from typing import List, Any from ..i18n import _ from ..error import DictConsistencyError -from ..config import RougailConfig from .target import TargetAnnotator from .param import ParamAnnotator @@ -75,13 +74,13 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk): for variable in self.get_variables(): if not variable.auto_freeze: continue - if variable.namespace != RougailConfig['variable_namespace']: + if variable.namespace != self.objectspace.rougailconfig['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 = RougailConfig['auto_freeze_variable'] + new_condition.source = self.objectspace.rougailconfig['auto_freeze_variable'] new_param = self.objectspace.param(variable.xmlfiles) new_param.text = True new_condition.param = [new_param] @@ -114,12 +113,9 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk): if condition.optional is False or \ self.objectspace.paths.path_is_defined(condition.source): continue - if hasattr(condition, 'apply_on_fallback'): - apply_action = condition.apply_on_fallback - else: - apply_on_fallback = condition.name.endswith('_if_in'): remove_conditions.append(idx) - if apply_action: + if (hasattr(condition, 'apply_on_fallback') and condition.apply_on_fallback) or \ + (not hasattr(condition, 'apply_on_fallback') and condition.name.endswith('_if_in')): self.force_actions_to_variable(condition) remove_conditions.sort(reverse=True) for idx in remove_conditions: diff --git a/src/rougail/annotator/variable.py b/src/rougail/annotator/variable.py index a6b07c67..db432fda 100644 --- a/src/rougail/annotator/variable.py +++ b/src/rougail/annotator/variable.py @@ -25,6 +25,8 @@ 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 from ..objspace import convert_boolean @@ -116,6 +118,9 @@ class VariableAnnotator(Walk): # pylint: disable=R0903 if not hasattr(objectspace.space, 'variables'): return self.objectspace = objectspace + self.forbidden_name = ['services', self.objectspace.rougailconfig['variable_namespace']] + for extra in self.objectspace.rougailconfig['extra_dictionaries']: + self.forbidden_name.append(extra) self.convert_variable() self.convert_test() self.convert_help() @@ -141,6 +146,10 @@ class VariableAnnotator(Walk): # pylint: disable=R0903 variable, variable_type: str, ) -> None: + if variable.namespace == self.objectspace.rougailconfig['variable_namespace'] and \ + variable.name in self.forbidden_name: + msg = _(f'the name of the variable "{variable.name}" cannot be the same as the name of a namespace') + raise DictConsistencyError(msg, 54, variable.xmlfiles) if variable.type != 'symlink' and not hasattr(variable, 'description'): variable.description = variable.name if hasattr(variable, 'value'): diff --git a/src/rougail/convert.py b/src/rougail/convert.py index 143bbf82..ad4f0b0d 100644 --- a/src/rougail/convert.py +++ b/src/rougail/convert.py @@ -58,16 +58,22 @@ from .error import DictConsistencyError class RougailConvert: """Rougail object """ - def __init__(self) -> None: - xmlreflector = XMLReflector() - rougailobjspace = RougailObjSpace(xmlreflector) + def __init__(self, + rougailconfig: RougailConfig=None, + ) -> None: + if rougailconfig is None: + rougailconfig = RougailConfig + xmlreflector = XMLReflector(rougailconfig) + rougailobjspace = RougailObjSpace(xmlreflector, + rougailconfig, + ) self._load_dictionaries(xmlreflector, rougailobjspace, - RougailConfig['variable_namespace'], - RougailConfig['dictionaries_dir'], + rougailconfig['variable_namespace'], + rougailconfig['dictionaries_dir'], ) - for namespace, extra_dir in RougailConfig['extra_dictionaries'].items(): - if namespace in ['services', RougailConfig['variable_namespace']]: + for namespace, extra_dir in rougailconfig['extra_dictionaries'].items(): + if namespace in ['services', rougailconfig['variable_namespace']]: msg = _(f'Namespace name "{namespace}" is not allowed') raise DictConsistencyError(msg, 21, None) self._load_dictionaries(xmlreflector, @@ -75,7 +81,7 @@ class RougailConvert: namespace, extra_dir, ) - functions_file = RougailConfig['functions_file'] + functions_file = rougailconfig['functions_file'] SpaceAnnotator(rougailobjspace, functions_file, ) diff --git a/src/rougail/objspace.py b/src/rougail/objspace.py index b32ba34e..127bc2c5 100644 --- a/src/rougail/objspace.py +++ b/src/rougail/objspace.py @@ -32,7 +32,6 @@ from .xmlreflector import XMLReflector from .utils import normalize_family from .error import SpaceObjShallNotBeUpdated, DictConsistencyError from .path import Path -from .config import RougailConfig # RougailObjSpace's elements that shall be forced to the Redefinable type FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables') @@ -101,9 +100,10 @@ class RougailObjSpace: def __init__(self, xmlreflector: XMLReflector, + rougailconfig: 'RougailConfig', ) -> None: self.space = ObjSpace() - self.paths = Path() + self.paths = Path(rougailconfig) self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME) self.list_conditions = {} @@ -112,6 +112,7 @@ class RougailObjSpace: self.has_dyn_option = False self.make_object_space_classes(xmlreflector) + self.rougailconfig = rougailconfig def make_object_space_classes(self, xmlreflector: XMLReflector, @@ -197,12 +198,12 @@ class RougailObjSpace: family_names.append(child.attrib['name']) try: # variable objects creation - variableobj = self.get_variableobj(xmlfile, - child, - space, - namespace, - redefine_variables, - ) + exists, variableobj = self.get_variableobj(xmlfile, + child, + space, + namespace, + redefine_variables, + ) except SpaceObjShallNotBeUpdated: continue self.set_text(child, @@ -216,11 +217,12 @@ class RougailObjSpace: variableobj, redefine_variables, ) - self.set_path(namespace, - document, - variableobj, - space, - ) + if not exists: + self.set_path(namespace, + document, + variableobj, + space, + ) self.add_to_tree_structure(variableobj, space, child, @@ -260,11 +262,12 @@ class RougailObjSpace: if child.tag in vars(space): # Atom instance has to be a singleton here # we do not re-create it, we reuse it - return getattr(space, child.tag) - return obj(xmlfile, name) + return False, getattr(space, child.tag) + return False, obj(xmlfile, name) + # UnRedefinable object if child.tag not in vars(space): setattr(space, child.tag, []) - return obj(xmlfile, name) + return False, obj(xmlfile, name) def _get_name(self, child, @@ -301,12 +304,12 @@ class RougailObjSpace: redefine = convert_boolean(subspace.get('redefine', default_redefine)) if redefine is True: if isinstance(existed_var, self.variable): # pylint: disable=E1101 - if namespace == RougailConfig['variable_namespace']: + if namespace == self.rougailconfig['variable_namespace']: redefine_variables.append(name) else: redefine_variables.append(space.path + '.' + name) existed_var.xmlfiles.append(xmlfile) - return existed_var + return True, existed_var exists = convert_boolean(subspace.get('exists', True)) if exists is False: raise SpaceObjShallNotBeUpdated() @@ -327,7 +330,7 @@ class RougailObjSpace: if tag not in vars(space): setattr(space, tag, {}) obj = getattr(self, child.tag)(xmlfile, name) - return obj + return False, obj def get_existed_obj(self, name: str, @@ -340,7 +343,7 @@ class RougailObjSpace: if child.tag in ['variable', 'family']: name = normalize_family(name) if child.tag == 'variable': # pylint: disable=E1101 - if namespace != RougailConfig['variable_namespace']: + if namespace != self.rougailconfig['variable_namespace']: name = space.path + '.' + name if not self.paths.path_is_defined(name): return None @@ -474,7 +477,7 @@ class RougailObjSpace: ) elif isinstance(variableobj, self.family): # pylint: disable=E1101 family_name = normalize_family(variableobj.name) - if namespace != RougailConfig['variable_namespace']: + if namespace != self.rougailconfig['variable_namespace']: family_name = namespace + '.' + family_name self.paths.add_family(namespace, family_name, diff --git a/src/rougail/path.py b/src/rougail/path.py index 3829efee..ff1a2b78 100644 --- a/src/rougail/path.py +++ b/src/rougail/path.py @@ -26,7 +26,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ from .i18n import _ from .error import DictConsistencyError -from .config import RougailConfig from .utils import normalize_family @@ -36,11 +35,14 @@ class Path: sample: path="creole.general.condition" """ - def __init__(self): + def __init__(self, + rougailconfig: 'RougailConfig', + ) -> None: self.variables = {} self.families = {} self.full_paths_families = {} self.full_paths_variables = {} + self.variable_namespace = rougailconfig['variable_namespace'] # Family def add_family(self, @@ -51,8 +53,10 @@ class Path: ) -> str: # pylint: disable=C0111 """Add a new family """ - if namespace == RougailConfig['variable_namespace']: + if namespace == self.variable_namespace: full_name = '.'.join([subpath, name]) + if name in self.full_paths_families: + raise DictConsistencyError(_(f'Duplicate family name "{name}"'), 55, variableobj.xmlfiles) self.full_paths_families[name] = full_name else: if '.' not in name: # pragma: no cover @@ -93,7 +97,7 @@ class Path: if name not in self.families: raise DictConsistencyError(_(f'unknown option {name}'), 42, []) dico = self.families[name] - if current_namespace not in [RougailConfig['variable_namespace'], 'services'] and \ + if current_namespace not in [self.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') @@ -117,7 +121,7 @@ class Path: self.variables[new_path]['leader'] = leadership_path self.variables[new_path]['variableobj'].path = new_path self.variables[new_path]['family'] = leadership_path - if namespace == RougailConfig['variable_namespace']: + if namespace == self.variable_namespace: self.full_paths_variables[name] = new_path def is_leader(self, path): # pylint: disable=C0111 @@ -141,7 +145,7 @@ class Path: """ if '.' not in name: full_path = '.'.join([family, name]) - if namespace == RougailConfig['variable_namespace']: + if namespace == self.variable_namespace: self.full_paths_variables[name] = full_path else: full_path = name @@ -180,7 +184,7 @@ class Path: with_suffix=True, ) namespace = dico['variableobj'].namespace - if namespace not in [RougailConfig['variable_namespace'], 'services'] and \ + if namespace not in [self.variable_namespace, 'services'] and \ current_namespace != namespace: msg = _(f'A variable located in the "{namespace}" namespace shall not be used ' f'in the "{current_namespace}" namespace') diff --git a/src/rougail/template.py b/src/rougail/template.py index b710563b..2cf63dcb 100644 --- a/src/rougail/template.py +++ b/src/rougail/template.py @@ -223,14 +223,17 @@ class RougailTemplate: """ def __init__(self, # pylint: disable=R0913 config: Config, + rougailconfig: RougailConfig=None, ) -> None: + if rougailconfig is None: + rougailconfig = RougailConfig self.config = config - self.destinations_dir = abspath(RougailConfig['destinations_dir']) - self.tmp_dir = abspath(RougailConfig['tmp_dir']) - self.templates_dir = abspath(RougailConfig['templates_dir']) - self.patches_dir = abspath(RougailConfig['patches_dir']) + self.destinations_dir = abspath(rougailconfig['destinations_dir']) + self.tmp_dir = abspath(rougailconfig['tmp_dir']) + self.templates_dir = abspath(rougailconfig['templates_dir']) + self.patches_dir = abspath(rougailconfig['patches_dir']) eos = {} - functions_file = RougailConfig['functions_file'] + functions_file = rougailconfig['functions_file'] if isfile(functions_file): eosfunc = load_modules(functions_file) for func in dir(eosfunc): @@ -238,6 +241,7 @@ class RougailTemplate: eos[func] = getattr(eosfunc, func) self.eosfunc = eos self.rougail_variables_dict = {} + self.rougailconfig = rougailconfig def patch_template(self, filename: str, @@ -343,7 +347,7 @@ class RougailTemplate: chdir(self.templates_dir) for option in await self.config.option.list(type='all'): namespace = await option.option.name() - is_variable_namespace = namespace == RougailConfig['variable_namespace'] + is_variable_namespace = namespace == self.rougailconfig['variable_namespace'] self.rougail_variables_dict[namespace] = await self.load_variables(option, is_variable_namespace, ) diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py index 9198b99a..0d78bb38 100644 --- a/src/rougail/tiramisureflector.py +++ b/src/rougail/tiramisureflector.py @@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from json import dumps from os.path import isfile -from .config import RougailConfig from .annotator import CONVERT_OPTION from .objspace import RootRougailObject @@ -96,10 +95,10 @@ class TiramisuReflector: because `extra` family could use `variable_namespace` variables. """ if hasattr(self.objectspace.space, 'variables'): - if RougailConfig['variable_namespace'] in self.objectspace.space.variables: - yield self.objectspace.space.variables[RougailConfig['variable_namespace']] + if self.objectspace.rougailconfig['variable_namespace'] in self.objectspace.space.variables: + yield self.objectspace.space.variables[self.objectspace.rougailconfig['variable_namespace']] for elt, value in self.objectspace.space.variables.items(): - if elt != RougailConfig['variable_namespace']: + if elt != self.objectspace.rougailconfig['variable_namespace']: yield value if hasattr(self.objectspace.space, 'services'): yield self.objectspace.space.services diff --git a/src/rougail/xmlreflector.py b/src/rougail/xmlreflector.py index e59408b2..48bf50bb 100644 --- a/src/rougail/xmlreflector.py +++ b/src/rougail/xmlreflector.py @@ -32,7 +32,6 @@ from lxml.etree import DTD, parse, XMLSyntaxError # pylint: disable=E0611 from .i18n import _ from .error import DictConsistencyError -from .config import RougailConfig class XMLReflector: @@ -40,14 +39,16 @@ class XMLReflector: parsing it, validating against the Creole DTD, writing the xml result on the disk """ - def __init__(self): + def __init__(self, + rougailconfig: 'RougailConfig', + ) -> None: """Loads the Creole DTD :raises IOError: if the DTD is not found :param dtdfilename: the full filename of the Creole DTD """ - dtdfilename = RougailConfig['dtdfilename'] + dtdfilename = rougailconfig['dtdfilename'] if not isfile(dtdfilename): raise IOError(_("no such DTD file: {}").format(dtdfilename)) with open(dtdfilename, 'r') as dtdfd: diff --git a/tests/dictionaries/60extra_variable_name_extra/00-base.xml b/tests/dictionaries/60extra_variable_name_extra/00-base.xml new file mode 100644 index 00000000..4eb9d373 --- /dev/null +++ b/tests/dictionaries/60extra_variable_name_extra/00-base.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/tests/dictionaries/60extra_variable_name_extra/__init__.py b/tests/dictionaries/60extra_variable_name_extra/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/60extra_variable_name_extra/extra_dirs/extra/00-base.xml b/tests/dictionaries/60extra_variable_name_extra/extra_dirs/extra/00-base.xml new file mode 100644 index 00000000..76a450e6 --- /dev/null +++ b/tests/dictionaries/60extra_variable_name_extra/extra_dirs/extra/00-base.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/dictionaries/60extra_variable_name_extra/makedict/base.json b/tests/dictionaries/60extra_variable_name_extra/makedict/base.json new file mode 100644 index 00000000..60d4d218 --- /dev/null +++ b/tests/dictionaries/60extra_variable_name_extra/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif": "non", "rougail.general.activer_ejabberd": "non", "extra.extra": null} diff --git a/tests/dictionaries/60extra_variable_name_extra/tiramisu/base.py b/tests/dictionaries/60extra_variable_name_extra/tiramisu/base.py new file mode 100644 index 00000000..dda96b9b --- /dev/null +++ b/tests/dictionaries/60extra_variable_name_extra/tiramisu/base.py @@ -0,0 +1,20 @@ +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec +loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +spec = spec_from_loader(loader.name, loader) +func = module_from_spec(spec) +loader.exec_module(func) +for key, value in dict(locals()).items(): + if key != ['SourceFileLoader', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"})) +option_4 = StrOption(name="activer_ejabberd", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_6 = StrOption(name="extra", doc="extra", properties=frozenset({"normal"})) +option_5 = OptionDescription(name="extra", doc="extra", children=[option_6]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_5]) diff --git a/tests/dictionaries/80family_unique/00-base.xml b/tests/dictionaries/80family_unique/00-base.xml new file mode 100644 index 00000000..da71a435 --- /dev/null +++ b/tests/dictionaries/80family_unique/00-base.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/tests/dictionaries/80family_unique/__init__.py b/tests/dictionaries/80family_unique/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80family_unique/errno_55 b/tests/dictionaries/80family_unique/errno_55 new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80variable_extra/00-base.xml b/tests/dictionaries/80variable_extra/00-base.xml new file mode 100644 index 00000000..4f429ec0 --- /dev/null +++ b/tests/dictionaries/80variable_extra/00-base.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/tests/dictionaries/80variable_extra/__init__.py b/tests/dictionaries/80variable_extra/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80variable_extra/errno_54 b/tests/dictionaries/80variable_extra/errno_54 new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80variable_extra/extra_dirs/extra/00-base.xml b/tests/dictionaries/80variable_extra/extra_dirs/extra/00-base.xml new file mode 100644 index 00000000..dd1a7ba3 --- /dev/null +++ b/tests/dictionaries/80variable_extra/extra_dirs/extra/00-base.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/dictionaries/80variable_rougail/00-base.xml b/tests/dictionaries/80variable_rougail/00-base.xml new file mode 100644 index 00000000..2d569b61 --- /dev/null +++ b/tests/dictionaries/80variable_rougail/00-base.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/tests/dictionaries/80variable_rougail/__init__.py b/tests/dictionaries/80variable_rougail/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/80variable_rougail/errno_54 b/tests/dictionaries/80variable_rougail/errno_54 new file mode 100644 index 00000000..e69de29b