check variable and family name

This commit is contained in:
Emmanuel Garette 2021-02-16 12:08:45 +01:00
parent bd299e3d2b
commit e60fd4adbc
24 changed files with 158 additions and 57 deletions

View file

@ -34,3 +34,4 @@ Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fi
## Les templates ## Les templates
- Type creole - Type creole
FIXME ^^

View file

@ -29,7 +29,6 @@ from typing import List, Any
from ..i18n import _ from ..i18n import _
from ..error import DictConsistencyError from ..error import DictConsistencyError
from ..config import RougailConfig
from .target import TargetAnnotator from .target import TargetAnnotator
from .param import ParamAnnotator from .param import ParamAnnotator
@ -75,13 +74,13 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
for variable in self.get_variables(): for variable in self.get_variables():
if not variable.auto_freeze: if not variable.auto_freeze:
continue 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}"') msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
raise DictConsistencyError(msg, 49, variable.xmlfiles) raise DictConsistencyError(msg, 49, variable.xmlfiles)
new_condition = self.objectspace.condition(variable.xmlfiles) new_condition = self.objectspace.condition(variable.xmlfiles)
new_condition.name = 'auto_frozen_if_not_in' new_condition.name = 'auto_frozen_if_not_in'
new_condition.namespace = variable.namespace 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 = self.objectspace.param(variable.xmlfiles)
new_param.text = True new_param.text = True
new_condition.param = [new_param] new_condition.param = [new_param]
@ -114,12 +113,9 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
if condition.optional is False or \ if condition.optional is False or \
self.objectspace.paths.path_is_defined(condition.source): self.objectspace.paths.path_is_defined(condition.source):
continue 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) 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) self.force_actions_to_variable(condition)
remove_conditions.sort(reverse=True) remove_conditions.sort(reverse=True)
for idx in remove_conditions: for idx in remove_conditions:

View file

@ -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 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" """
from ..i18n import _
from ..error import DictConsistencyError
from ..objspace import convert_boolean from ..objspace import convert_boolean
@ -116,6 +118,9 @@ class VariableAnnotator(Walk): # pylint: disable=R0903
if not hasattr(objectspace.space, 'variables'): if not hasattr(objectspace.space, 'variables'):
return return
self.objectspace = objectspace 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_variable()
self.convert_test() self.convert_test()
self.convert_help() self.convert_help()
@ -141,6 +146,10 @@ class VariableAnnotator(Walk): # pylint: disable=R0903
variable, variable,
variable_type: str, variable_type: str,
) -> None: ) -> 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'): if variable.type != 'symlink' and not hasattr(variable, 'description'):
variable.description = variable.name variable.description = variable.name
if hasattr(variable, 'value'): if hasattr(variable, 'value'):

View file

@ -58,16 +58,22 @@ from .error import DictConsistencyError
class RougailConvert: class RougailConvert:
"""Rougail object """Rougail object
""" """
def __init__(self) -> None: def __init__(self,
xmlreflector = XMLReflector() rougailconfig: RougailConfig=None,
rougailobjspace = RougailObjSpace(xmlreflector) ) -> None:
if rougailconfig is None:
rougailconfig = RougailConfig
xmlreflector = XMLReflector(rougailconfig)
rougailobjspace = RougailObjSpace(xmlreflector,
rougailconfig,
)
self._load_dictionaries(xmlreflector, self._load_dictionaries(xmlreflector,
rougailobjspace, rougailobjspace,
RougailConfig['variable_namespace'], rougailconfig['variable_namespace'],
RougailConfig['dictionaries_dir'], rougailconfig['dictionaries_dir'],
) )
for namespace, extra_dir in RougailConfig['extra_dictionaries'].items(): for namespace, extra_dir in rougailconfig['extra_dictionaries'].items():
if namespace in ['services', RougailConfig['variable_namespace']]: if namespace in ['services', rougailconfig['variable_namespace']]:
msg = _(f'Namespace name "{namespace}" is not allowed') msg = _(f'Namespace name "{namespace}" is not allowed')
raise DictConsistencyError(msg, 21, None) raise DictConsistencyError(msg, 21, None)
self._load_dictionaries(xmlreflector, self._load_dictionaries(xmlreflector,
@ -75,7 +81,7 @@ class RougailConvert:
namespace, namespace,
extra_dir, extra_dir,
) )
functions_file = RougailConfig['functions_file'] functions_file = rougailconfig['functions_file']
SpaceAnnotator(rougailobjspace, SpaceAnnotator(rougailobjspace,
functions_file, functions_file,
) )

