This commit is contained in:
2020-07-16 09:50:01 +02:00
parent 7191bbbcb3
commit 05ca7ed578
194 changed files with 427 additions and 760 deletions

View File

@ -39,7 +39,7 @@ modes = mode_factory()
# 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', 'submulti') # , '_real_container')
'level') # , '_real_container')
ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group')
FORCE_CHOICE = {'oui/non': ['oui', 'non'],
@ -405,9 +405,6 @@ class SpaceAnnotator(object):
if leader_is_hidden:
variable.frozen = True
variable.force_default_on_freeze = True
# followers are multi
if not variable.multi:
raise CreoleDictConsistencyError(_('the variable {} in a group must be multi or submulti').format(variable.name))
leader_space.variable.append(variable) # pylint: disable=E1101
self.paths.set_leader(namespace,
leader_family_name,
@ -534,8 +531,6 @@ class SpaceAnnotator(object):
variable.type = 'string'
if variable.type != 'symlink' and not hasattr(variable, 'description'):
variable.description = variable.name
if variable.submulti:
variable.multi = 'submulti'
def convert_auto_freeze(self): # pylint: disable=C0111
if hasattr(self.space, 'variables'):
@ -732,21 +727,21 @@ class SpaceAnnotator(object):
del self.space.constraints.fill
def filter_separators(self): # pylint: disable=C0111,R0201
# FIXME devrait etre dans la variable
if not hasattr(self.space, 'variables'):
return
for family in self.space.variables.values():
if (hasattr(family, 'separators') and hasattr(family.separators, 'separator')):
space = family.separators.separator
names = []
for idx, separator in enumerate(space):
namespace = self.paths.get_variable_namespace(separator.name)
subpath = self.paths.get_variable_path(separator.name, namespace)
separator.name = subpath
if separator.name in names:
raise CreoleDictConsistencyError(_('{} already has a separator').format(separator.name))
names.append(separator.name)
if not hasattr(family, 'separators'):
continue
if hasattr(family.separators, 'separator'):
for idx, separator in enumerate(family.separators.separator):
option = self.paths.get_variable_obj(separator.name)
if hasattr(option, 'separator'):
subpath = self.paths.get_variable_path(separator.name,
separator.namespace,
)
raise CreoleDictConsistencyError(_('{} already has a separator').format(subpath))
option.separator = separator.text
del family.separators
def load_params_in_validenum(self, param):
if param.type in ['string', 'python', 'number']:

View File

@ -101,7 +101,6 @@
<!ATTLIST variable hidden (True|False) "False">
<!ATTLIST variable disabled (True|False) "False">
<!ATTLIST variable multi (True|False) "False">
<!ATTLIST variable submulti (True|False) "False">
<!ATTLIST variable redefine (True|False) "False">
<!ATTLIST variable exists (True|False) "True">
<!ATTLIST variable mandatory (True|False) "False">

View File

@ -20,7 +20,6 @@ from .error import CreoleLoaderError
FUNC_TO_DICT = ['valid_not_equal']
KNOWN_TAGS = ['family', 'variable', 'separators', 'leader', 'property']
class ConvertDynOptionDescription(DynOptionDescription):
@ -31,22 +30,22 @@ class ConvertDynOptionDescription(DynOptionDescription):
check_name=False)
def convert_tiramisu_value(value, obj):
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
"""
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]
if value is None:
return value
func = {IntOption: int,
BoolOption: _convert_boolean}.get(obj, None)
func = CONVERT_OPTION[type_].get('func', None)
if func is None:
return value
if isinstance(value, list):
@ -55,12 +54,12 @@ def convert_tiramisu_value(value, obj):
return func(value)
CONVERT_OPTION = {'number': dict(opttype=IntOption),
CONVERT_OPTION = {'number': dict(opttype=IntOption, func=int),
'choice': dict(opttype=ChoiceOption),
'string': dict(opttype=StrOption),
'password': dict(opttype=PasswordOption),
'mail': dict(opttype=EmailOption),
'boolean': dict(opttype=BoolOption, initkwargs={'default': True}),
'boolean': dict(opttype=BoolOption, initkwargs={'default': True}, func=convert_boolean),
'symlink': dict(opttype=SymLinkOption),
'filename': dict(opttype=FilenameOption),
'date': dict(opttype=DateOption),
@ -83,11 +82,37 @@ CONVERT_OPTION = {'number': dict(opttype=IntOption),
}
class Elt:
class BaseElt:
def __init__(self, attrib):
self.attrib = attrib
class Common:
def populate_properties(self, child):
if child.get('type') == 'calculation':
kwargs = {'condition': child.attrib['source'],
'expected': ParamValue(child.attrib.get('expected'))}
if child.attrib['inverse'] == 'True':
kwargs['reverse_condition'] = ParamValue(True)
self.attrib['properties'].append((ParamValue(child.text), kwargs))
else:
self.attrib['properties'].append(child.text)
def build_properties(self):
for index, prop in enumerate(self.attrib['properties']):
if isinstance(prop, tuple):
action, kwargs = prop
kwargs['condition'] = ParamOption(self.storage.get(kwargs['condition']).get(), todict=True)
prop = Calculation(calc_value,
Params(action,
kwargs=kwargs))
self.attrib['properties'][index] = prop
if self.attrib['properties']:
self.attrib['properties'] = tuple(self.attrib['properties'])
else:
del self.attrib['properties']
class PopulateTiramisuObjects:
def __init__(self):
self.storage = ElementStorage()
@ -110,9 +135,19 @@ class PopulateTiramisuObjects:
if set(attr.itervalues()) == set(['True', 'False']):
self.booleans.append(attr.name)
def make_tiramisu_objects(self, xmlroot, eosfunc):
self.eosfunc = imp.load_source('eosfunc', eosfunc)
family = self.get_root_family()
for xmlelt in self.reorder_family(xmlroot):
self.iter_family(xmlelt,
family,
None,
)
def get_root_family(self):
family = Family(Elt({'name': 'baseoption',
'doc': 'baseoption'}),
family = Family(BaseElt({'name': 'baseoption',
'doc': 'baseoption'}),
self.booleans,
self.storage,
self.eosfunc,
@ -131,36 +166,24 @@ class PopulateTiramisuObjects:
xmlelts.append(xmlelt)
return xmlelts
def make_tiramisu_objects(self, xmlroot, eosfunc):
self.eosfunc = imp.load_source('eosfunc', eosfunc)
family = self.get_root_family()
for xmlelt in self.reorder_family(xmlroot):
self.iter_family(xmlelt,
family,
None,
)
def iter_family(self,
child,
family,
subpath,
):
if child.tag not in KNOWN_TAGS:
raise CreoleLoaderError(_('unknown tag {}').format(child.tag))
if child.tag in ['family', 'leader']:
self.populate_family(family,
child,
subpath,
)
elif child.tag == 'separators':
self.parse_separators(child)
function = self.populate_family
elif child.tag == 'variable':
self.populate_variable(child, subpath, family)
function = self.populate_variable
elif child.tag == 'property':
self.parse_properties(family, child)
# property already imported with family
return
else:
raise Exception('unknown tag {}'.format(child.tag))
function(family,
child,
subpath,
)
def populate_family(self,
parent_family,
@ -174,10 +197,9 @@ class PopulateTiramisuObjects:
self.booleans,
self.storage,
self.eosfunc,
elt.tag == 'leader',
)
self.storage.add(path, family)
if elt.tag == 'leader':
family.set_leader()
parent_family.add(family)
if len(elt) != 0:
for child in elt:
@ -186,14 +208,11 @@ class PopulateTiramisuObjects:
path,
)
def parse_separators(self,
separators,
):
for separator in separators:
elt = self.storage.get(separator.attrib['name'])
elt.add_information('separator', separator.text)
def populate_variable(self, elt, subpath, family):
def populate_variable(self,
family,
elt,
subpath,
):
is_follower = False
is_leader = False
if family.is_leader:
@ -214,16 +233,6 @@ class PopulateTiramisuObjects:
self.storage.add(path, variable)
family.add(variable)
def parse_properties(self, family, child):
if child.get('type') == 'calculation':
kwargs = {'condition': child.attrib['source'],
'expected': ParamValue(child.attrib.get('expected'))}
if child.attrib['inverse'] == 'True':
kwargs['reverse_condition'] = ParamValue(True)
family.attrib['properties'].append((ParamValue(child.text), kwargs))
else:
family.attrib['properties'].append(child.text)
def build_path(self,
subpath,
elt,
@ -248,22 +257,6 @@ class ElementStorage:
return self.paths[path]
class Common:
def build_properties(self):
for index, prop in enumerate(self.attrib['properties']):
if isinstance(prop, tuple):
action, kwargs = prop
kwargs['condition'] = ParamOption(self.storage.get(kwargs['condition']).get(), todict=True)
prop = Calculation(calc_value,
Params(action,
kwargs=kwargs))
self.attrib['properties'][index] = prop
if self.attrib['properties']:
self.attrib['properties'] = tuple(self.attrib['properties'])
else:
del self.attrib['properties']
class Variable(Common):
def __init__(self, elt, booleans, storage, is_follower, is_leader, eosfunc):
self.option = None
@ -282,185 +275,183 @@ class Variable(Common):
self.is_follower = is_follower
self.is_leader = is_leader
self.object_type = convert_option['opttype']
self.populate_choice(elt)
for child in elt:
self.populate_property(child)
self.populate_value(child)
self.populate_check(child)
if 'multi' in self.attrib and self.attrib['multi']:
self.attrib['default'] = []
if self.is_follower:
self.attrib['default_multi'] = []
self.parse_children(elt)
if self.is_follower:
if self.attrib['multi'] is True:
self.attrib['multi'] = submulti
else:
self.attrib['multi'] = True
if elt.attrib['type'] == 'symlink':
self.attrib['opt'] = storage.get(self.attrib['opt'])
def parse_children(self,
elt,
):
if elt.attrib['type'] == 'choice':
values = []
for child in elt:
if child.tag == 'property':
self.populate_properties(child)
elif child.tag == 'value':
if child.attrib.get('type') == 'calculation':
self.calc_value(child)
else:
self.populate_value(child, elt)
elif child.tag == 'check':
self.populate_check(child)
elif child.tag == 'choice':
value = child.text
if child.attrib['type'] == 'number':
value = int(value)
values.append(value)
if elt.attrib['type'] == 'choice':
self.attrib['values'] = tuple(values)
def populate_attrib(self,
elt,
):
for key, value in elt.attrib.items():
if key == 'multi' and elt.attrib['type'] == 'symlink':
continue
if key == 'multi' and value == 'submulti':
value = submulti
elif key in self.booleans:
if key in self.booleans:
if value == 'True':
value = True
elif value == 'False':
value = False
else:
raise CreoleLoaderError(_('unknown value {} for {}').format(value, key))
if key in ['help', 'test']:
if key in ['help', 'test', 'separator']:
self.add_information(key, value)
elif key != 'type':
self.attrib[key] = value
def populate_choice(self,
elt,
):
if elt.attrib['type'] == 'choice':
values = []
for child in elt:
if child.tag == 'choice':
value = child.text
if child.attrib['type'] == 'number':
value = int(value)
values.append(value)
self.attrib['values'] = tuple(values)
def calc_value(self, child):
args = []
kwargs = {}
if child.text is not None and child.text.strip():
function = child.text.strip()
else:
function = child.attrib['name']
for param in child:
self.parse_param(args,
kwargs,
param,
)
self.attrib['default'] = {'function': getattr(self.eosfunc, function),
'args': args,
'kwargs': kwargs,
'warnings_only': False}
def populate_property(self, child):
if child.tag == 'property':
if child.get('type') == 'calculation':
kwargs = {'condition': child.attrib['source'],
'expected': ParamValue(child.attrib.get('expected'))}
if child.attrib['inverse'] == 'True':
kwargs['reverse_condition'] = ParamValue(True)
self.attrib['properties'].append((ParamValue(child.text), kwargs))
def populate_value(self, child, elt):
if "type" in child.attrib:
type_ = child.attrib['type']
else:
type_ = elt.attrib['type']
value = convert_tiramisu_value(child.text, type_)
if self.is_follower:
if self.attrib['multi'] is True:
self.attrib['default_multi'].append(value)
else:
self.attrib['properties'].append(child.text)
def populate_value(self, child):
if child.tag == 'value':
if child.attrib.get('type') == 'calculation':
if child.text is not None and child.text.strip():
self.attrib['default'] = (child.text.strip(),)
else:
params = []
for param in child:
params.append(self.parse_param(param))
self.attrib['default'] = (child.attrib['name'], params, False)
else:
if "type" in child.attrib:
type_ = CONVERT_OPTION[child.attrib['type']]['opttype']
else:
type_ = self.object_type
if self.attrib['multi'] is True and not self.is_follower:
if 'default' not in self.attrib:
self.attrib['default'] = []
value = convert_tiramisu_value(child.text, type_)
self.attrib['default'].append(value)
if 'default_multi' not in self.attrib and not self.is_leader:
self.attrib['default_multi'] = value
elif self.attrib['multi'] == submulti:
if 'default' not in self.attrib:
self.attrib['default'] = []
value = convert_tiramisu_value(child.text, type_)
if not self.is_follower:
if not isinstance(value, list):
dvalue = [value]
else:
dvalue = value
self.attrib['default'].append(dvalue)
if value and 'default_multi' not in self.attrib and not self.is_leader:
self.attrib['default_multi'] = []
if not self.is_leader:
self.attrib['default_multi'].append(value)
else:
value = convert_tiramisu_value(child.text, type_)
if self.is_follower:
self.attrib['default_multi'] = value
else:
self.attrib['default'] = value
self.attrib['default_multi'] = value
elif self.attrib['multi'] is True:
self.attrib['default'].append(value)
if not self.is_leader:
self.attrib['default_multi'] = value
else:
self.attrib['default'] = value
def populate_check(self, child):
if child.tag == 'check':
params = []
for param in child:
params.append(self.parse_param(param))
#check.params = params
self.attrib['validators'].append((child.attrib['name'], params, child.attrib['warnings_only']))
args = [ParamSelfOption()]
kwargs = {}
for param in child:
self.parse_param(args,
kwargs,
param,
)
self.attrib['validators'].append({'function': getattr(self.eosfunc, child.attrib['name']),
'args': args,
'kwargs': kwargs,
'warnings_only': convert_boolean(child.attrib['warnings_only'])})
def parse_param(self, param):
name = param.attrib.get('name', '')
def parse_param(self,
args,
kwargs,
param,
):
if param.attrib['type'] == 'string':
value = param.text
elif param.attrib['type'] == 'variable':
transitive = param.attrib.get('transitive', 'False')
if transitive == 'True':
transitive = True
elif transitive == 'False':
transitive = False
else:
raise CreoleLoaderError(_('unknown transitive boolean {}').format(transitive))
value = [param.text, transitive, param.attrib.get('suffix')]
value = ParamValue(param.text)
elif param.attrib['type'] == 'number':
value = int(param.text)
value = ParamValue(int(param.text))
elif param.attrib['type'] == 'variable':
transitive = convert_boolean(param.attrib.get('transitive', 'False'))
value = {'option': param.text,
'notraisepropertyerror': transitive,
'todict': param.text in FUNC_TO_DICT,
}
if 'suffix' in param.attrib:
value['suffix'] = param.attrib['suffix']
else:
raise CreoleLoaderError(_('unknown param type {}').format(param.attrib['type']))
return(name, value)
if 'name' not in param.attrib:
args.append(value)
else:
kwargs[param.attrib['name']] = value
def build_calculator(self, key):
def build_param(param):
option = self.storage.get(param['option']).get()
if 'suffix' in param:
family = '.'.join(param['option'].split('.')[:-1])
return ParamDynOption(option,
param['suffix'],
self.storage.get(family).get(),
notraisepropertyerror=param['notraisepropertyerror'],
todict=param['todict'],
)
return ParamOption(option,
notraisepropertyerror=param['notraisepropertyerror'],
todict=param['todict'],
)
return param
def build_calculation(value):
args = []
kwargs = {}
for param in value['args']:
if isinstance(param, dict):
param = build_param(param)
args.append(param)
for key, param in value['kwargs'].items():
if isinstance(param, dict):
param = build_param(param)
kwargs[key] = param
return Calculation(value['function'],
Params(tuple(args),
kwargs=kwargs,
),
warnings_only=value['warnings_only'],
)
if key in self.attrib:
values = self.attrib[key]
if isinstance(values, list):
self.attrib[key] = []
for value in values:
if isinstance(value, dict):
value = build_calculation(value)
self.attrib[key].append(value)
else:
if isinstance(values, dict):
values = build_calculation(values)
self.attrib[key] = values
def add_information(self, key, value):
if key in self.informations:
raise CreoleLoaderError(_('key already exists in information {}').format(key))
self.informations[key] = value
def build_calculator(self, key):
if key in self.attrib:
values = self.attrib[key]
if isinstance(values, list):
is_list = True
else:
is_list = False
values = [values]
ret = []
for value in values:
if isinstance(value, tuple):
if key == 'validators':
args = [ParamSelfOption()]
else:
args = []
kwargs = {}
if len(value) == 3:
for param in value[1]:
if isinstance(param[1], list):
option = self.storage.get(param[1][0]).get()
param_kwargs = {'notraisepropertyerror': param[1][1]}
if value[0] in FUNC_TO_DICT:
param_kwargs['todict'] = True
if not param[1][2]:
param_value = ParamOption(option,
**param_kwargs,
)
else:
family = '.'.join(param[1][0].split('.', 3)[:2])
param_value = ParamDynOption(option,
param[1][2],
self.storage.get(family).get(),
**param_kwargs,
)
else:
param_value = ParamValue(param[1])
if not param[0]:
args.append(param_value)
else:
kwargs[param[0]] = param_value
ret.append(Calculation(getattr(self.eosfunc, value[0]),
Params(tuple(args),
kwargs=kwargs)))
else:
ret.append(value)
if not is_list:
self.attrib[key] = ret[0]
else:
self.attrib[key] = ret
def get(self):
if self.option is None:
if self.object_type is SymLinkOption:
@ -490,6 +481,7 @@ class Family(Common):
booleans,
storage,
eosfunc,
is_leader=False,
):
self.option = None
self.attrib = {}
@ -504,6 +496,11 @@ class Family(Common):
self.add_information(key, value)
else:
self.attrib[key] = value
if not isinstance(elt, BaseElt) and len(elt) != 0:
for child in elt:
if child.tag == 'property':
self.populate_properties(child)
self.is_leader = is_leader
def add(self, child):
self.children.append(child)
@ -513,9 +510,6 @@ class Family(Common):
raise CreoleLoaderError(_('key already exists in information {}').format(key))
self.informations[key] = value
def set_leader(self):
self.is_leader = True
def get(self):
if self.option is None:
self.attrib['children'] = []

View File

@ -38,7 +38,7 @@ FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
# CreoleObjSpace's elements that shall be forced to the UnRedefinable type
FORCE_UNREDEFINABLES = ('value',)
# CreoleObjSpace's elements that shall be set to the UnRedefinable type
UNREDEFINABLE = ('submulti', 'multi', 'type')
UNREDEFINABLE = ('multi', 'type')
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
'force_store_value', 'disabled', 'mandatory')
@ -46,17 +46,14 @@ CONVERT_PROPERTIES = {'auto_save': ['force_store_value'], 'auto_freeze': ['force
RENAME_ATTIBUTES = {'description': 'doc'}
INCOMPATIBLE_ATTRIBUTES = [['multi', 'submulti']]
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value', 'target')
CONVERT_EXPORT = {'Leadership': 'leader',
'Separators': 'separators',
'Variable': 'variable',
'Value': 'value',
'Property': 'property',
'Choice': 'choice',
'Param': 'param',
'Separator': 'separator',
'Check': 'check',
}
@ -461,13 +458,6 @@ class CreoleObjSpace:
if not (attr == 'name' and getattr(variableobj, 'name', None) != None):
setattr(variableobj, attr, val)
keys = list(vars(variableobj).keys())
for incompatible in INCOMPATIBLE_ATTRIBUTES:
found = False
for inc in incompatible:
if inc in keys:
if found:
raise CreoleDictConsistencyError(_('those attributes are incompatible {}').format(incompatible))
found = True
def variableobj_tree_visitor(self,
child,