diff --git a/src/rougail/__init__.py b/src/rougail/__init__.py
index bf8873fb4..959794d92 100644
--- a/src/rougail/__init__.py
+++ b/src/rougail/__init__.py
@@ -1,4 +1,5 @@
-#from .loader import load
+"""Rougail method
+"""
from .rougail import Rougail
from .annotator import modes
diff --git a/src/rougail/annotator/fill.py b/src/rougail/annotator/fill.py
index d30799ac7..a11f15db7 100644
--- a/src/rougail/annotator/fill.py
+++ b/src/rougail/annotator/fill.py
@@ -100,6 +100,10 @@ class FillAnnotator:
param.text = self.objectspace.paths.get_variable(path)
if suffix:
param.suffix = suffix
+ family_path = self.objectspace.paths.get_variable_family_path(path)
+ param.family = self.objectspace.paths.get_family(family_path,
+ param.text.namespace,
+ )
except DictConsistencyError as err:
if err.errno != 42 or not param.optional:
raise err
diff --git a/src/rougail/annotator/group.py b/src/rougail/annotator/group.py
index bce242a20..bd6ba4fc0 100644
--- a/src/rougail/annotator/group.py
+++ b/src/rougail/annotator/group.py
@@ -1,9 +1,6 @@
"""Annotate group
"""
-from typing import List
-
from ..i18n import _
-from ..config import Config
from ..error import DictConsistencyError
@@ -22,59 +19,45 @@ class GroupAnnotator:
def convert_groups(self): # pylint: disable=C0111
"""convert groups
"""
+ # store old leaders family name
cache_paths = {}
for group in self.objectspace.space.constraints.group:
- leader_fullname = group.leader
- leader = self.objectspace.paths.get_variable(leader_fullname)
- if leader_fullname in cache_paths:
- leader_family_path = cache_paths[leader_fullname]
+ if group.leader in cache_paths:
+ leader_fam_path = cache_paths[group.leader]
else:
- leader_family_path = self.objectspace.paths.get_variable_family_path(leader_fullname)
- cache_paths[leader_fullname] = leader_family_path
- if '.' not in leader_fullname:
- leader_fullname = '.'.join([leader_family_path, leader_fullname])
+ leader_fam_path = self.objectspace.paths.get_variable_family_path(group.leader)
+ cache_paths[group.leader] = leader_fam_path
follower_names = list(group.follower.keys())
- ori_leader_family = self.objectspace.paths.get_family(leader_family_path,
+ leader = self.objectspace.paths.get_variable(group.leader)
+ ori_leader_family = self.objectspace.paths.get_family(leader_fam_path,
leader.namespace,
)
has_a_leader = False
for variable in list(ori_leader_family.variable.values()):
- if has_a_leader:
- # it's a follower
- self.manage_follower(leader_family_path,
+ if isinstance(variable, self.objectspace.leadership) and \
+ variable.variable[0].name == leader.name:
+ # append follower to an existed leadership
+ leader_space = variable
+ has_a_leader = True
+ elif variable.name == leader.name:
+ # it's a leader
+ leader_space = self.manage_leader(variable,
+ group,
+ ori_leader_family,
+ )
+ has_a_leader = True
+ elif has_a_leader:
+ # it's should be a follower
+ self.manage_follower(follower_names.pop(0),
+ leader_fam_path,
variable,
- leadership_name,
- follower_names,
+ leader_space,
)
- if leader_is_hidden:
- variable.frozen = True
- variable.force_default_on_freeze = True
- leader_space.variable.append(variable)
+ # this variable is not more in ori_leader_family
ori_leader_family.variable.pop(variable.name)
if follower_names == []:
# no more follower
break
- elif variable.name == leader.name:
- # it's a leader
- if isinstance(variable, self.objectspace.leadership):
- # append follower to an existed leadership
- leader_space = variable
- # if variable.hidden:
- # leader_is_hidden = True
- else:
- leader_space = self.objectspace.leadership(variable.xmlfiles)
- if hasattr(group, 'name'):
- leadership_name = group.name
- else:
- leadership_name = leader.name
- leader_is_hidden = self.manage_leader(leader_space,
- leader_family_path,
- leadership_name,
- leader.name,
- variable,
- group,
- )
- has_a_leader = True
else:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
joined = '", "'.join(follower_names)
@@ -84,28 +67,27 @@ class GroupAnnotator:
del self.objectspace.space.constraints.group
def manage_leader(self,
- leader_space: 'Leadership',
- leader_family_name: str,
- leadership_name: str,
- leader_name: str,
variable: 'Variable',
group: 'Group',
- ) -> None:
+ ori_leader_family,
+ ) -> 'Leadership':
"""manage leader's variable
"""
if variable.multi is not True:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _(f'the variable "{variable.name}" in a group must be multi in {xmlfiles}')
raise DictConsistencyError(msg, 32)
+ if hasattr(group, 'name'):
+ leadership_name = group.name
+ else:
+ leadership_name = variable.name
+ leader_space = self.objectspace.leadership(variable.xmlfiles)
leader_space.variable = []
leader_space.name = leadership_name
leader_space.hidden = variable.hidden
if variable.hidden:
- leader_is_hidden = True
variable.frozen = True
variable.force_default_on_freeze = True
- else:
- leader_is_hidden = False
variable.hidden = None
if hasattr(group, 'description'):
leader_space.doc = group.description
@@ -113,38 +95,42 @@ class GroupAnnotator:
leader_space.doc = variable.description
else:
leader_space.doc = leadership_name
- namespace = variable.namespace
- leadership_path = leader_family_name + '.' + leadership_name
- self.objectspace.paths.add_leadership(namespace,
+ leadership_path = ori_leader_family.path + '.' + leadership_name
+ self.objectspace.paths.add_leadership(variable.namespace,
leadership_path,
leader_space,
)
- leader_family = self.objectspace.space.variables[namespace].family[leader_family_name.rsplit('.', 1)[-1]]
- leader_family.variable[leader_name] = leader_space
+ leader_family = self.objectspace.paths.get_family(ori_leader_family.path,
+ ori_leader_family.namespace,
+ )
+ leader_family.variable[variable.name] = leader_space
leader_space.variable.append(variable)
- self.objectspace.paths.set_leader(namespace,
- leader_family_name,
+ self.objectspace.paths.set_leader(variable.namespace,
+ ori_leader_family.path,
leadership_name,
- leader_name,
+ variable.name,
)
- return leader_is_hidden
+ return leader_space
def manage_follower(self,
+ follower_name: str,
leader_family_name: str,
variable: 'Variable',
- leadership_name: str,
- follower_names: List[str],
+ leader_space: 'Leadership',
) -> None:
"""manage follower
"""
- follower_name = follower_names.pop(0)
if variable.name != follower_name:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
- msg = _('when parsing leadership, we espect to find the follower '
+ msg = _('when parsing leadership, we expect to find the follower '
f'"{follower_name}" but we found "{variable.name}" in {xmlfiles}')
raise DictConsistencyError(msg, 33)
self.objectspace.paths.set_leader(variable.namespace,
leader_family_name,
- leadership_name,
+ leader_space.name,
variable.name,
)
+ if leader_space.hidden:
+ variable.frozen = True
+ variable.force_default_on_freeze = True
+ leader_space.variable.append(variable)
diff --git a/src/rougail/annotator/property.py b/src/rougail/annotator/property.py
index ec4767b1f..449578502 100644
--- a/src/rougail/annotator/property.py
+++ b/src/rougail/annotator/property.py
@@ -36,7 +36,8 @@ class PropertyAnnotator:
if hasattr(variable, 'mode') and variable.mode:
properties.append(variable.mode)
variable.mode = None
- if 'force_store_value' in properties and 'force_default_on_freeze' in properties: # pragma: no cover
+ if 'force_store_value' in properties and \
+ 'force_default_on_freeze' in properties: # pragma: no cover
# should not appened
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
msg = _('cannot have auto_freeze or auto_store with the hidden '
diff --git a/src/rougail/annotator/service.py b/src/rougail/annotator/service.py
index 5494020b0..dbd61d122 100644
--- a/src/rougail/annotator/service.py
+++ b/src/rougail/annotator/service.py
@@ -10,7 +10,7 @@ from ..error import DictConsistencyError
# 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', 'xmlfiles', 'type')
+ 'level', 'remove_fill', 'xmlfiles', 'type', 'reflector_name', 'reflector_object',)
KEY_TYPE = {'variable': 'symlink',
@@ -48,16 +48,18 @@ class ServiceAnnotator:
self.objectspace.space.services.hidden = True
self.objectspace.space.services.name = 'services'
self.objectspace.space.services.doc = 'services'
+ self.objectspace.space.services.path = 'services'
families = {}
for service_name in self.objectspace.space.services.service.keys():
service = self.objectspace.space.services.service[service_name]
new_service = self.objectspace.service(service.xmlfiles)
+ new_service.path = f'services.{service_name}'
for elttype, values in vars(service).items():
if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES:
setattr(new_service, elttype, values)
continue
eltname = elttype + 's'
- path = '.'.join(['services', service_name, eltname])
+ path = '.'.join(['services', normalize_family(service_name), eltname])
family = self._gen_family(eltname,
path,
service.xmlfiles,
@@ -145,7 +147,7 @@ class ServiceAnnotator:
c_name = name
if idx:
c_name += f'_{idx}'
- subpath = '{}.{}'.format(path, c_name)
+ subpath = '{}.{}'.format(path, normalize_family(c_name))
try:
self.objectspace.paths.get_family(subpath, 'services')
except DictConsistencyError as err:
diff --git a/src/rougail/annotator/variable.py b/src/rougail/annotator/variable.py
index 4ef19729b..bd23ec6d4 100644
--- a/src/rougail/annotator/variable.py
+++ b/src/rougail/annotator/variable.py
@@ -1,8 +1,7 @@
"""Annotate variable
"""
-from ..i18n import _
from ..utils import normalize_family
-from ..error import DictConsistencyError
+from ..config import Config
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
@@ -120,6 +119,7 @@ class VariableAnnotator:
"""
for families in self.objectspace.space.variables.values():
families.doc = families.name
+ families.path = families.name
for family in families.family.values():
family.doc = family.name
family.name = normalize_family(family.name)
diff --git a/src/rougail/error.py b/src/rougail/error.py
index fea027061..7e6cbc225 100644
--- a/src/rougail/error.py
+++ b/src/rougail/error.py
@@ -1,14 +1,18 @@
-# -*- coding: utf-8 -*-
+"""Standard error classes
+"""
class ConfigError(Exception):
- pass
+ """Standard error for templating
+ """
class FileNotFound(ConfigError):
- pass
+ """Template file is not found
+ """
class TemplateError(ConfigError):
- pass
+ """Templating generate an error
+ """
class TemplateDisabled(TemplateError):
@@ -29,7 +33,3 @@ class DictConsistencyError(Exception):
def __init__(self, msg, errno):
super().__init__(msg)
self.errno = errno
-
-
-class LoaderError(Exception):
- pass
diff --git a/src/rougail/objspace.py b/src/rougail/objspace.py
index f24f27853..6f1b38e0e 100644
--- a/src/rougail/objspace.py
+++ b/src/rougail/objspace.py
@@ -306,7 +306,7 @@ class RougailObjSpace:
) -> None:
"""if an object exists, return it
"""
- if child.tag == 'family':
+ if child.tag in ['variable', 'family']:
name = normalize_family(name)
if isinstance(space, self.family): # pylint: disable=E1101
if namespace != Config['variable_namespace']:
@@ -320,6 +320,7 @@ class RougailObjSpace:
f'now it is in "{space.path}" in {xmlfiles}')
raise DictConsistencyError(msg, 47)
return self.paths.get_variable(name)
+ # it's not a family
children = getattr(space, child.tag, {})
if name in children:
return children[name]
@@ -437,7 +438,7 @@ class RougailObjSpace:
if isinstance(variableobj, self.variable): # pylint: disable=E1101
family_name = normalize_family(document.attrib['name'])
self.paths.add_variable(namespace,
- variableobj.name,
+ normalize_family(variableobj.name),
namespace + '.' + family_name,
document.attrib.get('dynamic') is not None,
variableobj,
@@ -462,7 +463,7 @@ class RougailObjSpace:
variableobj.namespace = namespace
if isinstance(variableobj, Redefinable):
name = variableobj.name
- if child.tag == 'family':
+ if child.tag in ['family', 'variable']:
name = normalize_family(name)
getattr(space, child.tag)[name] = variableobj
elif isinstance(variableobj, UnRedefinable):
diff --git a/src/rougail/rougail.py b/src/rougail/rougail.py
index 718804350..ebd24e55b 100644
--- a/src/rougail/rougail.py
+++ b/src/rougail/rougail.py
@@ -28,12 +28,15 @@ from .annotator import SpaceAnnotator
class Rougail:
+ """Rougail object
+ """
def __init__(self,
dtdfilename: str,
) -> None:
self.xmlreflector = XMLReflector()
self.xmlreflector.parse_dtd(dtdfilename)
self.rougailobjspace = RougailObjSpace(self.xmlreflector)
+ self.funcs_path = None
def create_or_populate_from_xml(self,
namespace: str,
@@ -53,10 +56,14 @@ class Rougail:
def space_visitor(self,
eosfunc_file: str,
) -> None:
+ """All XML are loader, now annotate content
+ """
self.funcs_path = eosfunc_file
SpaceAnnotator(self.rougailobjspace, eosfunc_file)
def save(self) -> str:
+ """Return tiramisu object declaration as a string
+ """
tiramisu_objects = TiramisuReflector(self.rougailobjspace.space,
self.funcs_path,
)
diff --git a/src/rougail/template.py b/src/rougail/template.py
index 67baebcf8..99d4ea4d8 100644
--- a/src/rougail/template.py
+++ b/src/rougail/template.py
@@ -34,6 +34,8 @@ log.addHandler(logging.NullHandler())
@classmethod
def cl_compile(kls, *args, **kwargs):
+ """Rewrite compile methode to force some settings
+ """
kwargs['compilerSettings'] = {'directiveStartToken' : '%',
'cheetahVarStartToken' : '%%',
'EOLSlurpToken' : '%',
@@ -49,23 +51,16 @@ ChtTemplate.compile = cl_compile
class CheetahTemplate(ChtTemplate):
- """classe pour personnaliser et faciliter la construction
- du template Cheetah
+ """Construct a cheetah templating object
"""
def __init__(self,
filename: str,
context,
eosfunc: Dict,
- destfilename,
- variable,
+ extra_context: Dict,
):
"""Initialize Creole CheetahTemplate
"""
- extra_context = {'normalize_family': normalize_family,
- 'rougail_filename': destfilename
- }
- if variable:
- extra_context['rougail_variable'] = variable
ChtTemplate.__init__(self,
file=filename,
searchList=[context, eosfunc, extra_context])
@@ -75,12 +70,12 @@ class CheetahTemplate(ChtTemplate):
path=None,
normpath=normpath,
abspath=abspath
- ):
+ ): # pylint: disable=W0621
# strange...
if path is None and isinstance(self, str):
path = self
- if path:
+ if path: # pylint: disable=R1705
return normpath(abspath(path))
# original code return normpath(abspath(path.replace("\\", '/')))
elif hasattr(self, '_filePath') and self._filePath: # pragma: no cover
@@ -90,6 +85,8 @@ class CheetahTemplate(ChtTemplate):
class CreoleLeaderIndex:
+ """This object is create when access to a specified Index of the variable
+ """
def __init__(self,
value,
follower,
@@ -136,6 +133,9 @@ class CreoleLeaderIndex:
class CreoleLeader:
+ """Implement access to leader and follower variable
+ For examples: %%leader, %%leader[0].follower1
+ """
def __init__(self,
value,
) -> None:
@@ -170,6 +170,8 @@ class CreoleLeader:
name: str,
path: str,
):
+ """Add a new follower
+ """
self._follower[name] = []
for index in range(len(self._value)):
try:
@@ -180,6 +182,9 @@ class CreoleLeader:
class CreoleExtra:
+ """Object that implement access to extra variable
+ For example %%extra1.family.variable
+ """
def __init__(self,
suboption: Dict) -> None:
self.suboption = suboption
@@ -216,53 +221,6 @@ class CreoleTemplateEngine:
self.eosfunc = eos
self.rougail_variables_dict = {}
- async def load_eole_variables_rougail(self,
- optiondescription,
- ):
- for option in await optiondescription.list('all'):
- if await option.option.isoptiondescription():
- if await option.option.isleadership():
- for idx, suboption in enumerate(await option.list('all')):
- if idx == 0:
- leader = CreoleLeader(await suboption.value.get())
- self.rougail_variables_dict[await suboption.option.name()] = leader
- else:
- await leader.add_follower(self.config,
- await suboption.option.name(),
- await suboption.option.path(),
- )
- else:
- await self.load_eole_variables_rougail(option)
- else:
- self.rougail_variables_dict[await option.option.name()] = await option.value.get()
-
- async def load_eole_variables(self,
- optiondescription,
- ):
- families = {}
- for family in await optiondescription.list('all'):
- variables = {}
- for variable in await family.list('all'):
- if await variable.option.isoptiondescription():
- if await variable.option.isleadership():
- for idx, suboption in enumerate(await variable.list('all')):
- if idx == 0:
- leader = CreoleLeader(await suboption.value.get())
- leader_name = await suboption.option.name()
- else:
- await leader.add_follower(self.config,
- await suboption.option.name(),
- await suboption.option.path(),
- )
- variables[leader_name] = leader
- else:
- subfamilies = await self.load_eole_variables(variable)
- variables[await variable.option.name()] = subfamilies
- else:
- variables[await variable.option.name()] = await variable.value.get()
- families[await family.option.name()] = CreoleExtra(variables)
- return CreoleExtra(families)
-
def patch_template(self,
filename: str,
tmp_dir: str,
@@ -280,7 +238,9 @@ class CreoleTemplateEngine:
ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file])
if ret: # pragma: no cover
patch_cmd_err = ' '.join(patch_cmd + ['-i', rel_patch_file])
- log.error(_(f"Error applying patch: '{rel_patch_file}'\nTo reproduce and fix this error {patch_cmd_err}"))
+ msg = _(f"Error applying patch: '{rel_patch_file}'\n"
+ f"To reproduce and fix this error {patch_cmd_err}")
+ log.error(_(msg))
copy(join(self.distrib_dir, filename), tmp_dir)
def prepare_template(self,
@@ -305,18 +265,24 @@ class CreoleTemplateEngine:
# full path of the destination file
log.info(_(f"Cheetah processing: '{destfilename}'"))
try:
+ extra_context = {'normalize_family': normalize_family,
+ 'rougail_filename': true_destfilename
+ }
+ if variable:
+ extra_context['rougail_variable'] = variable
cheetah_template = CheetahTemplate(source,
self.rougail_variables_dict,
self.eosfunc,
- true_destfilename,
- variable,
+ extra_context,
)
data = str(cheetah_template)
except CheetahNotFound as err: # pragma: no cover
varname = err.args[0][13:-1]
- raise TemplateError(_(f"Error: unknown variable used in template {source} to {destfilename} : {varname}"))
+ msg = f"Error: unknown variable used in template {source} to {destfilename}: {varname}"
+ raise TemplateError(_(msg))
except Exception as err: # pragma: no cover
- raise TemplateError(_(f"Error while instantiating template {source} to {destfilename}: {err}"))
+ msg = _(f"Error while instantiating template {source} to {destfilename}: {err}")
+ raise TemplateError(msg)
with open(destfilename, 'w') as file_h:
file_h.write(data)
@@ -366,10 +332,9 @@ class CreoleTemplateEngine:
for option in await self.config.option.list(type='all'):
namespace = await option.option.name()
if namespace == Config['variable_namespace']:
- await self.load_eole_variables_rougail(option)
+ await self.load_variables_namespace(option)
else:
- families = await self.load_eole_variables(option)
- self.rougail_variables_dict[namespace] = families
+ self.rougail_variables_dict[namespace] = await self.load_variables_extra(option)
for template in listdir('.'):
self.prepare_template(template, tmp_dir, patch_dir)
for service_obj in await self.config.option('services').list('all'):
@@ -389,6 +354,57 @@ class CreoleTemplateEngine:
log.debug(_("Instantiation of file '{filename}' disabled"))
chdir(ori_dir)
+ async def load_variables_namespace (self,
+ optiondescription,
+ ):
+ """load variables from the "variable namespace
+ """
+ for option in await optiondescription.list('all'):
+ if await option.option.isoptiondescription():
+ if await option.option.isleadership():
+ for idx, suboption in enumerate(await option.list('all')):
+ if idx == 0:
+ leader = CreoleLeader(await suboption.value.get())
+ self.rougail_variables_dict[await suboption.option.name()] = leader
+ else:
+ await leader.add_follower(self.config,
+ await suboption.option.name(),
+ await suboption.option.path(),
+ )
+ else:
+ await self.load_variables_namespace(option)
+ else:
+ self.rougail_variables_dict[await option.option.name()] = await option.value.get()
+
+ async def load_variables_extra(self,
+ optiondescription,
+ ) -> CreoleExtra:
+ """Load all variables and set it in CreoleExtra objects
+ """
+ families = {}
+ for family in await optiondescription.list('all'):
+ variables = {}
+ for variable in await family.list('all'):
+ if await variable.option.isoptiondescription():
+ if await variable.option.isleadership():
+ for idx, suboption in enumerate(await variable.list('all')):
+ if idx == 0:
+ leader = CreoleLeader(await suboption.value.get())
+ leader_name = await suboption.option.name()
+ else:
+ await leader.add_follower(self.config,
+ await suboption.option.name(),
+ await suboption.option.path(),
+ )
+ variables[leader_name] = leader
+ else:
+ subfamilies = await self.load_variables_extra(variable)
+ variables[await variable.option.name()] = subfamilies
+ else:
+ variables[await variable.option.name()] = await variable.value.get()
+ families[await family.option.name()] = CreoleExtra(variables)
+ return CreoleExtra(families)
+
async def generate(config: Config,
eosfunc_file: str,
@@ -396,6 +412,8 @@ async def generate(config: Config,
tmp_dir: str,
dest_dir: str,
) -> None:
+ """Generate all files
+ """
engine = CreoleTemplateEngine(config,
eosfunc_file,
distrib_dir,
diff --git a/src/rougail/tiramisu.py b/src/rougail/tiramisu.py
index a2b6754c6..133382a56 100644
--- a/src/rougail/tiramisu.py
+++ b/src/rougail/tiramisu.py
@@ -1,3 +1,5 @@
+"""Redefine Tiramisu object
+"""
try:
from tiramisu3 import DynOptionDescription
except ModuleNotFoundError:
@@ -6,6 +8,9 @@ from .utils import normalize_family
class ConvertDynOptionDescription(DynOptionDescription):
+ """Suffix could be an integer, we should convert it in str
+ Suffix could also contain invalid character, so we should "normalize" it
+ """
def convert_suffix_to_path(self, suffix):
if not isinstance(suffix, str):
suffix = str(suffix)
diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py
index 77a43661b..5cccd1907 100644
--- a/src/rougail/tiramisureflector.py
+++ b/src/rougail/tiramisureflector.py
@@ -2,8 +2,6 @@
flattened XML specific
"""
from .config import Config
-from .i18n import _
-from .error import LoaderError
from .annotator import ERASED_ATTRIBUTES, CONVERT_OPTION
@@ -13,82 +11,76 @@ ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi')
class Root():
+ """Root classes
+ """
path = '.'
class TiramisuReflector:
+ """Convert object to tiramisu representation
+ """
def __init__(self,
- xmlroot,
+ space,
funcs_path,
):
- self.storage = ElementStorage()
- self.storage.text = ["from importlib.machinery import SourceFileLoader",
- f"func = SourceFileLoader('func', '{funcs_path}').load_module()",
- "for key, value in dict(locals()).items():",
- " if key != ['SourceFileLoader', 'func']:",
- " setattr(func, key, value)",
- "try:",
- " from tiramisu3 import *",
- "except:",
- " from tiramisu import *",
- "from rougail.tiramisu import ConvertDynOptionDescription",
- ]
- self.make_tiramisu_objects(xmlroot)
- # parse object
- self.storage.get(Root()).get()
+ self.index = 0
+ self.text = ["from importlib.machinery import SourceFileLoader",
+ f"func = SourceFileLoader('func', '{funcs_path}').load_module()",
+ "for key, value in dict(locals()).items():",
+ " if key != ['SourceFileLoader', 'func']:",
+ " setattr(func, key, value)",
+ "try:",
+ " from tiramisu3 import *",
+ "except:",
+ " from tiramisu import *",
+ "from rougail.tiramisu import ConvertDynOptionDescription",
+ ]
+ self.make_tiramisu_objects(space)
def make_tiramisu_objects(self,
- xmlroot,
+ space,
):
- family = self.get_root_family()
- for xmlelt in self.reorder_family(xmlroot):
- self.iter_family(xmlelt,
- family,
- None,
+ """make tiramisu objects
+ """
+ baseelt = BaseElt()
+ self.set_name(baseelt)
+ basefamily = Family(baseelt,
+ self,
+ )
+ for elt in self.reorder_family(space):
+ self.iter_family(basefamily,
+ elt,
)
+ # parse object
+ baseelt.reflector_object.get()
- def get_root_family(self):
- family = Family(BaseElt(),
- self.storage,
- False,
- '.',
- )
- return family
-
- def reorder_family(self, xmlroot):
- # variable_namespace family has to be loaded before any other family
- # because `extra` family could use `variable_namespace` variables.
- if hasattr(xmlroot, 'variables'):
- if Config['variable_namespace'] in xmlroot.variables:
- yield xmlroot.variables[Config['variable_namespace']]
- for xmlelt, value in xmlroot.variables.items():
- if xmlelt != Config['variable_namespace']:
+ @staticmethod
+ def reorder_family(space):
+ """variable_namespace family has to be loaded before any other family
+ because `extra` family could use `variable_namespace` variables.
+ """
+ if hasattr(space, 'variables'):
+ if Config['variable_namespace'] in space.variables:
+ yield space.variables[Config['variable_namespace']]
+ for elt, value in space.variables.items():
+ if elt != Config['variable_namespace']:
yield value
- if hasattr(xmlroot, 'services'):
- yield xmlroot.services
+ if hasattr(space, 'services'):
+ yield space.services
def get_attributes(self, space): # pylint: disable=R0201
+ """Get attributes
+ """
for attr in dir(space):
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
yield attr
- def get_children(self,
- space,
- ):
- for tag in self.get_attributes(space):
- children = getattr(space, tag)
- if children.__class__.__name__ == 'Family':
- children = [children]
- if isinstance(children, dict):
- children = list(children.values())
- if isinstance(children, list):
- yield tag, children
-
def iter_family(self,
- child,
family,
- subpath,
+ child,
):
+ """Iter each family
+ """
tag = child.__class__.__name__
if tag == 'Variable':
function = self.populate_variable
@@ -97,112 +89,102 @@ class TiramisuReflector:
return
else:
function = self.populate_family
- #else:
- # raise Exception('unknown tag {}'.format(child.tag))
function(family,
child,
- subpath,
)
def populate_family(self,
parent_family,
elt,
- subpath,
):
- path = self.build_path(subpath,
- elt,
- )
- tag = elt.__class__.__name__
+ """Populate family
+ """
+ self.set_name(elt)
family = Family(elt,
- self.storage,
- tag == 'Leadership',
- path,
+ self,
)
parent_family.add(family)
- for tag, children in self.get_children(elt):
+ for children in self.get_children(elt):
for child in children:
- self.iter_family(child,
- family,
- path,
+ self.iter_family(family,
+ child,
)
+ def get_children(self,
+ space,
+ ):
+ """Get children
+ """
+ for tag in self.get_attributes(space):
+ children = getattr(space, tag)
+ if children.__class__.__name__ == 'Family':
+ children = [children]
+ if isinstance(children, dict):
+ children = list(children.values())
+ if isinstance(children, list):
+ yield children
+
def populate_variable(self,
family,
elt,
- subpath,
):
- is_follower = False
- is_leader = False
+ """Populate variable
+ """
if family.is_leader:
- if elt.name != family.elt.name:
- is_follower = True
- else:
- is_leader = True
+ is_leader = elt.name == family.elt.variable[0].name
+ is_follower = not is_leader
+ else:
+ is_leader = False
+ is_follower = False
+ self.set_name(elt)
family.add(Variable(elt,
- self.storage,
+ self,
is_follower,
is_leader,
- self.build_path(subpath,
- elt,
- )
))
- def build_path(self,
- subpath,
- elt,
- ):
- if subpath is None:
- return elt.name
- return subpath + '.' + elt.name
+ def set_name(self,
+ elt,
+ ):
+ elt.reflector_name = f'option_{self.index}'
+ self.index += 1
def get_text(self):
- return '\n'.join(self.storage.get(Root()).get_text())
+ """Get text
+ """
+ return '\n'.join(self.text)
class BaseElt:
- def __init__(self) -> None:
- self.name = 'baseoption'
- self.doc = 'baseoption'
-
-
-class ElementStorage:
- def __init__(self,
- ):
- self.paths = {}
- self.text = []
- self.index = 0
-
- def add(self, path, elt):
- self.paths[path] = (elt, self.index)
- self.index += 1
-
- def get(self, obj):
- path = obj.path
- return self.paths[path][0]
-
- def get_name(self, path):
- return f'option_{self.paths[path][1]}'
+ """Base element
+ """
+ name = 'baseoption'
+ doc = 'baseoption'
+ path = '.'
class Common:
+ """Common function for variable and family
+ """
def __init__(self,
storage,
is_leader,
- path,
):
self.option_name = None
- self.path = path
self.attrib = {}
self.informations = {}
self.storage = storage
self.is_leader = is_leader
- self.storage.add(self.path, self)
+ self.elt.reflector_object = self
def populate_properties(self, child):
+ """Populate properties
+ """
assert child.type == 'calculation'
action = f"ParamValue('{child.name}')"
- option_name = self.storage.get(child.source).get()
- kwargs = f"'condition': ParamOption({option_name}, todict=True), 'expected': ParamValue('{child.expected}')"
+ option_name = child.source.reflector_object.get()
+ kwargs = (f"'condition': ParamOption({option_name}, todict=True), "
+ f"'expected': ParamValue('{child.expected}')")
if child.inverse:
kwargs += ", 'reverse_condition': ParamValue(True)"
prop = 'Calculation(calc_value, Params(' + action + ', kwargs={' + kwargs + '}))'
@@ -211,6 +193,8 @@ class Common:
self.attrib['properties'] += prop
def get_attrib(self):
+ """Get attributes
+ """
ret_list = []
for key, value in self.attrib.items():
if value is None:
@@ -227,16 +211,16 @@ class Common:
return ', '.join(ret_list)
def populate_informations(self):
+ """Populate Tiramisu's informations
+ """
for key, value in self.informations.items():
if isinstance(value, str):
value = '"' + value.replace('"', '\"') + '"'
self.storage.text.append(f'{self.option_name}.impl_set_information("{key}", {value})')
- def get_text(self,
- ):
- return self.storage.text
-
def get_attributes(self, space): # pylint: disable=R0201
+ """Get attributes
+ """
attributes = dir(space)
for attr in ATTRIBUTES_ORDER:
if attr in attributes:
@@ -245,12 +229,14 @@ class Common:
if attr not in ATTRIBUTES_ORDER:
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
value = getattr(space, attr)
- if not isinstance(value, (list, dict)) and not value.__class__.__name__ == 'Family':
+ if not isinstance(value, (list, dict)) and \
+ not value.__class__.__name__ == 'Family':
yield attr
- def get_children(self,
- space,
- ):
+ @staticmethod
+ def get_children(space):
+ """Get children
+ """
for attr in dir(space):
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
if isinstance(getattr(space, attr), list):
@@ -258,16 +244,17 @@ class Common:
class Variable(Common):
+ """Manage variable
+ """
def __init__(self,
elt,
storage,
is_follower,
is_leader,
- path,
):
+ self.elt = elt
super().__init__(storage,
is_leader,
- path,
)
self.is_follower = is_follower
convert_option = CONVERT_OPTION[elt.type]
@@ -276,22 +263,25 @@ class Variable(Common):
if self.object_type != 'SymLinkOption':
self.attrib['properties'] = []
self.attrib['validators'] = []
- self.elt = elt
def get(self):
+ """Get tiramisu's object
+ """
if self.option_name is None:
self.populate_attrib()
if self.object_type == 'SymLinkOption':
- self.attrib['opt'] = self.storage.get(self.attrib['opt']).get()
+ self.attrib['opt'] = self.attrib['opt'].reflector_object.get()
else:
self.parse_children()
attrib = self.get_attrib()
- self.option_name = self.storage.get_name(self.path)
+ self.option_name = self.elt.reflector_name
self.storage.text.append(f'{self.option_name} = {self.object_type}({attrib})')
self.populate_informations()
return self.option_name
def populate_attrib(self):
+ """Populate attributes
+ """
for key in self.get_attributes(self.elt):
value = getattr(self.elt, key)
if key in FORCE_INFORMATIONS:
@@ -306,6 +296,8 @@ class Variable(Common):
self.attrib[key] = value
def parse_children(self):
+ """Parse children
+ """
if 'default' not in self.attrib or self.attrib['multi']:
self.attrib['default'] = []
if self.attrib['multi'] == 'submulti' and self.is_follower:
@@ -313,7 +305,8 @@ class Variable(Common):
choices = []
if 'properties' in self.attrib:
if self.attrib['properties']:
- self.attrib['properties'] = "'" + "', '".join(sorted(list(self.attrib['properties']))) + "'"
+ self.attrib['properties'] = "'" + \
+ "', '".join(sorted(list(self.attrib['properties']))) + "'"
else:
self.attrib['properties'] = ''
for tag, children in self.get_children(self.elt):
@@ -326,10 +319,11 @@ class Variable(Common):
else:
self.populate_value(child)
elif tag == 'check':
- self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
+ validator = self.calculation_value(child, ['ParamSelfOption()'])
+ self.attrib['validators'].append(validator)
elif tag == 'choice':
if child.type == 'calculation':
- value = self.storage.get(child.name).get()
+ value = child.name.reflector_object.get()
choices = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
else:
choices.append(child.name)
@@ -347,7 +341,12 @@ class Variable(Common):
else:
self.attrib['validators'] = '[' + ', '.join(self.attrib['validators']) + ']'
- def calculation_value(self, child, args):
+ def calculation_value(self,
+ child,
+ args,
+ ) -> str:
+ """Generate calculated value
+ """
kwargs = []
# has parameters
function = child.name
@@ -358,7 +357,8 @@ class Variable(Common):
args.append(str(value))
else:
kwargs.append(f"'{param.name}': " + value)
- ret = f"Calculation(func.{function}, Params((" + ', '.join(args) + "), kwargs=" + "{" + ', '.join(kwargs) + "})"
+ ret = f"Calculation(func.{function}, Params((" + ', '.join(args) + \
+ "), kwargs=" + "{" + ', '.join(kwargs) + "})"
if hasattr(child, 'warnings_only'):
ret += f', warnings_only={child.warnings_only}'
return ret + ')'
@@ -367,6 +367,8 @@ class Variable(Common):
function: str,
param,
):
+ """Populate variable parameters
+ """
if param.type == 'string':
return f'ParamValue("{param.text}")'
if param.type == 'number':
@@ -378,16 +380,19 @@ class Variable(Common):
}
if hasattr(param, 'suffix'):
value['suffix'] = param.suffix
+ value['family'] = param.family
return self.build_param(value)
if param.type == 'information':
return f'ParamInformation("{param.text}", None)'
if param.type == 'suffix':
return 'ParamSuffix()'
- raise LoaderError(_('unknown param type {}').format(param.type)) # pragma: no cover
+ return '' # pragma: no cover
def populate_value(self,
child,
):
+ """Populate variable's values
+ """
value = child.name
if self.attrib['multi'] == 'submulti':
self.attrib['default_multi'].append(value)
@@ -405,61 +410,75 @@ class Variable(Common):
def build_param(self,
param,
):
- option_name = self.storage.get(param['option']).get()
+ """build variable parameters
+ """
+ option_name = param['option'].reflector_object.get()
+ ends = f"notraisepropertyerror={param['notraisepropertyerror']}, todict={param['todict']})"
if 'suffix' in param:
- family = '.'.join(param['option'].path.split('.')[:-1])
- family_option = self.storage.get_name(family)
- return f"ParamDynOption({option_name}, '{param['suffix']}', {family_option}, notraisepropertyerror={param['notraisepropertyerror']}, todict={param['todict']})"
- return f"ParamOption({option_name}, notraisepropertyerror={param['notraisepropertyerror']}, todict={param['todict']})"
+ family_name = param['family'].reflector_name
+ return f"ParamDynOption({option_name}, '{param['suffix']}', {family_name}, {ends}"
+ return f"ParamOption({option_name}, {ends}"
class Family(Common):
+ """Manage family
+ """
def __init__(self,
elt,
storage,
- is_leader,
- path,
):
+ self.elt = elt
super().__init__(storage,
- is_leader,
- path,
+ elt.__class__.__name__ == 'Leadership',
)
self.children = []
- self.elt = elt
def add(self, child):
+ """Add a child
+ """
self.children.append(child)
def get(self):
+ """Get tiramisu's object
+ """
if not self.option_name:
self.populate_attrib()
self.parse_children()
- self.option_name = self.storage.get_name(self.path)
+ self.option_name = self.elt.reflector_name
object_name = self.get_object_name()
- attrib = self.get_attrib() + ', children=[' + ', '.join([child.get() for child in self.children]) + ']'
+ attrib = self.get_attrib() + \
+ ', children=[' + ', '.join([child.get() for child in self.children]) + ']'
self.storage.text.append(f'{self.option_name} = {object_name}({attrib})')
self.populate_informations()
return self.option_name
def populate_attrib(self):
+ """parse a populate attributes
+ """
for key in self.get_attributes(self.elt):
value = getattr(self.elt, key)
if key in FORCE_INFORMATIONS:
self.informations[key] = value
elif key == 'dynamic':
- dynamic = self.storage.get(value).get()
- self.attrib['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dynamic}))))"
+ dynamic = value.reflector_object.get()
+ self.attrib['suffixes'] = \
+ f"Calculation(func.calc_value, Params((ParamOption({dynamic}))))"
else:
self.attrib[key] = value
def parse_children(self):
+ """parse current children
+ """
if 'properties' in self.attrib:
- self.attrib['properties'] = "'" + "', '".join(sorted(list(self.attrib['properties']))) + "'"
+ self.attrib['properties'] = "'" + \
+ "', '".join(sorted(list(self.attrib['properties']))) + "'"
if hasattr(self.elt, 'property'):
for child in self.elt.property:
self.populate_properties(child)
def get_object_name(self):
+ """Get family object's name
+ """
if 'suffixes' in self.attrib:
return 'ConvertDynOptionDescription'
if not self.is_leader:
diff --git a/tests/dictionaries/10leadership_append_hidden/00-base.xml b/tests/dictionaries/10leadership_append_hidden/00-base.xml
new file mode 100644
index 000000000..a6691165f
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_hidden/00-base.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ non
+
+
+
+
+
+
+
+
+
+
+
+ valfill
+
+
+ follower1
+
+
+ follower1
+ follower2
+
+
+
diff --git a/tests/dictionaries/10leadership_append_hidden/01-base.xml b/tests/dictionaries/10leadership_append_hidden/01-base.xml
new file mode 100644
index 000000000..93706d669
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_hidden/01-base.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+ follower3
+
+
+
diff --git a/tests/dictionaries/10leadership_append_hidden/__init__.py b/tests/dictionaries/10leadership_append_hidden/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/10leadership_append_hidden/makedict/base.json b/tests/dictionaries/10leadership_append_hidden/makedict/base.json
new file mode 100644
index 000000000..9e804f6a7
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_hidden/makedict/base.json
@@ -0,0 +1 @@
+{"rougail.general.mode_conteneur_actif": "non", "rougail.general1.leader.leader": []}
diff --git a/tests/dictionaries/10leadership_append_hidden/tiramisu/__init__.py b/tests/dictionaries/10leadership_append_hidden/tiramisu/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/10leadership_append_hidden/tiramisu/base.py b/tests/dictionaries/10leadership_append_hidden/tiramisu/base.py
new file mode 100644
index 000000000..590f41a1c
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_hidden/tiramisu/base.py
@@ -0,0 +1,20 @@
+from importlib.machinery import SourceFileLoader
+func = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py').load_module()
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+from rougail.tiramisu import ConvertDynOptionDescription
+option_3 = StrOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non')
+option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
+option_6 = StrOption(properties=frozenset({'force_default_on_freeze', 'frozen'}), name='leader', doc='leader', multi=True)
+option_7 = StrOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'normal'}), name='follower1', doc='follower1', multi=True, default=Calculation(func.calc_val, Params((), kwargs={'valeur': ParamValue("valfill")})))
+option_8 = StrOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'normal'}), name='follower2', doc='follower2', multi=True, default=Calculation(func.calc_val, Params((ParamOption(option_7, notraisepropertyerror=False, todict=False)), kwargs={})))
+option_9 = StrOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'normal'}), name='follower3', doc='follower3', multi=True)
+option_5 = Leadership(name='leader', doc='leader', properties=frozenset({'hidden', 'normal'}), children=[option_6, option_7, option_8, option_9])
+option_4 = OptionDescription(name='general1', doc='general1', properties=frozenset({'normal'}), children=[option_5])
+option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2, option_4])
+option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])
diff --git a/tests/dictionaries/10leadership_append_name/00-base.xml b/tests/dictionaries/10leadership_append_name/00-base.xml
new file mode 100644
index 000000000..da62fe5c8
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_name/00-base.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ non
+
+
+
+
+
+
+
+
+
+
+
+ valfill
+
+
+ follower1
+
+
+ follower1
+ follower2
+
+
+
diff --git a/tests/dictionaries/10leadership_append_name/01-base.xml b/tests/dictionaries/10leadership_append_name/01-base.xml
new file mode 100644
index 000000000..93706d669
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_name/01-base.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+ follower3
+
+
+
diff --git a/tests/dictionaries/10leadership_append_name/__init__.py b/tests/dictionaries/10leadership_append_name/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/10leadership_append_name/makedict/base.json b/tests/dictionaries/10leadership_append_name/makedict/base.json
new file mode 100644
index 000000000..2be64a766
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_name/makedict/base.json
@@ -0,0 +1 @@
+{"rougail.general.mode_conteneur_actif": "non", "rougail.general1.leadership.leader": []}
diff --git a/tests/dictionaries/10leadership_append_name/tiramisu/__init__.py b/tests/dictionaries/10leadership_append_name/tiramisu/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/10leadership_append_name/tiramisu/base.py b/tests/dictionaries/10leadership_append_name/tiramisu/base.py
new file mode 100644
index 000000000..f8e73ada8
--- /dev/null
+++ b/tests/dictionaries/10leadership_append_name/tiramisu/base.py
@@ -0,0 +1,20 @@
+from importlib.machinery import SourceFileLoader
+func = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py').load_module()
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+from rougail.tiramisu import ConvertDynOptionDescription
+option_3 = StrOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non')
+option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
+option_6 = StrOption(name='leader', doc='leader', multi=True)
+option_7 = StrOption(properties=frozenset({'normal'}), name='follower1', doc='follower1', multi=True, default=Calculation(func.calc_val, Params((), kwargs={'valeur': ParamValue("valfill")})))
+option_8 = StrOption(properties=frozenset({'normal'}), name='follower2', doc='follower2', multi=True, default=Calculation(func.calc_val, Params((ParamOption(option_7, notraisepropertyerror=False, todict=False)), kwargs={})))
+option_9 = StrOption(properties=frozenset({'normal'}), name='follower3', doc='follower3', multi=True)
+option_5 = Leadership(name='leadership', doc='leadership', properties=frozenset({'normal'}), children=[option_6, option_7, option_8, option_9])
+option_4 = OptionDescription(name='general1', doc='general1', properties=frozenset({'normal'}), children=[option_5])
+option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2, option_4])
+option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])