Compare commits
11 Commits
54df7aca12
...
d395f4a17b
Author | SHA1 | Date |
---|---|---|
Emmanuel Garette | d395f4a17b | |
Emmanuel Garette | 9feb45e165 | |
Emmanuel Garette | 02edd0eaf0 | |
Emmanuel Garette | f8959ef16f | |
Emmanuel Garette | 81d30612e8 | |
Emmanuel Garette | 23ce59a851 | |
Emmanuel Garette | 04ab1edd36 | |
Emmanuel Garette | fffd52eec6 | |
Emmanuel Garette | ca40aa5ec3 | |
Emmanuel Garette | 91fd7c9b7e | |
Emmanuel Garette | 4223e7e5a3 |
|
@ -1,5 +1,5 @@
|
||||||
#from .loader import load
|
#from .loader import load
|
||||||
from .objspace import RougailObjSpace
|
from .rougail import Rougail
|
||||||
from .annotator import modes
|
from .annotator import modes
|
||||||
|
|
||||||
__ALL__ = ('RougailObjSpace', 'modes')
|
__ALL__ = ('RougailObjSpace', 'modes')
|
||||||
|
|
|
@ -1,1292 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
from copy import copy
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from os.path import join, basename
|
|
||||||
from ast import literal_eval
|
|
||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
|
|
||||||
|
|
||||||
from .i18n import _
|
|
||||||
from .utils import normalize_family
|
|
||||||
from .error import DictConsistencyError
|
|
||||||
|
|
||||||
#mode order is important
|
|
||||||
modes_level = ('basic', 'normal', 'expert')
|
|
||||||
|
|
||||||
|
|
||||||
class Mode(object):
|
|
||||||
def __init__(self, name, level):
|
|
||||||
self.name = name
|
|
||||||
self.level = level
|
|
||||||
def __gt__(self, other):
|
|
||||||
return other.level < self.level
|
|
||||||
|
|
||||||
|
|
||||||
def mode_factory():
|
|
||||||
mode_obj = {}
|
|
||||||
for idx in range(len(modes_level)):
|
|
||||||
name = modes_level[idx]
|
|
||||||
mode_obj[name] = Mode(name, idx)
|
|
||||||
return mode_obj
|
|
||||||
|
|
||||||
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', 'remove_fill', 'xmlfiles')
|
|
||||||
ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group')
|
|
||||||
|
|
||||||
FORCE_CHOICE = {'oui/non': ['oui', 'non'],
|
|
||||||
'on/off': ['on', 'off'],
|
|
||||||
'yes/no': ['yes', 'no'],
|
|
||||||
'schedule': ['none', 'daily', 'weekly', 'monthly'],
|
|
||||||
'schedulemod': ['pre', 'post']}
|
|
||||||
|
|
||||||
KEY_TYPE = {'variable': 'symlink',
|
|
||||||
'SymLinkOption': 'symlink',
|
|
||||||
'PortOption': 'port',
|
|
||||||
'UnicodeOption': 'string',
|
|
||||||
'NetworkOption': 'network',
|
|
||||||
'NetmaskOption': 'netmask',
|
|
||||||
'URLOption': 'web_address',
|
|
||||||
'FilenameOption': 'filename'}
|
|
||||||
|
|
||||||
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
|
|
||||||
|
|
||||||
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
|
|
||||||
'force_store_value', 'disabled', 'mandatory')
|
|
||||||
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'], 'auto_freeze': ['force_store_value', 'auto_freeze']}
|
|
||||||
|
|
||||||
RENAME_ATTIBUTES = {'description': 'doc'}
|
|
||||||
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
|
|
||||||
|
|
||||||
class SpaceAnnotator:
|
|
||||||
"""Transformations applied on a CreoleObjSpace instance
|
|
||||||
"""
|
|
||||||
def __init__(self, objectspace, eosfunc_file):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
GroupAnnotator(objectspace)
|
|
||||||
ServiceAnnotator(objectspace)
|
|
||||||
VariableAnnotator(objectspace)
|
|
||||||
ConstraintAnnotator(objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
)
|
|
||||||
FamilyAnnotator(objectspace)
|
|
||||||
PropertyAnnotator(objectspace)
|
|
||||||
|
|
||||||
|
|
||||||
class GroupAnnotator:
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if not hasattr(self.objectspace.space, 'constraints') or not hasattr(self.objectspace.space.constraints, 'group'):
|
|
||||||
return
|
|
||||||
self.convert_groups()
|
|
||||||
|
|
||||||
def convert_groups(self): # pylint: disable=C0111
|
|
||||||
for group in self.objectspace.space.constraints.group:
|
|
||||||
leader_fullname = group.leader
|
|
||||||
leader_family_name = self.objectspace.paths.get_variable_family_name(leader_fullname)
|
|
||||||
leader_name = self.objectspace.paths.get_variable_name(leader_fullname)
|
|
||||||
namespace = self.objectspace.paths.get_variable_namespace(leader_fullname)
|
|
||||||
if '.' not in leader_fullname:
|
|
||||||
leader_fullname = '.'.join([namespace, leader_family_name, leader_fullname])
|
|
||||||
follower_names = list(group.follower.keys())
|
|
||||||
has_a_leader = False
|
|
||||||
ori_leader_family = self.objectspace.paths.get_family_obj(leader_fullname.rsplit('.', 1)[0])
|
|
||||||
for variable in list(ori_leader_family.variable.values()):
|
|
||||||
if has_a_leader:
|
|
||||||
# it's a follower
|
|
||||||
self.manage_follower(namespace,
|
|
||||||
leader_family_name,
|
|
||||||
variable,
|
|
||||||
leadership_name,
|
|
||||||
follower_names,
|
|
||||||
leader_space,
|
|
||||||
leader_is_hidden,
|
|
||||||
)
|
|
||||||
ori_leader_family.variable.pop(variable.name)
|
|
||||||
if follower_names == []:
|
|
||||||
# no more follower
|
|
||||||
break
|
|
||||||
elif variable.name == leader_name:
|
|
||||||
# it's a leader
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
# append follower to an existed leadership
|
|
||||||
leader_space = variable
|
|
||||||
# if variable.hidden:
|
|
||||||
# leader_is_hidden = True
|
|
||||||
else:
|
|
||||||
leader_space = self.objectspace.Leadership(variable.xmlfiles)
|
|
||||||
if hasattr(group, 'name'):
|
|
||||||
leadership_name = group.name
|
|
||||||
else:
|
|
||||||
leadership_name = leader_name
|
|
||||||
leader_is_hidden = self.manage_leader(leader_space,
|
|
||||||
leader_family_name,
|
|
||||||
leadership_name,
|
|
||||||
leader_name,
|
|
||||||
namespace,
|
|
||||||
variable,
|
|
||||||
group,
|
|
||||||
leader_fullname,
|
|
||||||
)
|
|
||||||
has_a_leader = True
|
|
||||||
else:
|
|
||||||
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',
|
|
||||||
group: 'Group',
|
|
||||||
leader_fullname: str,
|
|
||||||
) -> None:
|
|
||||||
# manage leader's variable
|
|
||||||
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 = leadership_name
|
|
||||||
leader_space.hidden = variable.hidden
|
|
||||||
if variable.hidden:
|
|
||||||
leader_is_hidden = True
|
|
||||||
variable.frozen = True
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
else:
|
|
||||||
leader_is_hidden = False
|
|
||||||
variable.hidden = None
|
|
||||||
if hasattr(group, 'description'):
|
|
||||||
leader_space.doc = group.description
|
|
||||||
elif hasattr(variable, 'description'):
|
|
||||||
leader_space.doc = variable.description
|
|
||||||
else:
|
|
||||||
leader_space.doc = variable.name
|
|
||||||
leadership_path = namespace + '.' + leader_family_name + '.' + leadership_name
|
|
||||||
self.objectspace.paths.add_family(namespace,
|
|
||||||
leadership_path,
|
|
||||||
leader_space,
|
|
||||||
)
|
|
||||||
leader_family = self.objectspace.space.variables[namespace].family[leader_family_name]
|
|
||||||
leader_family.variable[leader_name] = leader_space
|
|
||||||
leader_space.variable.append(variable)
|
|
||||||
self.objectspace.paths.set_leader(namespace,
|
|
||||||
leader_family_name,
|
|
||||||
leader_name,
|
|
||||||
leader_name,
|
|
||||||
)
|
|
||||||
leader_space.path = leader_fullname
|
|
||||||
return leader_is_hidden
|
|
||||||
|
|
||||||
def manage_follower(self,
|
|
||||||
namespace: str,
|
|
||||||
leader_family_name: str,
|
|
||||||
variable: 'Variable',
|
|
||||||
leader_name: str,
|
|
||||||
follower_names: List[str],
|
|
||||||
leader_space: 'Leadership',
|
|
||||||
leader_is_hidden: bool,
|
|
||||||
) -> None:
|
|
||||||
if variable.name != follower_names[0]:
|
|
||||||
raise DictConsistencyError(_('cannot found this follower {}').format(follower_names[0]))
|
|
||||||
follower_names.remove(variable.name)
|
|
||||||
if leader_is_hidden:
|
|
||||||
variable.frozen = True
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
leader_space.variable.append(variable) # pylint: disable=E1101
|
|
||||||
self.objectspace.paths.set_leader(namespace,
|
|
||||||
leader_family_name,
|
|
||||||
variable.name,
|
|
||||||
leader_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceAnnotator:
|
|
||||||
"""Manage service's object
|
|
||||||
for example::
|
|
||||||
<services>
|
|
||||||
<service name="test">
|
|
||||||
<service_access service='ntp'>
|
|
||||||
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
|
|
||||||
</service_access>
|
|
||||||
</service>
|
|
||||||
</services>
|
|
||||||
"""
|
|
||||||
def __init__(self, objectspace):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.convert_services()
|
|
||||||
|
|
||||||
def convert_services(self):
|
|
||||||
if not hasattr(self.objectspace.space, 'services'):
|
|
||||||
return
|
|
||||||
if not hasattr(self.objectspace.space.services, 'service'):
|
|
||||||
del self.objectspace.space.services
|
|
||||||
return
|
|
||||||
self.objectspace.space.services.hidden = True
|
|
||||||
self.objectspace.space.services.name = 'services'
|
|
||||||
self.objectspace.space.services.doc = 'services'
|
|
||||||
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(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)
|
|
||||||
continue
|
|
||||||
eltname = elttype + 's'
|
|
||||||
path = '.'.join(['services', service_name, eltname])
|
|
||||||
family = self.gen_family(eltname,
|
|
||||||
path,
|
|
||||||
service.xmlfiles,
|
|
||||||
)
|
|
||||||
if isinstance(values, dict):
|
|
||||||
values = list(values.values())
|
|
||||||
family.family = self.make_group_from_elts(service_name,
|
|
||||||
elttype,
|
|
||||||
values,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
setattr(new_service, elttype, family)
|
|
||||||
new_service.doc = new_service.name
|
|
||||||
families[service_name] = new_service
|
|
||||||
self.objectspace.space.services.service = families
|
|
||||||
|
|
||||||
def gen_family(self,
|
|
||||||
name,
|
|
||||||
path,
|
|
||||||
xmlfiles
|
|
||||||
):
|
|
||||||
family = self.objectspace.family(xmlfiles)
|
|
||||||
family.name = normalize_family(name)
|
|
||||||
family.doc = name
|
|
||||||
family.mode = None
|
|
||||||
self.objectspace.paths.add_family('services',
|
|
||||||
path,
|
|
||||||
family,
|
|
||||||
)
|
|
||||||
return family
|
|
||||||
|
|
||||||
def make_group_from_elts(self,
|
|
||||||
service_name,
|
|
||||||
name,
|
|
||||||
elts,
|
|
||||||
path,
|
|
||||||
):
|
|
||||||
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
|
|
||||||
and build elements and its attributes (the `Options` in tiramisu terms)
|
|
||||||
"""
|
|
||||||
families = []
|
|
||||||
new_elts = self._reorder_elts(name,
|
|
||||||
elts,
|
|
||||||
)
|
|
||||||
for index, elt_info in enumerate(new_elts):
|
|
||||||
elt = elt_info['elt']
|
|
||||||
elt_name = elt_info['elt_name']
|
|
||||||
|
|
||||||
# try to launch _update_xxxx() function
|
|
||||||
update_elt = '_update_' + elt_name
|
|
||||||
if hasattr(self, update_elt):
|
|
||||||
getattr(self, update_elt)(elt,
|
|
||||||
index,
|
|
||||||
path,
|
|
||||||
service_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
while True:
|
|
||||||
if hasattr(elt, 'source'):
|
|
||||||
c_name = elt.source
|
|
||||||
else:
|
|
||||||
c_name = elt.name
|
|
||||||
if idx:
|
|
||||||
c_name += f'_{idx}'
|
|
||||||
subpath = '{}.{}'.format(path, c_name)
|
|
||||||
if not self.objectspace.paths.family_is_defined(subpath):
|
|
||||||
break
|
|
||||||
idx += 1
|
|
||||||
family = self.gen_family(c_name,
|
|
||||||
subpath,
|
|
||||||
elt.xmlfiles,
|
|
||||||
)
|
|
||||||
family.variable = []
|
|
||||||
listname = '{}list'.format(name)
|
|
||||||
activate_path = '.'.join([subpath, 'activate'])
|
|
||||||
for key in dir(elt):
|
|
||||||
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
|
||||||
continue
|
|
||||||
value = getattr(elt, key)
|
|
||||||
if key == listname:
|
|
||||||
self.objectspace.list_conditions.setdefault(listname,
|
|
||||||
{}).setdefault(
|
|
||||||
value,
|
|
||||||
[]).append(activate_path)
|
|
||||||
continue
|
|
||||||
family.variable.append(self._generate_element(elt_name,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
elt,
|
|
||||||
f'{subpath}.{key}'
|
|
||||||
))
|
|
||||||
# FIXME ne devrait pas etre True par défaut
|
|
||||||
# devrait etre un calcule
|
|
||||||
family.variable.append(self._generate_element(elt_name,
|
|
||||||
'activate',
|
|
||||||
True,
|
|
||||||
elt,
|
|
||||||
activate_path,
|
|
||||||
))
|
|
||||||
families.append(family)
|
|
||||||
return families
|
|
||||||
|
|
||||||
def _generate_element(self,
|
|
||||||
elt_name,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
elt,
|
|
||||||
path,
|
|
||||||
):
|
|
||||||
variable = self.objectspace.variable(elt.xmlfiles)
|
|
||||||
variable.name = normalize_family(key)
|
|
||||||
variable.mode = None
|
|
||||||
if key == 'name':
|
|
||||||
true_key = elt_name
|
|
||||||
else:
|
|
||||||
true_key = key
|
|
||||||
dtd_key_type = true_key + '_type'
|
|
||||||
if key == 'activate':
|
|
||||||
type_ = 'boolean'
|
|
||||||
elif hasattr(elt, dtd_key_type):
|
|
||||||
type_ = KEY_TYPE[getattr(elt, dtd_key_type)]
|
|
||||||
elif key in self.objectspace.booleans_attributs:
|
|
||||||
type_ = 'boolean'
|
|
||||||
else:
|
|
||||||
type_ = 'string'
|
|
||||||
variable.type = type_
|
|
||||||
if type_ == 'symlink':
|
|
||||||
variable.opt = self.objectspace.paths.get_variable_path(value,
|
|
||||||
'services',
|
|
||||||
)
|
|
||||||
# variable.opt = value
|
|
||||||
variable.multi = None
|
|
||||||
else:
|
|
||||||
variable.doc = key
|
|
||||||
val = self.objectspace.value(elt.xmlfiles)
|
|
||||||
val.type = type_
|
|
||||||
val.name = value
|
|
||||||
variable.value = [val]
|
|
||||||
self.objectspace.paths.add_variable('services',
|
|
||||||
path,
|
|
||||||
'service',
|
|
||||||
False,
|
|
||||||
variable,
|
|
||||||
)
|
|
||||||
return variable
|
|
||||||
|
|
||||||
def _reorder_elts(self,
|
|
||||||
name,
|
|
||||||
elts,
|
|
||||||
):
|
|
||||||
"""Reorders by index the elts
|
|
||||||
"""
|
|
||||||
new_elts = {}
|
|
||||||
# reorder elts by index
|
|
||||||
for idx, elt in enumerate(elts):
|
|
||||||
new_elts.setdefault(idx, []).append(elt)
|
|
||||||
idxes = list(new_elts.keys())
|
|
||||||
idxes.sort()
|
|
||||||
result_elts = []
|
|
||||||
for idx in idxes:
|
|
||||||
for elt in new_elts[idx]:
|
|
||||||
result_elts.append({'elt_name': name, 'elt': elt})
|
|
||||||
return result_elts
|
|
||||||
|
|
||||||
def _update_override(self,
|
|
||||||
file_,
|
|
||||||
index,
|
|
||||||
service_path,
|
|
||||||
service_name,
|
|
||||||
):
|
|
||||||
file_.name = f'/systemd/system/{service_name}.service.d/rougail.conf'
|
|
||||||
# retrieve default value from File object
|
|
||||||
for attr in ['owner', 'group', 'mode']:
|
|
||||||
setattr(file_, attr, getattr(self.objectspace.file, attr))
|
|
||||||
if not hasattr(file_, 'source'):
|
|
||||||
file_.source = f'{service_name}.service'
|
|
||||||
self._update_file(file_,
|
|
||||||
index,
|
|
||||||
service_path,
|
|
||||||
service_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _update_file(self,
|
|
||||||
file_,
|
|
||||||
index,
|
|
||||||
service_path,
|
|
||||||
service_name,
|
|
||||||
):
|
|
||||||
if not hasattr(file_, 'file_type') or file_.file_type == "UnicodeOption":
|
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
class VariableAnnotator:
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.convert_variable()
|
|
||||||
self.convert_auto_freeze()
|
|
||||||
self.convert_separators()
|
|
||||||
|
|
||||||
def convert_variable(self):
|
|
||||||
def _convert_variable(variable,
|
|
||||||
variable_type,
|
|
||||||
):
|
|
||||||
if not hasattr(variable, 'type'):
|
|
||||||
variable.type = 'string'
|
|
||||||
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
|
||||||
variable.description = variable.name
|
|
||||||
if hasattr(variable, 'value'):
|
|
||||||
for value in variable.value:
|
|
||||||
if not hasattr(value, 'type'):
|
|
||||||
value.type = variable.type
|
|
||||||
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)
|
|
||||||
if variable_type == 'follower':
|
|
||||||
if variable.multi is True:
|
|
||||||
variable.multi = 'submulti'
|
|
||||||
else:
|
|
||||||
variable.multi = True
|
|
||||||
|
|
||||||
def _convert_valid_enum(namespace,
|
|
||||||
variable,
|
|
||||||
path,
|
|
||||||
):
|
|
||||||
if variable.type in FORCE_CHOICE:
|
|
||||||
check = self.objectspace.check(variable.xmlfiles)
|
|
||||||
check.name = 'valid_enum'
|
|
||||||
check.target = path
|
|
||||||
check.namespace = namespace
|
|
||||||
check.param = []
|
|
||||||
for value in FORCE_CHOICE[variable.type]:
|
|
||||||
param = self.objectspace.param(variable.xmlfiles)
|
|
||||||
param.text = value
|
|
||||||
check.param.append(param)
|
|
||||||
if not hasattr(self.objectspace.space, '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():
|
|
||||||
namespace = families.name
|
|
||||||
if hasattr(families, 'family'):
|
|
||||||
families.doc = families.name
|
|
||||||
for family in families.family.values():
|
|
||||||
family.doc = family.name
|
|
||||||
for key, value in RENAME_ATTIBUTES.items():
|
|
||||||
if hasattr(family, key):
|
|
||||||
setattr(family, value, getattr(family, key))
|
|
||||||
setattr(family, key, None)
|
|
||||||
family.name = normalize_family(family.name)
|
|
||||||
if hasattr(family, 'variable'):
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
for idx, follower in enumerate(variable.variable):
|
|
||||||
if idx == 0:
|
|
||||||
variable_type = 'master'
|
|
||||||
else:
|
|
||||||
variable_type = 'follower'
|
|
||||||
path = '{}.{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name, follower.name)
|
|
||||||
_convert_variable(follower,
|
|
||||||
variable_type,
|
|
||||||
)
|
|
||||||
_convert_valid_enum(namespace,
|
|
||||||
follower,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
_valid_type(follower)
|
|
||||||
else:
|
|
||||||
path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name)
|
|
||||||
_convert_variable(variable,
|
|
||||||
'variable',
|
|
||||||
)
|
|
||||||
_convert_valid_enum(namespace,
|
|
||||||
variable,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
_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(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(variable.xmlfiles)
|
|
||||||
new_param.text = 'oui'
|
|
||||||
new_condition.param = [new_param]
|
|
||||||
new_target = self.objectspace.target(variable.xmlfiles)
|
|
||||||
new_target.type = 'variable'
|
|
||||||
path = variable.namespace + '.' + normalize_family(family.name) + '.' + variable.name
|
|
||||||
new_target.name = path
|
|
||||||
new_condition.target = [new_target]
|
|
||||||
if not hasattr(self.objectspace.space.constraints, 'condition'):
|
|
||||||
self.objectspace.space.constraints.condition = []
|
|
||||||
self.objectspace.space.constraints.condition.append(new_condition)
|
|
||||||
if hasattr(self.objectspace.space, 'variables'):
|
|
||||||
for variables in self.objectspace.space.variables.values():
|
|
||||||
if hasattr(variables, 'family'):
|
|
||||||
namespace = variables.name
|
|
||||||
for family in variables.family.values():
|
|
||||||
if hasattr(family, 'variable'):
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
for follower in variable.variable:
|
|
||||||
_convert_auto_freeze(follower, namespace)
|
|
||||||
else:
|
|
||||||
_convert_auto_freeze(variable, namespace)
|
|
||||||
|
|
||||||
def convert_separators(self): # pylint: disable=C0111,R0201
|
|
||||||
if not hasattr(self.objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
for family in self.objectspace.space.variables.values():
|
|
||||||
if not hasattr(family, 'separators'):
|
|
||||||
continue
|
|
||||||
if hasattr(family.separators, 'separator'):
|
|
||||||
for idx, separator in enumerate(family.separators.separator):
|
|
||||||
option = self.objectspace.paths.get_variable_obj(separator.name)
|
|
||||||
if hasattr(option, 'separator'):
|
|
||||||
subpath = self.objectspace.paths.get_variable_path(separator.name,
|
|
||||||
separator.namespace,
|
|
||||||
)
|
|
||||||
xmlfiles = self.objectspace.display_xmlfiles(separator.xmlfiles)
|
|
||||||
raise DictConsistencyError(_(f'{subpath} already has a separator in {xmlfiles}'))
|
|
||||||
option.separator = separator.text
|
|
||||||
del family.separators
|
|
||||||
|
|
||||||
|
|
||||||
class ConstraintAnnotator:
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
):
|
|
||||||
if not hasattr(objectspace.space, 'constraints'):
|
|
||||||
return
|
|
||||||
self.objectspace = objectspace
|
|
||||||
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()
|
|
||||||
self.check_replace_text()
|
|
||||||
self.check_valid_enum()
|
|
||||||
self.check_change_warning()
|
|
||||||
self.convert_check()
|
|
||||||
if hasattr(self.objectspace.space.constraints, 'condition'):
|
|
||||||
self.check_params_target()
|
|
||||||
self.filter_targets()
|
|
||||||
self.convert_xxxlist_to_variable()
|
|
||||||
self.check_condition_fallback_optional()
|
|
||||||
self.check_choice_option_condition()
|
|
||||||
self.remove_condition_with_empty_target()
|
|
||||||
self.convert_condition()
|
|
||||||
if hasattr(self.objectspace.space.constraints, 'fill'):
|
|
||||||
self.convert_fill()
|
|
||||||
self.remove_constraints()
|
|
||||||
|
|
||||||
def check_check(self):
|
|
||||||
remove_indexes = []
|
|
||||||
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
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 == 'variable' and not self.objectspace.paths.path_is_defined(param.text):
|
|
||||||
if param.optional is True:
|
|
||||||
param_option_indexes.append(idx)
|
|
||||||
else:
|
|
||||||
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))
|
|
||||||
param_option_indexes.sort(reverse=True)
|
|
||||||
for idx in param_option_indexes:
|
|
||||||
check.param.pop(idx)
|
|
||||||
if check.param == []:
|
|
||||||
remove_indexes.append(check_idx)
|
|
||||||
remove_indexes.sort(reverse=True)
|
|
||||||
for idx in remove_indexes:
|
|
||||||
del self.objectspace.space.constraints.check[idx]
|
|
||||||
|
|
||||||
def check_replace_text(self):
|
|
||||||
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
namespace = check.namespace
|
|
||||||
if hasattr(check, 'param'):
|
|
||||||
for idx, param in enumerate(check.param):
|
|
||||||
if param.type == 'variable':
|
|
||||||
param.text = self.objectspace.paths.get_variable_path(param.text, namespace)
|
|
||||||
check.is_in_leadership = self.objectspace.paths.get_leader(check.target) != None
|
|
||||||
# let's replace the target by the path
|
|
||||||
check.target = self.objectspace.paths.get_variable_path(check.target, namespace)
|
|
||||||
|
|
||||||
def check_valid_enum(self):
|
|
||||||
remove_indexes = []
|
|
||||||
for idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
if check.name == 'valid_enum':
|
|
||||||
if check.target in self.valid_enums:
|
|
||||||
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_valid_enum(check.param,
|
|
||||||
variable.name,
|
|
||||||
variable.type,
|
|
||||||
)
|
|
||||||
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_valid_enum(self,
|
|
||||||
params,
|
|
||||||
variable_name,
|
|
||||||
variable_type,
|
|
||||||
):
|
|
||||||
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:
|
|
||||||
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):
|
|
||||||
#convert level to "warnings_only"
|
|
||||||
for check in self.objectspace.space.constraints.check:
|
|
||||||
if check.level == 'warning':
|
|
||||||
check.warnings_only = True
|
|
||||||
else:
|
|
||||||
check.warnings_only = False
|
|
||||||
check.level = None
|
|
||||||
|
|
||||||
def _get_family_variables_from_target(self,
|
|
||||||
target,
|
|
||||||
):
|
|
||||||
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]
|
|
||||||
# it's a family
|
|
||||||
variable = self.objectspace.paths.get_family_obj(target.name)
|
|
||||||
return variable, list(variable.variable.values())
|
|
||||||
|
|
||||||
def check_params_target(self):
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
if not hasattr(condition, 'target'):
|
|
||||||
raise DictConsistencyError(_('target is mandatory in condition'))
|
|
||||||
for target in condition.target:
|
|
||||||
if target.type.endswith('list') and condition.name not in ['disabled_if_in', 'disabled_if_not_in']:
|
|
||||||
raise DictConsistencyError(_(f'target in condition for {target.type} not allow in {condition.name}'))
|
|
||||||
|
|
||||||
def filter_targets(self): # pylint: disable=C0111
|
|
||||||
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
namespace = condition.namespace
|
|
||||||
for idx, target in enumerate(condition.target):
|
|
||||||
if target.type == 'variable':
|
|
||||||
if condition.source == target.name:
|
|
||||||
raise DictConsistencyError(_('target name and source name must be different: {}').format(condition.source))
|
|
||||||
try:
|
|
||||||
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_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))
|
|
||||||
|
|
||||||
def convert_xxxlist_to_variable(self): # pylint: disable=C0111
|
|
||||||
# transform *list to variable or family
|
|
||||||
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
new_targets = []
|
|
||||||
remove_targets = []
|
|
||||||
for target_idx, target in enumerate(condition.target):
|
|
||||||
if target.type.endswith('list'):
|
|
||||||
listname = target.type
|
|
||||||
listvars = self.objectspace.list_conditions.get(listname,
|
|
||||||
{}).get(target.name)
|
|
||||||
if listvars:
|
|
||||||
for listvar in listvars:
|
|
||||||
variable = self.objectspace.paths.get_variable_obj(listvar)
|
|
||||||
type_ = 'variable'
|
|
||||||
new_target = self.objectspace.target(variable.xmlfiles)
|
|
||||||
new_target.type = type_
|
|
||||||
new_target.name = listvar
|
|
||||||
new_target.index = target.index
|
|
||||||
new_targets.append(new_target)
|
|
||||||
remove_targets.append(target_idx)
|
|
||||||
remove_targets.sort(reverse=True)
|
|
||||||
for target_idx in remove_targets:
|
|
||||||
condition.target.pop(target_idx)
|
|
||||||
condition.target.extend(new_targets)
|
|
||||||
|
|
||||||
def check_condition_fallback_optional(self):
|
|
||||||
# a condition with a fallback **and** the source variable doesn't exist
|
|
||||||
remove_conditions = []
|
|
||||||
for idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
# fallback
|
|
||||||
if condition.fallback is True and not self.objectspace.paths.path_is_defined(condition.source):
|
|
||||||
apply_action = False
|
|
||||||
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
|
|
||||||
apply_action = not condition.force_condition_on_fallback
|
|
||||||
else:
|
|
||||||
apply_action = condition.force_inverse_condition_on_fallback
|
|
||||||
if apply_action:
|
|
||||||
actions = self._get_condition_actions(condition.name)
|
|
||||||
for target in condition.target:
|
|
||||||
leader_or_variable, variables = self._get_family_variables_from_target(target)
|
|
||||||
for action_idx, action in enumerate(actions):
|
|
||||||
if action_idx == 0:
|
|
||||||
setattr(leader_or_variable, action, True)
|
|
||||||
else:
|
|
||||||
for variable in variables:
|
|
||||||
setattr(variable, action, True)
|
|
||||||
remove_conditions.append(idx)
|
|
||||||
continue
|
|
||||||
|
|
||||||
remove_targets = []
|
|
||||||
# optional
|
|
||||||
for idx, target in enumerate(condition.target):
|
|
||||||
if target.optional is True and not self.objectspace.paths.path_is_defined(target.name):
|
|
||||||
remove_targets.append(idx)
|
|
||||||
remove_targets = list(set(remove_targets))
|
|
||||||
remove_targets.sort(reverse=True)
|
|
||||||
for idx in remove_targets:
|
|
||||||
condition.target.pop(idx)
|
|
||||||
remove_conditions = list(set(remove_conditions))
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def _get_condition_actions(self, condition_name):
|
|
||||||
if condition_name.startswith('disabled_if_'):
|
|
||||||
return ['disabled']
|
|
||||||
elif condition_name.startswith('hidden_if_'):
|
|
||||||
return ['hidden', 'frozen', 'force_default_on_freeze']
|
|
||||||
elif condition_name.startswith('mandatory_if_'):
|
|
||||||
return ['mandatory']
|
|
||||||
elif condition_name == 'auto_hidden_if_not_in':
|
|
||||||
return ['auto_frozen']
|
|
||||||
|
|
||||||
def check_choice_option_condition(self):
|
|
||||||
# remove condition for ChoiceOption that don't have param
|
|
||||||
remove_conditions = []
|
|
||||||
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
namespace = condition.namespace
|
|
||||||
condition.source = self.objectspace.paths.get_variable_path(condition.source, namespace, allow_source=True)
|
|
||||||
src_variable = self.objectspace.paths.get_variable_obj(condition.source)
|
|
||||||
valid_enum = None
|
|
||||||
if condition.source in self.valid_enums and self.valid_enums[condition.source]['type'] == 'string':
|
|
||||||
valid_enum = self.valid_enums[condition.source]['values']
|
|
||||||
if valid_enum is not None:
|
|
||||||
remove_param = []
|
|
||||||
for param_idx, param in enumerate(condition.param):
|
|
||||||
if param.text not in valid_enum:
|
|
||||||
remove_param.append(param_idx)
|
|
||||||
remove_param.sort(reverse=True)
|
|
||||||
for idx in remove_param:
|
|
||||||
del condition.param[idx]
|
|
||||||
if condition.param == []:
|
|
||||||
remove_targets = []
|
|
||||||
for target in condition.target:
|
|
||||||
leader_or_variable, variables = self._get_family_variables_from_target(target)
|
|
||||||
if condition.name == 'disabled_if_not_in':
|
|
||||||
leader_or_variable.disabled = True
|
|
||||||
elif condition.name == 'hidden_if_not_in':
|
|
||||||
leader_or_variable.hidden = True
|
|
||||||
for variable in variables:
|
|
||||||
variable.frozen = True
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
elif condition.name == 'mandatory_if_not_in':
|
|
||||||
variable.mandatory = True
|
|
||||||
remove_targets = list(set(remove_targets))
|
|
||||||
remove_targets.sort(reverse=True)
|
|
||||||
for target_idx in remove_targets:
|
|
||||||
condition.target.pop(target_idx)
|
|
||||||
remove_conditions.append(condition_idx)
|
|
||||||
remove_conditions = list(set(remove_conditions))
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def remove_condition_with_empty_target(self):
|
|
||||||
remove_conditions = []
|
|
||||||
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
if not condition.target:
|
|
||||||
remove_conditions.append(condition_idx)
|
|
||||||
remove_conditions = list(set(remove_conditions))
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def convert_condition(self):
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
inverse = condition.name.endswith('_if_not_in')
|
|
||||||
actions = self._get_condition_actions(condition.name)
|
|
||||||
for param in condition.param:
|
|
||||||
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
|
|
||||||
# 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_(leader_or_variable.xmlfiles)
|
|
||||||
prop.type = 'calculation'
|
|
||||||
prop.inverse = inverse
|
|
||||||
prop.source = condition.source
|
|
||||||
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_,
|
|
||||||
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(variable.xmlfiles)
|
|
||||||
try:
|
|
||||||
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 == '':
|
|
||||||
choice.name = None
|
|
||||||
choices.append(choice.name)
|
|
||||||
choice.type = type_
|
|
||||||
variable.choice.append(choice)
|
|
||||||
# check value or set first choice value has default value
|
|
||||||
if hasattr(variable, 'value'):
|
|
||||||
for value in variable.value:
|
|
||||||
value.type = type_
|
|
||||||
try:
|
|
||||||
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(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):
|
|
||||||
for check in self.objectspace.space.constraints.check:
|
|
||||||
variable = self.objectspace.paths.get_variable_obj(check.target)
|
|
||||||
name = check.name
|
|
||||||
if name == 'valid_entier':
|
|
||||||
if not hasattr(check, 'param'):
|
|
||||||
raise DictConsistencyError(_('{} must have, at least, 1 param').format(name))
|
|
||||||
for param in check.param:
|
|
||||||
if param.type not in ['string', 'number']:
|
|
||||||
raise DictConsistencyError(_(f'param in "valid_entier" must not be a "{param.type}"'))
|
|
||||||
if param.name == 'mini':
|
|
||||||
variable.min_number = int(param.text)
|
|
||||||
elif param.name == 'maxi':
|
|
||||||
variable.max_number = int(param.text)
|
|
||||||
else:
|
|
||||||
raise DictConsistencyError(_(f'unknown parameter {param.text} in check "valid_entier" for variable {check.target}'))
|
|
||||||
else:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
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
|
|
||||||
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'):
|
|
||||||
check_.param = check.param
|
|
||||||
if not hasattr(variable, 'check'):
|
|
||||||
variable.check = []
|
|
||||||
variable.check.append(check_)
|
|
||||||
del self.objectspace.space.constraints.check
|
|
||||||
|
|
||||||
def convert_fill(self): # pylint: disable=C0111,R0912
|
|
||||||
# sort fill/auto by index
|
|
||||||
fills = {fill.index: fill for idx, fill in enumerate(self.objectspace.space.constraints.fill)}
|
|
||||||
indexes = list(fills.keys())
|
|
||||||
indexes.sort()
|
|
||||||
targets = []
|
|
||||||
for idx in indexes:
|
|
||||||
fill = fills[idx]
|
|
||||||
# test if it's redefined calculation
|
|
||||||
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 self.functions:
|
|
||||||
raise DictConsistencyError(_('cannot find fill function {}').format(fill.name))
|
|
||||||
|
|
||||||
namespace = fill.namespace
|
|
||||||
# let's replace the target by the path
|
|
||||||
fill.target, suffix = self.objectspace.paths.get_variable_path(fill.target,
|
|
||||||
namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
)
|
|
||||||
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 ['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,
|
|
||||||
namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
)
|
|
||||||
if suffix:
|
|
||||||
param.suffix = suffix
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if param.optional is False:
|
|
||||||
raise err
|
|
||||||
param_to_delete.append(fill_idx)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
param.notraisepropertyerror = None
|
|
||||||
param_to_delete.sort(reverse=True)
|
|
||||||
for param_idx in param_to_delete:
|
|
||||||
fill.param.pop(param_idx)
|
|
||||||
value.param = fill.param
|
|
||||||
variable.value = [value]
|
|
||||||
del self.objectspace.space.constraints.fill
|
|
||||||
|
|
||||||
def remove_constraints(self):
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class FamilyAnnotator:
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.remove_empty_families()
|
|
||||||
self.change_variable_mode()
|
|
||||||
self.change_family_mode()
|
|
||||||
self.dynamic_families()
|
|
||||||
|
|
||||||
def remove_empty_families(self): # pylint: disable=C0111,R0201
|
|
||||||
if hasattr(self.objectspace.space, 'variables'):
|
|
||||||
for family in self.objectspace.space.variables.values():
|
|
||||||
if hasattr(family, 'family'):
|
|
||||||
space = family.family
|
|
||||||
removed_families = []
|
|
||||||
for family_name, family in space.items():
|
|
||||||
if not hasattr(family, 'variable') or len(family.variable) == 0:
|
|
||||||
removed_families.append(family_name)
|
|
||||||
for family_name in removed_families:
|
|
||||||
del space[family_name]
|
|
||||||
|
|
||||||
def change_family_mode(self): # pylint: disable=C0111
|
|
||||||
if not hasattr(self.objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
for family in self.objectspace.space.variables.values():
|
|
||||||
if hasattr(family, 'family'):
|
|
||||||
for family in family.family.values():
|
|
||||||
mode = modes_level[-1]
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
variable_mode = variable.variable[0].mode
|
|
||||||
variable.variable[0].mode = None
|
|
||||||
variable.mode = variable_mode
|
|
||||||
else:
|
|
||||||
variable_mode = variable.mode
|
|
||||||
if variable_mode is not None and modes[mode] > modes[variable_mode]:
|
|
||||||
mode = variable_mode
|
|
||||||
family.mode = mode
|
|
||||||
|
|
||||||
def dynamic_families(self): # pylint: disable=C0111
|
|
||||||
if not hasattr(self.objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
for family in self.objectspace.space.variables.values():
|
|
||||||
if hasattr(family, 'family'):
|
|
||||||
for family in family.family.values():
|
|
||||||
if 'dynamic' in vars(family):
|
|
||||||
namespace = self.objectspace.paths.get_variable_namespace(family.dynamic)
|
|
||||||
varpath = self.objectspace.paths.get_variable_path(family.dynamic, namespace)
|
|
||||||
family.dynamic = varpath
|
|
||||||
|
|
||||||
def annotate_variable(self, variable, family_mode, path, is_follower=False):
|
|
||||||
# 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(variable.xmlfiles)
|
|
||||||
new_value.name = True
|
|
||||||
new_value.type = 'boolean'
|
|
||||||
variable.value = [new_value]
|
|
||||||
if hasattr(variable, 'value') and variable.value:
|
|
||||||
has_value = True
|
|
||||||
for value in variable.value:
|
|
||||||
if value.type == 'calculation':
|
|
||||||
has_value = False
|
|
||||||
has_variable = False
|
|
||||||
if hasattr(value, 'param'):
|
|
||||||
for param in value.param:
|
|
||||||
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 has_value:
|
|
||||||
# if has value but without any calculation
|
|
||||||
variable.mandatory = True
|
|
||||||
if variable.mandatory is True and (not hasattr(variable, 'value') or is_follower):
|
|
||||||
variable.mode = modes_level[0]
|
|
||||||
if variable.mode != None and modes[variable.mode] < modes[family_mode] and (not is_follower or variable.mode != modes_level[0]):
|
|
||||||
variable.mode = family_mode
|
|
||||||
if variable.hidden is True:
|
|
||||||
variable.frozen = True
|
|
||||||
if not variable.auto_save is True and 'force_default_on_freeze' not in vars(variable):
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
|
|
||||||
def change_variable_mode(self): # pylint: disable=C0111
|
|
||||||
if not hasattr(self.objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
for variables in self.objectspace.space.variables.values():
|
|
||||||
namespace = variables.name
|
|
||||||
if hasattr(variables, 'family'):
|
|
||||||
for family in variables.family.values():
|
|
||||||
family_mode = family.mode
|
|
||||||
if hasattr(family, 'variable'):
|
|
||||||
for variable in family.variable.values():
|
|
||||||
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
mode = modes_level[-1]
|
|
||||||
for idx, follower in enumerate(variable.variable):
|
|
||||||
if follower.auto_save is True:
|
|
||||||
raise DictConsistencyError(_(f'leader/followers {follower.name} could not be auto_save'))
|
|
||||||
if follower.auto_freeze is True:
|
|
||||||
raise DictConsistencyError(_('leader/followers {follower.name} could not be auto_freeze'))
|
|
||||||
is_follower = idx != 0
|
|
||||||
path = '{}.{}.{}'.format(family.path, variable.name, follower.name)
|
|
||||||
self.annotate_variable(follower, family_mode, path, is_follower)
|
|
||||||
# leader's mode is minimum level
|
|
||||||
if modes[variable.variable[0].mode] > modes[follower.mode]:
|
|
||||||
follower.mode = variable.variable[0].mode
|
|
||||||
variable.mode = variable.variable[0].mode
|
|
||||||
else:
|
|
||||||
# auto_save's variable is set in 'basic' mode if its mode is 'normal'
|
|
||||||
if variable.auto_save is True and variable.mode != modes_level[-1]:
|
|
||||||
variable.mode = modes_level[0]
|
|
||||||
# auto_freeze's variable is set in 'basic' mode if its mode is 'normal'
|
|
||||||
if variable.auto_freeze is True and variable.mode != modes_level[-1]:
|
|
||||||
variable.mode = modes_level[0]
|
|
||||||
path = '{}.{}'.format(family.path, variable.name)
|
|
||||||
self.annotate_variable(variable, family_mode, path)
|
|
||||||
|
|
||||||
class PropertyAnnotator:
|
|
||||||
def __init__(self, objectspace):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.convert_annotator()
|
|
||||||
|
|
||||||
def convert_property(self,
|
|
||||||
variable,
|
|
||||||
):
|
|
||||||
properties = []
|
|
||||||
for prop in PROPERTIES:
|
|
||||||
if hasattr(variable, prop):
|
|
||||||
if getattr(variable, prop) == True:
|
|
||||||
for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
|
||||||
properties.append(subprop)
|
|
||||||
setattr(variable, prop, None)
|
|
||||||
if hasattr(variable, 'mode') and variable.mode:
|
|
||||||
properties.append(variable.mode)
|
|
||||||
variable.mode = None
|
|
||||||
if properties:
|
|
||||||
variable.properties = frozenset(properties)
|
|
||||||
|
|
||||||
def convert_annotator(self): # pylint: disable=C0111
|
|
||||||
if hasattr(self.objectspace.space, 'services'):
|
|
||||||
self.convert_property(self.objectspace.space.services)
|
|
||||||
for services in self.objectspace.space.services.service.values():
|
|
||||||
self.convert_property(services)
|
|
||||||
for service in vars(services).values():
|
|
||||||
if isinstance(service, self.objectspace.family):
|
|
||||||
self.convert_property(service)
|
|
||||||
if hasattr(service, 'family'):
|
|
||||||
self.convert_property(service)
|
|
||||||
for family in service.family:
|
|
||||||
self.convert_property(family)
|
|
||||||
if hasattr(family, 'variable'):
|
|
||||||
for variable in family.variable:
|
|
||||||
self.convert_property(variable)
|
|
||||||
if hasattr(self.objectspace.space, 'variables'):
|
|
||||||
for variables in self.objectspace.space.variables.values():
|
|
||||||
if hasattr(variables, 'family'):
|
|
||||||
for family in variables.family.values():
|
|
||||||
self.convert_property(family)
|
|
||||||
if hasattr(family, 'variable'):
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.Leadership):
|
|
||||||
self.convert_property(variable)
|
|
||||||
for follower in variable.variable:
|
|
||||||
self.convert_property(follower)
|
|
||||||
else:
|
|
||||||
self.convert_property(variable)
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
from .group import GroupAnnotator
|
||||||
|
from .service import ServiceAnnotator, ERASED_ATTRIBUTES
|
||||||
|
from .variable import VariableAnnotator, CONVERT_OPTION
|
||||||
|
from .constrainte import ConstrainteAnnotator
|
||||||
|
from .family import FamilyAnnotator, modes
|
||||||
|
from .property import PropertyAnnotator
|
||||||
|
|
||||||
|
class SpaceAnnotator:
|
||||||
|
"""Transformations applied on a CreoleObjSpace instance
|
||||||
|
"""
|
||||||
|
def __init__(self, objectspace, eosfunc_file):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
GroupAnnotator(objectspace)
|
||||||
|
ServiceAnnotator(objectspace)
|
||||||
|
VariableAnnotator(objectspace)
|
||||||
|
ConstrainteAnnotator(objectspace,
|
||||||
|
eosfunc_file,
|
||||||
|
)
|
||||||
|
FamilyAnnotator(objectspace)
|
||||||
|
PropertyAnnotator(objectspace)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ('SpaceAnnotator', 'ERASED_ATTRIBUTES', 'CONVERT_OPTION', 'modes')
|
|
@ -0,0 +1,563 @@
|
||||||
|
from typing import List
|
||||||
|
from importlib.machinery import SourceFileLoader
|
||||||
|
|
||||||
|
from .variable import CONVERT_OPTION
|
||||||
|
|
||||||
|
from ..i18n import _
|
||||||
|
from ..utils import normalize_family
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
from ..config import Config
|
||||||
|
|
||||||
|
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
|
||||||
|
|
||||||
|
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
|
||||||
|
|
||||||
|
|
||||||
|
class ConstrainteAnnotator:
|
||||||
|
def __init__(self,
|
||||||
|
objectspace,
|
||||||
|
eosfunc_file,
|
||||||
|
):
|
||||||
|
if not hasattr(objectspace.space, 'constraints'):
|
||||||
|
return
|
||||||
|
self.objectspace = objectspace
|
||||||
|
eosfunc = SourceFileLoader('eosfunc', eosfunc_file).load_module()
|
||||||
|
self.functions = dir(eosfunc)
|
||||||
|
self.functions.extend(INTERNAL_FUNCTIONS)
|
||||||
|
|
||||||
|
self.convert_auto_freeze()
|
||||||
|
self.valid_enums = {}
|
||||||
|
if hasattr(self.objectspace.space.constraints, 'check'):
|
||||||
|
self.check_check()
|
||||||
|
self.check_replace_text()
|
||||||
|
self.check_valid_enum()
|
||||||
|
self.check_change_warning()
|
||||||
|
self.convert_check()
|
||||||
|
if hasattr(self.objectspace.space.constraints, 'condition'):
|
||||||
|
self.check_params_target()
|
||||||
|
self.filter_targets()
|
||||||
|
self.convert_xxxlist_to_variable()
|
||||||
|
self.check_condition_fallback_optional()
|
||||||
|
self.check_choice_option_condition()
|
||||||
|
self.remove_condition_with_empty_target()
|
||||||
|
self.convert_condition()
|
||||||
|
if hasattr(self.objectspace.space.constraints, 'fill'):
|
||||||
|
self.convert_fill()
|
||||||
|
self.remove_constraints()
|
||||||
|
|
||||||
|
def convert_auto_freeze(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
def _convert_auto_freeze(variable, namespace):
|
||||||
|
if variable.auto_freeze:
|
||||||
|
if namespace != Config['variable_namespace']:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'auto_freeze is not allowed in extra "{namespace}" in {xmlfiles}'), 49)
|
||||||
|
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(variable.xmlfiles)
|
||||||
|
new_param.text = 'oui'
|
||||||
|
new_condition.param = [new_param]
|
||||||
|
new_target = self.objectspace.target(variable.xmlfiles)
|
||||||
|
new_target.type = 'variable'
|
||||||
|
new_target.name = variable.name
|
||||||
|
new_condition.target = [new_target]
|
||||||
|
if not hasattr(self.objectspace.space.constraints, 'condition'):
|
||||||
|
self.objectspace.space.constraints.condition = []
|
||||||
|
self.objectspace.space.constraints.condition.append(new_condition)
|
||||||
|
for variables in self.objectspace.space.variables.values():
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
namespace = variables.name
|
||||||
|
for family in variables.family.values():
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
for follower in variable.variable:
|
||||||
|
_convert_auto_freeze(follower, namespace)
|
||||||
|
else:
|
||||||
|
_convert_auto_freeze(variable, namespace)
|
||||||
|
|
||||||
|
def check_check(self):
|
||||||
|
remove_indexes = []
|
||||||
|
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
||||||
|
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}'), 1)
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
param_option_indexes = []
|
||||||
|
for idx, param in enumerate(check.param):
|
||||||
|
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:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'cannot find check param "{param.text}" in {xmlfiles}'), 2)
|
||||||
|
if param.type != 'variable':
|
||||||
|
param.notraisepropertyerror = None
|
||||||
|
param_option_indexes = list(set(param_option_indexes))
|
||||||
|
param_option_indexes.sort(reverse=True)
|
||||||
|
for idx in param_option_indexes:
|
||||||
|
check.param.pop(idx)
|
||||||
|
if check.param == []:
|
||||||
|
remove_indexes.append(check_idx)
|
||||||
|
remove_indexes.sort(reverse=True)
|
||||||
|
for idx in remove_indexes:
|
||||||
|
del self.objectspace.space.constraints.check[idx]
|
||||||
|
|
||||||
|
def check_replace_text(self):
|
||||||
|
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
||||||
|
namespace = check.namespace
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
for idx, param in enumerate(check.param):
|
||||||
|
if param.type == 'variable':
|
||||||
|
param.text = self.objectspace.paths.get_variable_path(param.text, namespace)
|
||||||
|
check.is_in_leadership = self.objectspace.paths.get_leader(check.target) != None
|
||||||
|
# let's replace the target by the path
|
||||||
|
check.target = self.objectspace.paths.get_variable_path(check.target, namespace)
|
||||||
|
|
||||||
|
def check_valid_enum(self):
|
||||||
|
remove_indexes = []
|
||||||
|
for idx, check in enumerate(self.objectspace.space.constraints.check):
|
||||||
|
if check.name == 'valid_enum':
|
||||||
|
if check.target in self.valid_enums:
|
||||||
|
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?'), 3)
|
||||||
|
if not hasattr(check, 'param'):
|
||||||
|
raise DictConsistencyError(_(f'param is mandatory for a valid_enum of variable {check.target}'), 4)
|
||||||
|
variable = self.objectspace.paths.get_variable_obj(check.target)
|
||||||
|
values = self.load_params_in_valid_enum(check.param,
|
||||||
|
variable.name,
|
||||||
|
variable.type,
|
||||||
|
)
|
||||||
|
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_valid_enum(self,
|
||||||
|
params,
|
||||||
|
variable_name,
|
||||||
|
variable_type,
|
||||||
|
):
|
||||||
|
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}'), 5)
|
||||||
|
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}'), 6)
|
||||||
|
values = param.text
|
||||||
|
else:
|
||||||
|
if has_variable:
|
||||||
|
raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'), 7)
|
||||||
|
if not hasattr(param, 'text'):
|
||||||
|
if param.type == 'number':
|
||||||
|
raise DictConsistencyError(_(f'value is mandatory for valid_enum of variable {variable_name}'), 8)
|
||||||
|
values.append(None)
|
||||||
|
else:
|
||||||
|
values.append(param.text)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def check_change_warning(self):
|
||||||
|
#convert level to "warnings_only"
|
||||||
|
for check in self.objectspace.space.constraints.check:
|
||||||
|
if check.level == 'warning':
|
||||||
|
check.warnings_only = True
|
||||||
|
else:
|
||||||
|
check.warnings_only = False
|
||||||
|
check.level = None
|
||||||
|
|
||||||
|
def _get_family_variables_from_target(self,
|
||||||
|
target,
|
||||||
|
):
|
||||||
|
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]
|
||||||
|
# it's a family
|
||||||
|
variable = self.objectspace.paths.get_family_obj(target.name)
|
||||||
|
return variable, list(variable.variable.values())
|
||||||
|
|
||||||
|
def check_params_target(self):
|
||||||
|
for condition in self.objectspace.space.constraints.condition:
|
||||||
|
if not hasattr(condition, 'target'):
|
||||||
|
raise DictConsistencyError(_('target is mandatory in condition'), 9)
|
||||||
|
for target in condition.target:
|
||||||
|
if target.type.endswith('list') and condition.name not in ['disabled_if_in', 'disabled_if_not_in']:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(target.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'target "{target.type}" not allow in condition "{condition.name}" in {xmlfiles}'), 10)
|
||||||
|
|
||||||
|
def filter_targets(self): # pylint: disable=C0111
|
||||||
|
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||||
|
namespace = condition.namespace
|
||||||
|
for idx, target in enumerate(condition.target):
|
||||||
|
if target.type == 'variable':
|
||||||
|
if condition.source == target.name:
|
||||||
|
raise DictConsistencyError(_('target name and source name must be different: {}').format(condition.source), 11)
|
||||||
|
try:
|
||||||
|
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_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), 12)
|
||||||
|
|
||||||
|
def convert_xxxlist_to_variable(self): # pylint: disable=C0111
|
||||||
|
# transform *list to variable or family
|
||||||
|
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||||
|
new_targets = []
|
||||||
|
remove_targets = []
|
||||||
|
for target_idx, target in enumerate(condition.target):
|
||||||
|
if target.type.endswith('list'):
|
||||||
|
listname = target.type
|
||||||
|
listvars = self.objectspace.list_conditions.get(listname,
|
||||||
|
{}).get(target.name)
|
||||||
|
if listvars:
|
||||||
|
for listvar in listvars:
|
||||||
|
variable = self.objectspace.paths.get_variable_obj(listvar)
|
||||||
|
type_ = 'variable'
|
||||||
|
new_target = self.objectspace.target(variable.xmlfiles)
|
||||||
|
new_target.type = type_
|
||||||
|
new_target.name = listvar
|
||||||
|
new_target.index = target.index
|
||||||
|
new_targets.append(new_target)
|
||||||
|
remove_targets.append(target_idx)
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for target_idx in remove_targets:
|
||||||
|
condition.target.pop(target_idx)
|
||||||
|
condition.target.extend(new_targets)
|
||||||
|
|
||||||
|
def check_condition_fallback_optional(self):
|
||||||
|
# a condition with a fallback **and** the source variable doesn't exist
|
||||||
|
remove_conditions = []
|
||||||
|
for idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||||
|
# fallback
|
||||||
|
if condition.fallback is True and not self.objectspace.paths.path_is_defined(condition.source):
|
||||||
|
apply_action = False
|
||||||
|
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
|
||||||
|
apply_action = not condition.force_condition_on_fallback
|
||||||
|
else:
|
||||||
|
apply_action = condition.force_inverse_condition_on_fallback
|
||||||
|
if apply_action:
|
||||||
|
actions = self._get_condition_actions(condition.name)
|
||||||
|
for target in condition.target:
|
||||||
|
leader_or_variable, variables = self._get_family_variables_from_target(target)
|
||||||
|
for action_idx, action in enumerate(actions):
|
||||||
|
if action_idx == 0:
|
||||||
|
setattr(leader_or_variable, action, True)
|
||||||
|
else:
|
||||||
|
for variable in variables:
|
||||||
|
setattr(variable, action, True)
|
||||||
|
remove_conditions.append(idx)
|
||||||
|
continue
|
||||||
|
|
||||||
|
remove_targets = []
|
||||||
|
# optional
|
||||||
|
for idx, target in enumerate(condition.target):
|
||||||
|
if target.optional is True and not self.objectspace.paths.path_is_defined(target.name):
|
||||||
|
remove_targets.append(idx)
|
||||||
|
remove_targets = list(set(remove_targets))
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for idx in remove_targets:
|
||||||
|
condition.target.pop(idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.objectspace.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def _get_condition_actions(self, condition_name):
|
||||||
|
if condition_name.startswith('disabled_if_'):
|
||||||
|
return ['disabled']
|
||||||
|
elif condition_name.startswith('hidden_if_'):
|
||||||
|
return ['hidden', 'frozen', 'force_default_on_freeze']
|
||||||
|
elif condition_name.startswith('mandatory_if_'):
|
||||||
|
return ['mandatory']
|
||||||
|
elif condition_name == 'auto_hidden_if_not_in':
|
||||||
|
return ['auto_frozen']
|
||||||
|
|
||||||
|
def check_choice_option_condition(self):
|
||||||
|
# remove condition for ChoiceOption that don't have param
|
||||||
|
remove_conditions = []
|
||||||
|
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||||
|
namespace = condition.namespace
|
||||||
|
condition.source = self.objectspace.paths.get_variable_path(condition.source, namespace, allow_source=True)
|
||||||
|
src_variable = self.objectspace.paths.get_variable_obj(condition.source)
|
||||||
|
valid_enum = None
|
||||||
|
if condition.source in self.valid_enums and self.valid_enums[condition.source]['type'] == 'string':
|
||||||
|
valid_enum = self.valid_enums[condition.source]['values']
|
||||||
|
if valid_enum is not None:
|
||||||
|
remove_param = []
|
||||||
|
for param_idx, param in enumerate(condition.param):
|
||||||
|
if param.text not in valid_enum:
|
||||||
|
remove_param.append(param_idx)
|
||||||
|
remove_param.sort(reverse=True)
|
||||||
|
for idx in remove_param:
|
||||||
|
del condition.param[idx]
|
||||||
|
if condition.param == []:
|
||||||
|
remove_targets = []
|
||||||
|
for target in condition.target:
|
||||||
|
leader_or_variable, variables = self._get_family_variables_from_target(target)
|
||||||
|
if condition.name == 'disabled_if_not_in':
|
||||||
|
leader_or_variable.disabled = True
|
||||||
|
elif condition.name == 'hidden_if_not_in':
|
||||||
|
leader_or_variable.hidden = True
|
||||||
|
for variable in variables:
|
||||||
|
variable.frozen = True
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
elif condition.name == 'mandatory_if_not_in':
|
||||||
|
variable.mandatory = True
|
||||||
|
remove_targets = list(set(remove_targets))
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for target_idx in remove_targets:
|
||||||
|
condition.target.pop(target_idx)
|
||||||
|
remove_conditions.append(condition_idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.objectspace.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def remove_condition_with_empty_target(self):
|
||||||
|
remove_conditions = []
|
||||||
|
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||||
|
if not condition.target:
|
||||||
|
remove_conditions.append(condition_idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.objectspace.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def convert_condition(self):
|
||||||
|
for condition in self.objectspace.space.constraints.condition:
|
||||||
|
inverse = condition.name.endswith('_if_not_in')
|
||||||
|
actions = self._get_condition_actions(condition.name)
|
||||||
|
for param in condition.param:
|
||||||
|
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
|
||||||
|
# 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
|
||||||
|
if isinstance(leader_or_variable, self.objectspace.variable) and \
|
||||||
|
(leader_or_variable.auto_save or leader_or_variable.auto_freeze) and \
|
||||||
|
'force_default_on_freeze' in actions:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(leader_or_variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'cannot have auto_freeze or auto_store with the hidden_if_in or hidden_if_not_in variable "{leader_or_variable.name}" in {xmlfiles}'), 51)
|
||||||
|
for idx, action in enumerate(actions):
|
||||||
|
prop = self.objectspace.property_(leader_or_variable.xmlfiles)
|
||||||
|
prop.type = 'calculation'
|
||||||
|
prop.inverse = inverse
|
||||||
|
prop.source = condition.source
|
||||||
|
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_,
|
||||||
|
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(variable.xmlfiles)
|
||||||
|
try:
|
||||||
|
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}"'), 13)
|
||||||
|
if choice.name == '':
|
||||||
|
choice.name = None
|
||||||
|
choices.append(choice.name)
|
||||||
|
choice.type = type_
|
||||||
|
variable.choice.append(choice)
|
||||||
|
# check value or set first choice value has default value
|
||||||
|
if hasattr(variable, 'value'):
|
||||||
|
for value in variable.value:
|
||||||
|
value.type = type_
|
||||||
|
try:
|
||||||
|
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}"'), 14)
|
||||||
|
if cvalue not in choices:
|
||||||
|
raise DictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices), 15)
|
||||||
|
else:
|
||||||
|
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), 16)
|
||||||
|
variable.type = 'choice'
|
||||||
|
|
||||||
|
def convert_check(self):
|
||||||
|
for check in self.objectspace.space.constraints.check:
|
||||||
|
variable = self.objectspace.paths.get_variable_obj(check.target)
|
||||||
|
name = check.name
|
||||||
|
if name == 'valid_entier':
|
||||||
|
if not hasattr(check, 'param'):
|
||||||
|
raise DictConsistencyError(_('{} must have, at least, 1 param').format(name), 17)
|
||||||
|
for param in check.param:
|
||||||
|
if param.type not in ['string', 'number']:
|
||||||
|
raise DictConsistencyError(_(f'param in "valid_entier" must not be a "{param.type}"'), 18)
|
||||||
|
if param.name == 'mini':
|
||||||
|
variable.min_number = int(param.text)
|
||||||
|
elif param.name == 'maxi':
|
||||||
|
variable.max_number = int(param.text)
|
||||||
|
else:
|
||||||
|
raise DictConsistencyError(_(f'unknown parameter {param.text} in check "valid_entier" for variable {check.target}'), 19)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'), 20)
|
||||||
|
elif name == 'valid_ipnetmask':
|
||||||
|
params_len = 1
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'), 21)
|
||||||
|
name = 'valid_ip_netmask'
|
||||||
|
elif name == 'valid_broadcast':
|
||||||
|
params_len = 2
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'), 22)
|
||||||
|
elif name == 'valid_in_network':
|
||||||
|
if len(check.param) not in (1, 2):
|
||||||
|
params_len = 2
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'), 23)
|
||||||
|
check_.name = name
|
||||||
|
check_.warnings_only = check.warnings_only
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
check_.param = check.param
|
||||||
|
if not hasattr(variable, 'check'):
|
||||||
|
variable.check = []
|
||||||
|
variable.check.append(check_)
|
||||||
|
del self.objectspace.space.constraints.check
|
||||||
|
|
||||||
|
def convert_fill(self): # pylint: disable=C0111,R0912
|
||||||
|
# sort fill/auto by index
|
||||||
|
fills = {fill.index: fill for idx, fill in enumerate(self.objectspace.space.constraints.fill)}
|
||||||
|
indexes = list(fills.keys())
|
||||||
|
indexes.sort()
|
||||||
|
targets = []
|
||||||
|
for idx in indexes:
|
||||||
|
fill = fills[idx]
|
||||||
|
# test if it's redefined calculation
|
||||||
|
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}'), 24)
|
||||||
|
targets.append(fill.target)
|
||||||
|
#
|
||||||
|
if fill.name not in self.functions:
|
||||||
|
raise DictConsistencyError(_('cannot find fill function {}').format(fill.name), 25)
|
||||||
|
|
||||||
|
namespace = fill.namespace
|
||||||
|
# let's replace the target by the path
|
||||||
|
fill.target, suffix = self.objectspace.paths.get_variable_path(fill.target,
|
||||||
|
namespace,
|
||||||
|
with_suffix=True,
|
||||||
|
)
|
||||||
|
if suffix is not None:
|
||||||
|
raise DictConsistencyError(_(f'Cannot add fill function to "{fill.target}" only with the suffix "{suffix}"'), 26)
|
||||||
|
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 ['suffix', 'string'] and not hasattr(param, 'text'):
|
||||||
|
raise DictConsistencyError(_(f"All '{param.type}' variables must have a value in order to calculate {fill.target}"), 27)
|
||||||
|
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}"), 28)
|
||||||
|
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,
|
||||||
|
namespace,
|
||||||
|
with_suffix=True,
|
||||||
|
)
|
||||||
|
if suffix:
|
||||||
|
param.suffix = suffix
|
||||||
|
except DictConsistencyError as err:
|
||||||
|
if err.errno != 42:
|
||||||
|
raise err
|
||||||
|
if param.optional is False:
|
||||||
|
raise err
|
||||||
|
param_to_delete.append(fill_idx)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
param.notraisepropertyerror = None
|
||||||
|
param_to_delete.sort(reverse=True)
|
||||||
|
for param_idx in param_to_delete:
|
||||||
|
fill.param.pop(param_idx)
|
||||||
|
value.param = fill.param
|
||||||
|
variable.value = [value]
|
||||||
|
del self.objectspace.space.constraints.fill
|
||||||
|
|
||||||
|
def remove_constraints(self):
|
||||||
|
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
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
from ..i18n import _
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
|
||||||
|
|
||||||
|
#mode order is important
|
||||||
|
modes_level = ('basic', 'normal', 'expert')
|
||||||
|
|
||||||
|
|
||||||
|
class Mode(object):
|
||||||
|
def __init__(self, name, level):
|
||||||
|
self.name = name
|
||||||
|
self.level = level
|
||||||
|
def __gt__(self, other):
|
||||||
|
return other.level < self.level
|
||||||
|
|
||||||
|
|
||||||
|
def mode_factory():
|
||||||
|
mode_obj = {}
|
||||||
|
for idx in range(len(modes_level)):
|
||||||
|
name = modes_level[idx]
|
||||||
|
mode_obj[name] = Mode(name, idx)
|
||||||
|
return mode_obj
|
||||||
|
|
||||||
|
modes = mode_factory()
|
||||||
|
|
||||||
|
|
||||||
|
class FamilyAnnotator:
|
||||||
|
def __init__(self,
|
||||||
|
objectspace,
|
||||||
|
):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.remove_empty_families()
|
||||||
|
self.change_variable_mode()
|
||||||
|
self.change_family_mode()
|
||||||
|
self.dynamic_families()
|
||||||
|
|
||||||
|
def remove_empty_families(self): # pylint: disable=C0111,R0201
|
||||||
|
if hasattr(self.objectspace.space, 'variables'):
|
||||||
|
for family in self.objectspace.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
space = family.family
|
||||||
|
removed_families = []
|
||||||
|
for family_name, family in space.items():
|
||||||
|
if not hasattr(family, 'variable') or len(family.variable) == 0:
|
||||||
|
removed_families.append(family_name)
|
||||||
|
for family_name in removed_families:
|
||||||
|
del space[family_name]
|
||||||
|
|
||||||
|
def change_family_mode(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.objectspace.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
for family in family.family.values():
|
||||||
|
mode = modes_level[-1]
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
variable_mode = variable.variable[0].mode
|
||||||
|
variable.variable[0].mode = None
|
||||||
|
variable.mode = variable_mode
|
||||||
|
else:
|
||||||
|
variable_mode = variable.mode
|
||||||
|
if variable_mode is not None and modes[mode] > modes[variable_mode]:
|
||||||
|
mode = variable_mode
|
||||||
|
family.mode = mode
|
||||||
|
|
||||||
|
def dynamic_families(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.objectspace.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
for family in family.family.values():
|
||||||
|
if 'dynamic' in vars(family):
|
||||||
|
namespace = self.objectspace.paths.get_variable_namespace(family.dynamic)
|
||||||
|
varpath = self.objectspace.paths.get_variable_path(family.dynamic, namespace)
|
||||||
|
family.dynamic = varpath
|
||||||
|
|
||||||
|
def annotate_variable(self, variable, family_mode, path, is_follower=False):
|
||||||
|
# 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(variable.xmlfiles)
|
||||||
|
new_value.name = True
|
||||||
|
new_value.type = 'boolean'
|
||||||
|
variable.value = [new_value]
|
||||||
|
if hasattr(variable, 'value') and variable.value:
|
||||||
|
has_value = True
|
||||||
|
for value in variable.value:
|
||||||
|
if value.type == 'calculation':
|
||||||
|
has_value = False
|
||||||
|
has_variable = False
|
||||||
|
if hasattr(value, 'param'):
|
||||||
|
for param in value.param:
|
||||||
|
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 has_value:
|
||||||
|
# if has value but without any calculation
|
||||||
|
variable.mandatory = True
|
||||||
|
if variable.mandatory is True and (not hasattr(variable, 'value') or is_follower):
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
if variable.mode != None and modes[variable.mode] < modes[family_mode] and (not is_follower or variable.mode != modes_level[0]):
|
||||||
|
variable.mode = family_mode
|
||||||
|
if variable.hidden is True:
|
||||||
|
variable.frozen = True
|
||||||
|
if not variable.auto_save is True and 'force_default_on_freeze' not in vars(variable):
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
|
||||||
|
def change_variable_mode(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
for variables in self.objectspace.space.variables.values():
|
||||||
|
namespace = variables.name
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
for family in variables.family.values():
|
||||||
|
family_mode = family.mode
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
mode = modes_level[-1]
|
||||||
|
for idx, follower in enumerate(variable.variable):
|
||||||
|
if follower.auto_save is True:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'leader/followers "{follower.name}" could not be auto_save in {xmlfiles}'), 29)
|
||||||
|
if follower.auto_freeze is True:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'leader/followers "{follower.name}" could not be auto_freeze in {xmlfiles}'), 30)
|
||||||
|
is_follower = idx != 0
|
||||||
|
path = '{}.{}.{}'.format(family.path, variable.name, follower.name)
|
||||||
|
self.annotate_variable(follower, family_mode, path, is_follower)
|
||||||
|
# leader's mode is minimum level
|
||||||
|
if modes[variable.variable[0].mode] > modes[follower.mode]:
|
||||||
|
follower.mode = variable.variable[0].mode
|
||||||
|
variable.mode = variable.variable[0].mode
|
||||||
|
else:
|
||||||
|
# auto_save's variable is set in 'basic' mode if its mode is 'normal'
|
||||||
|
if variable.auto_save is True and variable.mode != modes_level[-1]:
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
# auto_freeze's variable is set in 'basic' mode if its mode is 'normal'
|
||||||
|
if variable.auto_freeze is True and variable.mode != modes_level[-1]:
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
path = '{}.{}'.format(family.path, variable.name)
|
||||||
|
self.annotate_variable(variable, family_mode, path)
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ..i18n import _
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
|
||||||
|
|
||||||
|
class GroupAnnotator:
|
||||||
|
def __init__(self,
|
||||||
|
objectspace,
|
||||||
|
):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
if not hasattr(self.objectspace.space, 'constraints') or not hasattr(self.objectspace.space.constraints, 'group'):
|
||||||
|
return
|
||||||
|
self.convert_groups()
|
||||||
|
|
||||||
|
def convert_groups(self): # pylint: disable=C0111
|
||||||
|
for group in self.objectspace.space.constraints.group:
|
||||||
|
leader_fullname = group.leader
|
||||||
|
leader_family_name = self.objectspace.paths.get_variable_family_name(leader_fullname)
|
||||||
|
leader_name = self.objectspace.paths.get_variable_name(leader_fullname)
|
||||||
|
namespace = self.objectspace.paths.get_variable_namespace(leader_fullname)
|
||||||
|
if '.' not in leader_fullname:
|
||||||
|
leader_fullname = '.'.join([namespace, leader_family_name, leader_fullname])
|
||||||
|
follower_names = list(group.follower.keys())
|
||||||
|
has_a_leader = False
|
||||||
|
ori_leader_family = self.objectspace.paths.get_family_obj(leader_fullname.rsplit('.', 1)[0])
|
||||||
|
for variable in list(ori_leader_family.variable.values()):
|
||||||
|
if has_a_leader:
|
||||||
|
# it's a follower
|
||||||
|
self.manage_follower(namespace,
|
||||||
|
leader_family_name,
|
||||||
|
variable,
|
||||||
|
leadership_name,
|
||||||
|
follower_names,
|
||||||
|
leader_space,
|
||||||
|
leader_is_hidden,
|
||||||
|
)
|
||||||
|
ori_leader_family.variable.pop(variable.name)
|
||||||
|
if follower_names == []:
|
||||||
|
# no more follower
|
||||||
|
break
|
||||||
|
elif variable.name == leader_name:
|
||||||
|
# it's a leader
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
# append follower to an existed leadership
|
||||||
|
leader_space = variable
|
||||||
|
# if variable.hidden:
|
||||||
|
# leader_is_hidden = True
|
||||||
|
else:
|
||||||
|
leader_space = self.objectspace.leadership(variable.xmlfiles)
|
||||||
|
if hasattr(group, 'name'):
|
||||||
|
leadership_name = group.name
|
||||||
|
else:
|
||||||
|
leadership_name = leader_name
|
||||||
|
leader_is_hidden = self.manage_leader(leader_space,
|
||||||
|
leader_family_name,
|
||||||
|
leadership_name,
|
||||||
|
leader_name,
|
||||||
|
namespace,
|
||||||
|
variable,
|
||||||
|
group,
|
||||||
|
leader_fullname,
|
||||||
|
)
|
||||||
|
has_a_leader = True
|
||||||
|
else:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
joined = '", "'.join(follower_names)
|
||||||
|
raise DictConsistencyError(_(f'when parsing leadership, we espect to find those followers "{joined}" in {xmlfiles}'), 31)
|
||||||
|
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',
|
||||||
|
group: 'Group',
|
||||||
|
leader_fullname: str,
|
||||||
|
) -> None:
|
||||||
|
# manage leader's variable
|
||||||
|
if variable.multi is not True:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'the variable "{variable.name}" in a group must be multi in {xmlfiles}'), 32)
|
||||||
|
leader_space.variable = []
|
||||||
|
leader_space.name = leadership_name
|
||||||
|
leader_space.hidden = variable.hidden
|
||||||
|
if variable.hidden:
|
||||||
|
leader_is_hidden = True
|
||||||
|
variable.frozen = True
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
else:
|
||||||
|
leader_is_hidden = False
|
||||||
|
variable.hidden = None
|
||||||
|
if hasattr(group, 'description'):
|
||||||
|
leader_space.doc = group.description
|
||||||
|
elif hasattr(variable, 'description'):
|
||||||
|
leader_space.doc = variable.description
|
||||||
|
else:
|
||||||
|
leader_space.doc = variable.name
|
||||||
|
leadership_path = namespace + '.' + leader_family_name + '.' + leadership_name
|
||||||
|
self.objectspace.paths.add_family(namespace,
|
||||||
|
leadership_path,
|
||||||
|
leader_space,
|
||||||
|
)
|
||||||
|
leader_family = self.objectspace.space.variables[namespace].family[leader_family_name]
|
||||||
|
leader_family.variable[leader_name] = leader_space
|
||||||
|
leader_space.variable.append(variable)
|
||||||
|
self.objectspace.paths.set_leader(namespace,
|
||||||
|
leader_family_name,
|
||||||
|
leader_name,
|
||||||
|
leader_name,
|
||||||
|
)
|
||||||
|
leader_space.path = leader_fullname
|
||||||
|
return leader_is_hidden
|
||||||
|
|
||||||
|
def manage_follower(self,
|
||||||
|
namespace: str,
|
||||||
|
leader_family_name: str,
|
||||||
|
variable: 'Variable',
|
||||||
|
leader_name: str,
|
||||||
|
follower_names: List[str],
|
||||||
|
leader_space: 'Leadership',
|
||||||
|
leader_is_hidden: bool,
|
||||||
|
) -> None:
|
||||||
|
if variable.name != follower_names[0]:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'when parsing leadership, we espect to find the follower "{follower_names[0]}" but we found "{variable.name}" in {xmlfiles}'), 33)
|
||||||
|
follower_names.remove(variable.name)
|
||||||
|
if leader_is_hidden:
|
||||||
|
variable.frozen = True
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
leader_space.variable.append(variable) # pylint: disable=E1101
|
||||||
|
self.objectspace.paths.set_leader(namespace,
|
||||||
|
leader_family_name,
|
||||||
|
variable.name,
|
||||||
|
leader_name,
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
from ..i18n import _
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
|
||||||
|
|
||||||
|
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
|
||||||
|
'force_store_value', 'disabled', 'mandatory')
|
||||||
|
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'], 'auto_freeze': ['force_store_value', 'auto_freeze']}
|
||||||
|
|
||||||
|
|
||||||
|
class PropertyAnnotator:
|
||||||
|
def __init__(self, objectspace):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.convert_annotator()
|
||||||
|
|
||||||
|
def convert_property(self,
|
||||||
|
variable,
|
||||||
|
):
|
||||||
|
properties = []
|
||||||
|
for prop in PROPERTIES:
|
||||||
|
if hasattr(variable, prop):
|
||||||
|
if getattr(variable, prop) == True:
|
||||||
|
for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
||||||
|
properties.append(subprop)
|
||||||
|
setattr(variable, prop, None)
|
||||||
|
if hasattr(variable, 'mode') and variable.mode:
|
||||||
|
properties.append(variable.mode)
|
||||||
|
variable.mode = None
|
||||||
|
if 'force_store_value' in properties and 'force_default_on_freeze' in properties:
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'cannot have auto_freeze or auto_store with the hidden variable "{variable.name}" in {xmlfiles}'), 50)
|
||||||
|
if properties:
|
||||||
|
variable.properties = frozenset(properties)
|
||||||
|
|
||||||
|
def convert_annotator(self): # pylint: disable=C0111
|
||||||
|
if hasattr(self.objectspace.space, 'services'):
|
||||||
|
self.convert_property(self.objectspace.space.services)
|
||||||
|
for services in self.objectspace.space.services.service.values():
|
||||||
|
self.convert_property(services)
|
||||||
|
for service in vars(services).values():
|
||||||
|
if isinstance(service, self.objectspace.family):
|
||||||
|
self.convert_property(service)
|
||||||
|
if hasattr(service, 'family'):
|
||||||
|
self.convert_property(service)
|
||||||
|
for family in service.family:
|
||||||
|
self.convert_property(family)
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable:
|
||||||
|
self.convert_property(variable)
|
||||||
|
if hasattr(self.objectspace.space, 'variables'):
|
||||||
|
for variables in self.objectspace.space.variables.values():
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
for family in variables.family.values():
|
||||||
|
self.convert_property(family)
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
self.convert_property(variable)
|
||||||
|
for follower in variable.variable:
|
||||||
|
self.convert_property(follower)
|
||||||
|
else:
|
||||||
|
self.convert_property(variable)
|
|
@ -0,0 +1,252 @@
|
||||||
|
from os.path import basename
|
||||||
|
|
||||||
|
from ..i18n import _
|
||||||
|
from ..utils import normalize_family
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
# 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', 'remove_fill', 'xmlfiles')
|
||||||
|
|
||||||
|
|
||||||
|
KEY_TYPE = {'variable': 'symlink',
|
||||||
|
'SymLinkOption': 'symlink',
|
||||||
|
'PortOption': 'port',
|
||||||
|
'UnicodeOption': 'string',
|
||||||
|
'NetworkOption': 'network',
|
||||||
|
'NetmaskOption': 'netmask',
|
||||||
|
'URLOption': 'web_address',
|
||||||
|
'FilenameOption': 'filename'}
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAnnotator:
|
||||||
|
"""Manage service's object
|
||||||
|
for example::
|
||||||
|
<services>
|
||||||
|
<service name="test">
|
||||||
|
<service_access service='ntp'>
|
||||||
|
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
|
||||||
|
</service_access>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
"""
|
||||||
|
def __init__(self, objectspace):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.convert_services()
|
||||||
|
|
||||||
|
def convert_services(self):
|
||||||
|
if not hasattr(self.objectspace.space, 'services'):
|
||||||
|
return
|
||||||
|
if not hasattr(self.objectspace.space.services, 'service'):
|
||||||
|
del self.objectspace.space.services
|
||||||
|
return
|
||||||
|
self.objectspace.space.services.hidden = True
|
||||||
|
self.objectspace.space.services.name = 'services'
|
||||||
|
self.objectspace.space.services.doc = 'services'
|
||||||
|
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(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)
|
||||||
|
continue
|
||||||
|
eltname = elttype + 's'
|
||||||
|
path = '.'.join(['services', service_name, eltname])
|
||||||
|
family = self.gen_family(eltname,
|
||||||
|
path,
|
||||||
|
service.xmlfiles,
|
||||||
|
)
|
||||||
|
if isinstance(values, dict):
|
||||||
|
values = list(values.values())
|
||||||
|
family.family = self.make_group_from_elts(service_name,
|
||||||
|
elttype,
|
||||||
|
values,
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
setattr(new_service, elttype, family)
|
||||||
|
new_service.doc = new_service.name
|
||||||
|
families[service_name] = new_service
|
||||||
|
self.objectspace.space.services.service = families
|
||||||
|
|
||||||
|
def gen_family(self,
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
xmlfiles
|
||||||
|
):
|
||||||
|
family = self.objectspace.family(xmlfiles)
|
||||||
|
family.name = normalize_family(name)
|
||||||
|
family.doc = name
|
||||||
|
family.mode = None
|
||||||
|
self.objectspace.paths.add_family('services',
|
||||||
|
path,
|
||||||
|
family,
|
||||||
|
)
|
||||||
|
return family
|
||||||
|
|
||||||
|
def make_group_from_elts(self,
|
||||||
|
service_name,
|
||||||
|
name,
|
||||||
|
elts,
|
||||||
|
path,
|
||||||
|
):
|
||||||
|
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
|
||||||
|
and build elements and its attributes (the `Options` in tiramisu terms)
|
||||||
|
"""
|
||||||
|
families = []
|
||||||
|
new_elts = self._reorder_elts(name,
|
||||||
|
elts,
|
||||||
|
)
|
||||||
|
for index, elt_info in enumerate(new_elts):
|
||||||
|
elt = elt_info['elt']
|
||||||
|
elt_name = elt_info['elt_name']
|
||||||
|
|
||||||
|
# try to launch _update_xxxx() function
|
||||||
|
update_elt = '_update_' + elt_name
|
||||||
|
if hasattr(self, update_elt):
|
||||||
|
getattr(self, update_elt)(elt,
|
||||||
|
index,
|
||||||
|
path,
|
||||||
|
service_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
while True:
|
||||||
|
if hasattr(elt, 'source'):
|
||||||
|
c_name = elt.source
|
||||||
|
else:
|
||||||
|
c_name = elt.name
|
||||||
|
if idx:
|
||||||
|
c_name += f'_{idx}'
|
||||||
|
subpath = '{}.{}'.format(path, c_name)
|
||||||
|
if not self.objectspace.paths.family_is_defined(subpath):
|
||||||
|
break
|
||||||
|
idx += 1
|
||||||
|
family = self.gen_family(c_name,
|
||||||
|
subpath,
|
||||||
|
elt.xmlfiles,
|
||||||
|
)
|
||||||
|
family.variable = []
|
||||||
|
listname = '{}list'.format(name)
|
||||||
|
activate_path = '.'.join([subpath, 'activate'])
|
||||||
|
for key in dir(elt):
|
||||||
|
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
||||||
|
continue
|
||||||
|
value = getattr(elt, key)
|
||||||
|
if key == listname:
|
||||||
|
self.objectspace.list_conditions.setdefault(listname,
|
||||||
|
{}).setdefault(
|
||||||
|
value,
|
||||||
|
[]).append(activate_path)
|
||||||
|
continue
|
||||||
|
family.variable.append(self._generate_element(elt_name,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
elt,
|
||||||
|
f'{subpath}.{key}'
|
||||||
|
))
|
||||||
|
# FIXME ne devrait pas etre True par défaut
|
||||||
|
# devrait etre un calcule
|
||||||
|
family.variable.append(self._generate_element(elt_name,
|
||||||
|
'activate',
|
||||||
|
True,
|
||||||
|
elt,
|
||||||
|
activate_path,
|
||||||
|
))
|
||||||
|
families.append(family)
|
||||||
|
return families
|
||||||
|
|
||||||
|
def _generate_element(self,
|
||||||
|
elt_name,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
elt,
|
||||||
|
path,
|
||||||
|
):
|
||||||
|
variable = self.objectspace.variable(elt.xmlfiles)
|
||||||
|
variable.name = normalize_family(key)
|
||||||
|
variable.mode = None
|
||||||
|
if key == 'name':
|
||||||
|
true_key = elt_name
|
||||||
|
else:
|
||||||
|
true_key = key
|
||||||
|
dtd_key_type = true_key + '_type'
|
||||||
|
if key == 'activate':
|
||||||
|
type_ = 'boolean'
|
||||||
|
elif hasattr(elt, dtd_key_type):
|
||||||
|
type_ = KEY_TYPE[getattr(elt, dtd_key_type)]
|
||||||
|
elif key in self.objectspace.booleans_attributs:
|
||||||
|
type_ = 'boolean'
|
||||||
|
else:
|
||||||
|
type_ = 'string'
|
||||||
|
variable.type = type_
|
||||||
|
if type_ == 'symlink':
|
||||||
|
variable.opt = self.objectspace.paths.get_variable_path(value,
|
||||||
|
'services',
|
||||||
|
)
|
||||||
|
# variable.opt = value
|
||||||
|
variable.multi = None
|
||||||
|
else:
|
||||||
|
variable.doc = key
|
||||||
|
val = self.objectspace.value(elt.xmlfiles)
|
||||||
|
val.type = type_
|
||||||
|
val.name = value
|
||||||
|
variable.value = [val]
|
||||||
|
self.objectspace.paths.add_variable('services',
|
||||||
|
path,
|
||||||
|
'service',
|
||||||
|
False,
|
||||||
|
variable,
|
||||||
|
)
|
||||||
|
return variable
|
||||||
|
|
||||||
|
def _reorder_elts(self,
|
||||||
|
name,
|
||||||
|
elts,
|
||||||
|
):
|
||||||
|
"""Reorders by index the elts
|
||||||
|
"""
|
||||||
|
new_elts = {}
|
||||||
|
# reorder elts by index
|
||||||
|
for idx, elt in enumerate(elts):
|
||||||
|
new_elts.setdefault(idx, []).append(elt)
|
||||||
|
idxes = list(new_elts.keys())
|
||||||
|
idxes.sort()
|
||||||
|
result_elts = []
|
||||||
|
for idx in idxes:
|
||||||
|
for elt in new_elts[idx]:
|
||||||
|
result_elts.append({'elt_name': name, 'elt': elt})
|
||||||
|
return result_elts
|
||||||
|
|
||||||
|
def _update_override(self,
|
||||||
|
file_,
|
||||||
|
index,
|
||||||
|
service_path,
|
||||||
|
service_name,
|
||||||
|
):
|
||||||
|
file_.name = f'/systemd/system/{service_name}.service.d/rougail.conf'
|
||||||
|
# retrieve default value from File object
|
||||||
|
for attr in ['owner', 'group', 'mode']:
|
||||||
|
setattr(file_, attr, getattr(self.objectspace.file, attr))
|
||||||
|
if not hasattr(file_, 'source'):
|
||||||
|
file_.source = f'{service_name}.service'
|
||||||
|
self._update_file(file_,
|
||||||
|
index,
|
||||||
|
service_path,
|
||||||
|
service_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_file(self,
|
||||||
|
file_,
|
||||||
|
index,
|
||||||
|
service_path,
|
||||||
|
service_name,
|
||||||
|
):
|
||||||
|
if not hasattr(file_, 'file_type') or file_.file_type == "UnicodeOption":
|
||||||
|
if not hasattr(file_, 'source'):
|
||||||
|
file_.source = basename(file_.name)
|
||||||
|
elif not hasattr(file_, 'source'):
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(file_.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'attribute "source" is mandatory for the file "{file_.name}" in {xmlfiles}'), 34)
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
from ..i18n import _
|
||||||
|
from ..utils import normalize_family
|
||||||
|
from ..error import DictConsistencyError
|
||||||
|
|
||||||
|
|
||||||
|
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}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_CHOICE = {'oui/non': ['oui', 'non'],
|
||||||
|
'on/off': ['on', 'off'],
|
||||||
|
'yes/no': ['yes', 'no'],
|
||||||
|
'schedule': ['none', 'daily', 'weekly', 'monthly'],
|
||||||
|
'schedulemod': ['pre', 'post']}
|
||||||
|
|
||||||
|
|
||||||
|
RENAME_ATTIBUTES = {'description': 'doc'}
|
||||||
|
|
||||||
|
|
||||||
|
class VariableAnnotator:
|
||||||
|
def __init__(self,
|
||||||
|
objectspace,
|
||||||
|
):
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.convert_variable()
|
||||||
|
self.convert_separators()
|
||||||
|
|
||||||
|
def convert_variable(self):
|
||||||
|
def _convert_variable(variable,
|
||||||
|
variable_type,
|
||||||
|
):
|
||||||
|
if not hasattr(variable, 'type'):
|
||||||
|
variable.type = 'string'
|
||||||
|
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
||||||
|
variable.description = variable.name
|
||||||
|
if hasattr(variable, 'value'):
|
||||||
|
for value in variable.value:
|
||||||
|
if not hasattr(value, 'type'):
|
||||||
|
value.type = variable.type
|
||||||
|
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)
|
||||||
|
if variable_type == 'follower':
|
||||||
|
if variable.multi is True:
|
||||||
|
variable.multi = 'submulti'
|
||||||
|
else:
|
||||||
|
variable.multi = True
|
||||||
|
|
||||||
|
def _convert_valid_enum(namespace,
|
||||||
|
variable,
|
||||||
|
path,
|
||||||
|
):
|
||||||
|
if variable.type in FORCE_CHOICE:
|
||||||
|
check = self.objectspace.check(variable.xmlfiles)
|
||||||
|
check.name = 'valid_enum'
|
||||||
|
check.target = path
|
||||||
|
check.namespace = namespace
|
||||||
|
check.param = []
|
||||||
|
for value in FORCE_CHOICE[variable.type]:
|
||||||
|
param = self.objectspace.param(variable.xmlfiles)
|
||||||
|
param.text = value
|
||||||
|
check.param.append(param)
|
||||||
|
if not hasattr(self.objectspace.space, '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}'), 36)
|
||||||
|
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
for families in self.objectspace.space.variables.values():
|
||||||
|
namespace = families.name
|
||||||
|
if hasattr(families, 'family'):
|
||||||
|
families.doc = families.name
|
||||||
|
for family in families.family.values():
|
||||||
|
family.doc = family.name
|
||||||
|
family.name = normalize_family(family.name)
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.leadership):
|
||||||
|
for idx, follower in enumerate(variable.variable):
|
||||||
|
if idx == 0:
|
||||||
|
variable_type = 'master'
|
||||||
|
else:
|
||||||
|
variable_type = 'follower'
|
||||||
|
path = '{}.{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name, follower.name)
|
||||||
|
_convert_variable(follower,
|
||||||
|
variable_type,
|
||||||
|
)
|
||||||
|
_convert_valid_enum(namespace,
|
||||||
|
follower,
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
_valid_type(follower)
|
||||||
|
else:
|
||||||
|
path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name)
|
||||||
|
_convert_variable(variable,
|
||||||
|
'variable',
|
||||||
|
)
|
||||||
|
_convert_valid_enum(namespace,
|
||||||
|
variable,
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
_valid_type(variable)
|
||||||
|
|
||||||
|
def convert_separators(self): # pylint: disable=C0111,R0201
|
||||||
|
if not hasattr(self.objectspace.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.objectspace.space.variables.values():
|
||||||
|
if not hasattr(family, 'separators'):
|
||||||
|
continue
|
||||||
|
if hasattr(family.separators, 'separator'):
|
||||||
|
for idx, separator in enumerate(family.separators.separator):
|
||||||
|
option = self.objectspace.paths.get_variable_obj(separator.name)
|
||||||
|
if hasattr(option, 'separator'):
|
||||||
|
subpath = self.objectspace.paths.get_variable_path(separator.name,
|
||||||
|
separator.namespace,
|
||||||
|
)
|
||||||
|
xmlfiles = self.objectspace.display_xmlfiles(separator.xmlfiles)
|
||||||
|
raise DictConsistencyError(_(f'{subpath} already has a separator in {xmlfiles}'), 35)
|
||||||
|
option.separator = separator.text
|
||||||
|
del family.separators
|
||||||
|
|
|
@ -29,6 +29,9 @@ class DictConsistencyError(Exception):
|
||||||
"""It's not only that the Creole XML is valid against the Creole DTD
|
"""It's not only that the Creole XML is valid against the Creole DTD
|
||||||
it's that it is not consistent.
|
it's that it is not consistent.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, msg, errno):
|
||||||
|
super().__init__(msg)
|
||||||
|
self.errno = errno
|
||||||
|
|
||||||
|
|
||||||
class LoaderError(Exception):
|
class LoaderError(Exception):
|
||||||
|
|
|
@ -1,45 +1,22 @@
|
||||||
"""
|
|
||||||
Takes a bunch of Rougail XML dispatched in differents folders
|
|
||||||
as an input and outputs a Tiramisu's file
|
|
||||||
|
|
||||||
Sample usage::
|
from typing import List
|
||||||
|
|
||||||
eolobj.space_visitor(func)
|
|
||||||
xml = eolobj.save()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
>>> from rougail.objspace import RougailObjSpace
|
|
||||||
>>> eolobj = RougailObjSpace('/usr/share/rougail/rougail.dtd')
|
|
||||||
>>> eolobj.create_or_populate_from_xml('rougail', ['/usr/share/rougail/dicos'])
|
|
||||||
>>> eolobj.space_visitor('/usr/share/rougail/funcs.py')
|
|
||||||
>>> tiramisu = eolobj.save()
|
|
||||||
|
|
||||||
The RougailObjSpace
|
|
||||||
|
|
||||||
- loads the XML into an internal RougailObjSpace representation
|
|
||||||
- visits/annotates the objects
|
|
||||||
- dumps the object space as Tiramisu string
|
|
||||||
|
|
||||||
The visit/annotation stage is a complex step that corresponds to the Rougail
|
|
||||||
procedures.
|
|
||||||
"""
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .xmlreflector import XMLReflector
|
from .xmlreflector import XMLReflector
|
||||||
from .annotator import SpaceAnnotator
|
|
||||||
from .tiramisureflector import TiramisuReflector
|
|
||||||
from .utils import normalize_family
|
from .utils import normalize_family
|
||||||
from .error import OperationError, SpaceObjShallNotBeUpdated, DictConsistencyError
|
from .error import SpaceObjShallNotBeUpdated, DictConsistencyError
|
||||||
from .path import Path
|
from .path import Path
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
# RougailObjSpace's elements like 'family' or 'follower', that shall be forced to the Redefinable type
|
# RougailObjSpace's elements that shall be forced to the Redefinable type
|
||||||
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
|
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
|
||||||
# RougailObjSpace's elements that shall be forced to the UnRedefinable type
|
# RougailObjSpace's elements that shall be forced to the UnRedefinable type
|
||||||
FORCE_UNREDEFINABLES = ('value',)
|
FORCE_UNREDEFINABLES = ('value',)
|
||||||
# RougailObjSpace's elements that shall be set to the UnRedefinable type
|
# RougailObjSpace's elements that shall not be modify
|
||||||
UNREDEFINABLE = ('multi', 'type')
|
UNREDEFINABLE = ('multi', 'type')
|
||||||
|
# RougailObjSpace's elements that did not created automaticly
|
||||||
|
FORCE_ELEMENTS = ('choice', 'property_', 'leadership')
|
||||||
|
# XML text are convert has name
|
||||||
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value', 'target')
|
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value', 'target')
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,47 +41,38 @@ class UnRedefinable(RootRougailObject):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RougailObjSpace:
|
class ObjSpace:
|
||||||
"""DOM XML reflexion free internal representation of a Rougail Dictionary
|
|
||||||
"""
|
|
||||||
choice = type('Choice', (RootRougailObject,), dict())
|
|
||||||
property_ = type('Property', (RootRougailObject,), dict())
|
|
||||||
# Rougail ObjectSpace's Leadership variable class type
|
|
||||||
Leadership = type('Leadership', (RootRougailObject,), dict())
|
|
||||||
"""
|
|
||||||
This Atom type stands for singleton, that is
|
|
||||||
an Object Space's atom object is present only once in the
|
|
||||||
object space's tree
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, dtdfilename): # pylint: disable=R0912
|
|
||||||
self.index = 0
|
|
||||||
class ObjSpace: # pylint: disable=R0903
|
|
||||||
"""
|
"""
|
||||||
Base object space
|
Base object space
|
||||||
"""
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RougailObjSpace:
|
||||||
|
"""Rougail ObjectSpace is an object's reflexion of the XML elements
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
xmlreflector: XMLReflector,
|
||||||
|
) -> None:
|
||||||
|
self.index = 0
|
||||||
|
self.xmlreflector = xmlreflector
|
||||||
self.space = ObjSpace()
|
self.space = ObjSpace()
|
||||||
self.paths = Path()
|
self.paths = Path()
|
||||||
self.xmlreflector = XMLReflector()
|
|
||||||
self.xmlreflector.parse_dtd(dtdfilename)
|
|
||||||
self.redefine_variables = None
|
self.redefine_variables = None
|
||||||
self.check_removed = None
|
|
||||||
self.condition_removed = None
|
|
||||||
|
|
||||||
# ['variable', 'separator', 'family']
|
|
||||||
self.forced_text_elts = set()
|
self.forced_text_elts = set()
|
||||||
self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME)
|
self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME)
|
||||||
self.list_conditions = {}
|
self.list_conditions = {}
|
||||||
self.booleans_attributs = []
|
self.booleans_attributs = []
|
||||||
|
|
||||||
self.make_object_space_class()
|
self.make_object_space_classes()
|
||||||
|
|
||||||
def make_object_space_class(self):
|
def make_object_space_classes(self):
|
||||||
"""Create Rougail ObjectSpace class types, it enables us to create objects like:
|
"""Create Rougail ObjectSpace class types from DDT file
|
||||||
|
It enables us to create objects like:
|
||||||
File(), Variable(), Ip(), Family(), Constraints()... and so on.
|
File(), Variable(), Ip(), Family(), Constraints()... and so on.
|
||||||
|
"""
|
||||||
Rougail ObjectSpace is an object's reflexion of the XML elements"""
|
|
||||||
|
|
||||||
for dtd_elt in self.xmlreflector.dtd.iterelements():
|
for dtd_elt in self.xmlreflector.dtd.iterelements():
|
||||||
attrs = {}
|
attrs = {}
|
||||||
|
@ -138,23 +106,19 @@ class RougailObjSpace:
|
||||||
|
|
||||||
# create ObjectSpace object
|
# create ObjectSpace object
|
||||||
setattr(self, dtd_elt.name, type(dtd_elt.name.capitalize(), (clstype,), attrs))
|
setattr(self, dtd_elt.name, type(dtd_elt.name.capitalize(), (clstype,), attrs))
|
||||||
|
for elt in FORCE_ELEMENTS:
|
||||||
|
name = elt.capitalize()
|
||||||
|
if name.endswith('_'):
|
||||||
|
name = name[:-1]
|
||||||
|
setattr(self, elt, type(name, (RootRougailObject,), dict()))
|
||||||
|
self.Leadership = self.leadership
|
||||||
|
|
||||||
def create_or_populate_from_xml(self,
|
def display_xmlfiles(self,
|
||||||
namespace,
|
xmlfiles: list,
|
||||||
xmlfolders,
|
) -> str:
|
||||||
):
|
if len(xmlfiles) == 1:
|
||||||
"""Parses a bunch of XML files
|
return '"' + xmlfiles[0] + '"'
|
||||||
populates the RougailObjSpace
|
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
|
||||||
"""
|
|
||||||
for xmlfile, document in self.xmlreflector.load_xml_from_folders(xmlfolders):
|
|
||||||
self.redefine_variables = []
|
|
||||||
self.check_removed = []
|
|
||||||
self.condition_removed = []
|
|
||||||
self.xml_parse_document(xmlfile,
|
|
||||||
document,
|
|
||||||
self.space,
|
|
||||||
namespace,
|
|
||||||
)
|
|
||||||
|
|
||||||
def xml_parse_document(self,
|
def xml_parse_document(self,
|
||||||
xmlfile,
|
xmlfile,
|
||||||
|
@ -162,8 +126,7 @@ class RougailObjSpace:
|
||||||
space,
|
space,
|
||||||
namespace,
|
namespace,
|
||||||
):
|
):
|
||||||
"""Parses a Rougail XML file
|
"""Parses a Rougail XML file and populates the RougailObjSpace
|
||||||
populates the RougailObjSpace
|
|
||||||
"""
|
"""
|
||||||
# var to check unique family name in a XML file
|
# var to check unique family name in a XML file
|
||||||
family_names = []
|
family_names = []
|
||||||
|
@ -174,12 +137,13 @@ class RougailObjSpace:
|
||||||
continue
|
continue
|
||||||
if child.tag == 'family':
|
if child.tag == 'family':
|
||||||
if child.attrib['name'] in family_names:
|
if child.attrib['name'] in family_names:
|
||||||
raise DictConsistencyError(_(f'Family "{child.attrib["name"]}" is set several times in "{xmlfile}"'))
|
raise DictConsistencyError(_(f'Family "{child.attrib["name"]}" is set several times in "{xmlfile}"'), 44)
|
||||||
family_names.append(child.attrib['name'])
|
family_names.append(child.attrib['name'])
|
||||||
if child.tag == 'variables':
|
if child.tag == 'variables':
|
||||||
# variables has no name, so force namespace name
|
# variables has no name, so force namespace name
|
||||||
child.attrib['name'] = namespace
|
child.attrib['name'] = namespace
|
||||||
if child.tag == 'value' and child.text is None:
|
if child.tag == 'value' and child.text is None:
|
||||||
|
# remove empty value
|
||||||
continue
|
continue
|
||||||
# variable objects creation
|
# variable objects creation
|
||||||
try:
|
try:
|
||||||
|
@ -226,7 +190,7 @@ class RougailObjSpace:
|
||||||
namespace,
|
namespace,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
instanciates or creates Rougail Object Subspace objects
|
retrieves or creates Rougail Object Subspace objects
|
||||||
"""
|
"""
|
||||||
obj = getattr(self, child.tag)
|
obj = getattr(self, child.tag)
|
||||||
if Redefinable in obj.__mro__:
|
if Redefinable in obj.__mro__:
|
||||||
|
@ -276,7 +240,7 @@ class RougailObjSpace:
|
||||||
if exists is False:
|
if exists is False:
|
||||||
raise SpaceObjShallNotBeUpdated()
|
raise SpaceObjShallNotBeUpdated()
|
||||||
xmlfiles = self.display_xmlfiles(existed_var.xmlfiles)
|
xmlfiles = self.display_xmlfiles(existed_var.xmlfiles)
|
||||||
raise DictConsistencyError(_(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", already defined in {xmlfiles}'))
|
raise DictConsistencyError(_(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", already defined in {xmlfiles}'), 45)
|
||||||
# if this object must only be modified if object already exists
|
# if this object must only be modified if object already exists
|
||||||
exists = self.convert_boolean(subspace.get('exists', False))
|
exists = self.convert_boolean(subspace.get('exists', False))
|
||||||
if exists is True:
|
if exists is True:
|
||||||
|
@ -286,14 +250,7 @@ class RougailObjSpace:
|
||||||
if child.tag not in vars(space):
|
if child.tag not in vars(space):
|
||||||
setattr(space, child.tag, {})
|
setattr(space, child.tag, {})
|
||||||
return getattr(self, child.tag)(xmlfile)
|
return getattr(self, child.tag)(xmlfile)
|
||||||
raise DictConsistencyError(_(f'Redefined object in "{xmlfile}": "{name}" does not exist yet'))
|
raise DictConsistencyError(_(f'Redefined object in "{xmlfile}": "{name}" does not exist yet'), 46)
|
||||||
|
|
||||||
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,
|
def get_existed_obj(self,
|
||||||
name: str,
|
name: str,
|
||||||
|
@ -301,32 +258,30 @@ class RougailObjSpace:
|
||||||
child,
|
child,
|
||||||
namespace: str,
|
namespace: str,
|
||||||
):
|
):
|
||||||
if isinstance(space, self.family): # pylint: disable=E1101
|
if isinstance(space, self.family):
|
||||||
if namespace != Config['variable_namespace']:
|
if namespace != Config['variable_namespace']:
|
||||||
name = space.path + '.' + name
|
name = space.path + '.' + name
|
||||||
if self.paths.path_is_defined(name):
|
if not self.paths.path_is_defined(name):
|
||||||
|
return
|
||||||
old_family_name = self.paths.get_variable_family_name(name)
|
old_family_name = self.paths.get_variable_family_name(name)
|
||||||
if namespace != Config['variable_namespace']:
|
if namespace != Config['variable_namespace']:
|
||||||
old_family_name = namespace + '.' + old_family_name
|
old_family_name = namespace + '.' + old_family_name
|
||||||
if space.path != old_family_name:
|
if space.path != old_family_name:
|
||||||
xmlfiles = self.display_xmlfiles(space.xmlfiles)
|
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}'))
|
raise DictConsistencyError(_(f'Variable was previously create in family "{old_family_name}", now it is in "{space.path}" in {xmlfiles}'), 47)
|
||||||
return self.paths.get_variable_obj(name)
|
return self.paths.get_variable_obj(name)
|
||||||
return
|
|
||||||
children = getattr(space, child.tag, {})
|
children = getattr(space, child.tag, {})
|
||||||
if name in children:
|
if name in children:
|
||||||
return children[name]
|
return children[name]
|
||||||
|
|
||||||
def convert_boolean(self, value): # pylint: disable=R0201
|
def convert_boolean(self, value):
|
||||||
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`
|
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`
|
||||||
"""
|
"""
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return value
|
return value
|
||||||
if value == 'True':
|
if value == 'True':
|
||||||
return True
|
return True
|
||||||
elif value == 'False':
|
|
||||||
return False
|
return False
|
||||||
raise TypeError(_('{} is not True or False').format(value)) # pragma: no cover
|
|
||||||
|
|
||||||
def set_text(self,
|
def set_text(self,
|
||||||
child,
|
child,
|
||||||
|
@ -356,7 +311,7 @@ class RougailObjSpace:
|
||||||
for attr, val in child.attrib.items():
|
for attr, val in child.attrib.items():
|
||||||
if redefine and attr in UNREDEFINABLE:
|
if redefine and attr in UNREDEFINABLE:
|
||||||
xmlfiles = self.display_xmlfiles(variableobj.xmlfiles[:-1])
|
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}'))
|
raise DictConsistencyError(_(f'cannot redefine attribute "{attr}" for variable "{child.attrib["name"]}" in "{xmlfile}", already defined in {xmlfiles}'), 48)
|
||||||
if attr in self.booleans_attributs:
|
if attr in self.booleans_attributs:
|
||||||
val = self.convert_boolean(val)
|
val = self.convert_boolean(val)
|
||||||
if attr == 'name' and getattr(variableobj, 'name', None):
|
if attr == 'name' and getattr(variableobj, 'name', None):
|
||||||
|
@ -380,37 +335,37 @@ class RougailObjSpace:
|
||||||
if child.tag == 'fill' and child.attrib['target'] in self.redefine_variables:
|
if child.tag == 'fill' and child.attrib['target'] in self.redefine_variables:
|
||||||
self.remove_fill(child.attrib['target'])
|
self.remove_fill(child.attrib['target'])
|
||||||
|
|
||||||
def remove_check(self, name): # pylint: disable=C0111
|
def remove_check(self, name):
|
||||||
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'):
|
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'):
|
||||||
remove_checks = []
|
remove_checks = []
|
||||||
for idx, check in enumerate(self.space.constraints.check): # pylint: disable=E1101
|
for idx, check in enumerate(self.space.constraints.check):
|
||||||
if hasattr(check, 'target') and check.target == name:
|
if hasattr(check, 'target') and check.target == name:
|
||||||
remove_checks.append(idx)
|
remove_checks.append(idx)
|
||||||
|
|
||||||
remove_checks = list(set(remove_checks))
|
remove_checks = list(set(remove_checks))
|
||||||
remove_checks.sort(reverse=True)
|
remove_checks.sort(reverse=True)
|
||||||
for idx in remove_checks:
|
for idx in remove_checks:
|
||||||
self.space.constraints.check.pop(idx) # pylint: disable=E1101
|
self.space.constraints.check.pop(idx)
|
||||||
|
|
||||||
def remove_condition(self, name): # pylint: disable=C0111
|
def remove_condition(self, name):
|
||||||
remove_conditions = []
|
remove_conditions = []
|
||||||
for idx, condition in enumerate(self.space.constraints.condition): # pylint: disable=E1101
|
for idx, condition in enumerate(self.space.constraints.condition):
|
||||||
if condition.source == name:
|
if condition.source == name:
|
||||||
remove_conditions.append(idx)
|
remove_conditions.append(idx)
|
||||||
for idx in remove_conditions:
|
for idx in remove_conditions:
|
||||||
del self.space.constraints.condition[idx]
|
del self.space.constraints.condition[idx]
|
||||||
|
|
||||||
def remove_fill(self, name): # pylint: disable=C0111
|
def remove_fill(self, name):
|
||||||
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'fill'):
|
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'fill'):
|
||||||
remove_fills= []
|
remove_fills= []
|
||||||
for idx, fill in enumerate(self.space.constraints.fill): # pylint: disable=E1101
|
for idx, fill in enumerate(self.space.constraints.fill):
|
||||||
if hasattr(fill, 'target') and fill.target == name:
|
if hasattr(fill, 'target') and fill.target == name:
|
||||||
remove_fills.append(idx)
|
remove_fills.append(idx)
|
||||||
|
|
||||||
remove_fills = list(set(remove_fills))
|
remove_fills = list(set(remove_fills))
|
||||||
remove_fills.sort(reverse=True)
|
remove_fills.sort(reverse=True)
|
||||||
for idx in remove_fills:
|
for idx in remove_fills:
|
||||||
self.space.constraints.fill.pop(idx) # pylint: disable=E1101
|
self.space.constraints.fill.pop(idx)
|
||||||
|
|
||||||
def set_path(self,
|
def set_path(self,
|
||||||
space,
|
space,
|
||||||
|
@ -418,7 +373,7 @@ class RougailObjSpace:
|
||||||
namespace,
|
namespace,
|
||||||
document,
|
document,
|
||||||
variableobj,
|
variableobj,
|
||||||
): # pylint: disable=R0913
|
):
|
||||||
"""Fill self.paths attributes
|
"""Fill self.paths attributes
|
||||||
"""
|
"""
|
||||||
if child.tag == 'variable':
|
if child.tag == 'variable':
|
||||||
|
@ -452,7 +407,7 @@ class RougailObjSpace:
|
||||||
space,
|
space,
|
||||||
child,
|
child,
|
||||||
namespace,
|
namespace,
|
||||||
): # pylint: disable=R0201
|
):
|
||||||
if not hasattr(variableobj, 'index'):
|
if not hasattr(variableobj, 'index'):
|
||||||
variableobj.index = self.index
|
variableobj.index = self.index
|
||||||
variableobj.namespace = namespace
|
variableobj.namespace = namespace
|
||||||
|
@ -465,14 +420,3 @@ class RougailObjSpace:
|
||||||
getattr(space, child.tag).append(variableobj)
|
getattr(space, child.tag).append(variableobj)
|
||||||
else:
|
else:
|
||||||
setattr(space, child.tag, variableobj)
|
setattr(space, child.tag, variableobj)
|
||||||
|
|
||||||
def space_visitor(self, eosfunc_file): # pylint: disable=C0111
|
|
||||||
self.funcs_path = eosfunc_file
|
|
||||||
SpaceAnnotator(self, eosfunc_file)
|
|
||||||
|
|
||||||
def save(self,
|
|
||||||
):
|
|
||||||
tiramisu_objects = TiramisuReflector(self.space,
|
|
||||||
self.funcs_path,
|
|
||||||
)
|
|
||||||
return tiramisu_objects.get_text() + '\n'
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Path:
|
||||||
else:
|
else:
|
||||||
full_name = name
|
full_name = name
|
||||||
if full_name in self.families and self.families[full_name]['variableobj'] != variableobj:
|
if full_name in self.families and self.families[full_name]['variableobj'] != variableobj:
|
||||||
raise DictConsistencyError(_(f'Duplicate family name {name}'))
|
raise DictConsistencyError(_(f'Duplicate family name {name}'), 37)
|
||||||
self.families[full_name] = dict(name=name,
|
self.families[full_name] = dict(name=name,
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
variableobj=variableobj,
|
variableobj=variableobj,
|
||||||
|
@ -45,9 +45,7 @@ class Path:
|
||||||
raise OperationError('current_namespace must not be None')
|
raise OperationError('current_namespace must not be None')
|
||||||
dico = self.families[name]
|
dico = self.families[name]
|
||||||
if dico['namespace'] != Config['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 '
|
raise DictConsistencyError(_(f'A family located in the "{dico["namespace"]}" namespace shall not be used in the "{current_namespace}" namespace'), 38)
|
||||||
'shall not be used in the {} namespace').format(
|
|
||||||
dico['namespace'], current_namespace))
|
|
||||||
return dico['name']
|
return dico['name']
|
||||||
|
|
||||||
def get_family_obj(self,
|
def get_family_obj(self,
|
||||||
|
@ -56,7 +54,7 @@ class Path:
|
||||||
if '.' not in name and name in self.full_paths_families:
|
if '.' not in name and name in self.full_paths_families:
|
||||||
name = self.full_paths_families[name]
|
name = self.full_paths_families[name]
|
||||||
if name not in self.families:
|
if name not in self.families:
|
||||||
raise DictConsistencyError(_('unknown family {}').format(name))
|
raise DictConsistencyError(_('unknown family {}').format(name), 39)
|
||||||
dico = self.families[name]
|
dico = self.families[name]
|
||||||
return dico['variableobj']
|
return dico['variableobj']
|
||||||
|
|
||||||
|
@ -92,7 +90,7 @@ class Path:
|
||||||
dico = self._get_variable(name)
|
dico = self._get_variable(name)
|
||||||
if dico['leader'] != None:
|
if dico['leader'] != None:
|
||||||
raise DictConsistencyError(_('Already defined leader {} for variable'
|
raise DictConsistencyError(_('Already defined leader {} for variable'
|
||||||
' {}'.format(dico['leader'], name)))
|
' {}'.format(dico['leader'], name)), 40)
|
||||||
dico['leader'] = leader_name
|
dico['leader'] = leader_name
|
||||||
|
|
||||||
def get_leader(self, name): # pylint: disable=C0111
|
def get_leader(self, name): # pylint: disable=C0111
|
||||||
|
@ -156,9 +154,7 @@ class Path:
|
||||||
dico = self._get_variable(name)
|
dico = self._get_variable(name)
|
||||||
if not allow_source:
|
if not allow_source:
|
||||||
if dico['namespace'] not in [Config['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 '
|
raise DictConsistencyError(_(f'A variable located in the "{dico["namespace"]}" namespace shall not be used in the "{current_namespace}" namespace'), 41)
|
||||||
'shall not be used in the {} namespace').format(
|
|
||||||
dico['namespace'], current_namespace))
|
|
||||||
if '.' in dico['name']:
|
if '.' in dico['name']:
|
||||||
value = dico['name']
|
value = dico['name']
|
||||||
else:
|
else:
|
||||||
|
@ -200,7 +196,7 @@ class Path:
|
||||||
if not with_suffix:
|
if not with_suffix:
|
||||||
raise Exception('This option is dynamic, should use "with_suffix" attribute')
|
raise Exception('This option is dynamic, should use "with_suffix" attribute')
|
||||||
return variable, name[len(var_name):]
|
return variable, name[len(var_name):]
|
||||||
raise DictConsistencyError(_('unknown option {}').format(name))
|
raise DictConsistencyError(_('unknown option {}').format(name), 42)
|
||||||
if with_suffix:
|
if with_suffix:
|
||||||
return self.variables[name], None
|
return self.variables[name], None
|
||||||
return self.variables[name]
|
return self.variables[name]
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
"""
|
||||||
|
Takes a bunch of Rougail XML dispatched in differents folders
|
||||||
|
as an input and outputs a Tiramisu's file
|
||||||
|
|
||||||
|
Sample usage::
|
||||||
|
|
||||||
|
>>> from rougail import Rougail
|
||||||
|
>>> rougail = Rougail('/usr/share/rougail/rougail.dtd')
|
||||||
|
>>> rougail.create_or_populate_from_xml('rougail', ['/usr/share/rougail/dicos'])
|
||||||
|
>>> rougail.space_visitor('/usr/share/rougail/funcs.py')
|
||||||
|
>>> tiramisu = rougail.save()
|
||||||
|
|
||||||
|
The Rougail
|
||||||
|
|
||||||
|
- loads the XML into an internal RougailObjSpace representation
|
||||||
|
- visits/annotates the objects
|
||||||
|
- dumps the object space as Tiramisu string
|
||||||
|
|
||||||
|
The visit/annotation stage is a complex step that corresponds to the Rougail
|
||||||
|
procedures.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from .objspace import RougailObjSpace
|
||||||
|
from .xmlreflector import XMLReflector
|
||||||
|
from .tiramisureflector import TiramisuReflector
|
||||||
|
from .annotator import SpaceAnnotator
|
||||||
|
|
||||||
|
|
||||||
|
class Rougail:
|
||||||
|
def __init__(self,
|
||||||
|
dtdfilename: str,
|
||||||
|
) -> None:
|
||||||
|
self.xmlreflector = XMLReflector()
|
||||||
|
self.xmlreflector.parse_dtd(dtdfilename)
|
||||||
|
self.rougailobjspace = RougailObjSpace(self.xmlreflector)
|
||||||
|
|
||||||
|
def create_or_populate_from_xml(self,
|
||||||
|
namespace: str,
|
||||||
|
xmlfolders: List[str],
|
||||||
|
) -> List[str]:
|
||||||
|
"""Parses a bunch of XML files and populates the RougailObjSpace
|
||||||
|
"""
|
||||||
|
for xmlfile in self.xmlreflector.load_xml_from_folders(xmlfolders):
|
||||||
|
document = self.xmlreflector.parse_xmlfile(xmlfile)
|
||||||
|
self.rougailobjspace.redefine_variables = []
|
||||||
|
self.rougailobjspace.xml_parse_document(xmlfile,
|
||||||
|
document,
|
||||||
|
self.rougailobjspace.space,
|
||||||
|
namespace,
|
||||||
|
)
|
||||||
|
|
||||||
|
def space_visitor(self,
|
||||||
|
eosfunc_file: str,
|
||||||
|
) -> None:
|
||||||
|
self.funcs_path = eosfunc_file
|
||||||
|
SpaceAnnotator(self.rougailobjspace, eosfunc_file)
|
||||||
|
|
||||||
|
def save(self) -> str:
|
||||||
|
tiramisu_objects = TiramisuReflector(self.rougailobjspace.space,
|
||||||
|
self.funcs_path,
|
||||||
|
)
|
||||||
|
return tiramisu_objects.get_text() + '\n'
|
|
@ -5,11 +5,6 @@ from unicodedata import normalize, combining
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
|
|
||||||
|
|
||||||
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:
|
def normalize_family(family_name: str) -> str:
|
||||||
"""replace space, accent, uppercase, ... by valid character
|
"""replace space, accent, uppercase, ... by valid character
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from os.path import join, isfile, basename, isdir
|
from typing import List
|
||||||
|
from os.path import join, isfile
|
||||||
from os import listdir
|
from os import listdir
|
||||||
#from io import BytesIO
|
|
||||||
|
|
||||||
from lxml.etree import DTD, parse, tostring # , XMLParser
|
from lxml.etree import DTD, parse, tostring, XMLSyntaxError
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .error import DictConsistencyError
|
from .error import DictConsistencyError
|
||||||
|
@ -34,57 +34,23 @@ class XMLReflector(object):
|
||||||
|
|
||||||
:returns: the root element tree object
|
:returns: the root element tree object
|
||||||
"""
|
"""
|
||||||
# document = parse(BytesIO(xmlfile), XMLParser(remove_blank_text=True))
|
try:
|
||||||
document = parse(xmlfile)
|
document = parse(xmlfile)
|
||||||
|
except XMLSyntaxError as err:
|
||||||
|
raise DictConsistencyError(_(f'{xmlfile} is not an XML file: {err}'), 52)
|
||||||
if not self.dtd.validate(document):
|
if not self.dtd.validate(document):
|
||||||
raise DictConsistencyError(_(f'"{xmlfile}" not a valid xml file: {self.dtd.error_log.filter_from_errors()[0]}'))
|
raise DictConsistencyError(_(f'"{xmlfile}" not a valid XML file: {self.dtd.error_log.filter_from_errors()[0]}'), 43)
|
||||||
return document.getroot()
|
return document.getroot()
|
||||||
|
|
||||||
def load_xml_from_folders(self, xmlfolders):
|
def load_xml_from_folders(self,
|
||||||
|
xmlfolders: List[str],
|
||||||
|
):
|
||||||
"""Loads all the XML files located in the xmlfolders' list
|
"""Loads all the XML files located in the xmlfolders' list
|
||||||
|
|
||||||
:param xmlfolders: list of full folder's name
|
:param xmlfolders: list of full folder's name
|
||||||
"""
|
"""
|
||||||
documents = []
|
|
||||||
if not isinstance(xmlfolders, list):
|
|
||||||
xmlfolders = [xmlfolders]
|
|
||||||
for xmlfolder in xmlfolders:
|
for xmlfolder in xmlfolders:
|
||||||
if isinstance(xmlfolder, list) or isinstance(xmlfolder, tuple):
|
filenames = [join(xmlfolder, filename) for filename in listdir(xmlfolder) if filename.endswith('.xml')]
|
||||||
# directory group : collect files from each
|
|
||||||
# directory and sort them before loading
|
|
||||||
group_files = []
|
|
||||||
for idx, subdir in enumerate(xmlfolder):
|
|
||||||
if isdir(subdir):
|
|
||||||
for filename in listdir(subdir):
|
|
||||||
group_files.append((filename, idx, subdir))
|
|
||||||
else:
|
|
||||||
group_files.append(basename(subdir), idx, dirname(subdir))
|
|
||||||
def sort_group(file1, file2):
|
|
||||||
if file1[0] == file2[0]:
|
|
||||||
# sort by initial xmlfolder order if same name
|
|
||||||
return file1[1].__cmp__(file2[1])
|
|
||||||
# sort by filename
|
|
||||||
elif file1[0] > file2[0]:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return -1
|
|
||||||
group_files.sort(sort_group)
|
|
||||||
filenames = [join(f[2], f[0]) for f in group_files]
|
|
||||||
elif isdir(xmlfolder):
|
|
||||||
filenames = []
|
|
||||||
for filename in listdir(xmlfolder):
|
|
||||||
filenames.append(join(xmlfolder, filename))
|
|
||||||
filenames.sort()
|
filenames.sort()
|
||||||
else:
|
|
||||||
filenames = [xmlfolder]
|
|
||||||
for xmlfile in filenames:
|
for xmlfile in filenames:
|
||||||
if xmlfile.endswith('.xml'):
|
yield xmlfile
|
||||||
#xmlfile_path = join(xmlfolder, xmlfile)
|
|
||||||
documents.append((xmlfile, self.parse_xmlfile(xmlfile)))
|
|
||||||
return documents
|
|
||||||
|
|
||||||
def save_xmlfile(self, xmlfilename, xml): # pylint: disable=R0201
|
|
||||||
"""Write a bunch of XML on the disk
|
|
||||||
"""
|
|
||||||
with open(xmlfilename, 'w') as xmlfh:
|
|
||||||
xmlfh.write(tostring(xml, pretty_print=True, encoding="UTF-8", xml_declaration=True).decode('utf8'))
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.autosavevar": "oui"}
|
|
|
@ -1,15 +0,0 @@
|
||||||
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({'basic', 'force_store_value', Calculation(calc_value, Params(ParamValue('hidden'), kwargs={'condition': ParamOption(option_3, todict=True), 'expected': ParamValue('oui')})), Calculation(calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_3, todict=True), 'expected': ParamValue('oui')})), Calculation(calc_value, Params(ParamValue('force_default_on_freeze'), kwargs={'condition': ParamOption(option_3, todict=True), 'expected': ParamValue('oui')}))}), name='autosavevar', doc='autosave variable', multi=False, default=Calculation(func.calc_val, Params((ParamValue("oui")), kwargs={})))
|
|
||||||
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])
|
|
|
@ -1,24 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="redefine help" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Description" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help>
|
|
||||||
<variable name="mode_conteneur_actif">redefine help</variable>
|
|
||||||
</help>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<rougail>
|
||||||
|
<variables>
|
||||||
|
<family name="general">
|
||||||
|
<variable name="mode_conteneur_actif" redefine="True" exists="True">
|
||||||
|
<value>oui</value>
|
||||||
|
</variable>
|
||||||
|
<variable name="mode_conteneur_actif1" redefine="True" exists="True">
|
||||||
|
<value>oui</value>
|
||||||
|
</variable>
|
||||||
|
</family>
|
||||||
|
</variables>
|
||||||
|
</rougail>
|
||||||
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
|
-->
|
|
@ -0,0 +1 @@
|
||||||
|
{"rougail.general.mode_conteneur_actif": "oui"}
|
|
@ -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='Description', multi=False, default='oui', values=('oui', 'non'))
|
||||||
|
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
|
||||||
|
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
|
||||||
|
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" redefine="True" multi="True"/>
|
<variable name="mode_conteneur_actif" redefine="True" multi="True"/>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" redefine="True" type="number"/>
|
<variable name="mode_conteneur_actif" redefine="True" type="number"/>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="général">
|
<family name="général">
|
||||||
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="général">
|
<family name="général">
|
||||||
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="général">
|
<family name="général">
|
||||||
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="général">
|
<family name="général">
|
||||||
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
<rougail>
|
<rougail>
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change" auto_freeze="True">
|
<variable name="module_instancie" type="oui/non" description="No change">
|
||||||
|
<value>non</value>
|
||||||
|
</variable>
|
||||||
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change" auto_freeze="True" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
</variables>
|
</variables>
|
||||||
<constraints>
|
<constraints>
|
||||||
<fill name="calc_val" target="mode_conteneur_actif">
|
<fill name="calc_val" target="mode_conteneur_actif">
|
||||||
<param>value</param>
|
<param>oui</param>
|
||||||
</fill>
|
</fill>
|
||||||
</constraints>
|
</constraints>
|
||||||
</rougail>
|
</rougail>
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change">
|
<variable name="mode_conteneur_actif" type="string" description="No change">
|
||||||
|
@ -10,7 +7,6 @@
|
||||||
</variable>
|
</variable>
|
||||||
<variable name="int" type="number" description="No change"/>
|
<variable name="int" type="number" description="No change"/>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
|
@ -19,9 +15,6 @@
|
||||||
<param name="maxi">100</param>
|
<param name="maxi">100</param>
|
||||||
</check>
|
</check>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change">
|
<variable name="mode_conteneur_actif" type="string" description="No change">
|
||||||
<value>b</value>
|
<value>b</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
|
@ -18,9 +14,6 @@
|
||||||
</check>
|
</check>
|
||||||
|
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
<target type="variable">replicationType</target>
|
<target type="variable">replicationType</target>
|
||||||
</condition>
|
</condition>
|
||||||
</constraints>
|
</constraints>
|
||||||
<help/>
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="condition" type="oui/non" description="No change">
|
<variable name="condition" type="oui/non" description="No change">
|
||||||
|
@ -18,9 +15,6 @@
|
||||||
<target type="family">notexists</target>
|
<target type="family">notexists</target>
|
||||||
</condition>
|
</condition>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -14,14 +14,7 @@
|
||||||
<value>oui</value>
|
<value>oui</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<rougail>
|
||||||
|
<variables>
|
||||||
|
<family name="général">
|
||||||
|
<variable name="module_instancie" type="oui/non" description="No change">
|
||||||
|
<value>non</value>
|
||||||
|
</variable>
|
||||||
|
<variable name="activer_ejabberd" type="oui/non" description="No change" hidden="True">
|
||||||
|
<value>non</value>
|
||||||
|
</variable>
|
||||||
|
</family>
|
||||||
|
</variables>
|
||||||
|
</rougail>
|
||||||
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
|
-->
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<rougail>
|
||||||
|
<variables>
|
||||||
|
<family name='ejabberd'>
|
||||||
|
<variable name="mode" type="string" auto_freeze="True">
|
||||||
|
<value>pre</value>
|
||||||
|
</variable>
|
||||||
|
</family>
|
||||||
|
</variables>
|
||||||
|
</rougail>
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
@ -28,7 +25,4 @@
|
||||||
<follower>follower2</follower>
|
<follower>follower2</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
|
@ -18,9 +17,6 @@
|
||||||
<param name="valeur">valeur2</param>
|
<param name="valeur">valeur2</param>
|
||||||
</fill>
|
</fill>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change" hidden="True">
|
||||||
|
@ -12,7 +9,6 @@
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
|
@ -23,9 +19,6 @@
|
||||||
<param name="variable">mode_conteneur_actif1</param>
|
<param name="variable">mode_conteneur_actif1</param>
|
||||||
</fill>
|
</fill>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
|
@ -25,9 +24,6 @@
|
||||||
<target type="filelist">afilllist</target>
|
<target type="filelist">afilllist</target>
|
||||||
</condition>
|
</condition>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<rougail>
|
||||||
|
<services>
|
||||||
|
<unknown>
|
||||||
|
</unknown>
|
||||||
|
</services>
|
||||||
|
</rougail>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<rougail>
|
||||||
|
<services/>
|
||||||
|
</services>
|
||||||
|
</rougail>
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
@ -26,7 +23,4 @@
|
||||||
<follower>follower2</follower>
|
<follower>follower2</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="leader" type="string" description="leader" auto_freeze="True"/>
|
<variable name="module_instancie" type="oui/non" description="No change"/>
|
||||||
<variable name="follower1" type="string" description="follower1"/>
|
<variable name="leader" type="string" description="leader" auto_freeze="True" multi="True"/>
|
||||||
<variable name="follower2" type="string" description="follower2"/>
|
<variable name="follower1" type="string" description="follower1" multi="True"/>
|
||||||
|
<variable name="follower2" type="string" description="follower2" multi="True"/>
|
||||||
</family>
|
</family>
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
|
@ -23,7 +21,4 @@
|
||||||
<follower>follower2</follower>
|
<follower>follower2</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<rougail>
|
||||||
|
<variables>
|
||||||
|
<family name="general">
|
||||||
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
<value>non</value>
|
||||||
|
</variable>
|
||||||
|
</family>
|
||||||
|
<family name="general1">
|
||||||
|
<variable name="leader" type="string" description="leader"/>
|
||||||
|
<variable name="follower1" type="string" description="follower1"/>
|
||||||
|
<variable name="follower2" type="string" description="follower2"/>
|
||||||
|
</family>
|
||||||
|
</variables>
|
||||||
|
|
||||||
|
<constraints>
|
||||||
|
<group leader="leader">
|
||||||
|
<follower>follower1</follower>
|
||||||
|
<follower>follower2</follower>
|
||||||
|
</group>
|
||||||
|
</constraints>
|
||||||
|
</rougail>
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="leader" type="string" description="leader" multi="True"/>
|
<variable name="leader" type="string" description="leader" multi="True"/>
|
||||||
|
@ -26,7 +23,4 @@
|
||||||
<follower>follower2</follower>
|
<follower>follower2</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
@ -28,7 +25,4 @@
|
||||||
<follower>follower1</follower>
|
<follower>follower1</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
@ -30,7 +27,4 @@
|
||||||
<follower>follower2</follower>
|
<follower>follower2</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
<variable name="mode_conteneur_actif" type="oui/non" description="No change">
|
||||||
|
@ -30,7 +27,4 @@
|
||||||
<follower>follower4</follower>
|
<follower>follower4</follower>
|
||||||
</group>
|
</group>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
|
@ -11,12 +8,6 @@
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
<separators/>
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
|
@ -11,12 +8,6 @@
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
<separators/>
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
|
||||||
<separators/>
|
|
||||||
</variables>
|
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help>
|
|
||||||
<variable name="mode_conteneur_actif">redefine help</variable>
|
|
||||||
</help>
|
|
||||||
|
|
||||||
</rougail>
|
|
|
@ -1,22 +1,12 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<rougail>
|
<rougail>
|
||||||
|
|
||||||
<services/>
|
|
||||||
|
|
||||||
<variables>
|
<variables>
|
||||||
<family name="general">
|
<family name="general">
|
||||||
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
<variable name="mode_conteneur_actif" type="oui/non" description="Redefine description" hidden="True">
|
||||||
<value>non</value>
|
<value>non</value>
|
||||||
</variable>
|
</variable>
|
||||||
</family>
|
</family>
|
||||||
<separators/>
|
|
||||||
</variables>
|
</variables>
|
||||||
|
|
||||||
<constraints>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
<help/>
|
|
||||||
|
|
||||||
</rougail>
|
</rougail>
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
<!-- vim: ts=4 sw=4 expandtab
|
||||||
-->
|
-->
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue