474 lines
16 KiB
Python
474 lines
16 KiB
Python
"""loader
|
|
flattened XML specific
|
|
"""
|
|
from .config import Config
|
|
from .annotator import ERASED_ATTRIBUTES, CONVERT_OPTION
|
|
from .objspace import RootRougailObject
|
|
|
|
|
|
FUNC_TO_DICT = []
|
|
ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi', 'properties', 'min_number', 'max_number')
|
|
|
|
|
|
class Root(): # pylint: disable=R0903
|
|
"""Root classes
|
|
"""
|
|
path = '.'
|
|
|
|
|
|
class BaseElt: # pylint: disable=R0903
|
|
"""Base element
|
|
"""
|
|
name = 'baseoption'
|
|
doc = 'baseoption'
|
|
path = '.'
|
|
|
|
|
|
class TiramisuReflector:
|
|
"""Convert object to tiramisu representation
|
|
"""
|
|
def __init__(self,
|
|
objectspace,
|
|
funcs_path,
|
|
):
|
|
self.index = 0
|
|
self.text = ["from importlib.machinery import SourceFileLoader",
|
|
"from importlib.util import spec_from_loader, module_from_spec",
|
|
f"loader = SourceFileLoader('func', '{funcs_path}')",
|
|
"spec = spec_from_loader(loader.name, loader)",
|
|
"func = module_from_spec(spec)",
|
|
"loader.exec_module(func)",
|
|
"for key, value in dict(locals()).items():",
|
|
" if key != ['SourceFileLoader', 'func']:",
|
|
" setattr(func, key, value)",
|
|
"try:",
|
|
" from tiramisu3 import *",
|
|
"except:",
|
|
" from tiramisu import *",
|
|
"from rougail.tiramisu import ConvertDynOptionDescription",
|
|
]
|
|
self.objectspace = objectspace
|
|
self.make_tiramisu_objects()
|
|
|
|
def make_tiramisu_objects(self) -> None:
|
|
"""make tiramisu objects
|
|
"""
|
|
baseelt = BaseElt()
|
|
self.set_name(baseelt)
|
|
basefamily = Family(baseelt,
|
|
self.text,
|
|
False,
|
|
)
|
|
for elt in self.reorder_family():
|
|
self.populate_family(basefamily,
|
|
elt,
|
|
)
|
|
# parse object
|
|
baseelt.reflector_object.get() # pylint: disable=E1101
|
|
|
|
def reorder_family(self):
|
|
"""variable_namespace family has to be loaded before any other family
|
|
because `extra` family could use `variable_namespace` variables.
|
|
"""
|
|
if hasattr(self.objectspace.space, 'variables'):
|
|
if Config['variable_namespace'] in self.objectspace.space.variables:
|
|
yield self.objectspace.space.variables[Config['variable_namespace']]
|
|
for elt, value in self.objectspace.space.variables.items():
|
|
if elt != Config['variable_namespace']:
|
|
yield value
|
|
if hasattr(self.objectspace.space, 'services'):
|
|
yield self.objectspace.space.services
|
|
|
|
def populate_family(self,
|
|
parent_family,
|
|
elt,
|
|
):
|
|
"""Populate family
|
|
"""
|
|
self.set_name(elt)
|
|
family = Family(elt,
|
|
self.text,
|
|
isinstance(elt, self.objectspace.leadership),
|
|
)
|
|
parent_family.add(family)
|
|
for children in vars(elt).values():
|
|
if isinstance(children, self.objectspace.family):
|
|
self.populate_family(family,
|
|
children,
|
|
)
|
|
continue
|
|
if isinstance(children, dict):
|
|
children = list(children.values())
|
|
if isinstance(children, list):
|
|
for child in children:
|
|
if isinstance(child, self.objectspace.property_) or \
|
|
not isinstance(child, RootRougailObject):
|
|
continue
|
|
if isinstance(child, self.objectspace.variable):
|
|
function = self.populate_variable
|
|
else:
|
|
function = self.populate_family
|
|
function(family,
|
|
child,
|
|
)
|
|
|
|
def populate_variable(self,
|
|
family,
|
|
elt,
|
|
):
|
|
"""Populate variable
|
|
"""
|
|
if family.is_leadership:
|
|
is_leader = elt.name == family.elt.variable[0].name
|
|
is_follower = not is_leader
|
|
else:
|
|
is_leader = False
|
|
is_follower = False
|
|
self.set_name(elt)
|
|
family.add(Variable(elt,
|
|
self.text,
|
|
is_follower,
|
|
is_leader,
|
|
))
|
|
|
|
def set_name(self,
|
|
elt,
|
|
):
|
|
"""Set name
|
|
"""
|
|
elt.reflector_name = f'option_{self.index}'
|
|
self.index += 1
|
|
|
|
def get_text(self):
|
|
"""Get text
|
|
"""
|
|
return '\n'.join(self.text)
|
|
|
|
|
|
class Common:
|
|
"""Common function for variable and family
|
|
"""
|
|
def __init__(self,
|
|
elt,
|
|
text,
|
|
):
|
|
self.elt = elt
|
|
self.option_name = None
|
|
self.attrib = {}
|
|
self.text = text
|
|
self.elt.reflector_object = self
|
|
|
|
def populate_properties(self, child):
|
|
"""Populate properties
|
|
"""
|
|
assert child.type == 'calculation'
|
|
action = f"ParamValue('{child.name}')"
|
|
option_name = child.source.reflector_object.get()
|
|
kwargs = (f"'condition': ParamOption({option_name}, todict=True), "
|
|
f"'expected': ParamValue('{child.expected}')")
|
|
if child.inverse:
|
|
kwargs += ", 'reverse_condition': ParamValue(True)"
|
|
prop = 'Calculation(calc_value, Params(' + action + ', kwargs={' + kwargs + '}))'
|
|
if self.attrib['properties']:
|
|
self.attrib['properties'] += ', '
|
|
self.attrib['properties'] += prop
|
|
|
|
def properties_to_string(self):
|
|
"""Change properties to string
|
|
"""
|
|
if not self.attrib['properties']:
|
|
self.attrib['properties'] = ''
|
|
else:
|
|
self.attrib['properties'] = "'" + "', '".join(sorted(self.attrib['properties'])) + "'"
|
|
|
|
def get_attrib(self):
|
|
"""Get attributes
|
|
"""
|
|
ret_list = []
|
|
for key, value in self.attrib.items():
|
|
if value is None:
|
|
continue
|
|
if key == 'properties':
|
|
if not self.attrib[key]:
|
|
continue
|
|
value = "frozenset({" + self.attrib[key] + "})"
|
|
elif key in ['default', 'multi', 'suffixes', 'validators', 'values']:
|
|
value = self.attrib[key]
|
|
elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
|
|
value = "'" + value.replace("'", "\\\'") + "'"
|
|
ret_list.append(f'{key}={value}')
|
|
return ', '.join(ret_list)
|
|
|
|
def populate_informations(self):
|
|
"""Populate Tiramisu's informations
|
|
"""
|
|
if not hasattr(self.elt, 'information'):
|
|
return
|
|
for key, value in vars(self.elt.information).items():
|
|
if key == 'xmlfiles':
|
|
continue
|
|
if isinstance(value, str):
|
|
value = '"' + value.replace('"', '\"') + '"'
|
|
self.text.append(f'{self.option_name}.impl_set_information("{key}", {value})')
|
|
|
|
@staticmethod
|
|
def get_attributes(space):
|
|
"""Get attributes
|
|
"""
|
|
for attr in ATTRIBUTES_ORDER:
|
|
if hasattr(space, attr):
|
|
yield attr
|
|
|
|
@staticmethod
|
|
def get_children(space):
|
|
"""Get children
|
|
"""
|
|
for attr in dir(space):
|
|
if not attr.startswith('_') and attr not in ERASED_ATTRIBUTES:
|
|
if isinstance(getattr(space, attr), list):
|
|
yield attr, getattr(space, attr)
|
|
|
|
|
|
class Variable(Common):
|
|
"""Manage variable
|
|
"""
|
|
def __init__(self,
|
|
elt,
|
|
text,
|
|
is_follower,
|
|
is_leader,
|
|
):
|
|
super().__init__(elt, text)
|
|
self.is_leader = is_leader
|
|
self.is_follower = is_follower
|
|
convert_option = CONVERT_OPTION[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['validators'] = []
|
|
|
|
def get(self):
|
|
"""Get tiramisu's object
|
|
"""
|
|
if self.option_name is None:
|
|
self.populate_attrib()
|
|
if self.object_type == 'SymLinkOption':
|
|
self.attrib['opt'] = self.elt.opt.reflector_object.get()
|
|
else:
|
|
self.parse_children()
|
|
attrib = self.get_attrib()
|
|
self.option_name = self.elt.reflector_name
|
|
self.text.append(f'{self.option_name} = {self.object_type}({attrib})')
|
|
self.populate_informations()
|
|
return self.option_name
|
|
|
|
def populate_attrib(self):
|
|
"""Populate attributes
|
|
"""
|
|
for key in self.get_attributes(self.elt):
|
|
self.attrib[key] = getattr(self.elt, key)
|
|
|
|
def parse_children(self):
|
|
"""Parse children
|
|
"""
|
|
if 'default' not in self.attrib or self.attrib['multi']:
|
|
self.attrib['default'] = []
|
|
if self.attrib['multi'] == 'submulti' and self.is_follower:
|
|
self.attrib['default_multi'] = []
|
|
choices = []
|
|
self.properties_to_string()
|
|
for tag, children in self.get_children(self.elt):
|
|
for child in children:
|
|
self.parse_child(tag,
|
|
child,
|
|
choices,
|
|
)
|
|
if choices:
|
|
self.attrib['values'] = str(tuple(choices))
|
|
if not self.attrib['default']:
|
|
del self.attrib['default']
|
|
elif not self.attrib['multi'] and isinstance(self.attrib['default'], list):
|
|
self.attrib['default'] = self.attrib['default'][-1]
|
|
if not self.attrib['validators']:
|
|
del self.attrib['validators']
|
|
else:
|
|
self.attrib['validators'] = '[' + ', '.join(self.attrib['validators']) + ']'
|
|
|
|
def parse_child(self,
|
|
tag,
|
|
child,
|
|
choices,
|
|
) -> None:
|
|
"""Parse child
|
|
"""
|
|
if tag == 'property':
|
|
self.populate_properties(child)
|
|
elif tag == 'value':
|
|
self.populate_value(child)
|
|
elif tag == 'check':
|
|
validator = self.calculation_value(child, ['ParamSelfOption()'])
|
|
self.attrib['validators'].append(validator)
|
|
elif tag == 'choice':
|
|
self.calculate_choice(child,
|
|
choices,
|
|
)
|
|
else: # pragma: no cover
|
|
raise Exception(f'unknown tag {tag}')
|
|
|
|
def calculate_choice(self,
|
|
child,
|
|
choices: list,
|
|
) -> None:
|
|
"""Calculating choice
|
|
"""
|
|
if child.type == 'calculation':
|
|
value = child.name.reflector_object.get()
|
|
self.attrib['values'] = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
|
|
else:
|
|
choices.append(child.name)
|
|
|
|
def calculation_value(self,
|
|
child,
|
|
args,
|
|
) -> str:
|
|
"""Generate calculated value
|
|
"""
|
|
kwargs = []
|
|
# has parameters
|
|
function = child.name
|
|
if hasattr(child, 'param'):
|
|
for param in child.param:
|
|
value = self.populate_param(function, param)
|
|
if not hasattr(param, 'name'):
|
|
args.append(str(value))
|
|
else:
|
|
kwargs.append(f"'{param.name}': " + value)
|
|
ret = f"Calculation(func.{function}, Params((" + ', '.join(args) + \
|
|
"), kwargs=" + "{" + ', '.join(kwargs) + "})"
|
|
if hasattr(child, 'warnings_only'):
|
|
ret += f', warnings_only={child.warnings_only}'
|
|
return ret + ')'
|
|
|
|
def populate_param(self,
|
|
function: str,
|
|
param,
|
|
):
|
|
"""Populate variable parameters
|
|
"""
|
|
if param.type == 'string':
|
|
return f'ParamValue("{param.text}")'
|
|
if param.type == 'number':
|
|
return f'ParamValue({param.text})'
|
|
if param.type == 'variable':
|
|
value = {'option': param.text,
|
|
'notraisepropertyerror': param.notraisepropertyerror,
|
|
'todict': function in FUNC_TO_DICT,
|
|
}
|
|
if hasattr(param, 'suffix'):
|
|
value['suffix'] = param.suffix
|
|
value['family'] = param.family
|
|
return self.build_param(value)
|
|
if param.type == 'information':
|
|
return f'ParamInformation("{param.text}", None)'
|
|
if param.type == 'suffix':
|
|
return 'ParamSuffix()'
|
|
return '' # pragma: no cover
|
|
|
|
def populate_value(self,
|
|
child,
|
|
):
|
|
"""Populate variable's values
|
|
"""
|
|
if child.type == 'calculation':
|
|
self.attrib['default'] = self.calculation_value(child, [])
|
|
else:
|
|
value = child.name
|
|
if self.attrib['multi'] == 'submulti':
|
|
self.attrib['default_multi'].append(value)
|
|
elif self.is_follower:
|
|
self.attrib['default_multi'] = value
|
|
elif self.attrib['multi']:
|
|
self.attrib['default'].append(value)
|
|
if not self.is_leader:
|
|
self.attrib['default_multi'] = value
|
|
elif isinstance(value, (int, float)) or value is None:
|
|
self.attrib['default'].append(value)
|
|
else:
|
|
self.attrib['default'].append("'" + value + "'")
|
|
|
|
@staticmethod
|
|
def build_param(param) -> str:
|
|
"""build variable parameters
|
|
"""
|
|
option_name = param['option'].reflector_object.get()
|
|
ends = f"notraisepropertyerror={param['notraisepropertyerror']}, todict={param['todict']})"
|
|
if 'suffix' in param:
|
|
family_name = param['family'].reflector_name
|
|
return f"ParamDynOption({option_name}, '{param['suffix']}', {family_name}, {ends}"
|
|
return f"ParamOption({option_name}, {ends}"
|
|
|
|
|
|
class Family(Common):
|
|
"""Manage family
|
|
"""
|
|
def __init__(self,
|
|
elt,
|
|
text,
|
|
is_leadership
|
|
):
|
|
super().__init__(elt, text)
|
|
self.is_leadership = is_leadership
|
|
self.children = []
|
|
|
|
def add(self, child):
|
|
"""Add a child
|
|
"""
|
|
self.children.append(child)
|
|
|
|
def get(self):
|
|
"""Get tiramisu's object
|
|
"""
|
|
if not self.option_name:
|
|
self.populate_attrib()
|
|
self.parse_properties()
|
|
self.populate_dynamic()
|
|
self.option_name = self.elt.reflector_name
|
|
object_name = self.get_object_name()
|
|
attrib = self.get_attrib() + \
|
|
', children=[' + ', '.join([child.get() for child in self.children]) + ']'
|
|
self.text.append(f'{self.option_name} = {object_name}({attrib})')
|
|
self.populate_informations()
|
|
return self.option_name
|
|
|
|
def populate_dynamic(self):
|
|
"""populate dynamic family
|
|
"""
|
|
if hasattr(self.elt, 'dynamic'):
|
|
dyn = self.elt.dynamic.reflector_object.get()
|
|
self.attrib['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}))))"
|
|
|
|
def populate_attrib(self):
|
|
"""parse a populate attributes
|
|
"""
|
|
for key in self.get_attributes(self.elt):
|
|
self.attrib[key] = getattr(self.elt, key)
|
|
|
|
def parse_properties(self):
|
|
"""parse current children
|
|
"""
|
|
if 'properties' in self.attrib:
|
|
self.properties_to_string()
|
|
if hasattr(self.elt, 'property'):
|
|
for child in self.elt.property:
|
|
self.populate_properties(child)
|
|
|
|
def get_object_name(self):
|
|
"""Get family object's name
|
|
"""
|
|
if 'suffixes' in self.attrib:
|
|
return 'ConvertDynOptionDescription'
|
|
if self.is_leadership:
|
|
return 'Leadership'
|
|
return 'OptionDescription'
|