{% 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()