Compare commits

...

29 Commits

Author SHA1 Message Date
ccc6924866 simplify objectspace 2020-12-23 17:36:50 +01:00
6c6746c58f help in variable or family now 2020-12-23 13:29:51 +01:00
08ed28fc95 remove_check and remove_condition must be explicite 2020-12-23 11:50:20 +01:00
afdc19887f simplify normalize_family 2020-12-23 11:28:43 +01:00
0e08757e22 test multiple xmlfiles in error 2020-12-22 21:11:14 +01:00
90bd72de69 remove condition not only a target 2020-12-22 20:19:01 +01:00
80b7f1b083 remove xml_compare 2020-12-22 17:25:13 +01:00
7d42517430 remove 'imp' warning 2020-12-22 17:22:56 +01:00
d18906e011 better debug 2020-11-21 08:11:34 +01:00
62bccfc352 set good path in include file and patch file 2020-11-20 18:02:40 +01:00
7db3e2c2a9 allow filename with \ 2020-11-18 22:18:16 +01:00
3ad1bf0604 valid_not_equal must be launch with 'todict' argument 2020-11-15 11:22:09 +01:00
d5129d6fe7 add float support 2020-11-15 11:02:04 +01:00
6bda645903 leadership name 2020-11-14 17:20:55 +01:00
a835e5a017 valid_in_network should works with CIDR 2020-11-14 08:02:38 +01:00
5a45885ed8 family and var with same name 2020-11-11 16:59:44 +01:00
3d49f2fe8c add xmlfiles in various error message 2020-11-11 16:24:06 +01:00
f7db6b0289 add remove_fill attribute 2020-11-08 09:51:33 +01:00
8f01d2c1d8 can add personal function (tests file) 2020-11-08 09:43:45 +01:00
4b21b1507f can add personal function 2020-10-14 18:17:31 +02:00
43e30bba47 support empty param 2020-10-04 20:11:00 +02:00
3040a11de9 cherry-pick 2020-10-04 17:34:48 +02:00
8cb27a79d6 doc 2020-10-04 17:28:07 +02:00
101dfefaa9 dynamic variable and calculation 2020-10-04 17:27:55 +02:00
5eba872969 add OptionInformation support 2020-10-04 17:25:42 +02:00
246ac25791 refactor valid_enum 2020-10-04 17:23:55 +02:00
d789787d5c change Config object 2020-08-12 08:23:38 +02:00
d3670c6476 try tiramisu3 before tiramisu 2020-08-08 11:03:06 +02:00
4eb42e4290 merge flattener_dico and templates in tests 2020-08-08 08:58:35 +02:00
1377 changed files with 7886 additions and 8101 deletions

23
doc/auto.rst Normal file
View File

@ -0,0 +1,23 @@
Valeur automatiquement modifiée
===============================
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand le serveur sera déployé.
Voici un variable a valeur automatiquement modifiée :
<variable name="my_variable" type="oui/non" description="My variable" auto_save="True">
Dans ce cas la valeur est fixée à la valeur actuelle.
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
Valeur en lecture seule automatique
===================================
Une variable avec valeur en lecture seule automatique est une variable dont la valeur ne sera plus modifiable par l'utilisateur quand le serveur sera déployé.
Voici un variable à valeur en lecture seule automatique :
<variable name="my_variable" type="oui/non" description="My variable" auto_freeze="True">
Dans ce cas la valeur est fixée à la valeur actuelle et elle ne sera plus modifiable par l'utilisateur.
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.

285
doc/fill.rst Normal file
View File

