Files
rougail/src/rougail/tiramisureflector.py

373 lines
13 KiB
Python
Raw Normal View History

2020-07-06 20:58:11 +02:00
"""loader
2019-11-23 08:17:35 +01:00
flattened XML specific
"""
2020-08-12 08:23:38 +02:00
from .config import Config
2021-01-23 21:21:45 +01:00
from .annotator import CONVERT_OPTION
2021-01-23 13:44:21 +01:00
from .objspace import RootRougailObject
2019-11-23 08:17:35 +01:00
2020-12-26 15:15:51 +01:00
FUNC_TO_DICT = []
2021-01-23 21:21:45 +01:00
ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'values', 'multi', 'default_multi', 'properties',
'validators', 'min_number', 'max_number',
)
2019-11-24 20:25:09 +01:00
2021-01-19 19:28:29 +01:00
class Root(): # pylint: disable=R0903
2021-01-18 17:46:21 +01:00
"""Root classes
"""
2021-01-16 20:11:46 +01:00
path = '.'
2021-01-23 13:44:21 +01:00
class BaseElt: # pylint: disable=R0903
"""Base element
"""
name = 'baseoption'
doc = 'baseoption'
path = '.'
class TiramisuReflector:
2021-01-18 17:46:21 +01:00
"""Convert object to tiramisu representation
"""
2020-07-20 18:13:53 +02:00
def __init__(self,
2021-01-23 13:44:21 +01:00
objectspace,
2020-07-20 18:13:53 +02:00
funcs_path,
):
2021-01-18 17:46:21 +01:00
self.index = 0
self.text = ["from importlib.machinery import SourceFileLoader",
2021-01-19 19:05:07 +01:00
"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)",
2021-01-18 17:46:21 +01:00
"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",
]
2021-01-23 13:44:21 +01:00
self.objectspace = objectspace
self.make_tiramisu_objects()
2020-07-20 18:13:53 +02:00
2021-01-23 13:44:21 +01:00
def make_tiramisu_objects(self) -> None:
2021-01-18 17:46:21 +01:00
"""make tiramisu objects
"""
baseelt = BaseElt()
self.set_name(baseelt)
basefamily = Family(baseelt,
2021-01-19 18:04:13 +01:00
self.text,
2021-01-23 13:44:21 +01:00
False,
2021-01-18 17:46:21 +01:00
)
2021-01-23 13:44:21 +01:00
for elt in self.reorder_family():
self.populate_family(basefamily,
elt,
)
2021-01-18 17:46:21 +01:00
# parse object
2021-01-19 19:28:29 +01:00
baseelt.reflector_object.get() # pylint: disable=E1101
2021-01-18 17:46:21 +01:00
2021-01-23 13:44:21 +01:00
def reorder_family(self):
2021-01-18 17:46:21 +01:00
"""variable_namespace family has to be loaded before any other family
because `extra` family could use `variable_namespace` variables.
"""
2021-01-23 13:44:21 +01:00
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():
2021-01-18 17:46:21 +01:00
if elt != Config['variable_namespace']:
2020-07-29 08:59:40 +02:00
yield value
2021-01-23 13:44:21 +01:00
if hasattr(self.objectspace.space, 'services'):
yield self.objectspace.space.services
2019-11-26 20:33:24 +01:00
2020-07-07 18:12:16 +02:00
def populate_family(self,
parent_family,
elt,
):
2021-01-18 17:46:21 +01:00
"""Populate family
"""
self.set_name(elt)
2020-07-07 18:12:16 +02:00
family = Family(elt,
2021-01-19 18:04:13 +01:00
self.text,
2021-01-23 13:44:21 +01:00
isinstance(elt, self.objectspace.leadership),
2020-07-07 18:12:16 +02:00
)
parent_family.add(family)
2021-01-23 13:44:21 +01:00
for children in vars(elt).values():
if isinstance(children, self.objectspace.family):
self.populate_family(family,
children,
)
continue
2021-01-18 17:46:21 +01:00
if isinstance(children, dict):
children = list(children.values())
if isinstance(children, list):
2021-01-23 13:44:21 +01:00
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,
)
2021-01-18 17:46:21 +01:00
2020-07-16 09:50:01 +02:00
def populate_variable(self,
family,
elt,
):
2021-01-18 17:46:21 +01:00
"""Populate variable
"""
2021-01-19 18:04:13 +01:00
if family.is_leadership:
2021-01-18 17:46:21 +01:00
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)
2020-07-20 18:13:53 +02:00
family.add(Variable(elt,
2021-01-19 18:04:13 +01:00
self.text,
2021-01-23 21:15:26 +01:00
self.objectspace,
2020-07-07 18:12:16 +02:00
is_follower,
is_leader,
2020-07-20 18:13:53 +02:00
))
2020-07-07 18:12:16 +02:00
2021-01-18 17:46:21 +01:00
def set_name(self,
elt,
):
2021-01-19 18:04:13 +01:00
"""Set name
"""
2021-01-18 17:46:21 +01:00
elt.reflector_name = f'option_{self.index}'
self.index += 1
2019-11-23 08:17:35 +01:00
2020-07-20 18:13:53 +02:00
def get_text(self):
2021-01-18 17:46:21 +01:00
"""Get text
"""
return '\n'.join(self.text)
2020-07-20 18:13:53 +02:00
class Common:
2021-01-18 17:46:21 +01:00
"""Common function for variable and family
"""
2020-07-20 18:13:53 +02:00
def __init__(self,
2021-01-19 18:04:13 +01:00
elt,
text,
2020-07-20 18:13:53 +02:00
):
2021-01-19 18:04:13 +01:00
self.elt = elt
2020-07-20 18:13:53 +02:00
self.option_name = None
2019-11-23 08:17:35 +01:00
self.attrib = {}
2021-01-19 18:04:13 +01:00
self.text = text
2021-01-18 17:46:21 +01:00
self.elt.reflector_object = self
2020-07-20 18:13:53 +02:00
2021-01-23 21:21:45 +01:00
@staticmethod
def populate_properties(child) -> str:
2021-01-18 17:46:21 +01:00
"""Populate properties
"""
2020-12-24 18:31:13 +01:00
assert child.type == 'calculation'
action = f"ParamValue('{child.name}')"
2021-01-18 17:46:21 +01:00
option_name = child.source.reflector_object.get()
kwargs = (f"'condition': ParamOption({option_name}, todict=True), "
f"'expected': ParamValue('{child.expected}')")
2020-12-24 18:31:13 +01:00
if child.inverse:
kwargs += ", 'reverse_condition': ParamValue(True)"
2021-01-23 21:15:26 +01:00
return 'Calculation(calc_value, Params(' + action + ', kwargs={' + kwargs + '}))'
2020-07-20 18:13:53 +02:00
2021-01-19 18:04:13 +01:00
def properties_to_string(self):
"""Change properties to string
"""
2021-01-23 21:15:26 +01:00
properties = []
calc_properties = []
for property_ in self.attrib['properties']:
if not isinstance(property_, str):
calc_properties.append(self.populate_properties(property_))
else:
properties.append(f"'{property_}'")
return 'frozenset({' + ', '.join(sorted(properties) + calc_properties) + '})'
2021-01-19 18:04:13 +01:00
2020-12-26 17:06:56 +01:00
def get_attrib(self):
2021-01-18 17:46:21 +01:00
"""Get attributes
"""
2020-07-20 18:13:53 +02:00
ret_list = []
for key, value in self.attrib.items():
2020-07-29 08:59:40 +02:00
if value is None:
continue
2020-07-20 18:13:53 +02:00
if key == 'properties':
2021-01-23 21:15:26 +01:00
value = self.properties_to_string()
elif key == 'validators':
2021-01-23 21:21:45 +01:00
value = '[' + ', '.join([self.calculation_value(val, ['ParamSelfOption()']) \
for val in value]) + ']'
2021-01-23 21:15:26 +01:00
elif key == 'values':
if value[0].type == 'calculation':
value = value[0].name.reflector_object.get()
value = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
else:
value = str(tuple([val.name for val in value]))
elif key in ['multi', 'suffixes']:
2021-01-24 10:17:01 +01:00
if not value:
continue
2020-07-20 18:13:53 +02:00
elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
value = "'" + value.replace("'", "\\\'") + "'"
2021-01-23 21:15:26 +01:00
elif isinstance(value, self.objectspace.value):
value = self.calculation_value(value, [])
2020-07-20 18:13:53 +02:00
ret_list.append(f'{key}={value}')
return ', '.join(ret_list)
def populate_informations(self):
2021-01-18 17:46:21 +01:00
"""Populate Tiramisu's informations
"""
2021-01-23 11:57:46 +01:00
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('"', '\"') + '"'
2021-01-19 18:04:13 +01:00
self.text.append(f'{self.option_name}.impl_set_information("{key}", {value})')
2020-07-20 18:13:53 +02:00
2021-01-23 21:15:26 +01:00
def populate_attrib(self):
"""Populate attributes
2021-01-18 17:46:21 +01:00
"""
2020-07-29 08:59:40 +02:00
for attr in ATTRIBUTES_ORDER:
2021-01-23 21:15:26 +01:00
if hasattr(self.elt, attr):
self.attrib[attr] = getattr(self.elt, attr)
2020-07-29 08:59:40 +02:00
2020-07-20 18:13:53 +02:00
class Variable(Common):
2021-01-18 17:46:21 +01:00
"""Manage variable
"""
2020-07-20 18:13:53 +02:00
def __init__(self,
elt,
2021-01-19 18:04:13 +01:00
text,
2021-01-23 21:15:26 +01:00
objectspace,
2020-07-20 18:13:53 +02:00
is_follower,
is_leader,
):
2021-01-19 18:04:13 +01:00
super().__init__(elt, text)
2021-01-23 21:15:26 +01:00
self.objectspace = objectspace
2021-01-19 18:04:13 +01:00
self.is_leader = is_leader
2020-07-20 18:13:53 +02:00
self.is_follower = is_follower
2020-07-29 08:59:40 +02:00
convert_option = CONVERT_OPTION[elt.type]
2020-07-07 18:12:16 +02:00
self.object_type = convert_option['opttype']
2020-07-20 18:13:53 +02:00
self.attrib.update(convert_option.get('initkwargs', {}))
def get(self):
2021-01-18 17:46:21 +01:00
"""Get tiramisu's object
"""
2020-07-20 18:13:53 +02:00
if self.option_name is None:
self.populate_attrib()
if self.object_type == 'SymLinkOption':
2021-01-23 12:04:58 +01:00
self.attrib['opt'] = self.elt.opt.reflector_object.get()
2020-12-26 17:06:56 +01:00
attrib = self.get_attrib()
2021-01-18 17:46:21 +01:00
self.option_name = self.elt.reflector_name
2021-01-19 18:04:13 +01:00
self.text.append(f'{self.option_name} = {self.object_type}({attrib})')
2020-07-20 18:13:53 +02:00
self.populate_informations()
return self.option_name
2021-01-18 17:46:21 +01:00
def calculation_value(self,
child,
args,
) -> str:
"""Generate calculated value
"""
2020-07-20 18:13:53 +02:00
kwargs = []
2020-12-24 18:31:13 +01:00
# 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)
2021-01-18 17:46:21 +01:00
ret = f"Calculation(func.{function}, Params((" + ', '.join(args) + \
"), kwargs=" + "{" + ', '.join(kwargs) + "})"
2020-07-29 08:59:40 +02:00
if hasattr(child, 'warnings_only'):
ret += f', warnings_only={child.warnings_only}'
2020-07-20 18:13:53 +02:00
return ret + ')'
def populate_param(self,
function: str,
2020-07-20 18:13:53 +02:00
param,
):
2021-01-18 17:46:21 +01:00
"""Populate variable parameters
"""
2020-07-29 08:59:40 +02:00
if param.type == 'string':
2020-07-20 18:13:53 +02:00
return f'ParamValue("{param.text}")'
2020-12-26 17:06:56 +01:00
if param.type == 'number':
2020-07-20 18:13:53 +02:00
return f'ParamValue({param.text})'
2020-12-26 17:06:56 +01:00
if param.type == 'variable':
2020-07-16 09:50:01 +02:00
value = {'option': param.text,
2020-07-29 08:59:40 +02:00
'notraisepropertyerror': param.notraisepropertyerror,
'todict': function in FUNC_TO_DICT,
2020-07-16 09:50:01 +02:00
}
2020-07-29 08:59:40 +02:00
if hasattr(param, 'suffix'):
value['suffix'] = param.suffix
2021-01-18 17:46:21 +01:00
value['family'] = param.family
2020-07-20 18:13:53 +02:00
return self.build_param(value)
2020-12-26 17:06:56 +01:00
if param.type == 'information':
2020-10-04 15:11:44 +02:00
return f'ParamInformation("{param.text}", None)'
2020-12-26 17:06:56 +01:00
if param.type == 'suffix':
return 'ParamSuffix()'
2021-01-18 17:46:21 +01:00
return '' # pragma: no cover
2019-11-27 15:03:05 +01:00
2021-01-19 18:04:13 +01:00
@staticmethod
def build_param(param) -> str:
2021-01-18 17:46:21 +01:00
"""build variable parameters
"""
option_name = param['option'].reflector_object.get()
ends = f"notraisepropertyerror={param['notraisepropertyerror']}, todict={param['todict']})"
2020-07-20 18:13:53 +02:00
if 'suffix' in param:
2021-01-18 17:46:21 +01:00
family_name = param['family'].reflector_name
return f"ParamDynOption({option_name}, '{param['suffix']}', {family_name}, {ends}"
return f"ParamOption({option_name}, {ends}"
2019-11-23 08:17:35 +01:00
2019-11-26 20:33:24 +01:00
class Family(Common):
2021-01-18 17:46:21 +01:00
"""Manage family
"""
2020-07-07 18:12:16 +02:00
def __init__(self,
elt,
2021-01-19 18:04:13 +01:00
text,
2021-01-23 13:44:21 +01:00
is_leadership
2020-07-07 18:12:16 +02:00
):
2021-01-19 18:04:13 +01:00
super().__init__(elt, text)
2021-01-23 13:44:21 +01:00
self.is_leadership = is_leadership
2019-11-23 08:17:35 +01:00
self.children = []
def add(self, child):
2021-01-18 17:46:21 +01:00
"""Add a child
"""
2019-11-23 08:17:35 +01:00
self.children.append(child)
def get(self):
2021-01-18 17:46:21 +01:00
"""Get tiramisu's object
"""
2020-07-20 18:13:53 +02:00
if not self.option_name:
self.populate_attrib()
2021-01-23 12:06:18 +01:00
self.populate_dynamic()
2021-01-18 17:46:21 +01:00
self.option_name = self.elt.reflector_name
2020-07-20 18:13:53 +02:00
object_name = self.get_object_name()
2021-01-18 17:46:21 +01:00
attrib = self.get_attrib() + \
', children=[' + ', '.join([child.get() for child in self.children]) + ']'
2021-01-19 18:04:13 +01:00
self.text.append(f'{self.option_name} = {object_name}({attrib})')
2020-07-20 18:13:53 +02:00
self.populate_informations()
return self.option_name
2021-01-23 12:06:18 +01:00
def populate_dynamic(self):
2021-01-23 13:44:21 +01:00
"""populate dynamic family
"""
2021-01-23 12:06:18 +01:00
if hasattr(self.elt, 'dynamic'):
dyn = self.elt.dynamic.reflector_object.get()
self.attrib['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}))))"
2020-07-20 18:13:53 +02:00
def get_object_name(self):
2021-01-18 17:46:21 +01:00
"""Get family object's name
"""
2020-07-20 18:13:53 +02:00
if 'suffixes' in self.attrib:
return 'ConvertDynOptionDescription'
2021-01-19 18:04:13 +01:00
if self.is_leadership:
return 'Leadership'
return 'OptionDescription'