343 lines
19 KiB
Django/Jinja
343 lines
19 KiB
Django/Jinja
{% if is_first_by_category %}
|
|
##
|
|
## object types
|
|
##
|
|
|
|
{% endif %}
|
|
{% if render_imports %}
|
|
import uuid
|
|
import pprint
|
|
from typing import Dict, List, Optional, TypeVar
|
|
|
|
from autobahn.wamp.request import Publication, Subscription, Registration
|
|
|
|
import flatbuffers
|
|
from flatbuffers.compat import import_numpy
|
|
np = import_numpy()
|
|
|
|
{% endif %}
|
|
|
|
# https://stackoverflow.com/a/46064289/884770
|
|
T_{{ metadata.classname }} = TypeVar('T_{{ metadata.classname }}', bound='{{ metadata.classname }}')
|
|
|
|
|
|
class {{ metadata.classname }}(object):
|
|
"""
|
|
{{ metadata.docs }}
|
|
"""
|
|
__slots__ = ['_tab', {% for field_name in metadata.fields_by_id %}'_{{ metadata.fields[field_name].name }}', {% endfor %}]
|
|
|
|
def __init__(self):
|
|
# the underlying FlatBuffers vtable
|
|
self._tab = None
|
|
|
|
{% for field_name in metadata.fields_by_id %}
|
|
# {{ metadata.fields[field_name]['docs'] }}
|
|
self._{{ metadata.fields[field_name].name }} = None
|
|
|
|
{% endfor %}
|
|
|
|
{% for field_name in metadata.fields_by_id %}
|
|
@property
|
|
def {{ metadata.fields[field_name].name }}(self) -> {{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}:
|
|
"""
|
|
{{ metadata.fields[field_name]['docs'] }}
|
|
"""
|
|
if self._{{ metadata.fields[field_name].name }} is None and self._tab:
|
|
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset({{ metadata.fields[field_name].offset }}))
|
|
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'str' %}
|
|
# access type "string" attribute:
|
|
value = ''
|
|
if o != 0:
|
|
_value = self._tab.String(o + self._tab.Pos)
|
|
if _value is not None:
|
|
value = _value.decode('utf8')
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bytes' %}
|
|
# access type "bytes" attribute:
|
|
value = b''
|
|
if o != 0:
|
|
_off = self._tab.Vector(o)
|
|
_len = self._tab.VectorLen(o)
|
|
_value = memoryview(self._tab.Bytes)[_off:_off + _len]
|
|
if _value is not None:
|
|
value = _value
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['int', 'float', 'double'] %}
|
|
# access type "int|float|double" attribute:
|
|
value = 0
|
|
if o != 0:
|
|
_value = self._tab.Get(flatbuffers.number_types.{{ FbsType.FBS2FLAGS[metadata.fields[field_name].type.basetype] }}, o + self._tab.Pos)
|
|
if _value is not None:
|
|
value = _value
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bool' %}
|
|
# access type "bool" attribute:
|
|
value = False
|
|
if o != 0:
|
|
_value = self._tab.Get(flatbuffers.number_types.BoolFlags, o + self._tab.Pos)
|
|
if _value is not None:
|
|
value = _value
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'uuid.UUID' %}
|
|
# access type "uuid.UUID" attribute:
|
|
value = uuid.UUID(bytes=b'\x00' * 16)
|
|
if o != 0:
|
|
_off = self._tab.Vector(o)
|
|
_len = self._tab.VectorLen(o)
|
|
_value = memoryview(self._tab.Bytes)[_off:_off + _len]
|
|
if _value is not None:
|
|
value = uuid.UUID(bytes=bytes(_value))
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'np.datetime64' %}
|
|
# access type "np.datetime64" attribute:
|
|
value = np.datetime64(0, 'ns')
|
|
if o != 0:
|
|
_value = self._tab.Get(flatbuffers.number_types.Uint64Flags, o + self._tab.Pos)
|
|
if value is not None:
|
|
value = np.datetime64(_value, 'ns')
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Vector %}
|
|
# access type "Vector" attribute:
|
|
value = []
|
|
if o != 0:
|
|
_start_off = self._tab.Vector(o)
|
|
_len = self._tab.VectorLen(o)
|
|
for j in range(_len):
|
|
_off = _start_off + flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4
|
|
_off = self._tab.Indirect(_off)
|
|
{% if metadata.fields[field_name].type.element == FbsType.Obj %}
|
|
_value = {{ metadata.fields[field_name].type.objtype.split('.')[-1] }}.cast(self._tab.Bytes, _off)
|
|
{% else %}
|
|
# FIXME [8]
|
|
{% endif %}
|
|
value.append(_value)
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Obj %}
|
|
# access type "Object" attribute:
|
|
value = {{ metadata.fields[field_name].type.objtype.split('.')[-1] }}()
|
|
if o != 0:
|
|
_off = self._tab.Indirect(o + self._tab.Pos)
|
|
value = {{ metadata.fields[field_name].type.objtype.split('.')[-1] }}.cast(self._tab.Bytes, _off)
|
|
|
|
{% else %}
|
|
# FIXME [5]
|
|
raise NotImplementedError('implement processing [5] of FlatBuffers type "{}"'.format({{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}))
|
|
{% endif %}
|
|
assert value is not None
|
|
self._{{ metadata.fields[field_name].name }} = value
|
|
return self._{{ metadata.fields[field_name].name }}
|
|
|
|
@{{ metadata.fields[field_name].name }}.setter
|
|
def {{ metadata.fields[field_name].name }}(self, value: Optional[{{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}]):
|
|
if value is not None:
|
|
self._{{ metadata.fields[field_name].name }} = value
|
|
else:
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'str' %}
|
|
# set default value on type "string" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = ''
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bytes' %}
|
|
# set default value on type "bytes" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = b''
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['int', 'float', 'double'] %}
|
|
# set default value on type "int|float|double" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = 0
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bool' %}
|
|
# set default value on type "bool" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = False
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'uuid.UUID' %}
|
|
# set default value on type "uuid.UUID" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = uuid.UUID(bytes=b'\x00' * 16)
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'np.datetime64' %}
|
|
# set default value on type "np.datetime64" attribute:
|
|
self._{{ metadata.fields[field_name].name }} = np.datetime64(0, 'ns')
|
|
# set default value on type "List" attribute:
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Vector %}
|
|
self._{{ metadata.fields[field_name].name }} = []
|
|
# set default value on type "Object" attribute:
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Obj %}
|
|
self._{{ metadata.fields[field_name].name }} = {{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}()
|
|
{% else %}
|
|
# FIXME [6]
|
|
raise NotImplementedError('implement processing [2] of FlatBuffers type "{}", basetype {}'.format({{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}, {{ metadata.fields[field_name].type.basetype }}))
|
|
{% endif %}
|
|
|
|
{% endfor %}
|
|
|
|
@staticmethod
|
|
def parse(data: Dict) -> T_{{ metadata.classname }}:
|
|
"""
|
|
Parse generic, native language object into a typed, native language object.
|
|
|
|
:param data: Generic native language object to parse, e.g. output of ``cbor2.loads``.
|
|
|
|
:returns: Typed object of this class.
|
|
"""
|
|
for key in data.keys():
|
|
assert key in {{ metadata.fields_by_id }}
|
|
obj = {{ metadata.classname }}()
|
|
{% for field_name in metadata.fields_by_id %}
|
|
if '{{ field_name }}' in data:
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'str' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == str), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
obj.{{ metadata.fields[field_name].name }} = data['{{ field_name }}']
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bytes' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == bytes), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
obj.{{ metadata.fields[field_name].name }} = data['{{ field_name }}']
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'int' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == int), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
obj.{{ metadata.fields[field_name].name }} = data['{{ field_name }}']
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'float' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == float), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
obj.{{ metadata.fields[field_name].name }} = data['{{ field_name }}']
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'bool' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == bool), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
obj.{{ metadata.fields[field_name].name }} = data['{{ field_name }}']
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'uuid.UUID' %}
|
|
assert (data['{{ field_name }}'] is None or (type(data['{{ field_name }}']) == bytes and len(data['{{ field_name }}']) == 16)), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
if data['{{ field_name }}'] is not None:
|
|
obj.{{ metadata.fields[field_name].name }} = uuid.UUID(bytes=data['{{ field_name }}'])
|
|
else:
|
|
obj.{{ metadata.fields[field_name].name }} = None
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'np.datetime64' %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == int), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
if data['{{ field_name }}'] is not None:
|
|
obj.{{ metadata.fields[field_name].name }} = np.datetime64(data['{{ field_name }}'], 'ns')
|
|
else:
|
|
obj.{{ metadata.fields[field_name].name }} = np.datetime64(0, 'ns')
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Vector %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == list), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
_value = []
|
|
for v in data['{{ field_name }}']:
|
|
{% if metadata.fields[field_name].type.element == FbsType.Obj %}
|
|
_value.append({{ metadata.fields[field_name].type.objtype.split('.')[-1] }}.parse(v))
|
|
{% else %}
|
|
_value.append(v)
|
|
{% endif %}
|
|
obj.{{ metadata.fields[field_name].name }} = _value
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Obj %}
|
|
assert (data['{{ field_name }}'] is None or type(data['{{ field_name }}']) == dict), '{} has wrong type {}'.format('{{ field_name }}', type(data['{{ field_name }}']))
|
|
_value = {{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}.parse(data['{{ field_name }}'])
|
|
obj.{{ metadata.fields[field_name].name }} = _value
|
|
|
|
{% else %}
|
|
# FIXME [3]
|
|
raise NotImplementedError('implement processing [3] of FlatBuffers type "{}"'.format({{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}))
|
|
{% endif %}
|
|
{% endfor %}
|
|
return obj
|
|
|
|
def marshal(self) -> Dict:
|
|
"""
|
|
Marshal all data contained in this typed native object into a generic object.
|
|
|
|
:returns: Generic object that can be serialized to bytes using e.g. ``cbor2.dumps``.
|
|
"""
|
|
obj = {
|
|
{% for field_name in metadata.fields_by_id %}
|
|
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['str', 'bytes', 'int', 'long', 'float', 'double', 'bool'] %}
|
|
'{{ metadata.fields[field_name].name }}': self.{{ metadata.fields[field_name].name }},
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'uuid.UUID' %}
|
|
'{{ metadata.fields[field_name].name }}': self.{{ metadata.fields[field_name].name }}.bytes if self.{{ metadata.fields[field_name].name }} is not None else None,
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'np.datetime64' %}
|
|
'{{ metadata.fields[field_name].name }}': int(self.{{ metadata.fields[field_name].name }}) if self.{{ metadata.fields[field_name].name }} is not None else None,
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Vector %}
|
|
{% if metadata.fields[field_name].type.element == FbsType.Obj %}
|
|
'{{ metadata.fields[field_name].name }}': [o.marshal() for o in self.{{ metadata.fields[field_name].name }}] if self.{{ metadata.fields[field_name].name }} is not None else None,
|
|
{% else %}
|
|
'{{ metadata.fields[field_name].name }}': self.{{ metadata.fields[field_name].name }},
|
|
{% endif %}
|
|
|
|
{% elif metadata.fields[field_name].type.basetype == FbsType.Obj %}
|
|
'{{ metadata.fields[field_name].name }}': self.{{ metadata.fields[field_name].name }}.marshal() if self.{{ metadata.fields[field_name].name }} is not None else None,
|
|
|
|
{% else %}
|
|
# FIXME [4]: implement processing [4] of FlatBuffers type "{{ metadata.fields[field_name].type | string }}" (Python type "{{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}")
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
return obj
|
|
|
|
def __str__(self) -> str:
|
|
"""
|
|
Return string representation of this object, suitable for e.g. logging.
|
|
|
|
:returns: String representation of this object.
|
|
"""
|
|
return '\n{}\n'.format(pprint.pformat(self.marshal()))
|
|
|
|
@staticmethod
|
|
def cast(buf: bytes, offset: int = 0) -> T_{{ metadata.classname }}:
|
|
"""
|
|
Cast a FlatBuffers raw input buffer as a typed object of this class.
|
|
|
|
:param buf: The raw input buffer to cast.
|
|
:param offset: Offset into raw buffer from which to cast flatbuffers from.
|
|
|
|
:returns: New native object that wraps the FlatBuffers raw buffer.
|
|
"""
|
|
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
|
|
x = {{ metadata.classname }}()
|
|
x._tab = flatbuffers.table.Table(buf, n + offset)
|
|
return x
|
|
|
|
def build(self, builder):
|
|
"""
|
|
Build a FlatBuffers raw output buffer from this typed object.
|
|
|
|
:returns: Constructs the FlatBuffers using the builder and
|
|
returns ``builder.EndObject()``.
|
|
"""
|
|
# first, write all string|bytes|etc typed attribute values (in order) to the buffer
|
|
{% for field_name in metadata.fields_by_id %}
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['str', 'bytes'] %}
|
|
_{{ field_name }} = self.{{ field_name }}
|
|
if _{{ field_name }}:
|
|
_{{ field_name }} = builder.CreateString(_{{ field_name }})
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'uuid.UUID' %}
|
|
_{{ field_name }} = self.{{ field_name }}.bytes if self.{{ field_name }} else None
|
|
if _{{ field_name }}:
|
|
_{{ field_name }} = builder.CreateString(_{{ field_name }})
|
|
{% else %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
# now start a new object in the buffer and write the actual object attributes (in field
|
|
# order) to the buffer
|
|
builder.StartObject({{ metadata.fields_by_id|length }})
|
|
|
|
{% for field_name in metadata.fields_by_id %}
|
|
|
|
{% if metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['str', 'bytes', 'uuid.UUID'] %}
|
|
if _{{ field_name }}:
|
|
builder.PrependUOffsetTRelativeSlot({{ metadata.fields[field_name].id }}, flatbuffers.number_types.UOffsetTFlags.py_type(_{{ field_name }}), 0)
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) == 'np.datetime64' %}
|
|
if self.{{ field_name }}:
|
|
builder.PrependUint64Slot({{ metadata.fields[field_name].id }}, int(self.{{ field_name }}), 0)
|
|
|
|
{% elif metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) in ['bool', 'int', 'float'] %}
|
|
if self.{{ field_name }}:
|
|
builder.{{ FbsType.FBS2PREPEND[metadata.fields[field_name].type.basetype] }}({{ metadata.fields[field_name].id }}, self.{{ field_name }}, 0)
|
|
|
|
{% else %}
|
|
# FIXME [1]
|
|
# raise NotImplementedError('implement builder [1] for type "{}"'.format({{ metadata.fields[field_name].type.map('python', metadata.fields[field_name].attrs, True) }}))
|
|
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
return builder.EndObject()
|