View file

@ -32,7 +32,6 @@ from .xmlreflector import XMLReflector
from .utils import normalize_family from .utils import normalize_family
from .error import SpaceObjShallNotBeUpdated, DictConsistencyError from .error import SpaceObjShallNotBeUpdated, DictConsistencyError
from .path import Path from .path import Path
from .config import RougailConfig
# RougailObjSpace's elements that shall be forced to the Redefinable type # RougailObjSpace's elements that shall be forced to the Redefinable type
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables') FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
@ -101,9 +100,10 @@ class RougailObjSpace:
def __init__(self, def __init__(self,
xmlreflector: XMLReflector, xmlreflector: XMLReflector,
rougailconfig: 'RougailConfig',
) -> None: ) -> None:
self.space = ObjSpace() self.space = ObjSpace()
self.paths = Path() self.paths = Path(rougailconfig)
self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME) self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME)
self.list_conditions = {} self.list_conditions = {}
@ -112,6 +112,7 @@ class RougailObjSpace:
self.has_dyn_option = False self.has_dyn_option = False
self.make_object_space_classes(xmlreflector) self.make_object_space_classes(xmlreflector)
self.rougailconfig = rougailconfig
def make_object_space_classes(self, def make_object_space_classes(self,
xmlreflector: XMLReflector, xmlreflector: XMLReflector,
@ -197,12 +198,12 @@ class RougailObjSpace:
family_names.append(child.attrib['name']) family_names.append(child.attrib['name'])
try: try:
# variable objects creation # variable objects creation
variableobj = self.get_variableobj(xmlfile, exists, variableobj = self.get_variableobj(xmlfile,
child, child,
space, space,
namespace, namespace,
redefine_variables, redefine_variables,
) )
except SpaceObjShallNotBeUpdated: except SpaceObjShallNotBeUpdated:
continue continue
self.set_text(child, self.set_text(child,
@ -216,11 +217,12 @@ class RougailObjSpace:
variableobj, variableobj,
redefine_variables, redefine_variables,
) )
self.set_path(namespace, if not exists:
document, self.set_path(namespace,
variableobj, document,
space, variableobj,
) space,
)
self.add_to_tree_structure(variableobj, self.add_to_tree_structure(variableobj,
space, space,
child, child,
@ -260,11 +262,12 @@ class RougailObjSpace:
if child.tag in vars(space): if child.tag in vars(space):
# Atom instance has to be a singleton here # Atom instance has to be a singleton here
# we do not re-create it, we reuse it # we do not re-create it, we reuse it
return getattr(space, child.tag) return False, getattr(space, child.tag)
return obj(xmlfile, name) return False, obj(xmlfile, name)
# UnRedefinable object
if child.tag not in vars(space): if child.tag not in vars(space):
setattr(space, child.tag, []) setattr(space, child.tag, [])
return obj(xmlfile, name) return False, obj(xmlfile, name)
def _get_name(self, def _get_name(self,
child, child,
@ -301,12 +304,12 @@ class RougailObjSpace:
redefine = convert_boolean(subspace.get('redefine', default_redefine)) redefine = convert_boolean(subspace.get('redefine', default_redefine))
if redefine is True: if redefine is True:
if isinstance(existed_var, self.variable): # pylint: disable=E1101 if isinstance(existed_var, self.variable): # pylint: disable=E1101
if namespace == RougailConfig['variable_namespace']: if namespace == self.rougailconfig['variable_namespace']:
redefine_variables.append(name) redefine_variables.append(name)
else: else:
redefine_variables.append(space.path + '.' + name) redefine_variables.append(space.path + '.' + name)
existed_var.xmlfiles.append(xmlfile) existed_var.xmlfiles.append(xmlfile)
return existed_var return True, existed_var
exists = convert_boolean(subspace.get('exists', True)) exists = convert_boolean(subspace.get('exists', True))
if exists is False: if exists is False:
raise SpaceObjShallNotBeUpdated() raise SpaceObjShallNotBeUpdated()
@ -327,7 +330,7 @@ class RougailObjSpace:
if tag not in vars(space): if tag not in vars(space):
setattr(space, tag, {}) setattr(space, tag, {})
obj = getattr(self, child.tag)(xmlfile, name) obj = getattr(self, child.tag)(xmlfile, name)
return obj return False, obj
def get_existed_obj(self, def get_existed_obj(self,
name: str, name: str,
@ -340,7 +343,7 @@ class RougailObjSpace:
if child.tag in ['variable', 'family']: if child.tag in ['variable', 'family']:
name = normalize_family(name) name = normalize_family(name)
if child.tag == 'variable': # pylint: disable=E1101 if child.tag == 'variable': # pylint: disable=E1101
if namespace != RougailConfig['variable_namespace']: if namespace != self.rougailconfig['variable_namespace']:
name = space.path + '.' + name name = space.path + '.' + name
if not self.paths.path_is_defined(name): if not self.paths.path_is_defined(name):
return None return None
@ -474,7 +477,7 @@ class RougailObjSpace:
) )
elif isinstance(variableobj, self.family): # pylint: disable=E1101 elif isinstance(variableobj, self.family): # pylint: disable=E1101
family_name = normalize_family(variableobj.name) family_name = normalize_family(variableobj.name)
if namespace != RougailConfig['variable_namespace']: if namespace != self.rougailconfig['variable_namespace']:
family_name = namespace + '.' + family_name family_name = namespace + '.' + family_name
self.paths.add_family(namespace, self.paths.add_family(namespace,
family_name, family_name,

View file

@ -26,7 +26,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" """
from .i18n import _ from .i18n import _
from .error import DictConsistencyError from .error import DictConsistencyError
from .config import RougailConfig
from .utils import normalize_family from .utils import normalize_family
@ -36,11 +35,14 @@ class Path:
sample: path="creole.general.condition" sample: path="creole.general.condition"
""" """
def __init__(self): def __init__(self,
rougailconfig: 'RougailConfig',
) -> None:
self.variables = {} self.variables = {}
self.families = {} self.families = {}
self.full_paths_families = {} self.full_paths_families = {}
self.full_paths_variables = {} self.full_paths_variables = {}
self.variable_namespace = rougailconfig['variable_namespace']
# Family # Family
def add_family(self, def add_family(self,
@ -51,8 +53,10 @@ class Path:
) -> str: # pylint: disable=C0111 ) -> str: # pylint: disable=C0111
"""Add a new family """Add a new family
""" """
if namespace == RougailConfig['variable_namespace']: if namespace == self.variable_namespace:
full_name = '.'.join([subpath, name]) 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 self.full_paths_families[name] = full_name
else: else:
if '.' not in name: # pragma: no cover if '.' not in name: # pragma: no cover
@ -93,7 +97,7 @@ class Path:
if name not in self.families: if name not in self.families:
raise DictConsistencyError(_(f'unknown option {name}'), 42, []) raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
dico = self.families[name] 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']: current_namespace != dico['namespace']:
msg = _(f'A family located in the "{dico["namespace"]}" namespace ' msg = _(f'A family located in the "{dico["namespace"]}" namespace '
f'shall not be used in the "{current_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]['leader'] = leadership_path
self.variables[new_path]['variableobj'].path = new_path self.variables[new_path]['variableobj'].path = new_path
self.variables[new_path]['family'] = leadership_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 self.full_paths_variables[name] = new_path
def is_leader(self, path): # pylint: disable=C0111 def is_leader(self, path): # pylint: disable=C0111
@ -141,7 +145,7 @@ class Path:
""" """
if '.' not in name: if '.' not in name:
full_path = '.'.join([family, name]) full_path = '.'.join([family, name])
if namespace == RougailConfig['variable_namespace']: if namespace == self.variable_namespace:
self.full_paths_variables[name] = full_path self.full_paths_variables[name] = full_path
else: else:
full_path = name full_path = name
@ -180,7 +184,7 @@ class Path:
with_suffix=True, with_suffix=True,
) )
namespace = dico['variableobj'].namespace 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: current_namespace != namespace:
msg = _(f'A variable located in the "{namespace}" namespace shall not be used ' msg = _(f'A variable located in the "{namespace}" namespace shall not be used '
f'in the "{current_namespace}" namespace') f'in the "{current_namespace}" namespace')

View file

@ -223,14 +223,17 @@ class RougailTemplate:
""" """
def __init__(self, # pylint: disable=R0913 def __init__(self, # pylint: disable=R0913
config: Config, config: Config,
rougailconfig: RougailConfig=None,
) -> None: ) -> None:
if rougailconfig is None:
rougailconfig = RougailConfig
self.config = config self.config = config
self.destinations_dir = abspath(RougailConfig['destinations_dir']) self.destinations_dir = abspath(rougailconfig['destinations_dir'])
self.tmp_dir = abspath(RougailConfig['tmp_dir']) self.tmp_dir = abspath(rougailconfig['tmp_dir'])
self.templates_dir = abspath(RougailConfig['templates_dir']) self.templates_dir = abspath(rougailconfig['templates_dir'])
self.patches_dir = abspath(RougailConfig['patches_dir']) self.patches_dir = abspath(rougailconfig['patches_dir'])
eos = {} eos = {}
functions_file = RougailConfig['functions_file'] functions_file = rougailconfig['functions_file']
if isfile(functions_file): if isfile(functions_file):
eosfunc = load_modules(functions_file) eosfunc = load_modules(functions_file)
for func in dir(eosfunc): for func in dir(eosfunc):
@ -238,6 +241,7 @@ class RougailTemplate:
eos[func] = getattr(eosfunc, func) eos[func] = getattr(eosfunc, func)
self.eosfunc = eos self.eosfunc = eos
self.rougail_variables_dict = {} self.rougail_variables_dict = {}
self.rougailconfig = rougailconfig
def patch_template(self, def patch_template(self,
filename: str, filename: str,
@ -343,7 +347,7 @@ class RougailTemplate:
chdir(self.templates_dir) chdir(self.templates_dir)
for option in await self.config.option.list(type='all'): for option in await self.config.option.list(type='all'):
namespace = await option.option.name() 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, self.rougail_variables_dict[namespace] = await self.load_variables(option,
is_variable_namespace, is_variable_namespace,
) )

View file

@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from json import dumps from json import dumps
from os.path import isfile from os.path import isfile
from .config import RougailConfig
from .annotator import CONVERT_OPTION from .annotator import CONVERT_OPTION
from .objspace import RootRougailObject from .objspace import RootRougailObject
@ -96,10 +95,10 @@ class TiramisuReflector:
because `extra` family could use `variable_namespace` variables. because `extra` family could use `variable_namespace` variables.
""" """
if hasattr(self.objectspace.space, 'variables'): if hasattr(self.objectspace.space, 'variables'):
if RougailConfig['variable_namespace'] in self.objectspace.space.variables: if self.objectspace.rougailconfig['variable_namespace'] in self.objectspace.space.variables:
yield self.objectspace.space.variables[RougailConfig['variable_namespace']] yield self.objectspace.space.variables[self.objectspace.rougailconfig['variable_namespace']]
for elt, value in self.objectspace.space.variables.items(): for elt, value in self.objectspace.space.variables.items():
if elt != RougailConfig['variable_namespace']: if elt != self.objectspace.rougailconfig['variable_namespace']:
yield value yield value
if hasattr(self.objectspace.space, 'services'): if hasattr(self.objectspace.space, 'services'):
yield self.objectspace.space.services yield self.objectspace.space.services

View file

@ -32,7 +32,6 @@ from lxml.etree import DTD, parse, XMLSyntaxError # pylint: disable=E0611
from .i18n import _ from .i18n import _
from .error import DictConsistencyError from .error import DictConsistencyError
from .config import RougailConfig
class XMLReflector: class XMLReflector:
@ -40,14 +39,16 @@ class XMLReflector:
parsing it, validating against the Creole DTD, parsing it, validating against the Creole DTD,
writing the xml result on the disk writing the xml result on the disk
""" """
def __init__(self): def __init__(self,
rougailconfig: 'RougailConfig',
) -> None:
"""Loads the Creole DTD """Loads the Creole DTD
:raises IOError: if the DTD is not found :raises IOError: if the DTD is not found
:param dtdfilename: the full filename of the Creole DTD :param dtdfilename: the full filename of the Creole DTD
""" """
dtdfilename = RougailConfig['dtdfilename'] dtdfilename = rougailconfig['dtdfilename']
if not isfile(dtdfilename): if not isfile(dtdfilename):
raise IOError(_("no such DTD file: {}").format(dtdfilename)) raise IOError(_("no such DTD file: {}").format(dtdfilename))
with open(dtdfilename, 'r') as dtdfd: with open(dtdfilename, 'r') as dtdfd:

View file

@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="général">
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
<value>non</value>
</variable>
<variable name="activer_ejabberd" type="string" description="No change" hidden="True">
<value>non</value>
</variable>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<rougail>
<variables>
<variable name="extra"/>
</variables>
</rougail>

View file

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.activer_ejabberd": "non", "extra.extra": null}

View file

@ -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])

View file

@ -0,0 +1,12 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<family name="general">
<variable name="my_variable"/>
</family>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<variable name="extra"/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<rougail>
<variables>
<variable name="day"/>
</variables>
</rougail>

View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<variable name="rougail"/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->