{% if is_first_by_category %} ## ## object types ## {% endif %} {% if render_imports %} import uuid import pprint from typing import Dict, List, Optional from autobahn.wamp.request import Publication, Subscription, Registration import flatbuffers from flatbuffers.compat import import_numpy np = import_numpy() {% endif %} class {{ metadata.classname }}(object): """ {{ metadata.docs }} """ __slots__ = ['_tab', {% for field in metadata.fields_by_id %}'_{{ field.name }}', {% endfor %}] def __init__(self, {% for field in metadata.fields_by_id %}{{ field.name }}: {{ field.type.map('python', field.attrs, required=False, objtype_as_string=True) }} = None, {% endfor %}): # the underlying FlatBuffers vtable self._tab = None {% for field in metadata.fields_by_id %} # {{ field.docs }} self._{{ field.name }}: {{ field.type.map('python', field.attrs, required=False, objtype_as_string=True) }} = {{ field.name }} {% endfor %} def __eq__(self, other): if not isinstance(other, self.__class__): return False {% for field in metadata.fields_by_id %} if other.{{ field.name }} != self.{{ field.name }}: return False {% endfor %} return True def __ne__(self, other): return not self.__eq__(other) {% for field in metadata.fields_by_id %} @property def {{ field.name }}(self) -> {{ field.type.map('python', field.attrs, required=False, objtype_as_string=True) }}: """ {{ field.docs }} """ if self._{{ field.name }} is None and self._tab: o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset({{ field.offset }})) {% if field.type.map('python', field.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 field.type.map('python', field.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 field.type.map('python', field.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[field.type.basetype] }}, o + self._tab.Pos) if _value is not None: value = _value {% elif field.type.map('python', field.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 field.type.map('python', field.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 field.type.map('python', field.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 field.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 False and field.type.element == FbsType.Obj %} _value = {{ field.type.element.split('.')[-1] }}.cast(self._tab.Bytes, _off) {% else %} # FIXME [8] _value = {{ field.type.element }}() {% endif %} value.append(_value) {% elif field.type.basetype == FbsType.Obj %} # access type "Object" attribute: {% if field.type.objtype %} value = {{ field.type.objtype.split('.')[-1] }}() if o != 0: _off = self._tab.Indirect(o + self._tab.Pos) value = {{ field.type.objtype.split('.')[-1] }}.cast(self._tab.Bytes, _off) {% else %} # FIXME [9]: objtype of field "{{ field.name }}" is None value = '' {% endif %} {% else %} # FIXME [5] raise NotImplementedError('implement processing [5] of FlatBuffers type "{}"'.format({{ field.type.map('python', field.attrs, True) }})) {% endif %} assert value is not None self._{{ field.name }} = value return self._{{ field.name }} @{{ field.name }}.setter def {{ field.name }}(self, value: {{ field.type.map('python', field.attrs, required=False, objtype_as_string=True) }}): if value is not None: self._{{ field.name }} = value else: {% if field.type.map('python', field.attrs, True) == 'str' %} # set default value on type "string" attribute: self._{{ field.name }} = '' {% elif field.type.map('python', field.attrs, True) == 'bytes' %} # set default value on type "bytes" attribute: self._{{ field.name }} = b'' {% elif field.type.map('python', field.attrs, True) in ['int', 'float', 'double'] %} # set default value on type "int|float|double" attribute: self._{{ field.name }} = 0 {% elif field.type.map('python', field.attrs, True) == 'bool' %} # set default value on type "bool" attribute: self._{{ field.name }} = False {% elif field.type.map('python', field.attrs, True) == 'uuid.UUID' %} # set default value on type "uuid.UUID" attribute: self._{{ field.name }} = uuid.UUID(bytes=b'\x00' * 16) {% elif field.type.map('python', field.attrs, True) == 'np.datetime64' %} # set default value on type "np.datetime64" attribute: self._{{ field.name }} = np.datetime64(0, 'ns') # set default value on type "List" attribute: {% elif field.type.basetype == FbsType.Vector %} self._{{ field.name }} = [] # set default value on type "Object" attribute: {% elif field.type.basetype == FbsType.Obj %} self._{{ field.name }} = {{ field.type.map('python', field.attrs, True) }}() {% else %} # FIXME [6] raise NotImplementedError('implement processing [2] of FlatBuffers type "{}", basetype {}'.format({{ field.type.map('python', field.attrs, True) }}, {{ field.type.basetype }})) {% endif %} {% endfor %} @staticmethod def parse(data: Dict) -> '{{ 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. """ # FIXME # for key in data.keys(): # assert key in {{ metadata.fields.keys() }} obj = {{ metadata.classname }}() {% for field in metadata.fields_by_id %} if '{{ field.name }}' in data: {% if field.type.map('python', field.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.{{ field.name }} = data['{{ field.name }}'] {% elif field.type.map('python', field.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.{{ field.name }} = data['{{ field.name }}'] {% elif field.type.map('python', field.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.{{ field.name }} = data['{{ field.name }}'] {% elif field.type.map('python', field.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.{{ field.name }} = data['{{ field.name }}'] {% elif field.type.map('python', field.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.{{ field.name }} = data['{{ field.name }}'] {% elif field.type.map('python', field.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.{{ field.name }} = uuid.UUID(bytes=data['{{ field.name }}']) else: obj.{{ field.name }} = None {% elif field.type.map('python', field.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.{{ field.name }} = np.datetime64(data['{{ field.name }}'], 'ns') else: obj.{{ field.name }} = np.datetime64(0, 'ns') {% elif field.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 False and field.type.element == FbsType.Obj %} # FIXME _value.append({{ field.type.objtype.split('.')[-1] }}.parse(v)) {% else %} _value.append(v) {% endif %} obj.{{ field.name }} = _value {% elif field.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 = {{ field.type.map('python', field.attrs, True) }}.parse(data['{{ field.name }}']) obj.{{ field.name }} = _value {% else %} # FIXME [3] raise NotImplementedError('implement processing [3] of FlatBuffers type "{}"'.format({{ field.type.map('python', field.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 in metadata.fields_by_id %} {% if field.type.map('python', field.attrs, True) in ['str', 'bytes', 'int', 'long', 'float', 'double', 'bool'] %} '{{ field.name }}': self.{{ field.name }}, {% elif field.type.map('python', field.attrs, True) == 'uuid.UUID' %} '{{ field.name }}': self.{{ field.name }}.bytes if self.{{ field.name }} is not None else None, {% elif field.type.map('python', field.attrs, True) == 'np.datetime64' %} '{{ field.name }}': int(self.{{ field.name }}) if self.{{ field.name }} is not None else None, {% elif field.type.basetype == FbsType.Vector %} {% if field.type.element == FbsType.Obj %} '{{ field.name }}': [o.marshal() for o in self.{{ field.name }}] if self.{{ field.name }} is not None else None, {% else %} '{{ field.name }}': self.{{ field.name }}, {% endif %} {% elif field.type.basetype == FbsType.Obj %} '{{ field.name }}': self.{{ field.name }}.marshal() if self.{{ field.name }} is not None else None, {% else %} # FIXME [4]: implement processing [4] of FlatBuffers type "{{ field.type | string }}" (Python type "{{ field.type.map('python', field.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) -> '{{ 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 in metadata.fields_by_id %} {% if field.type.map('python', field.attrs, True) in ['str', 'bytes'] %} _{{ field.name }} = self.{{ field.name }} if _{{ field.name }}: _{{ field.name }} = builder.CreateString(_{{ field.name }}) {% elif field.type.map('python', field.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 in metadata.fields_by_id %} {% if field.type.map('python', field.attrs, True) in ['str', 'bytes', 'uuid.UUID'] %} if _{{ field.name }}: builder.PrependUOffsetTRelativeSlot({{ field.id }}, flatbuffers.number_types.UOffsetTFlags.py_type(_{{ field.name }}), 0) {% elif field.type.map('python', field.attrs, True) == 'np.datetime64' %} if self.{{ field.name }}: builder.PrependUint64Slot({{ field.id }}, int(self.{{ field.name }}), 0) {% elif field.type.map('python', field.attrs, True) in ['bool', 'int', 'float'] %} if self.{{ field.name }}: builder.{{ FbsType.FBS2PREPEND[field.type.basetype] }}({{ field.id }}, self.{{ field.name }}, 0) {% else %} # FIXME [1] # raise NotImplementedError('implement builder [1] for type "{}"'.format({{ field.type.map('python', field.attrs, True) }})) {% endif %} {% endfor %} return builder.EndObject()