@ -0,0 +1,285 @@
Les variables calculées
=======================
Une variable calculée est une variable donc sa valeur est le résultat d'une fonction python.
Variable avec une valeur par défaut calculée
--------------------------------------------
Créons une variable de type "oui/non" donc la valeur est retournée par la fonction "return_no" :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_no" target="my_calculated_variable"/>
</constraints>
Puis créons la fonction "return_no" :
def return_no():
return 'non'
Dans ce cas, la valeur par défaut est la valeur retournée par la fonction (ici "non"), elle sera calculée tant que l'utilisateur n'a pas de spécifié une valeur à cette variable.
Si l'utilisateur à définit une valeur par défaut à "my_calculated_variable" :
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable">
<value>oui</value>
</variable>
Cette valeur par défaut sera complètement ignorée.
Variable avec une valeur calculée
---------------------------------
En ajoutant le paramètre "hidden" à "True" dans la variable précédente, l'utilisateur n'aura plus la possibilité de modifié la valeur. La valeur de la variable sera donc systématiquement calculée :
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable" hidden="True"/>
Si une condition "hidden_if_in" est spécifié à la variable, la valeur sera modifiable par l'utilisateur si elle n'est pas cachée mais elle sera systèmatiquement calculée (même si elle a déjà était modifiée) si la variable est cachée.
Variable avec valeur calculée obligatoire
-----------------------------------------
Par défaut les variables calculées ne sont pas des varibles obligatoires.
Dans ce cas un calcul peut retourner None, mais surtout un utilisateur peut spécifier une valeur nulle à cette variable. Dans ce cas le calcul ne sera pas réalisé.
Fonction avec une valeur fixe comme paramètre positionnel
---------------------------------------------------------
Déclarons un calcul avec paramètre :
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param>non</param>
</fill>
</constraints>
Créons la fonction correspondante :
def return_value(value):
return value
La variable aura donc "non" comme valeur puisque le paramètre aura la valeur fixe "non".
Paramètre nommée
----------------
Déclarons une contrainte avec un paramètre nommée :
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param name="valeur">non</param>
</fill>
</constraints>
Dans ce cas la fonction return_value sera exécuté avec le paramètre nommé "valeur" dont sa valeur sera "non".
Paramètre avec un nombre
------------------------
Déclarons un calcul avec paramètre avec un nombre :
<constraints>
<fill name="return_value_with_number" target="my_calculated_variable">
<param type="number">1</param>
</fill>
</constraints>
Créons la fonction correspondante :
def return_value_with_number(value):
if value == 1:
return 'non'
return 'oui'
La variable aura donc "non" comme valeur puisque le paramètre aura la valeur fixe "1".
Paramètre dont la valeur est issue d'une autre variable
-------------------------------------------------------
Créons deux variables avec une contrainte de type variable qui contient le nom de la variable dont sa valeur sera utilisé comme paramètre :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
<variable name="my_variable" type="number" description="My variable">
<value>1</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value_with_number" target="my_calculated_variable">
<param type="variable">my_variable</param>
</fill>
</constraints>
Si l'utilisateur laisse la valeur 1 à "my_variable", la valeur par défault de la variable "my_calculated_variable" sera "non".
Si la valeur de "my_variable" est différent de 1, la valeur par défaut de la variable "my_calculated_variable" sera "oui".
Paramètre dont la valeur est issue d'une information de la configuration
------------------------------------------------------------------------
Créons une variable et la contrainte :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="string" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="information">server_name</param>
</fill>
</constraints>
Dans ce cas, l'information de la configuration "server_name" sera utilisé comme valeur de la variable "my_calculated_variable".
Si l'information n'existe pas, la paramètre aura la valeur "None".
Paramètre avec variable potentiellement non existante
-----------------------------------------------------
Suivant le contexte une variable peut exister ou ne pas exister.
Un paramètre de type "variable" peut être "optional" :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="variable" optional="True">unknow_variable</param>
</fill>
</constraints>
Dans ce cas la fonction "return_value" est exécuté sans paramètre.
Paramètre avec variable potentiellement désactivée
--------------------------------------------------
FIXME :
<!ATTLIST param notraisepropertyerror (True|False) "False">
Il n'y a pas spécialement de test !
Les variables suiveuses
-----------------------
FIXME :
- tests/flattener_dicos/10leadership_append/00-base.xml
- tests/flattener_dicos/10leadership_auto/00-base.xml
- tests/flattener_dicos/10leadership_autoleader/00-base.xml
- tests/flattener_dicos/10leadership_autoleader_expert/00-base.xml
Les variables dynamiques
------------------------
Paramètre avec variable dynamique
'''''''''''''''''''''''''''''''''
Il est possible de faire un calcul avec comme paramètre une variable dynamique mais pour une suffix particulier :
<variables>
<family name='family'>
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
<value>val1</value>
<value>val2</value>
</variable>
<variable name="my_calculated_variable" type="string" description="My calculated variable"/>
</family>
<family name='dyn' dynamic="suffixes">
<variable name='vardyn' type='string' description="Dynamic variable">
<value>val</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="variable">vardynval1</param>
</fill>
</constraints>
Dans ce cas, valeur du paramètre de la fonction "return_value" sera la valeur de la variable "vardyn" avec le suffix "val1".
Calcule d'une variable dynamique
''''''''''''''''''''''''''''''''
Il est également possible de calculer une variable dynamique à partir d'une variable standard :
<variables>
<family name='family'>
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
<value>val1</value>
<value>val2</value>
</variable>
<variable name="my_variable" type="string" description="My variable">
<value>val</value>
</variable>
</family>
<family name='dyn' dynamic="suffixes">
<variable name="my_calculated_variable_dyn_" type="string" description="My calculated variable"/>
<value>val</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable_dyn_">
<param type="variable">my_variable</param>
</fill>
</constraints>
Dans ce cas, les variables dynamiques "my_calculated_variable_dyn_" seront calculés à partir de la valeur de la variable "my_variable".
Que cela soit pour la variable "my_calculated_variable_dyn_val1" et "my_calculated_variable_dyn_val2".
Par contre, il n'est pas possible de faire un calcul pour une seule des deux variables issues de la variable dynamique.
Si c'est ce que vous cherchez à faire, il faudra prévoir un traitement particulier dans votre fonction.
Dans ce cas, il faut explicitement demander la valeur du suffix dans la fonction :
<constraints>
<fill name="return_value_suffix" target="my_calculated_variable_dyn_">
<param type="variable">my_variable</param>
<param type="suffix"/>
</fill>
</constraints>
Et ainsi faire un traitement spécifique pour ce suffix :
def return_value_suffix(value, suffix):
if suffix == 'val1':
return value
Redéfinition des calcules
-------------------------
Dans un premier dictionnaire déclarons notre variable et notre calcule :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_no" target="my_calculated_variable"/>
</constraints>
Dans un second dictionnaire il est possible de redéfinir le calcul :
<variables>
<family name="family">
<variable name="my_calculated_variable" redefine="True"/>
</family>
</variables>
<constraints>
<fill name="return_yes" target="my_calculated_variable"/>
</constraints>
Dans ce cas, à aucun moment la fonction "return_no" ne sera exécuté. Seul la fonction "return_yes" le sera.

13
doc/variable.rst Normal file
View File

@ -0,0 +1,13 @@
Variable
========
Variable obligatoire
--------------------
Variable dont une valeur est requise :
<variables>
<family name="family">
<variable name="my_variable" type="oui/non" description="My variable" mandatory="True"/>
</family>
</variables>

View File

@ -5,7 +5,7 @@ from typing import List
from collections import OrderedDict
from os.path import join, basename
from ast import literal_eval
import imp
from importlib.machinery import SourceFileLoader
from .i18n import _
@ -34,11 +34,38 @@ def mode_factory():
modes = mode_factory()
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
'float': dict(opttype="FloatOption", func=float),
'choice': dict(opttype="ChoiceOption"),
'string': dict(opttype="StrOption"),
'password': dict(opttype="PasswordOption"),
'mail': dict(opttype="EmailOption"),
'boolean': dict(opttype="BoolOption"),
'symlink': dict(opttype="SymLinkOption"),
'filename': dict(opttype="FilenameOption"),
'date': dict(opttype="DateOption"),
'unix_user': dict(opttype="UsernameOption"),
'ip': dict(opttype="IPOption", initkwargs={'allow_reserved': True}),
'local_ip': dict(opttype="IPOption", initkwargs={'private_only': True, 'warnings_only': True}),
'netmask': dict(opttype="NetmaskOption"),
'network': dict(opttype="NetworkOption"),
'broadcast': dict(opttype="BroadcastOption"),
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios', 'warnings_only': True}),
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname', 'allow_ip': False}),
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname', 'allow_ip': False}),
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True, 'allow_without_dot': True}),
'port': dict(opttype="PortOption", initkwargs={'allow_private': True}),
'mac': dict(opttype="MACOption"),
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
}
# a CreoleObjSpace's attribute has some annotations
# 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') # , '_real_container')
'level', 'remove_fill', 'xmlfiles')
ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group')
FORCE_CHOICE = {'oui/non': ['oui', 'non'],
@ -56,11 +83,6 @@ KEY_TYPE = {'variable': 'symlink',
'URLOption': 'web_address',
'FilenameOption': 'filename'}
TYPE_PARAM_CHECK = ('string', 'python', 'variable')
TYPE_PARAM_CONDITION = ('string', 'python', 'number', 'variable')
TYPE_PARAM_FILL = ('string', 'number', 'variable')
CONVERSION = {'number': int}
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
@ -111,7 +133,7 @@ class GroupAnnotator:
self.manage_follower(namespace,
leader_family_name,
variable,
leader_name,
leadership_name,
follower_names,
leader_space,
leader_is_hidden,
@ -128,9 +150,14 @@ class GroupAnnotator:
# if variable.hidden:
# leader_is_hidden = True
else:
leader_space = self.objectspace.Leadership()
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_name,
leadership_name,
leader_name,
namespace,
variable,
@ -139,12 +166,13 @@ class GroupAnnotator:
)
has_a_leader = True
else:
raise DictConsistencyError(_('cannot found followers {}').format(follower_names))
raise DictConsistencyError(_('cannot found followers "{}"').format('", "'.join(follower_names)))
del self.objectspace.space.constraints.group
def manage_leader(self,
leader_space: 'Leadership',
leader_family_name: str,
leadership_name: str,
leader_name: str,
namespace: str,
variable: 'Variable',
@ -155,7 +183,7 @@ class GroupAnnotator:
if variable.multi is not True:
raise DictConsistencyError(_('the variable {} in a group must be multi').format(variable.name))
leader_space.variable = []
leader_space.name = leader_name
leader_space.name = leadership_name
leader_space.hidden = variable.hidden
if variable.hidden:
leader_is_hidden = True
@ -170,9 +198,9 @@ class GroupAnnotator:
leader_space.doc = variable.description
else:
leader_space.doc = variable.name
leader_path = namespace + '.' + leader_family_name + '.' + leader_name
leadership_path = namespace + '.' + leader_family_name + '.' + leadership_name
self.objectspace.paths.add_family(namespace,
leader_path,
leadership_path,
leader_space,
)
leader_family = self.objectspace.space.variables[namespace].family[leader_family_name]
@ -236,7 +264,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)
@ -245,6 +273,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())
@ -261,8 +290,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
@ -310,7 +340,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'])
@ -348,7 +381,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':
@ -373,7 +406,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]
@ -431,8 +464,7 @@ class ServiceAnnotator:
if not hasattr(file_, 'source'):
file_.source = basename(file_.name)
elif not hasattr(file_, 'source'):
raise DictConsistencyError(_('attribute source mandatory for file with variable name '
'for {}').format(file_.name))
raise DictConsistencyError(_('attribute source mandatory for file with variable name for {}').format(file_.name))
class VariableAnnotator:
@ -441,7 +473,6 @@ class VariableAnnotator:
):
self.objectspace = objectspace
self.convert_variable()
self.convert_helps()
self.convert_auto_freeze()
self.convert_separators()
@ -457,7 +488,7 @@ class VariableAnnotator:
for value in variable.value:
if not hasattr(value, 'type'):
value.type = variable.type
value.name = CONVERSION.get(value.type, str)(value.name)
value.name = CONVERT_OPTION.get(value.type, {}).get('func', str)(value.name)
for key, value in RENAME_ATTIBUTES.items():
setattr(variable, value, getattr(variable, key))
setattr(variable, key, None)
@ -472,21 +503,28 @@ 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
param = self.objectspace.param()
param.text = str(FORCE_CHOICE[variable.type])
check.param = [param]
check.param = []
for value in FORCE_CHOICE[variable.type]:
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 = []
self.objectspace.space.constraints.check.append(check)
variable.type = 'string'
def _valid_type(variable):
if variable.type not in CONVERT_OPTION:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
raise DictConsistencyError(_(f'unvalid type "{variable.type}" for variable "{variable.name}" in {xmlfiles}'))
if not hasattr(self.objectspace.space, 'variables'):
return
for families in self.objectspace.space.variables.values():
@ -516,6 +554,7 @@ class VariableAnnotator:
follower,
path,
)
_valid_type(follower)
else:
path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name)
_convert_variable(variable,
@ -525,32 +564,19 @@ class VariableAnnotator:
variable,
path,
)
def convert_helps(self):
if not hasattr(self.objectspace.space, 'help'):
return
helps = self.objectspace.space.help
if hasattr(helps, 'variable'):
for hlp in helps.variable.values():
variable = self.objectspace.paths.get_variable_obj(hlp.name)
variable.help = hlp.text
if hasattr(helps, 'family'):
for hlp in helps.family.values():
variable = self.objectspace.paths.get_family_obj(hlp.name)
variable.help = hlp.text
del self.objectspace.space.help
_valid_type(variable)
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
@ -584,7 +610,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
@ -597,7 +624,9 @@ class ConstraintAnnotator:
if not hasattr(objectspace.space, 'constraints'):
return
self.objectspace = objectspace
self.eosfunc = imp.load_source('eosfunc', eosfunc_file)
eosfunc = SourceFileLoader('eosfunc', eosfunc_file).load_module()
self.functions = dir(eosfunc)
self.functions.extend(INTERNAL_FUNCTIONS)
self.valid_enums = {}
if hasattr(self.objectspace.space.constraints, 'check'):
self.check_check()
@ -619,21 +648,19 @@ class ConstraintAnnotator:
def check_check(self):
remove_indexes = []
functions = dir(self.eosfunc)
functions.extend(INTERNAL_FUNCTIONS)
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
if not check.name in functions:
raise DictConsistencyError(_('cannot find check function {}').format(check.name))
if not check.name in self.functions:
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):
if param.type not in TYPE_PARAM_CHECK:
raise DictConsistencyError(_('cannot use {} type as a param in check for {}').format(param.type, check.target))
if param.type == 'variable' and not self.objectspace.paths.path_is_defined(param.text):
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))
@ -661,55 +688,53 @@ class ConstraintAnnotator:
remove_indexes = []
for idx, check in enumerate(self.objectspace.space.constraints.check):
if check.name == 'valid_enum':
if len(check.param) != 1:
raise DictConsistencyError(_(f'cannot set more than one param for valid_enum for variable {check.target}'))
param = check.param[0]
if check.target in self.valid_enums:
raise DictConsistencyError(_(f'valid_enum already set for {check.target}'))
if param.type not in ['string', 'python', 'number']:
raise DictConsistencyError(_(f'unknown type {param.type} for param in valid_enum for {check.target}'))
old_xmlfiles = self.objectspace.display_xmlfiles(self.valid_enums[check.target]['xmlfiles'])
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'valid_enum define in {xmlfiles} but already set in {old_xmlfiles} for "{check.target}", did you forget remove_check?'))
if not hasattr(check, 'param'):
raise DictConsistencyError(_(f'param is mandatory for a valid_enum of variable {check.target}'))
variable = self.objectspace.paths.get_variable_obj(check.target)
values = self.load_params_in_validenum(param,
values = self.load_params_in_valid_enum(check.param,
variable.name,
variable.type,
)
self.valid_enums[check.target] = {'type': param.type,
'values': values}
self._set_valid_enum(variable,
values,
variable.type,
check.target,
check.xmlfiles,
)
remove_indexes.append(idx)
remove_indexes.sort(reverse=True)
for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx]
def load_params_in_validenum(self,
param,
def load_params_in_valid_enum(self,
params,
variable_name,
variable_type,
):
if not hasattr(param, 'text') and (param.type == 'python' or param.type == 'number'):
raise DictConsistencyError(_(f"All '{param.type}' variables shall be set in order to calculate valid_enum for variable {variable_name}"))
if variable_type == 'string' and param.type == 'number':
raise DictConsistencyError(_(f'Unconsistency valid_enum type ({param.type}), for variable {variable_name}'))
if param.type == 'python':
try:
values = eval(param.text, {'eosfunc': self.eosfunc, '__builtins__': {'range': range, 'str': str}})
except NameError:
raise DictConsistencyError(_('The function {} is unknown').format(param.text))
has_variable = None
values = []
for param in params:
if param.type == 'variable':
if has_variable is not None:
raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
has_variable = True
variable = self.objectspace.paths.get_variable_obj(param.text)
if not variable.multi:
raise DictConsistencyError(_(f'only multi "variable" parameter is allowed for valid_enum of variable {variable_name}'))
values = param.text
else:
try:
values = literal_eval(param.text)
except ValueError:
raise DictConsistencyError(_(f'Cannot load {param.text} in valid_enum'))
if not isinstance(values, list):
raise DictConsistencyError(_('Function {} shall return a list').format(param.text))
for value in values:
if variable_type == 'string' and not isinstance(value, str):
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a string'))
if variable_type == 'number' and not isinstance(value, int):
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a number'))
if has_variable:
raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
if not hasattr(param, 'text'):
if param.type == 'number':
raise DictConsistencyError(_(f'value is mandatory for valid_enum of variable {variable_name}'))
values.append(None)
else:
values.append(param.text)
return values
def check_change_warning(self):
@ -727,6 +752,7 @@ class ConstraintAnnotator:
if target.type == 'variable':
variable = self.objectspace.paths.get_variable_obj(target.name)
family = self.objectspace.paths.get_family_obj(target.name.rsplit('.', 1)[0])
# it's a leader, so apply property to leadership
if isinstance(family, self.objectspace.Leadership) and family.name == variable.name:
return family, family.variable
return variable, [variable]
@ -736,9 +762,6 @@ class ConstraintAnnotator:
def check_params_target(self):
for condition in self.objectspace.space.constraints.condition:
for param in condition.param:
if param.type not in TYPE_PARAM_CONDITION:
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a condition'))
if not hasattr(condition, 'target'):
raise DictConsistencyError(_('target is mandatory in condition'))
for target in condition.target:
@ -753,13 +776,15 @@ class ConstraintAnnotator:
if condition.source == target.name:
raise DictConsistencyError(_('target name and source name must be different: {}').format(condition.source))
try:
target.name = self.objectspace.paths.get_variable_path(target.name, namespace)
target_names = [normalize_family(name) for name in target.name.split('.')]
target.name = self.objectspace.paths.get_variable_path('.'.join(target_names), namespace)
except DictConsistencyError:
# for optional variable
pass
elif target.type == 'family':
try:
target.name = self.objectspace.paths.get_family_path(target.name, namespace)
target_names = [normalize_family(name) for name in target.name.split('.')]
target.name = self.objectspace.paths.get_family_path('.'.join(target_names), namespace)
except KeyError:
raise DictConsistencyError(_('cannot found family {}').format(target.name))
@ -777,7 +802,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
@ -892,43 +917,63 @@ class ConstraintAnnotator:
inverse = condition.name.endswith('_if_not_in')
actions = self._get_condition_actions(condition.name)
for param in condition.param:
if hasattr(param, 'text'):
param = param.text
else:
param = None
text = getattr(param, 'text', None)
for target in condition.target:
leader_or_variable, variables = self._get_family_variables_from_target(target)
# if option is already disable, do not apply disable_if_in
if hasattr(leader_or_variable, actions[0]) and getattr(leader_or_variable, actions[0]) is True:
# check only the first action (example of multiple actions: 'hidden', 'frozen', 'force_default_on_freeze')
main_action = actions[0]
if getattr(leader_or_variable, main_action, False) 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
prop.expected = param
prop.expected = text
prop.name = action
if idx == 0:
# main action is for the variable or family
if not hasattr(leader_or_variable, 'property'):
leader_or_variable.property = []
leader_or_variable.property.append(prop)
else:
# other actions are set to the variable or children of family
for variable in variables:
if not hasattr(variable, 'property'):
variable.property = []
variable.property.append(prop)
del self.objectspace.space.constraints.condition
def _set_valid_enum(self, variable, values, type_):
def _set_valid_enum(self,
variable,
values,
type_,
target: str,
xmlfiles: List[str],
):
# value for choice's variable is mandatory
variable.mandatory = True
# build choice
variable.choice = []
if isinstance(values, str):
choice = self.objectspace.choice()
choice.type = 'calculation'
choice.name = values
variable.choice.append(choice)
else:
self.valid_enums[target] = {'type': type_,
'values': values,
'xmlfiles': xmlfiles,
}
choices = []
for value in values:
choice = self.objectspace.choice()
choice = self.objectspace.choice(variable.xmlfiles)
try:
choice.name = CONVERSION.get(type_, str)(value)
if value is not None:
choice.name = CONVERT_OPTION[type_].get('func', str)(value)
else:
choice.name = value
except:
raise DictConsistencyError(_(f'unable to change type of a valid_enum entry "{value}" is not a valid "{type_}" for "{variable.name}"'))
if choice.name == '':
@ -936,23 +981,23 @@ class ConstraintAnnotator:
choices.append(choice.name)
choice.type = type_
variable.choice.append(choice)
if not variable.choice:
raise DictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name))
# check value or set first choice value has default value
if hasattr(variable, 'value'):
for value in variable.value:
value.type = type_
try:
cvalue = CONVERSION.get(type_, str)(value.name)
cvalue = CONVERT_OPTION[type_].get('func', str)(value.name)
except:
raise DictConsistencyError(_(f'unable to change type of value "{value}" is not a valid "{type_}" for "{variable.name}"'))
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.name = values[0]
new_value = self.objectspace.value(variable.xmlfiles)
new_value.name = choices[0]
new_value.type = type_
variable.value = [new_value]
if not variable.choice:
raise DictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name))
variable.type = 'choice'
def convert_check(self):
@ -972,26 +1017,30 @@ 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':
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':
if len(check.param) not in (1, 2):
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}'))
check_.name = name
check_.warnings_only = check.warnings_only
if hasattr(check, 'param'):
@ -1007,33 +1056,39 @@ class ConstraintAnnotator:
indexes = list(fills.keys())
indexes.sort()
targets = []
eosfunc = dir(self.eosfunc)
for idx in indexes:
fill = fills[idx]
# test if it's redefined calculation
if fill.target in targets and not fill.redefine:
raise DictConsistencyError(_(f"A fill already exists for the target: {fill.target}"))
if fill.target in targets:
xmlfiles = self.objectspace.display_xmlfiles(fill.xmlfiles)
raise DictConsistencyError(_(f'A fill already exists for the target of "{fill.target}" created in {xmlfiles}'))
targets.append(fill.target)
#
if fill.name not in eosfunc:
if fill.name not in self.functions:
raise DictConsistencyError(_('cannot find fill function {}').format(fill.name))
namespace = fill.namespace
# let's replace the target by the path
fill.target = self.objectspace.paths.get_variable_path(fill.target,
fill.target, suffix = self.objectspace.paths.get_variable_path(fill.target,
namespace,
with_suffix=True,
)
value = self.objectspace.value()
if suffix is not None:
raise DictConsistencyError(_(f'Cannot add fill function to "{fill.target}" only with the suffix "{suffix}"'))
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'):
param_to_delete = []
for fill_idx, param in enumerate(fill.param):
if param.type not in TYPE_PARAM_FILL:
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a fill/auto'))
if param.type != 'string' and not hasattr(param, 'text'):
raise DictConsistencyError(_(f"All '{param.type}' variables shall have a value in order to calculate {fill.target}"))
if param.type not in ['suffix', 'string'] and not hasattr(param, 'text'):
raise DictConsistencyError(_(f"All '{param.type}' variables must have a value in order to calculate {fill.target}"))
if param.type == 'suffix' and hasattr(param, 'text'):
raise DictConsistencyError(_(f"All '{param.type}' variables must not have a value in order to calculate {fill.target}"))
if param.type == 'string':
if not hasattr(param, 'text'):
param.text = None
if param.type == 'variable':
try:
param.text, suffix = self.objectspace.paths.get_variable_path(param.text,
@ -1053,7 +1108,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
@ -1061,6 +1115,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
@ -1121,7 +1176,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]
@ -1136,9 +1191,9 @@ class FamilyAnnotator:
if param.type == 'variable':
has_variable = True
break
if not has_variable:
# if one parameter is a variable, let variable choice if it's mandatory
variable.mandatory = True
#if not has_variable:
# # if one parameter is a variable, let variable choice if it's mandatory
# variable.mandatory = True
if has_value:
# if has value but without any calculation
variable.mandatory = True

View File

@ -3,17 +3,20 @@
fichier de configuration pour rougail
"""
from os.path import join, isfile, abspath, dirname
from pathlib import Path
from os.path import join, abspath, dirname
rougailroot = '/var/rougail'
patch_dir = join(rougailroot, 'patches')
manifests_dir = join(rougailroot, 'manifests')
templates_dir = join(rougailroot, 'templates')
dtddir = join(dirname(abspath(__file__)), 'data')
dtdfilename = join(dtddir, 'rougail.dtd')
Config = {'rougailroot': rougailroot,
'patch_dir': join(rougailroot, 'patches'),
'manifests_dir': join(rougailroot, 'manifests'),
'templates_dir': join(rougailroot, 'templates'),
'dtdfilename': join(dtddir, 'rougail.dtd'),
'dtddir': dtddir,
# chemin du répertoire source des fichiers templates
patch_dir = '/srv/rougail/patch'
variable_namespace = 'rougail'
'patch_dir': '/srv/rougail/patch',
'variable_namespace': 'rougail',
}

View File

@ -45,26 +45,26 @@
<!ELEMENT services (service*)>
<!ELEMENT service ((port* | tcpwrapper* | ip* | interface* | package* | file* | digitalcertificate* | override*)*) >
<!ELEMENT service ((port* | tcpwrapper* | ip* | interface* | package* | file* | override*)*) >
<!ATTLIST service name CDATA #REQUIRED>
<!ATTLIST service manage (True|False) "True">
<!ELEMENT port (#PCDATA)>
<!ATTLIST port port_type (PortOption|SymLinkOption|variable) "PortOption">
<!ATTLIST port port_type (PortOption|variable) "PortOption">
<!ATTLIST port portlist CDATA #IMPLIED >
<!ATTLIST port protocol (tcp|udp) "tcp">
<!ELEMENT ip (#PCDATA)>
<!ATTLIST ip iplist CDATA #IMPLIED >
<!ATTLIST ip ip_type (NetworkOption|SymLinkOption|variable) "NetworkOption">
<!ATTLIST ip interface_type (UnicodeOption|SymLinkOption|variable) "UnicodeOption">
<!ATTLIST ip ip_type (NetworkOption|variable) "NetworkOption">
<!ATTLIST ip interface_type (UnicodeOption|variable) "UnicodeOption">
<!ATTLIST ip interface CDATA #REQUIRED>
<!ATTLIST ip netmask_type (NetmaskOption|SymLinkOption|variable) "NetmaskOption">
<!ATTLIST ip netmask_type (NetmaskOption|variable) "NetmaskOption">
<!ATTLIST ip netmask CDATA "255.255.255.255">
<!ELEMENT file EMPTY>
<!ATTLIST file name CDATA #REQUIRED >
<!ATTLIST file file_type (UnicodeOption|SymLinkOption|variable) "UnicodeOption">
<!ATTLIST file file_type (UnicodeOption|variable) "UnicodeOption">
<!ATTLIST file variable CDATA #IMPLIED>
<!ATTLIST file variable_type (variable) "variable">
<!ATTLIST file source CDATA #IMPLIED>
@ -75,30 +75,24 @@
<!ATTLIST file redefine (True|False) "False">
<!ATTLIST file templating (True|False) "True">
<!ELEMENT digitalcertificate EMPTY>
<!ATTLIST digitalcertificate name CDATA #REQUIRED >
<!ATTLIST digitalcertificate digitalcertificate_type (variable) "variable">
<!ATTLIST digitalcertificate certificate CDATA #REQUIRED >
<!ATTLIST digitalcertificate certificate_type (variable) "variable">
<!ATTLIST digitalcertificate type CDATA #REQUIRED >
<!ATTLIST digitalcertificate ca CDATA #REQUIRED >
<!ELEMENT override EMPTY>
<!ATTLIST override source CDATA #IMPLIED >
<!ATTLIST override templating (True|False) "True">
<!ELEMENT variables (family*, separators*)>
<!ELEMENT family (#PCDATA | variable)*>
<!ELEMENT family (variable*)>
<!ATTLIST family name CDATA #REQUIRED>
<!ATTLIST family description CDATA #IMPLIED>
<!ATTLIST family help CDATA #IMPLIED>
<!ATTLIST family mode (basic|normal|expert) "basic">
<!ATTLIST family hidden (True|False) "False">
<!ATTLIST family dynamic CDATA #IMPLIED>
<!ELEMENT variable (#PCDATA | value)*>
<!ELEMENT variable (value*)>
<!ATTLIST variable name CDATA #REQUIRED>
<!ATTLIST variable type CDATA #IMPLIED>
<!ATTLIST variable description CDATA #IMPLIED>
<!ATTLIST variable help CDATA #IMPLIED>
<!ATTLIST variable hidden (True|False) "False">
<!ATTLIST variable disabled (True|False) "False">
<!ATTLIST variable multi (True|False) "False">
@ -110,6 +104,7 @@
<!ATTLIST variable mode (basic|normal|expert) "normal">
<!ATTLIST variable remove_check (True|False) "False">
<!ATTLIST variable remove_condition (True|False) "False">
<!ATTLIST variable remove_fill (True|False) "False">
<!ATTLIST variable test CDATA #IMPLIED>
<!ELEMENT separators (separator*)>
@ -138,10 +133,11 @@
<!ELEMENT group (follower+)>
<!ATTLIST group leader CDATA #REQUIRED>
<!ATTLIST group name CDATA #IMPLIED>
<!ATTLIST group description CDATA #IMPLIED>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param type (string|variable|number|python) "string">
<!ATTLIST param type (string|number|variable|information|suffix) "string">
<!ATTLIST param name CDATA #IMPLIED>
<!ATTLIST param notraisepropertyerror (True|False) "False">
<!ATTLIST param optional (True|False) "False">
@ -151,5 +147,3 @@
<!ATTLIST target optional (True|False) "False">
<!ELEMENT follower (#PCDATA)>
<!ELEMENT help ((variable* | family*)*)>

View File

@ -1,20 +1,25 @@
"""
Creole flattener. Takes a bunch of Creole XML dispatched in differents folders
as an input and outputs a human readable flatened XML
Takes a bunch of Creole XML dispatched in differents folders
as an input and outputs a Tiramisu's file
Sample usage::
eolobj.space_visitor(func)
xml = eolobj.save()
>>> from rougail.objspace import CreoleObjSpace
>>> eolobj = CreoleObjSpace('/usr/share/rougail/rougail.dtd')
>>> eolobj.create_or_populate_from_xml('rougail', ['/usr/share/eole/rougail/dicos'])
>>> eolobj.space_visitor()
>>> eolobj.save('/tmp/rougail_flatened_output.xml')
>>> eolobj.create_or_populate_from_xml('rougail', ['/usr/share/rougail/dicos'])
>>> eolobj.space_visitor('/usr/share/rougail/funcs.py')
>>> tiramisu = eolobj.save()
The CreoleObjSpace
- loads the XML into an internal CreoleObjSpace representation
- visits/annotates the objects
- dumps the object space as XML output into a single XML target
- dumps the object space as Tiramisu string
The visit/annotation stage is a complex step that corresponds to the Creole
procedures.
@ -23,16 +28,14 @@ For example: a variable is redefined and shall be moved to another family
means that a variable1 = Variable() object in the object space who lives in the family1 parent
has to be moved in family2. The visit procedure changes the varable1's object space's parent.
"""
from lxml.etree import Element, SubElement # pylint: disable=E0611
from .i18n import _
from .xmlreflector import XMLReflector
from .annotator import ERASED_ATTRIBUTES, SpaceAnnotator
from .annotator import SpaceAnnotator
from .tiramisureflector import TiramisuReflector
from .utils import normalize_family
from .error import OperationError, SpaceObjShallNotBeUpdated, DictConsistencyError
from .path import Path
from .config import variable_namespace
from .config import Config
# CreoleObjSpace's elements like 'family' or 'follower', that shall be forced to the Redefinable type
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
@ -41,25 +44,28 @@ FORCE_UNREDEFINABLES = ('value',)
# CreoleObjSpace's elements that shall be set to the UnRedefinable type
UNREDEFINABLE = ('multi', 'type')
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'], 'auto_freeze': ['force_store_value', 'auto_freeze']}
RENAME_ATTIBUTES = {'description': 'doc'}
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value', 'target')
CONVERT_EXPORT = {'Leadership': 'leader',
'Variable': 'variable',
'Value': 'value',
'Property': 'property',
'Choice': 'choice',
'Param': 'param',
'Check': 'check',
}
# _____________________________________________________________________________
# 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 Atom(RootCreoleObject):
pass
class Redefinable(RootCreoleObject):
pass
class UnRedefinable(RootCreoleObject):
pass
class CreoleObjSpace:
@ -74,11 +80,6 @@ class CreoleObjSpace:
an Object Space's atom object is present only once in the
object space's tree
"""
Atom = type('Atom', (RootCreoleObject,), dict())
"A variable that can't be redefined"
Redefinable = type('Redefinable', (RootCreoleObject,), dict())
"A variable can be redefined"
UnRedefinable = type('UnRedefinable', (RootCreoleObject,), dict())
def __init__(self, dtdfilename): # pylint: disable=R0912
@ -112,25 +113,25 @@ class CreoleObjSpace:
for dtd_elt in self.xmlreflector.dtd.iterelements():
attrs = {}
if dtd_elt.name in FORCE_REDEFINABLES:
clstype = self.Redefinable
clstype = Redefinable
elif not dtd_elt.attributes() and dtd_elt.name not in FORCE_UNREDEFINABLES:
clstype = Atom
else:
clstype = self.UnRedefinable
atomic = dtd_elt.name not in FORCE_UNREDEFINABLES and dtd_elt.name not in FORCE_REDEFINABLES
clstype = UnRedefinable
forced_text_elt = dtd_elt.type == 'mixed'
for dtd_attr in dtd_elt.iterattributes():
atomic = False
if set(dtd_attr.itervalues()) == set(['True', 'False']):
if set(dtd_attr.itervalues()) == {'True', 'False'}:
# it's a boolean
self.booleans_attributs.append(dtd_attr.name)
if dtd_attr.default_value:
# set default value for this attribute
default_value = dtd_attr.default_value
if dtd_attr.name in self.booleans_attributs:
default_value = self.convert_boolean(dtd_attr.default_value)
default_value = self.convert_boolean(default_value)
attrs[dtd_attr.name] = default_value
if dtd_attr.name == 'redefine':
# has a redefine attribute, so it's a Redefinable object
clstype = self.Redefinable
clstype = Redefinable
if dtd_attr.name == 'name' and forced_text_elt:
# child.text should be transform has a "name" attribute
self.forced_text_elts.add(dtd_elt.name)
@ -138,16 +139,14 @@ class CreoleObjSpace:
if forced_text_elt is True:
self.forced_text_elts_as_name.add(dtd_elt.name)
if atomic:
# has any attribute so it's an Atomic object
clstype = self.Atom
# create ObjectSpace object
setattr(self, dtd_elt.name, type(dtd_elt.name.capitalize(), (clstype,), attrs))
def create_or_populate_from_xml(self,
namespace,
xmlfolders):
xmlfolders,
):
"""Parses a bunch of XML files
populates the CreoleObjSpace
"""
@ -155,12 +154,14 @@ class CreoleObjSpace:
self.redefine_variables = []
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,
@ -168,41 +169,43 @@ class CreoleObjSpace:
"""Parses a Creole XML file
populates the CreoleObjSpace
"""
# var to check unique family name in a XML file
family_names = []
for child in document:
# this index enables us to reorder objects
self.index += 1
# doesn't proceed the XML commentaries
if not isinstance(child.tag, str):
# doesn't proceed the XML commentaries
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':
# variables has no name, so force namespace name
child.attrib['name'] = namespace
if child.tag == 'value' and child.text == None:
# FIXME should not be here
if child.tag == 'value' and child.text is None:
continue
# variable objects creation
try:
variableobj = self.generate_variableobj(child,
variableobj = self.get_variableobj(xmlfile,
child,
space,
namespace,
)
except SpaceObjShallNotBeUpdated:
continue
self.set_text_to_obj(child,
self.index += 1
self.set_text(child,
variableobj,
)
self.set_xml_attributes_to_obj(child,
self.set_attributes(xmlfile,
child,
variableobj,
)
self.variableobj_tree_visitor(child,
self.remove(child,
variableobj,
namespace,
)
self.fill_variableobj_path_attribute(space,
self.set_path(space,
child,
namespace,
document,
@ -211,132 +214,112 @@ class CreoleObjSpace:
self.add_to_tree_structure(variableobj,
space,
child,
namespace,
)
if list(child) != []:
self.xml_parse_document(child,
self.xml_parse_document(xmlfile,
child,
variableobj,
namespace,
)
def generate_variableobj(self,
child,
def get_variableobj(self,
xmlfile: str,
child: list,
space,
namespace,
):
"""
instanciates or creates Creole Object Subspace objects
"""
variableobj = getattr(self, child.tag)()
if isinstance(variableobj, self.Redefinable):
variableobj = self.create_or_update_redefinable_object(child.attrib,
obj = getattr(self, child.tag)
if Redefinable in obj.__mro__:
return 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
# this Atom instance has to be a singleton here
elif Atom in obj.__mro__:
if child.tag in vars(space):
# Atom instance has to be a singleton here
# we do not re-create it, we reuse it
variableobj = getattr(space, child.tag)
self.create_tree_structure(space,
child,
variableobj,
)
return variableobj
return getattr(space, child.tag)
return obj(xmlfile)
if child.tag not in vars(space):
setattr(space, child.tag, [])
return obj(xmlfile)
def create_or_update_redefinable_object(self,
xmlfile,
subspace,
space,
child,
namespace,
):
"""Creates or retrieves the space object that corresponds
to the `child` XML object
Two attributes of the `child` XML object are important:
- with the `redefine` boolean flag attribute we know whether
the corresponding space object shall be created or updated
- `True` means that the corresponding space object shall be updated
- `False` means that the corresponding space object shall be created
- with the `exists` boolean flag attribute we know whether
the corresponding space object shall be created
(or nothing -- that is the space object isn't modified)
- `True` means that the corresponding space object shall be created
- `False` means that the corresponding space object is not updated
In the special case `redefine` is True and `exists` is False,
we create the corresponding space object if it doesn't exist
and we update it if it exists.
:return: the corresponding space object of the `child` XML object
"""
if child.tag in self.forced_text_elts_as_name:
name = child.text
else:
name = subspace['name']
if self.is_already_exists(name,
space,
child,
namespace,
):
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:
return self.translate_in_space(name,
if child.tag == 'family':
name = normalize_family(name)
existed_var = self.get_existed_obj(name,
space,
child,
namespace,
)
elif exists is False:
if existed_var:
# if redefine is set to object, default value is False
# otherwise it's always a redefinable object
default_redefine = child.tag in FORCE_REDEFINABLES
redefine = self.convert_boolean(subspace.get('redefine', default_redefine))
if redefine is True:
existed_var.xmlfiles.append(xmlfile)
return existed_var
exists = self.convert_boolean(subspace.get('exists', True))
if exists is False:
raise SpaceObjShallNotBeUpdated()
raise DictConsistencyError(_(f'Already present in another XML file, {name} cannot be re-created'))
redefine = self.convert_boolean(subspace.get('redefine', False))
xmlfiles = self.display_xmlfiles(existed_var.xmlfiles)
raise DictConsistencyError(_(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", already defined in {xmlfiles}'))
# if this object must only be modified if object already exists
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'))
def create_tree_structure(self,
space,
child,
variableobj,
): # pylint: disable=R0201
"""
Builds the tree structure of the object space here
we set services attributes in order to be populated later on
for example::
space = Family()
space.variable = dict()
another example:
space = Variable()
space.value = list()
"""
if exists is True:
raise SpaceObjShallNotBeUpdated()
redefine = self.convert_boolean(subspace.get('redefine', False))
if redefine is False:
if child.tag not in vars(space):
if isinstance(variableobj, self.Redefinable):
setattr(space, child.tag, dict())
elif isinstance(variableobj, self.UnRedefinable):
setattr(space, child.tag, [])
elif not isinstance(variableobj, self.Atom): # pragma: no cover
raise OperationError(_("Creole object {} "
"has a wrong type").format(type(variableobj)))
setattr(space, child.tag, {})
return getattr(self, child.tag)(xmlfile)
raise DictConsistencyError(_(f'Redefined object in "{xmlfile}": "{name}" does not exist yet'))
def is_already_exists(self, name, space, child, namespace):
def display_xmlfiles(self,
xmlfiles: list,
) -> str:
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
def get_existed_obj(self,
name: str,
space: str,
child,
namespace: str,
):
if isinstance(space, self.family): # pylint: disable=E1101
if namespace != variable_namespace:
if namespace != Config['variable_namespace']:
name = space.path + '.' + name
return self.paths.path_is_defined(name)
if child.tag == 'family':
norm_name = normalize_family(name)
else:
norm_name = name
return norm_name in getattr(space, child.tag, {})
if self.paths.path_is_defined(name):
old_family_name = self.paths.get_variable_family_name(name)
if namespace != Config['variable_namespace']:
old_family_name = namespace + '.' + old_family_name
if space.path != old_family_name:
xmlfiles = self.display_xmlfiles(space.xmlfiles)
raise DictConsistencyError(_(f'Variable was previously create in family "{old_family_name}", now it is in "{space.path}" in {xmlfiles}'))
return self.paths.get_variable_obj(name)
return
children = getattr(space, child.tag, {})
if name in children:
return children[name]
def convert_boolean(self, value): # pylint: disable=R0201
"""Boolean coercion. The Creole XML may contain srings like `True` or `False`
@ -347,41 +330,59 @@ class CreoleObjSpace:
return True
elif value == 'False':
return False
else:
raise TypeError(_('{} is not True or False').format(value)) # pragma: no cover
def translate_in_space(self,
name,
family,
variable,
namespace,
def set_text(self,
child,
variableobj,
):
if not isinstance(family, self.family): # pylint: disable=E1101
if variable.tag == 'family':
norm_name = normalize_family(name)
if child.text is None:
return
text = child.text.strip()
if not text:
return
if child.tag in self.forced_text_elts_as_name:
variableobj.name = text
else:
norm_name = name
return getattr(family, variable.tag)[norm_name]
if namespace == variable_namespace:
path = name
else:
path = family.path + '.' + name
old_family_name = self.paths.get_variable_family_name(path)
if normalize_family(family.name) == old_family_name:
return getattr(family, variable.tag)[name]
old_family = self.space.variables[variable_namespace].family[old_family_name] # pylint: disable=E1101
variable_obj = old_family.variable[name]
del old_family.variable[name]
if 'variable' not in vars(family):
family.variable = dict()
family.variable[name] = variable_obj
self.paths.add_variable(namespace,
name,
family.name,
False,
variable_obj,
)
return variable_obj
variableobj.text = text
def set_attributes(self,
xmlfile,
child,
variableobj,
):
redefine = self.convert_boolean(child.attrib.get('redefine', False))
if redefine and child.tag == 'variable':
# delete old values
has_value = hasattr(variableobj, 'value')
if has_value and len(child) != 0:
del variableobj.value
for attr, val in child.attrib.items():
if redefine and attr in UNREDEFINABLE:
xmlfiles = self.display_xmlfiles(variableobj.xmlfiles[:-1])
raise DictConsistencyError(_(f'cannot redefine attribute "{attr}" for variable "{child.attrib["name"]}" in "{xmlfile}", already defined in {xmlfiles}'))
if attr in self.booleans_attributs:
val = self.convert_boolean(val)
if attr == 'name' and getattr(variableobj, 'name', None):
# do not redefine name
continue
setattr(variableobj, attr, val)
def remove(self,
child,
variableobj,
):
"""Creole object tree manipulations
"""
if child.tag == 'variable':
if child.attrib.get('remove_check', False):
self.remove_check(variableobj.name)
if child.attrib.get('remove_condition', False):
self.remove_condition(variableobj.name)
if child.attrib.get('remove_fill', False):
self.remove_fill(variableobj.name)
if child.tag == 'fill' and child.attrib['target'] in self.redefine_variables:
self.remove_fill(child.attrib['target'])
def remove_check(self, name): # pylint: disable=C0111
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'):
@ -396,93 +397,26 @@ class CreoleObjSpace:
self.space.constraints.check.pop(idx) # pylint: disable=E1101
def remove_condition(self, name): # pylint: disable=C0111
remove_conditions = []
for idx, condition in enumerate(self.space.constraints.condition): # pylint: disable=E1101
remove_targets = []
if hasattr(condition, 'target'):
for target_idx, target in enumerate(condition.target):
if target.name == name:
remove_targets.append(target_idx)
remove_targets = list(set(remove_targets))
remove_targets.sort(reverse=True)
for idx in remove_targets:
del condition.target[idx]
if condition.source == name:
remove_conditions.append(idx)
for idx in remove_conditions:
del self.space.constraints.condition[idx]
def add_to_tree_structure(self,
variableobj,
space,
child,
): # pylint: disable=R0201
if isinstance(variableobj, self.Redefinable):
name = variableobj.name
if child.tag == 'family':
name = normalize_family(name)
getattr(space, child.tag)[name] = variableobj
elif isinstance(variableobj, self.UnRedefinable):
getattr(space, child.tag).append(variableobj)
else:
setattr(space, child.tag, variableobj)
def remove_fill(self, name): # pylint: disable=C0111
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'fill'):
remove_fills= []
for idx, fill in enumerate(self.space.constraints.fill): # pylint: disable=E1101
if hasattr(fill, 'target') and fill.target == name:
remove_fills.append(idx)
def set_text_to_obj(self,
child,
variableobj,
):
if child.text is None:
text = None
else:
text = child.text.strip()
if text:
if child.tag in self.forced_text_elts_as_name:
variableobj.name = text
else:
variableobj.text = text
remove_fills = list(set(remove_fills))
remove_fills.sort(reverse=True)
for idx in remove_fills:
self.space.constraints.fill.pop(idx) # pylint: disable=E1101
def set_xml_attributes_to_obj(self,
child,
variableobj,
):
redefine = self.convert_boolean(child.attrib.get('redefine', False))
has_value = hasattr(variableobj, 'value')
if redefine is True and child.tag == 'variable' and has_value and len(child) != 0:
del variableobj.value
for attr, val in child.attrib.items():
if redefine and attr in UNREDEFINABLE:
# 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}'))
if attr in self.booleans_attributs:
val = self.convert_boolean(val)
if not (attr == 'name' and getattr(variableobj, 'name', None) != None):
setattr(variableobj, attr, val)
keys = list(vars(variableobj).keys())
def variableobj_tree_visitor(self,
child,
variableobj,
namespace,
):
"""Creole object tree manipulations
"""
if child.tag == 'variable':
if child.attrib.get('remove_check', False):
self.remove_check(variableobj.name)
if child.attrib.get('remove_condition', False):
self.remove_condition(variableobj.name)
if child.tag == 'fill':
# if variable is a redefine in current dictionary
# XXX not working with variable not in variable and in leader/followers
variableobj.redefine = child.attrib['target'] in self.redefine_variables
if not hasattr(variableobj, 'index'):
variableobj.index = self.index
if child.tag == 'check' and child.attrib['target'] in self.redefine_variables and child.attrib['target'] not in self.check_removed:
self.remove_check(child.attrib['target'])
self.check_removed.append(child.attrib['target'])
if child.tag == 'condition' and child.attrib['source'] in self.redefine_variables and child.attrib['source'] not in self.check_removed:
self.remove_condition(child.attrib['source'])
self.condition_removed.append(child.attrib['source'])
variableobj.namespace = namespace
def fill_variableobj_path_attribute(self,
def set_path(self,
space,
child,
namespace,
@ -491,17 +425,17 @@ class CreoleObjSpace:
): # pylint: disable=R0913
"""Fill self.paths attributes
"""
if isinstance(space, self.help): # pylint: disable=E1101
return
if child.tag == 'variable':
family_name = normalize_family(document.attrib['name'])
family_name = document.attrib['name']
family_name = normalize_family(family_name)
self.paths.add_variable(namespace,
child.attrib['name'],
family_name,
document.attrib.get('dynamic') != None,
variableobj)
variableobj,
)
if child.attrib.get('redefine', 'False') == 'True':
if namespace == variable_namespace:
if namespace == Config['variable_namespace']:
self.redefine_variables.append(child.attrib['name'])
else:
self.redefine_variables.append(namespace + '.' + family_name + '.' +
@ -509,7 +443,7 @@ class CreoleObjSpace:
elif child.tag == 'family':
family_name = normalize_family(child.attrib['name'])
if namespace != variable_namespace:
if namespace != Config['variable_namespace']:
family_name = namespace + '.' + family_name
self.paths.add_family(namespace,
family_name,
@ -517,6 +451,25 @@ class CreoleObjSpace:
)
variableobj.path = self.paths.get_family_path(family_name, namespace)
def add_to_tree_structure(self,
variableobj,
space,
child,
namespace,
): # pylint: disable=R0201
if not hasattr(variableobj, 'index'):
variableobj.index = self.index
variableobj.namespace = namespace
if isinstance(variableobj, Redefinable):
name = variableobj.name
if child.tag == 'family':
name = normalize_family(name)
getattr(space, child.tag)[name] = variableobj
elif isinstance(variableobj, UnRedefinable):
getattr(space, child.tag).append(variableobj)
else:
setattr(space, child.tag, variableobj)
def space_visitor(self, eosfunc_file): # pylint: disable=C0111
self.funcs_path = eosfunc_file
SpaceAnnotator(self, eosfunc_file)

View File

@ -1,7 +1,7 @@
from .i18n import _
from .utils import normalize_family
from .error import OperationError, DictConsistencyError
from .config import variable_namespace
from .config import Config
class Path:
@ -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,
@ -21,9 +22,9 @@ class Path:
name: str,
variableobj: str,
) -> str: # pylint: disable=C0111
if '.' not in name and namespace == variable_namespace:
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:
@ -37,16 +38,13 @@ class Path:
name: str,
current_namespace: str,
) -> str: # pylint: disable=C0111
name = normalize_family(name,
check_name=False,
allow_dot=True,
)
if '.' not in name and current_namespace == variable_namespace and name in self.full_paths:
name = self.full_paths[name]
#name = normalize_family(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]
if dico['namespace'] != variable_namespace and current_namespace != dico['namespace']:
if dico['namespace'] != Config['variable_namespace'] and current_namespace != dico['namespace']:
raise DictConsistencyError(_('A family located in the {} namespace '
'shall not be used in the {} namespace').format(
dico['namespace'], current_namespace))
@ -55,8 +53,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 +63,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
@ -87,8 +85,8 @@ class Path:
False,
dico['variableobj'],
)
if namespace == variable_namespace:
self.full_paths[name] = new_path
if namespace == Config['variable_namespace']:
self.full_paths_variables[name] = new_path
else:
name = new_path
dico = self._get_variable(name)
@ -110,10 +108,10 @@ 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 == variable_namespace:
if namespace == Config['variable_namespace']:
name = name.rsplit('.', 1)[1]
self.variables[full_name] = dict(name=name,
family=family,
@ -157,7 +155,7 @@ class Path:
else:
dico = self._get_variable(name)
if not allow_source:
if dico['namespace'] not in [variable_namespace, 'services'] and current_namespace != dico['namespace']:
if dico['namespace'] not in [Config['variable_namespace'], 'services'] and current_namespace != dico['namespace']:
raise DictConsistencyError(_('A variable located in the {} namespace '
'shall not be used in the {} namespace').format(
dico['namespace'], current_namespace))
@ -176,7 +174,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,17 +184,21 @@ 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):
if not with_suffix:
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')
return variable, name[len(var_name):]
raise DictConsistencyError(_('unknown option {}').format(name))
if with_suffix:

View File

@ -4,25 +4,25 @@ Gestion du mini-langage de template
On travaille sur les fichiers cibles
"""
import imp
from importlib.machinery import SourceFileLoader
from shutil import copy
import logging
from typing import Dict, Any
from subprocess import call
from os import listdir, makedirs
from os.path import dirname, join, isfile
from os import listdir, makedirs, getcwd, chdir
from os.path import dirname, join, isfile, abspath, normpath, relpath
from Cheetah.Template import Template as ChtTemplate
from Cheetah.NameMapper import NotFound as CheetahNotFound
try:
from tiramisu import Config
from tiramisu.error import PropertiesOptionError
except:
from tiramisu3 import Config
from tiramisu3.error import PropertiesOptionError
except:
from tiramisu import Config
from tiramisu.error import PropertiesOptionError
from .config import patch_dir, variable_namespace
from .config import Config
from .error import FileNotFound, TemplateError
from .i18n import _
from .utils import normalize_family
@ -79,7 +79,8 @@ class CheetahTemplate(ChtTemplate):
context,
eosfunc: Dict,
destfilename,
variable):
variable,
):
"""Initialize Creole CheetahTemplate
"""
extra_context = {'is_defined' : IsDefined(context),
@ -92,6 +93,24 @@ class CheetahTemplate(ChtTemplate):
file=filename,
searchList=[context, eosfunc, extra_context])
# FORK of Cheetah fonction, do not replace '\\' by '/'
def serverSidePath(self,
path=None,
normpath=normpath,
abspath=abspath
):
# strange...
if path is None and isinstance(self, str):
path = self
if path:
return normpath(abspath(path))
# return normpath(abspath(path.replace("\\", '/')))
elif hasattr(self, '_filePath') and self._filePath:
return normpath(abspath(self._filePath))
else:
return None
class CreoleLeader:
def __init__(self, value, follower=None, index=None):
@ -233,7 +252,7 @@ class CreoleTemplateEngine:
self.distrib_dir = distrib_dir
eos = {}
if eosfunc_file is not None:
eosfunc = imp.load_source('eosfunc', eosfunc_file)
eosfunc = SourceFileLoader('eosfunc', eosfunc_file).load_module()
for func in dir(eosfunc):
if not func.startswith('_'):
eos[func] = getattr(eosfunc, func)
@ -287,37 +306,43 @@ class CreoleTemplateEngine:
return CreoleExtra(families)
def patch_template(self,
filename: str):
filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Apply patch to a template
"""
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1']
patch_cmd = ['patch', '-d', tmp_dir, '-N', '-p1']
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
# patches variante + locaux
for directory in [join(patch_dir, 'variante'), patch_dir]:
patch_file = join(directory, f'{filename}.patch')
patch_file = join(patch_dir, f'{filename}.patch')
if isfile(patch_file):
log.info(_("Patching template '{filename}' with '{patch_file}'"))
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
rel_patch_file = relpath(patch_file, tmp_dir)
ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file])
if ret:
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file])
log.error(_(f"Error applying patch: '{patch_file}'\nTo reproduce and fix this error {patch_cmd_err}"))
copy(filename, self.tmp_dir)
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}"))
copy(join(self.distrib_dir, filename), tmp_dir)
def prepare_template(self,
filename: str):
filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Prepare template source file
"""
log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'"))
copy(filename, self.tmp_dir)
self.patch_template(filename)
log.info(_("Copy template: '{filename}' -> '{tmp_dir}'"))
copy(filename, tmp_dir)
self.patch_template(filename, tmp_dir, patch_dir)
def process(self,
source: str,
true_destfilename: str,
destfilename: str,
filevar: Dict,
variable: Any):
variable: Any,
):
"""Process a cheetah template
"""
# full path of the destination file
@ -332,16 +357,19 @@ class CreoleTemplateEngine:
data = str(cheetah_template)
except CheetahNotFound as err:
varname = err.args[0][13:-1]
raise TemplateError(_(f"Error: unknown variable used in template {destfilename} : {varname}"))
raise TemplateError(_(f"Error: unknown variable used in template {source} to {destfilename} : {varname}"))
except Exception as err:
raise TemplateError(_(f"Error while instantiating template {destfilename}: {err}"))
raise TemplateError(_(f"Error while instantiating template {source} to {destfilename}: {err}"))
with open(destfilename, 'w') as file_h:
file_h.write(data)
def instance_file(self,
filevar: Dict,
service_name: str) -> None:
service_name: str,
tmp_dir: str,
dest_dir: str,
) -> None:
"""Run templatisation on one file
"""
log.info(_("Instantiating file '{filename}'"))
@ -355,13 +383,13 @@ class CreoleTemplateEngine:
if variable:
variable = [variable]
for idx, filename in enumerate(filenames):
destfilename = join(self.dest_dir, filename[1:])
destfilename = join(dest_dir, filename[1:])
makedirs(dirname(destfilename), exist_ok=True)
if variable:
var = variable[idx]
else:
var = None
source = join(self.tmp_dir, filevar['source'])
source = join(tmp_dir, filevar['source'])
if filevar['templating']:
self.process(source,
filename,
@ -374,16 +402,21 @@ class CreoleTemplateEngine:
async def instance_files(self) -> None:
"""Run templatisation on all files
"""
ori_dir = getcwd()
tmp_dir = relpath(self.tmp_dir, self.distrib_dir)
dest_dir = relpath(self.dest_dir, self.distrib_dir)
patch_dir = relpath(Config['patch_dir'], self.distrib_dir)
chdir(self.distrib_dir)
for option in await self.config.option.list(type='all'):
namespace = await option.option.name()
if namespace == variable_namespace:
if namespace == Config['variable_namespace']:
await self.load_eole_variables_rougail(option)
else:
families = await self.load_eole_variables(namespace,
option)
self.rougail_variables_dict[namespace] = families
for template in listdir(self.distrib_dir):
self.prepare_template(join(self.distrib_dir, template))
for template in listdir('.'):
self.prepare_template(template, tmp_dir, patch_dir)
for service_obj in await self.config.option('services').list('all'):
service_name = await service_obj.option.doc()
for fills in await service_obj.list('all'):
@ -391,15 +424,17 @@ class CreoleTemplateEngine:
for fill_obj in await fills.list('all'):
fill = await fill_obj.value.dict()
filename = fill['source']
distib_file = join(self.distrib_dir, filename)
if not isfile(distib_file):
raise FileNotFound(_(f"File {distib_file} does not exist."))
if not isfile(filename):
raise FileNotFound(_(f"File {filename} does not exist."))
if fill.get('activate', False):
self.instance_file(fill,
service_name,
tmp_dir,
dest_dir,
)
else:
log.debug(_("Instantiation of file '{filename}' disabled"))
chdir(ori_dir)
async def generate(config: Config,

View File

@ -1,7 +1,7 @@
try:
from tiramisu import DynOptionDescription
except:
from tiramisu3 import DynOptionDescription
except:
from tiramisu import DynOptionDescription
from .utils import normalize_family
@ -9,5 +9,4 @@ class ConvertDynOptionDescription(DynOptionDescription):
def convert_suffix_to_path(self, suffix):
if not isinstance(suffix, str):
suffix = str(suffix)
return normalize_family(suffix,
check_name=False)
return normalize_family(suffix)

View File

@ -1,13 +1,10 @@
"""loader
flattened XML specific
"""
from os.path import isfile
from lxml.etree import DTD
from .config import dtdfilename, variable_namespace
from .config import Config
from .i18n import _
from .error import LoaderError
from .annotator import ERASED_ATTRIBUTES
from .annotator import ERASED_ATTRIBUTES, CONVERT_OPTION
FUNC_TO_DICT = ['valid_not_equal']
@ -15,45 +12,22 @@ FORCE_INFORMATIONS = ['help', 'test', 'separator', 'manage']
ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi')
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
'choice': dict(opttype="ChoiceOption"),
'string': dict(opttype="StrOption"),
'password': dict(opttype="PasswordOption"),
'mail': dict(opttype="EmailOption"),
'boolean': dict(opttype="BoolOption"),
'symlink': dict(opttype="SymLinkOption"),
'filename': dict(opttype="FilenameOption"),
'date': dict(opttype="DateOption"),
'unix_user': dict(opttype="UsernameOption"),
'ip': dict(opttype="IPOption", initkwargs={'allow_reserved': True}),
'local_ip': dict(opttype="IPOption", initkwargs={'private_only': True, 'warnings_only': True}),
'netmask': dict(opttype="NetmaskOption"),
'network': dict(opttype="NetworkOption"),
'broadcast': dict(opttype="BroadcastOption"),
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios', 'warnings_only': True}),
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname', 'allow_ip': False}),
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname', 'allow_ip': False}),
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True, 'allow_without_dot': True}),
'port': dict(opttype="PortOption", initkwargs={'allow_private': True}),
'mac': dict(opttype="MACOption"),
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
}
class TiramisuReflector:
def __init__(self,
xmlroot,
funcs_path,
):
self.storage = ElementStorage()
self.storage.text = ["try:",
" from tiramisu import *",
"except:",
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",
"import imp",
f"func = imp.load_source('func', '{funcs_path}')",
]
self.make_tiramisu_objects(xmlroot)
# parse object
@ -83,10 +57,10 @@ class TiramisuReflector:
# variable_namespace family has to be loaded before any other family
# because `extra` family could use `variable_namespace` variables.
if hasattr(xmlroot, 'variables'):
if variable_namespace in xmlroot.variables:
yield xmlroot.variables[variable_namespace]
if Config['variable_namespace'] in xmlroot.variables:
yield xmlroot.variables[Config['variable_namespace']]
for xmlelt, value in xmlroot.variables.items():
if xmlelt != variable_namespace:
if xmlelt != Config['variable_namespace']:
yield value
if hasattr(xmlroot, 'services'):
yield xmlroot.services
@ -255,7 +229,7 @@ class Common:
if not self.attrib[key]:
continue
value = "frozenset({" + self.attrib[key] + "})"
elif key in ['default', 'multi', 'suffixes', 'validators']:
elif key in ['default', 'multi', 'suffixes', 'validators', 'values']:
value = self.attrib[key]
elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
value = "'" + value.replace("'", "\\\'") + "'"
@ -344,6 +318,8 @@ class Variable(Common):
value = value.split(',')
if self.object_type == 'IntOption':
value = [int(v) for v in value]
elif self.object_type == 'FloatOption':
value = [float(v) for v in value]
self.informations[key] = value
else:
self.attrib[key] = value
@ -371,9 +347,16 @@ class Variable(Common):
elif tag == 'check':
self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
elif tag == 'choice':
if child.type == 'calculation':
value = self.storage.get(child.name).get()
choices = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
else:
choices.append(child.name)
if choices:
self.attrib['values'] = tuple(choices)
if isinstance(choices, list):
self.attrib['values'] = str(tuple(choices))
else:
self.attrib['values'] = choices
if self.attrib['default'] == []:
del self.attrib['default']
elif not self.attrib['multi'] and isinstance(self.attrib['default'], list):
@ -390,7 +373,7 @@ class Variable(Common):
function = child.name
if hasattr(child, 'param'):
for param in child.param:
value = self.populate_param(param)
value = self.populate_param(function, param)
if not hasattr(param, 'name'):
args.append(str(value))
else:
@ -404,6 +387,7 @@ class Variable(Common):
return ret + ')'
def populate_param(self,
function: str,
param,
):
if param.type == 'string':
@ -413,11 +397,15 @@ class Variable(Common):
elif param.type == 'variable':
value = {'option': param.text,
'notraisepropertyerror': param.notraisepropertyerror,
'todict': param.text in FUNC_TO_DICT,
'todict': function in FUNC_TO_DICT,
}
if hasattr(param, 'suffix'):
value['suffix'] = param.suffix
return self.build_param(value)
elif param.type == 'information':
return f'ParamInformation("{param.text}", None)'
elif param.type == 'suffix':
return f'ParamSuffix()'
raise LoaderError(_('unknown param type {}').format(param.type))
def populate_value(self,
@ -432,7 +420,7 @@ class Variable(Common):
self.attrib['default'].append(value)
if not self.is_leader:
self.attrib['default_multi'] = value
elif isinstance(value, int):
elif isinstance(value, (int, float)):
self.attrib['default'].append(value)
else:
self.attrib['default'].append("'" + value + "'")

View File

@ -1,23 +1,19 @@
"""
utilitaires créole
"""
import unicodedata
from unicodedata import normalize, combining
from .i18n import _
def normalize_family(family_name: str,
check_name: bool=True,
allow_dot: bool=False) -> str:
def valid_name(name: str) -> None:
if '.' in name:
raise ValueError(_(f'"{name}" is a forbidden variable or family name, dot is not allowed'))
def normalize_family(family_name: str) -> str:
"""replace space, accent, uppercase, ... by valid character
"""
family_name = family_name.replace('-', '_')
if not allow_dot:
family_name = family_name.replace('.', '_')
family_name = family_name.replace(' ', '_')
nfkd_form = unicodedata.normalize('NFKD', family_name)
family_name = ''.join([c for c in nfkd_form if not unicodedata.combining(c)])
family_name = family_name.lower()
if check_name and family_name == 'containers':
raise ValueError(_(f'"{family_name}" is a forbidden family name'))
return family_name
family_name = family_name.replace('-', '_').replace(' ', '_').replace('.', '_')
nfkd_form = normalize('NFKD', family_name)
family_name = ''.join([c for c in nfkd_form if not combining(c)])
return family_name.lower()

View File

@ -1,161 +0,0 @@
try:
import doctest
doctest.OutputChecker
except (AttributeError, ImportError): # Python < 2.4
import util.doctest24 as doctest
try:
import xml.etree.ElementTree as ET
except ImportError:
import elementtree.ElementTree as ET
from xml.parsers.expat import ExpatError as XMLParseError
RealOutputChecker = doctest.OutputChecker
def debug(*msg):
import sys
print >> sys.stderr, ' '.join(map(str, msg))
class HTMLOutputChecker(RealOutputChecker):
def check_output(self, want, got, optionflags):
normal = RealOutputChecker.check_output(self, want, got, optionflags)
if normal or not got:
return normal
try:
want_xml = make_xml(want)
except XMLParseError:
pass
else:
try:
got_xml = make_xml(got)
except XMLParseError:
pass
else:
if xml_compare(want_xml, got_xml):
return True
return False
def output_difference(self, example, got, optionflags):
actual = RealOutputChecker.output_difference(
self, example, got, optionflags)
want_xml = got_xml = None
try:
want_xml = make_xml(example.want)
want_norm = make_string(want_xml)
except XMLParseError as e:
if example.want.startswith('<'):
want_norm = '(bad XML: %s)' % e
# '<xml>%s</xml>' % example.want
else:
return actual
try:
got_xml = make_xml(got)
got_norm = make_string(got_xml)
except XMLParseError as e:
if example.want.startswith('<'):
got_norm = '(bad XML: %s)' % e
else:
return actual
s = '%s\nXML Wanted: %s\nXML Got : %s\n' % (
actual, want_norm, got_norm)
if got_xml and want_xml:
result = []
xml_compare(want_xml, got_xml, result.append)
s += 'Difference report:\n%s\n' % '\n'.join(result)
return s
def xml_sort(children):
tcl1 = {}
#idx = 0
for child in children:
if 'name' in child.attrib:
key = child.attrib['name']
else:
key = child.tag
if key not in tcl1:
tcl1[key] = []
tcl1[key].append(child)
cl1_keys = list(tcl1.keys())
cl1_keys.sort()
cl1 = []
for key in cl1_keys:
cl1.extend(tcl1[key])
return cl1
def xml_compare(x1, x2):
if x1.tag != x2.tag:
print ('Tags do not match: %s and %s' % (x1.tag, x2.tag))
return False
for name, value in x1.attrib.items():
if x2.attrib.get(name) != value:
print ('Attributes do not match: %s=%r, %s=%r'
% (name, value, name, x2.attrib.get(name)))
return False
for name in x2.attrib:
if name not in x1.attrib:
print ('x2 has an attribute x1 is missing: %s'
% name)
return False
if not text_compare(x1.text, x2.text):
print ('text: %r != %r' % (x1.text, x2.text))
return False
if not text_compare(x1.tail, x2.tail):
print ('tail: %r != %r' % (x1.tail, x2.tail))
return False
cl1 = xml_sort(x1.getchildren())
cl2 = xml_sort(x2.getchildren())
if len(cl1) != len(cl2):
cl1_tags = []
for c in cl1:
cl1_tags.append(c.tag)
cl2_tags = []
for c in cl2:
cl2_tags.append(c.tag)
print ('children length differs, %i != %i (%s != %s)'
% (len(cl1), len(cl2), cl1_tags, cl2_tags))
return False
i = 0
for c1, c2 in zip(cl1, cl2):
i += 1
if not xml_compare(c1, c2):
if 'name' in c1.attrib:
name = c1.attrib['name']
else:
name = i
print ('in tag "%s" with name "%s"'
% (c1.tag, name))
return False
return True
def text_compare(t1, t2):
if not t1 and not t2:
return True
if t1 == '*' or t2 == '*':
return True
return (t1 or '').strip() == (t2 or '').strip()
def make_xml(s):
return ET.XML('<xml>%s</xml>' % s)
def make_string(xml):
if isinstance(xml, (str, unicode)):
xml = make_xml(xml)
s = ET.tostring(xml)
if s == '<xml />':
return ''
assert s.startswith('<xml>') and s.endswith('</xml>'), repr(s)
return s[5:-6]
def install():
doctest.OutputChecker = HTMLOutputChecker

View File

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

View File

@ -0,0 +1,14 @@
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_2 = OptionDescription(name='tata', doc='tata', children=[])
option_2.impl_set_information("manage", True)
option_1 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

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="oui/non" description="No change" auto_freeze="True">
<value>non</value>
</variable>
<variable name="module_instancie" type="oui/non" description="No change">
<value>non</value>
</variable>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1,15 @@
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_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='module_instancie', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'auto_freeze', 'basic', 'force_store_value', 'mandatory', Calculation(calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue('oui'), 'reverse_condition': ParamValue(True)}))}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'basic'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

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="oui/non" description="No change" auto_freeze="True" mode="expert">
<value>non</value>
</variable>
<variable name="module_instancie" type="oui/non" description="No change">
<value>non</value>
</variable>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1,15 @@
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_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='module_instancie', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'auto_freeze', 'expert', 'force_store_value', 'mandatory', Calculation(calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue('oui'), 'reverse_condition': ParamValue(True)}))}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

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

View File

@ -0,0 +1,14 @@
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 = ChoiceOption(properties=frozenset({'basic', 'force_store_value', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'basic'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

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

View File

@ -0,0 +1,14 @@
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 = ChoiceOption(properties=frozenset({'expert', 'force_store_value', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'expert'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

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

View File

@ -0,0 +1,14 @@
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 = 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_2 = OptionDescription(name='general', doc='général', 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])

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="oui/non" description="No change" hidden="True">
<value>non</value>
</variable>
<variable name="without_type">
<value>non</value>
</variable>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1,15 @@
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 = 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 = StrOption(properties=frozenset({'mandatory', 'normal'}), name='without_type', doc='without_type', multi=False, default='non')
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

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

View File

@ -0,0 +1,14 @@
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 = 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_2 = OptionDescription(name='general', doc='général', 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])

View File

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

View File

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

View File

@ -0,0 +1,15 @@
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 = 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='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,21 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="No change" hidden="True">
<value>non</value>
</variable>
<variable name="mode_conteneur_actif1" type="oui/non" description="No change">
<value>non</value>
</variable>
</family>
</variables>
<constraints>
<fill name="calc_val" target="mode_conteneur_actif">
<param type="variable">mode_conteneur_actif1</param>
</fill>
</constraints>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1,15 @@
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_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default=Calculation(func.calc_val, Params((ParamOption(option_4, notraisepropertyerror=False, todict=False)), kwargs={})), values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="No change" hidden="True">
<value>non</value>
</variable>
<variable name="mode_conteneur_actif1" type="oui/non" description="No change">
<value>non</value>
</variable>
</family>
</variables>
<constraints>
<fill name="calc_val" target="mode_conteneur_actif">
</fill>
</constraints>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1,15 @@
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 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default=Calculation(func.calc_val, Params((), kwargs={})), values=('oui', 'non'))
option_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1,26 @@
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 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', 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_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
%include "incfile"

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1,5 @@
--- tmpl/file 2020-11-20 07:44:38.588472784 +0100
+++ dest/file 2020-11-20 07:44:54.588536011 +0100
@@ -1 +1 @@
-unpatched
+patched

View File

@ -0,0 +1 @@
patched

View File

@ -0,0 +1,26 @@
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 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', 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_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
unpatched

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/systemd-makefs@dev-disk-by\\x2dpartlabel"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.group": "root", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.mode": "0644", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.name": "/etc/systemd-makefs@dev-disk-by\\x2dpartlabel", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.owner": "root", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.source": "systemd-makefs@dev-disk-by\\x2dpartlabel", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.templating": true, "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.activate": true}

View File

@ -0,0 +1,26 @@
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 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', 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_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/systemd-makefs@dev-disk-by\\x2dpartlabel')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='systemd-makefs@dev-disk-by\\x2dpartlabel')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='systemd_makefs@dev_disk_by\\x2dpartlabel', doc='systemd-makefs@dev-disk-by\\x2dpartlabel', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
%%mode_conteneur_actif

View File

@ -0,0 +1,16 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<variable name="float" type="float" description="Description">
<value>0.527</value>
</variable>
<variable name="float_multi" type="float" description="Description" multi="True">
<value>0.527</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.float": 0.527, "rougail.general.float_multi": [0.527]}

View File

@ -0,0 +1,15 @@
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 = FloatOption(properties=frozenset({'mandatory', 'normal'}), name='float', doc='Description', multi=False, default=0.527)
option_4 = FloatOption(properties=frozenset({'mandatory', 'normal'}), name='float_multi', doc='Description', multi=True, default=[0.527], default_multi=0.527)
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,12 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True" multi="True">
<value>non</value>
</variable>
</family>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

Some files were not shown because too many files have changed in this diff Show More