import xml.etree.ElementTree as ET
# make sure to use a python implementation of ET.Element
if hasattr(ET, '_Element_Py'):
XMLElement = ET._Element_Py
else:
XMLElement = ET.Element
def _count(values):
if type(values) == list or type(values) == tuple:
return len(values)
elif values is None:
return 0
else:
return 1
def _stringify(values):
if isinstance(values, list) or isinstance(values, tuple):
return ' '.join([str(x) for x in values])
elif isinstance(values, type(True)):
return '1' if values else '0'
else:
return str(values)
class XMLNode(XMLElement):
def __init__(self, tag=None, attrib={}, parent=None):
if tag is None:
tag = 'ServerManagerConfiguration'
ET._Element_Py.__init__(self, tag, attrib)
self.parent = parent
if self.parent is not None:
self.parent.append(self)
self.context = {}
@staticmethod
def wrap(element):
result = XMLNode(element.tag, element.attrib)
result.text = element.text
result.tail = element.tail
for child in element:
wrapped_child = XMLNode.wrap(child)
result.append(wrapped_child)
wrapped_child.parent = result
return result
def _find_group(self, name):
if self.tag == 'ServerManagerConfiguration':
child = self.find('./ProxyGroup[@name="{}"]'.format(name))
if child is not None:
return child
return XMLNode('ProxyGroup', {'name': name}, self)
elif self.parent is None:
raise Exception('No XML root.')
else:
return self.parent._find_group(name)
def _get_hints(self):
hints = self.find('./Hints')
if hints is None:
hints = XMLNode('Hints', {}, self)
return hints
def _find_source(self):
if self.tag == 'SourceProxy':
return self
elif self.parent is None:
raise Exception('No source proxy.')
else:
return self.parent._find_source()
# ------------------------------------------------------------------------------
# source proxies
# ------------------------------------------------------------------------------
[docs] def source(self, name=None, label=None, class_=None, group='sources'):
root = self._find_group(group)
d = {'name': name, 'label': label, 'class': class_}
try:
if d['class'] is None:
d['class'] = self.context['class']
if d['name'] is None:
d['name'] = self.context.get('name', d['class'])
if d['label'] is None:
d['label'] = self.context.get('label', d['name'])
except KeyError as e:
raise Exception('Missing parameter: {}'.format(e.args[0]))
return XMLNode('SourceProxy', d, root)
[docs] def filter(self, name=None, label=None, class_=None):
return self.source(name, label, class_, group='filters')
# ------------------------------------------------------------------------------
# properties
# ------------------------------------------------------------------------------
[docs] def _vector(self, type_=None, name=None, label=None,
command=None, command_prefix='Set', group_id=None,
number_of_elements=None, default_values=None, animateable=None,
panel_visibility=None):
root = self._find_source()
d = {
'name': name,
'label': label,
'command': command,
'number_of_elements': number_of_elements,
'default_values': default_values,
}
if animateable is not None:
d['animateable'] = _stringify(animateable)
if panel_visibility is not None:
if panel_visibility not in ['default', 'never', 'advanced']:
raise Exception('Invalid value for panel_visibility')
d['panel_visibility'] = panel_visibility
try:
if type_ is None:
type_ = self.context['type']
if d['name'] is None:
d['name'] = self.context['name']
if d['label'] is None:
d['label'] = self.context.get('label', d['name'])
if d['command'] is None:
d['command'] = self.context.get('command', command_prefix + d['name'])
if d['default_values'] is None:
d['default_values'] = self.context['default_values']
if d['number_of_elements'] is None:
d['number_of_elements'] = self.context.get('number_of_elements', _count(d['default_values']))
except KeyError as e:
raise Exception('Missing parameter: {}'.format(e.args[0]))
d['default_values'] = _stringify(d['default_values'])
d['number_of_elements'] = _stringify(d['number_of_elements'])
if type_ == 'int' or type_ == 'bool':
xml_type = 'Int'
elif type_ == 'double' or type_ == 'float':
xml_type = 'Double'
else:
raise Exception('Invalid vector property type: {}'.format(type_))
node = XMLNode('{}VectorProperty'.format(xml_type), d, root)
node.group_id = group_id
return node.boolean() if type_ == 'bool' else node
[docs] def intvector(self, **kwargs):
return self._vector(type_='int', **kwargs)
[docs] def doublevector(self, *args, **kwargs):
return self._vector(type_='double', **kwargs)
[docs] def autovector(self, *args, **kwargs):
return self._vector(**kwargs)
[docs] def group(self, label, group_id, panel_visibility=None):
root = self._find_source()
d = {'label': label}
if panel_visibility is not None:
d['panel_visibility'] = panel_visibility
node = XMLNode('PropertyGroup', d, root)
for child in root:
if hasattr(child, 'group_id') and child.group_id == group_id:
XMLNode('Property', {'name': child.attrib['name']}, node)
return node
# ------------------------------------------------------------------------------
# property domains
# ------------------------------------------------------------------------------
[docs] def array_domain(self, name='input_array', attribute_type='any', number_of_components=None, optional=None):
if self.tag != 'InputProperty':
raise Exception('"input_array" cannot be added to "{}"'.format(self.tag))
if attribute_type not in ['point', 'cell', 'any', 'row']:
raise Exception('Invalid attribute_type "{}"'.format(attribute_type))
d = {
'name': name,
'attribute_type': attribute_type
}
if number_of_components is not None:
d['number_of_components'] = _stringify(number_of_components)
if optional is not None:
d['optional'] = _stringify(optional)
XMLNode('InputArrayDomain', d, self)
return self
[docs] def enumeration(self, items, values):
if self.tag != 'IntVectorProperty':
raise Exception('"enumeration" cannot be added to "{}"'.format(self.tag))
if len(items) != len(values):
raise Exception('items and values must be of same length')
domain = XMLNode('EnumerationDomain', {'name': 'enum'}, self)
for value, text in zip(values, items):
XMLNode('Entry', {'value': _stringify(value), 'text': _stringify(text)}, domain)
return self
[docs] def boolean(self, *args, **kwargs):
if self.tag != 'IntVectorProperty':
raise Exception('"boolean" cannot be added to "{}"'.format(self.tag))
XMLNode('BooleanDomain', {'name': 'bool'}, self)
return self
[docs] def range(self, min=None, max=None):
if self.tag == 'IntVectorProperty':
tag = 'IntRangeDomain'
elif self.tag == 'DoubleVectorProperty':
tag = 'DoubleRangeDomain'
else:
raise Exception('"range" cannot be added to "{}"'.format(self.tag))
d = {
'name': 'range',
}
if min is not None:
d['min'] = _stringify(min)
if max is not None:
d['max'] = _stringify(max)
XMLNode(tag, d, self)
return self
# ------------------------------------------------------------------------------
# hints
# ------------------------------------------------------------------------------
[docs] def documentation(self, text=None, short_help=None, long_help=None):
d = {}
if short_help is not None:
d['short_help'] = short_help
if long_help is not None:
d['long_help'] = long_help
node = XMLNode('Documentation', d, self)
if text is not None:
node.text = text
return self
# ------------------------------------------------------------------------------
# raw XML methods
# ------------------------------------------------------------------------------
[docs] def xml(self, xml_string):
root = XMLNode.wrap(ET.fromstring(xml_string))
self.append(root)
root.parent = self
return root
[docs] def xml_property(self, xml_string):
root = self._find_source()
return root.xml(xml_string)
[docs] def xml_hint(self, xml_string):
root = self
if root.tag not in ['PropertyGroup', 'SourceProxy'] and not root.tag.endswith('VectorProperty'):
root = self.parent
if root is None or root.tag not in ['PropertyGroup', 'SourceProxy'] and not root.tag.endswith('VectorProperty'):
raise Exception('hint cannot be added to "{}"'.format(self.tag))
return root._get_hints().xml(xml_string)