refactoring

This commit is contained in:
2020-07-29 08:59:40 +02:00
parent d18248544c
commit 7a62ebf294
339 changed files with 1662 additions and 7139 deletions

View File

@ -1,5 +1,5 @@
from .loader import load
#from .loader import load
from .objspace import CreoleObjSpace
from .annotator import modes
__ALL__ = ('load', 'CreoleObjSpace', 'modes')
__ALL__ = ('CreoleObjSpace', 'modes')

View File

@ -63,6 +63,11 @@ CONVERSION = {'number': int}
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'}
class SpaceAnnotator:
"""Transformations applied on a CreoleObjSpace instance
@ -80,222 +85,7 @@ class SpaceAnnotator:
FamilyAnnotator(objectspace,
self.force_not_mandatory,
)
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 gen_family(self,
name,
path,
):
family = self.objectspace.family()
family.name = name
family.doc = name
family.mode = None
self.objectspace.paths.add_family('services',
path,
family,
)
return family
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
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()
for elttype, values in vars(service).items():
if elttype == 'name' or elttype in ERASED_ATTRIBUTES:
setattr(new_service, elttype, values)
continue
eltname = elttype + 's'
path = '.'.join(['services', eltname])
family = self.gen_family(eltname,
path,
)
if isinstance(values, dict):
values = list(values.values())
family.family = self.make_group_from_elts(service_name,
elttype,
values,
f'services.{service_name}.{eltname}',
)
setattr(new_service, elttype, family)
families[service_name] = new_service
self.objectspace.space.services.service = families
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,
)
if hasattr(elt, 'source'):
c_name = elt.source
else:
c_name = elt.name
subpath = '{}.{}'.format(path, c_name)
family = self.gen_family(c_name, subpath)
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()
variable.name = 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()
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))
PropertyAnnotator(objectspace)
class GroupAnnotator:
@ -422,6 +212,225 @@ class GroupAnnotator:
)
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()
for elttype, values in vars(service).items():
if elttype == 'name' or elttype in ERASED_ATTRIBUTES:
setattr(new_service, elttype, values)
continue
eltname = elttype + 's'
path = '.'.join(['services', eltname])
family = self.gen_family(eltname,
path,
)
if isinstance(values, dict):
values = list(values.values())
family.family = self.make_group_from_elts(service_name,
elttype,
values,
f'services.{service_name}.{eltname}',
)
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,
):
family = self.objectspace.family()
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,
)
if hasattr(elt, 'source'):
c_name = elt.source
else:
c_name = elt.name
subpath = '{}.{}'.format(path, c_name)
family = self.gen_family(c_name, subpath)
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()
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()
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,
@ -433,7 +442,9 @@ class VariableAnnotator:
self.convert_separators()
def convert_variable(self):
def _convert_variable(variable):
def _convert_variable(variable,
variable_type,
):
if not hasattr(variable, 'type'):
variable.type = 'string'
if variable.type != 'symlink' and not hasattr(variable, 'description'):
@ -442,6 +453,15 @@ class VariableAnnotator:
for value in variable.value:
if not hasattr(value, 'type'):
value.type = variable.type
value.name = CONVERSION.get(value.type, str)(value.name)
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,
@ -468,20 +488,31 @@ class VariableAnnotator:
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 follower in variable.variable:
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)
_convert_variable(follower,
variable_type,
)
_convert_valid_enum(namespace,
follower,
path,
)
else:
path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name)
_convert_variable(variable)
_convert_variable(variable,
'variable',
)
_convert_valid_enum(namespace,
variable,
path,
@ -894,6 +925,8 @@ class ConstraintAnnotator:
choice.name = CONVERSION.get(type_, str)(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)
@ -1112,3 +1145,53 @@ class FamilyAnnotator:
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)

View File

@ -7,34 +7,12 @@ from lxml.etree import DTD
from .config import dtdfilename, variable_namespace
from .i18n import _
from .error import LoaderError
from .annotator import ERASED_ATTRIBUTES
FUNC_TO_DICT = ['valid_not_equal']
FORCE_INFORMATIONS = ['help', 'test', 'separator']
def convert_boolean(value):
prop = {'True': True,
'False': False,
'None': None}
if value not in prop:
raise Exception('unknown value {} while trying to cast {} to boolean'.format(value, obj))
return prop[value]
def convert_tiramisu_value(value, type_):
"""
convertit les variables dans le bon type si nécessaire
"""
if value is None:
return value
func = CONVERT_OPTION[type_].get('func', None)
if func is None:
return value
if isinstance(value, list):
return [func(val) for val in value]
else:
return func(value)
ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi')
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
@ -42,7 +20,7 @@ CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
'string': dict(opttype="StrOption"),
'password': dict(opttype="PasswordOption"),
'mail': dict(opttype="EmailOption"),
'boolean': dict(opttype="BoolOption", initkwargs={'default': [True]}, func=convert_boolean),
'boolean': dict(opttype="BoolOption", initkwargs={'default': [True]}),
'symlink': dict(opttype="SymLinkOption"),
'filename': dict(opttype="FilenameOption"),
'date': dict(opttype="DateOption"),
@ -70,7 +48,7 @@ class PopulateTiramisuObjects:
xmlroot,
funcs_path,
):
self.storage = ElementStorage(self.parse_dtd())
self.storage = ElementStorage()
self.storage.text = ["from tiramisu import *",
"from rougail.tiramisu import ConvertDynOptionDescription",
"import imp",
@ -80,25 +58,6 @@ class PopulateTiramisuObjects:
# parse object
self.storage.get('.').get()
def parse_dtd(self):
"""Loads the DTD
:raises IOError: if the DTD is not found
:param dtdfilename: the full filename of the DTD
"""
if not isfile(dtdfilename):
raise IOError(_("no such DTD file: {}").format(dtdfilename))
booleans = []
with open(dtdfilename, 'r') as dtdfd:
dtd = DTD(dtdfd)
for elt in dtd.iterelements():
if elt.name == 'variable':
for attr in elt.iterattributes():
if set(attr.itervalues()) == set(['True', 'False']):
booleans.append(attr.name)
return booleans
def make_tiramisu_objects(self,
xmlroot,
):
@ -110,8 +69,9 @@ class PopulateTiramisuObjects:
)
def get_root_family(self):
family = Family(BaseElt({'name': 'baseoption',
'doc': 'baseoption'}),
family = Family(BaseElt('baseoption',
'baseoption',
),
self.storage,
False,
'.',
@ -119,30 +79,49 @@ class PopulateTiramisuObjects:
return family
def reorder_family(self, xmlroot):
xmlelts = []
for xmlelt in xmlroot:
# variable_namespace family has to be loaded before any other family
# because `extra` family could use `variable_namespace` variables.
if xmlelt.attrib['name'] == variable_namespace:
xmlelts.insert(0, xmlelt)
else:
xmlelts.append(xmlelt)
return xmlelts
# variable_namespace family has to be loaded before any other family
# because `extra` family could use `variable_namespace` variables.
if hasattr(xmlroot, 'variables'):
if variable_namespace in xmlroot.variables:
yield xmlroot.variables[variable_namespace]
for xmlelt, value in xmlroot.variables.items():
if xmlelt != variable_namespace:
yield value
if hasattr(xmlroot, 'services'):
yield xmlroot.services
def get_attributes(self, space): # pylint: disable=R0201
for attr in dir(space):
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
yield attr
def get_children(self,
space,
):
for tag in self.get_attributes(space):
children = getattr(space, tag)
if children.__class__.__name__ == 'Family':
children = [children]
if isinstance(children, dict):
children = list(children.values())
if isinstance(children, list):
yield tag, children
def iter_family(self,
child,
family,
subpath,
):
if child.tag in ['family', 'leader']:
function = self.populate_family
elif child.tag == 'variable':
tag = child.__class__.__name__
if tag == 'Variable':
function = self.populate_variable
elif child.tag == 'property':
elif tag == 'Property':
# property already imported with family
return
else:
raise Exception('unknown tag {}'.format(child.tag))
function = self.populate_family
#else:
# raise Exception('unknown tag {}'.format(child.tag))
function(family,
child,
subpath,
@ -156,14 +135,15 @@ class PopulateTiramisuObjects:
path = self.build_path(subpath,
elt,
)
tag = elt.__class__.__name__
family = Family(elt,
self.storage,
elt.tag == 'leader',
tag == 'Leadership',
path,
)
parent_family.add(family)
if len(elt) != 0:
for child in elt:
for tag, children in self.get_children(elt):
for child in children:
self.iter_family(child,
family,
path,
@ -177,7 +157,7 @@ class PopulateTiramisuObjects:
is_follower = False
is_leader = False
if family.is_leader:
if elt.attrib['name'] != family.elt.attrib['name']:
if elt.name != family.elt.name:
is_follower = True
else:
is_leader = True
@ -195,16 +175,20 @@ class PopulateTiramisuObjects:
elt,
):
if subpath is None:
return elt.attrib['name']
return subpath + '.' + elt.attrib['name']
return elt.name
return subpath + '.' + elt.name
def get_text(self):
return '\n'.join(self.storage.get('.').get_text())
class BaseElt:
def __init__(self, attrib):
self.attrib = attrib
def __init__(self,
name,
doc,
):
self.name = name
self.doc = doc
def __iter__(self):
return iter([])
@ -212,12 +196,10 @@ class BaseElt:
class ElementStorage:
def __init__(self,
booleans,
):
self.paths = {}
self.text = []
self.index = 0
self.booleans = booleans
def add(self, path, elt):
self.paths[path] = (elt, self.index)
@ -250,11 +232,11 @@ class Common:
self.storage.add(self.path, self)
def populate_properties(self, child):
if child.get('type') == 'calculation':
action = f"ParamValue('{child.text}')"
option_name = self.storage.get(child.attrib['source']).get()
kwargs = f"'condition': ParamOption({option_name}, todict=True), 'expected': ParamValue('{child.attrib.get('expected')}')"
if child.attrib['inverse'] == 'True':
if child.type == 'calculation':
action = f"ParamValue('{child.name}')"
option_name = self.storage.get(child.source).get()
kwargs = f"'condition': ParamOption({option_name}, todict=True), 'expected': ParamValue('{child.expected}')"
if child.inverse:
kwargs += ", 'reverse_condition': ParamValue(True)"
prop = 'Calculation(calc_value, Params(' + action + ', kwargs={' + kwargs + '}))'
else:
@ -262,14 +244,16 @@ class Common:
if self.attrib['properties']:
self.attrib['properties'] += ', '
self.attrib['properties'] += prop
if not self.attrib['properties']:
del self.attrib['properties']
def get_attrib(self, attrib):
ret_list = []
for key, value in self.attrib.items():
if value is None:
continue
if key == 'properties':
value = 'frozenset([' + self.attrib[key] + '])'
if not self.attrib[key]:
continue
value = "frozenset({" + self.attrib[key] + "})"
elif key in ['default', 'multi', 'suffixes', 'validators']:
value = self.attrib[key]
elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
@ -286,6 +270,33 @@ class Common:
):
return self.storage.text
def get_attributes(self, space): # pylint: disable=R0201
attributes = dir(space)
for attr in ATTRIBUTES_ORDER:
if attr in attributes:
yield attr
for attr in dir(space):
if attr not in ATTRIBUTES_ORDER:
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
value = getattr(space, attr)
if not isinstance(value, (list, dict)) and not value.__class__.__name__ == 'Family':
yield attr
def get_children(self,
space,
):
for attr in dir(space):
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
value = getattr(space, attr)
if isinstance(value, (list, dict)):
children = getattr(space, attr)
if children.__class__.__name__ == 'Family':
children = [children]
if isinstance(children, dict):
children = list(children.values())
if children and isinstance(children, list):
yield attr, children
class Variable(Common):
def __init__(self,
@ -301,12 +312,12 @@ class Variable(Common):
path,
)
self.is_follower = is_follower
convert_option = CONVERT_OPTION[elt.attrib['type']]
del elt.attrib['type']
convert_option = CONVERT_OPTION[elt.type]
del elt.type
self.object_type = convert_option['opttype']
self.attrib.update(convert_option.get('initkwargs', {}))
if self.object_type != 'SymLinkOption':
self.attrib['properties'] = ''
self.attrib['properties'] = []
self.attrib['validators'] = []
self.elt = elt
@ -316,7 +327,6 @@ class Variable(Common):
if self.object_type == 'SymLinkOption':
self.attrib['opt'] = self.storage.get(self.attrib['opt']).get()
else:
self.parse_multi()
self.parse_children()
attrib = self.get_attrib(self.attrib)
self.option_name = self.storage.get_name(self.path)
@ -325,9 +335,8 @@ class Variable(Common):
return self.option_name
def populate_attrib(self):
for key, value in self.elt.attrib.items():
if key in self.storage.booleans:
value = convert_boolean(value)
for key in self.get_attributes(self.elt):
value = getattr(self.elt, key)
if key in FORCE_INFORMATIONS:
self.informations[key] = value
else:
@ -339,18 +348,24 @@ class Variable(Common):
if self.attrib['multi'] == 'submulti' and self.is_follower:
self.attrib['default_multi'] = []
choices = []
for child in self.elt:
if child.tag == 'property':
self.populate_properties(child)
elif child.tag == 'value':
if child.attrib['type'] == 'calculation':
self.attrib['default'] = self.calculation_value(child, [])
else:
self.populate_value(child)
elif child.tag == 'check':
self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
elif child.tag == 'choice':
choices.append(convert_tiramisu_value(child.text, child.attrib['type']))
if 'properties' in self.attrib:
if self.attrib['properties']:
self.attrib['properties'] = "'" + "', '".join(sorted(list(self.attrib['properties']))) + "'"
else:
self.attrib['properties'] = ''
for tag, children in self.get_children(self.elt):
for child in children:
if tag == 'property':
self.populate_properties(child)
elif tag == 'value':
if child.type == 'calculation':
self.attrib['default'] = self.calculation_value(child, [])
else:
self.populate_value(child)
elif tag == 'check':
self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
elif tag == 'choice':
choices.append(child.name)
if choices:
self.attrib['values'] = tuple(choices)
if self.attrib['default'] == []:
@ -362,53 +377,47 @@ class Variable(Common):
else:
self.attrib['validators'] = '[' + ', '.join(self.attrib['validators']) + ']'
def parse_multi(self):
if self.is_follower:
if self.attrib['multi'] is True:
self.attrib['multi'] = 'submulti'
else:
self.attrib['multi'] = True
def calculation_value(self, child, args):
kwargs = []
if 'name' in child.attrib:
if hasattr(child, 'name'):
# has parameters
function = child.attrib['name']
for param in child:
value = self.populate_param(param)
if 'name' not in param.attrib:
args.append(str(value))
else:
kwargs.append(f"'{param.attrib['name']}': " + value)
function = child.name
if hasattr(child, 'param'):
for param in child.param:
value = self.populate_param(param)
if not hasattr(param, 'name'):
args.append(str(value))
else:
kwargs.append(f"'{param.name}': " + value)
else:
# function without any parameter
function = child.text.strip()
ret = f"Calculation(func.{function}, Params((" + ', '.join(args) + "), kwargs=" + "{" + ', '.join(kwargs) + "})"
if 'warnings_only' in child.attrib:
ret += f', warnings_only={child.attrib["warnings_only"]}'
if hasattr(child, 'warnings_only'):
ret += f', warnings_only={child.warnings_only}'
return ret + ')'
def populate_param(self,
param,
):
if param.attrib['type'] == 'string':
if param.type == 'string':
return f'ParamValue("{param.text}")'
elif param.attrib['type'] == 'number':
elif param.type == 'number':
return f'ParamValue({param.text})'
elif param.attrib['type'] == 'variable':
elif param.type == 'variable':
value = {'option': param.text,
'notraisepropertyerror': convert_boolean(param.attrib['notraisepropertyerror']),
'notraisepropertyerror': param.notraisepropertyerror,
'todict': param.text in FUNC_TO_DICT,
}
if 'suffix' in param.attrib:
value['suffix'] = param.attrib['suffix']
if hasattr(param, 'suffix'):
value['suffix'] = param.suffix
return self.build_param(value)
raise LoaderError(_('unknown param type {}').format(param.attrib['type']))
raise LoaderError(_('unknown param type {}').format(param.type))
def populate_value(self,
child,
):
value = convert_tiramisu_value(child.text, child.attrib['type'])
value = child.name
if self.attrib['multi'] == 'submulti':
self.attrib['default_multi'].append(value)
elif self.is_follower:
@ -463,7 +472,8 @@ class Family(Common):
return self.option_name
def populate_attrib(self):
for key, value in self.elt.attrib.items():
for key in self.get_attributes(self.elt):
value = getattr(self.elt, key)
if key == 'help':
self.informations[key] = value
elif key == 'dynamic':
@ -473,12 +483,14 @@ class Family(Common):
self.attrib[key] = value
def parse_children(self):
self.attrib['properties'] = ''
for child in self.elt:
if child.tag == 'property':
if 'properties' in self.attrib:
self.attrib['properties'] = "'" + "', '".join(sorted(list(self.attrib['properties']))) + "'"
if hasattr(self.elt, 'property'):
#self.attrib['properties'] = ''
for child in self.elt.property:
self.populate_properties(child)
if not self.attrib['properties']:
del self.attrib['properties']
if not self.attrib['properties']:
del self.attrib['properties']
def get_object_name(self):
if 'suffixes' in self.attrib:
@ -486,11 +498,3 @@ class Family(Common):
elif not self.is_leader:
return 'OptionDescription'
return 'Leadership'
def load(xmlroot: str,
funcs_path: str):
tiramisu_objects = PopulateTiramisuObjects(xmlroot,
funcs_path,
)
return tiramisu_objects.get_text()

View File

@ -29,6 +29,7 @@ from lxml.etree import Element, SubElement # pylint: disable=E0611
from .i18n import _
from .xmlreflector import XMLReflector
from .annotator import ERASED_ATTRIBUTES, SpaceAnnotator
from .loader import PopulateTiramisuObjects
from .utils import normalize_family
from .error import OperationError, SpaceObjShallNotBeUpdated, DictConsistencyError
from .path import Path
@ -41,8 +42,6 @@ FORCE_UNREDEFINABLES = ('value',)
# CreoleObjSpace's elements that shall be set to the UnRedefinable type
UNREDEFINABLE = ('multi', 'type')
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'}
@ -520,81 +519,13 @@ class CreoleObjSpace:
variableobj.path = self.paths.get_family_path(family_name, namespace)
def space_visitor(self, eosfunc_file): # pylint: disable=C0111
self.funcs_path = eosfunc_file
SpaceAnnotator(self, eosfunc_file)
def save(self, filename, force_no_save=False):
"""Save an XML output on disk
:param filename: the full XML filename
"""
xml = Element('rougail')
self._xml_export(xml, self.space)
if not force_no_save:
self.xmlreflector.save_xmlfile(filename, xml)
return xml
def get_attributes(self, space): # pylint: disable=R0201
for attr in dir(space):
if not attr.startswith('_'):
yield attr
def _sub_xml_export(self, name, node, node_name, space, current_space):
if isinstance(space, dict):
space = list(space.values())
if isinstance(space, list):
for subspace in space:
if name == 'value' and (not hasattr(subspace, 'name') or subspace.name is None):
raise Exception('pfff')
continue
_name = CONVERT_EXPORT.get(subspace.__class__.__name__, 'family')
child_node = SubElement(node, _name)
self._xml_export(child_node, subspace, _name)
elif isinstance(space, (self.Atom, (self.Redefinable, self.UnRedefinable))):
_name = CONVERT_EXPORT.get(space.__class__.__name__, 'family')
child_node = SubElement(node, _name)
if _name != name:
child_node.attrib['name'] = name
if 'doc' not in child_node.attrib.keys():
child_node.attrib['doc'] = name
for subname in self.get_attributes(space):
subspace = getattr(space, subname)
self._sub_xml_export(subname, child_node, name, subspace, space)
elif name not in ERASED_ATTRIBUTES:
# # FIXME plutot dans annotator ...
if node.tag in ['variable', 'family', 'leader']:
if name in PROPERTIES:
if space is True:
for prop in CONVERT_PROPERTIES.get(name, [name]):
SubElement(node, 'property').text = prop
return
if name == 'mode' and space:
SubElement(node, 'property').text = space
return
# Not param for calculation ...
if name == 'name' and node_name in FORCED_TEXT_ELTS_AS_NAME and not hasattr(current_space, 'param'):
node.text = str(space)
elif name == 'text' and node_name in self.forced_text_elts:
node.text = space
elif node.tag == 'family' and name == 'name':
if 'doc' not in node.attrib.keys():
node.attrib['doc'] = space
node.attrib['name'] = normalize_family(space, check_name=False)
else:
if name in RENAME_ATTIBUTES:
name = RENAME_ATTIBUTES[name]
if space is not None:
node.attrib[name] = str(space)
def _xml_export(self,
node,
space,
node_name=variable_namespace,
):
for name in self.get_attributes(space):
subspace = getattr(space, name)
self._sub_xml_export(name,
node,
node_name,
subspace,
space,
)
def save(self,
filename,
):
tiramisu_objects = PopulateTiramisuObjects(self.space,
self.funcs_path,
)
return tiramisu_objects.get_text() + '\n'