Generateurv2/backend/env/lib/python3.10/site-packages/autobahn/xbr/_schema.py
2022-06-24 17:14:37 +02:00

1203 lines
35 KiB
Python

###############################################################################
#
# The MIT License (MIT)
#
# Copyright (c) 2018 Luis Teixeira
# - copied & modified from https://github.com/vergl4s/ethereum-mnemonic-utils
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
###############################################################################
import os
import pprint
import hashlib
from typing import Dict, Optional, TypeVar
from pathlib import Path
from zlmdb.flatbuffers.reflection.Schema import Schema as _Schema
from zlmdb.flatbuffers.reflection.BaseType import BaseType as _BaseType
# https://stackoverflow.com/a/46064289/884770
T_FbsRepository = TypeVar('T_FbsRepository', bound='FbsRepository')
class FbsType(object):
"""
Flatbuffers type.
See: https://github.com/google/flatbuffers/blob/master/reflection/reflection.fbs
"""
# no type
None_ = _BaseType.None_
# ???
UType = _BaseType.UType
# scalar types
Bool = _BaseType.Bool
Byte = _BaseType.Byte
UByte = _BaseType.UByte
Short = _BaseType.Short
UShort = _BaseType.UShort
Int = _BaseType.Int
UInt = _BaseType.UInt
Long = _BaseType.Long
ULong = _BaseType.ULong
Float = _BaseType.Float
Double = _BaseType.Double
String = _BaseType.String
SCALAR_TYPES = [_BaseType.Bool,
_BaseType.Byte,
_BaseType.UByte,
_BaseType.Short,
_BaseType.UShort,
_BaseType.Int,
_BaseType.UInt,
_BaseType.Long,
_BaseType.ULong,
_BaseType.Float,
_BaseType.Double,
_BaseType.String]
# structured types
Vector = _BaseType.Vector
Obj = _BaseType.Obj
Union = _BaseType.Union
STRUCTURED_TYPES = [_BaseType.Vector,
_BaseType.Obj,
_BaseType.Union]
FBS2PY = {
_BaseType.None_: 'type(None)',
_BaseType.UType: 'int',
_BaseType.Bool: 'bool',
_BaseType.Byte: 'bytes',
_BaseType.UByte: 'int',
_BaseType.Short: 'int',
_BaseType.UShort: 'int',
_BaseType.Int: 'int',
_BaseType.UInt: 'int',
_BaseType.Long: 'int',
_BaseType.ULong: 'int',
_BaseType.Float: 'float',
_BaseType.Double: 'float',
_BaseType.String: 'str',
_BaseType.Vector: 'List',
_BaseType.Obj: 'object',
_BaseType.Union: 'Union',
}
FBS2FLAGS = {
_BaseType.Bool: 'BoolFlags',
_BaseType.Byte: 'Int8Flags',
_BaseType.UByte: 'Uint8Flags',
_BaseType.Short: 'Int16Flags',
_BaseType.UShort: 'Uint16Flags',
_BaseType.Int: 'Int32Flags',
_BaseType.UInt: 'Uint32Flags',
_BaseType.Long: 'Int64Flags',
_BaseType.ULong: 'Uint64Flags',
_BaseType.Float: 'Float32Flags',
_BaseType.Double: 'Float64Flags',
}
FBS2PREPEND = {
_BaseType.Bool: 'PrependBoolSlot',
_BaseType.Byte: 'PrependInt8Slot',
_BaseType.UByte: 'PrependUint8Slot',
_BaseType.Short: 'PrependInt16Slot',
_BaseType.UShort: 'PrependUint16Slot',
_BaseType.Int: 'PrependInt32Slot',
_BaseType.UInt: 'PrependUint32Slot',
_BaseType.Long: 'PrependInt64Slot',
_BaseType.ULong: 'PrependUint64Slot',
_BaseType.Float: 'PrependFloat32Slot',
_BaseType.Double: 'PrependFloat64Slot',
}
FBS2STR = {
_BaseType.None_: 'None',
_BaseType.UType: 'UType',
_BaseType.Bool: 'Bool',
_BaseType.Byte: 'Byte',
_BaseType.UByte: 'UByte',
_BaseType.Short: 'Short',
_BaseType.UShort: 'UShort',
_BaseType.Int: 'Int',
_BaseType.UInt: 'UInt',
_BaseType.Long: 'Long',
_BaseType.ULong: 'ULong',
_BaseType.Float: 'Float',
_BaseType.Double: 'Double',
_BaseType.String: 'String',
_BaseType.Vector: 'Vector',
_BaseType.Obj: 'Obj',
_BaseType.Union: 'Union',
}
STR2FBS = {
'None': _BaseType.None_,
'UType': _BaseType.UType,
'Bool': _BaseType.Bool,
'Byte': _BaseType.Byte,
'UByte': _BaseType.UByte,
'Short': _BaseType.Short,
'UShort': _BaseType.UShort,
'Int': _BaseType.Int,
'UInt': _BaseType.UInt,
'Long': _BaseType.Long,
'ULong': _BaseType.ULong,
'Float': _BaseType.Float,
'Double': _BaseType.Double,
'String': _BaseType.String,
'Vector': _BaseType.Vector,
'Obj': _BaseType.Obj,
'Union': _BaseType.Union,
}
def __init__(self,
repository: T_FbsRepository,
basetype: int,
element: int,
index: int,
objtype: Optional[str] = None):
self._repository = repository
self._basetype = basetype
self._element = element
self._index = index
self._objtype = objtype
@property
def basetype(self):
"""
Flatbuffers base type.
:return:
"""
return self._basetype
@property
def element(self):
"""
Only if basetype == Vector or basetype == Array.
:return:
"""
return self._element
@property
def index(self):
"""
If basetype == Object, index into "objects".
:return:
"""
return self._index
@property
def objtype(self):
"""
If basetype == Object, fully qualified object type name.
:return:
"""
return self._objtype
def map(self, language: str, attrs: Optional[Dict] = None, required: Optional[bool] = True) -> str:
"""
:param language:
:return:
"""
if language == 'python':
_mapped_type = None
if self.basetype == FbsType.Vector:
# vectors of uint8 are mapped to byte strings ..
if self.element == FbsType.UByte:
if attrs and 'uuid' in attrs:
_mapped_type = 'uuid.UUID'
else:
_mapped_type = 'bytes'
# .. whereas all other vectors are mapped to lists of the same element type
else:
if self.objtype:
# FIXME
_mapped_type = 'List[{}]'.format(self.objtype.split('.')[-1])
# _mapped_type = 'List[{}.{}]'.format(self._repository.render_to_basemodule, self.objtype)
else:
_mapped_type = 'List[{}]'.format(FbsType.FBS2PY[self.element])
elif self.basetype == FbsType.Obj:
if self.objtype:
# FIXME
_mapped_type = self.objtype.split('.')[-1]
# _mapped_type = '{}.{}'.format(self._repository.render_to_basemodule, self.objtype)
else:
_mapped_type = 'List[{}]'.format(FbsType.FBS2PY[self.element])
elif self.basetype in FbsType.SCALAR_TYPES + [FbsType.UType, FbsType.Union]:
# FIXME: follow up processing of Unions (UType/Union)
if self.basetype == FbsType.ULong and attrs and 'timestamp' in attrs:
_mapped_type = 'np.datetime64'
else:
_mapped_type = FbsType.FBS2PY[self.basetype]
else:
raise NotImplementedError('FIXME: implement mapping of FlatBuffers type "{}" to Python in {}'.format(self.basetype, self.map))
if required:
return _mapped_type
else:
return 'Optional[{}]'.format(_mapped_type)
else:
raise RuntimeError('cannot map FlatBuffers type to target language "{}" in {}'.format(language, self.map))
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'basetype': self.FBS2STR.get(self._basetype, None),
'element': self.FBS2STR.get(self._element, None),
'index': self._index,
'objtype': self._objtype,
}
return obj
class FbsAttribute(object):
def __init__(self):
pass
def __str__(self):
return ''.format()
class FbsField(object):
def __init__(self,
repository: T_FbsRepository,
name: str,
type: FbsType,
id: int,
offset: int,
default_int: int,
default_real: float,
deprecated: bool,
required: bool,
attrs: Dict[str, FbsAttribute],
docs: str):
self._repository = repository
self._name = name
self._type = type
self._id = id
self._offset = offset
self._default_int = default_int
self._default_real = default_real
self._deprecated = deprecated
self._required = required
self._attrs = attrs
self._docs = docs
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def type(self):
return self._type
@property
def id(self):
return self._id
@property
def offset(self):
return self._offset
@property
def default_int(self):
return self._default_int
@property
def default_real(self):
return self._default_real
@property
def deprecated(self):
return self._deprecated
@property
def required(self):
return self._required
@property
def attrs(self):
return self._attrs
@property
def docs(self):
return self._docs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'type': self._type.marshal() if self._type else None,
'id': self._id,
'offset': self._offset,
'default_int': self._default_int,
'default_real': self._default_real,
'deprecated': self._deprecated,
'required': self._required,
'attrs': {},
'docs': self._docs,
}
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
def parse_attr(obj):
attrs = {}
for j in range(obj.AttributesLength()):
fbs_attr = obj.Attributes(j)
attr_key = fbs_attr.Key()
if attr_key:
attr_key = attr_key.decode('utf8')
attr_value = fbs_attr.Value()
if attr_value:
attr_value = attr_value.decode('utf8')
assert attr_key not in attrs
attrs[attr_key] = attr_value
return attrs
def parse_docs(obj):
docs = []
for j in range(obj.DocumentationLength()):
doc_line = obj.Documentation(j)
if doc_line:
doc_line = doc_line.decode('utf8')
docs.append(doc_line)
docs = '\n'.join(docs).strip()
return docs
def parse_fields(repository, obj, objs_lst=None):
fields = {}
fields_by_id = {}
for j in range(obj.FieldsLength()):
fbs_field = obj.Fields(j)
field_name = fbs_field.Name()
if field_name:
field_name = field_name.decode('utf8')
field_id = int(fbs_field.Id())
fbs_field_type = fbs_field.Type()
_objtype = None
if fbs_field_type.Index() >= 0:
_obj = objs_lst[fbs_field_type.Index()]
_objtype = _obj.name
field_type = FbsType(repository=repository,
basetype=fbs_field_type.BaseType(),
element=fbs_field_type.Element(),
index=fbs_field_type.Index(),
objtype=_objtype)
field = FbsField(repository=repository,
name=field_name,
type=field_type,
id=field_id,
offset=fbs_field.Offset(),
default_int=fbs_field.DefaultInteger(),
default_real=fbs_field.DefaultReal(),
deprecated=fbs_field.Deprecated(),
required=fbs_field.Required(),
attrs=parse_attr(fbs_field),
docs=parse_docs(fbs_field))
assert field_name not in fields, 'field "{}" with id "{}" already in fields {}'.format(field_name, field_id, sorted(fields.keys()))
fields[field_name] = field
assert field_id not in fields_by_id, 'field "{}" with id " {}" already in fields {}'.format(field_name, field_id, sorted(fields.keys()))
fields_by_id[field_id] = field_name
res = []
for _, value in sorted(fields_by_id.items()):
res.append(value)
fields_by_id = res
return fields, fields_by_id
def parse_calls(repository, svc_obj, objs_lst=None):
calls = {}
calls_by_id = {}
for j in range(svc_obj.CallsLength()):
fbs_call = svc_obj.Calls(j)
call_name = fbs_call.Name()
if call_name:
call_name = call_name.decode('utf8')
# FIXME: schema reflection.RPCCall lacks "Id" (!)
# call_id = int(fbs_call.Id())
call_id = j
fbs_call_req = fbs_call.Request()
call_req_name = fbs_call_req.Name()
if call_req_name:
call_req_name = call_req_name.decode('utf8')
call_req_is_struct = fbs_call_req.IsStruct()
call_req_min_align = fbs_call_req.Minalign()
call_req_bytesize = fbs_call_req.Bytesize()
call_req_docs = parse_docs(fbs_call_req)
call_req_attrs = parse_attr(fbs_call_req)
call_req_fields, call_fields_by_id = parse_fields(repository, fbs_call_req, objs_lst=objs_lst)
call_req = FbsObject(repository=repository,
name=call_req_name,
fields=call_req_fields,
fields_by_id=call_fields_by_id,
is_struct=call_req_is_struct,
min_align=call_req_min_align,
bytesize=call_req_bytesize,
attrs=call_req_attrs,
docs=call_req_docs)
fbs_call_resp = fbs_call.Response()
call_resp_name = fbs_call_resp.Name()
if call_resp_name:
call_resp_name = call_resp_name.decode('utf8')
call_resp_is_struct = fbs_call_resp.IsStruct()
call_resp_min_align = fbs_call_resp.Minalign()
call_resp_bytesize = fbs_call_resp.Bytesize()
call_resp_docs = parse_docs(fbs_call_resp)
call_resp_attrs = parse_attr(fbs_call_resp)
call_resp_fields, call_resp_fields_by_id = parse_fields(repository, fbs_call_resp, objs_lst=objs_lst)
call_resp = FbsObject(repository=repository,
name=call_resp_name,
fields=call_resp_fields,
fields_by_id=call_resp_fields_by_id,
is_struct=call_resp_is_struct,
min_align=call_resp_min_align,
bytesize=call_resp_bytesize,
attrs=call_resp_attrs,
docs=call_resp_docs)
call_docs = parse_docs(fbs_call)
call_attrs = parse_attr(fbs_call)
call = FbsRPCCall(repository,
name=call_name,
id=call_id,
request=call_req,
response=call_resp,
docs=call_docs,
attrs=call_attrs)
assert call_name not in calls, 'call "{}" with id "{}" already in calls {}'.format(call_name, call_id, sorted(calls.keys()))
calls[call_name] = call
assert call_id not in calls_by_id, 'call "{}" with id " {}" already in calls {}'.format(call_name, call_id, sorted(calls.keys()))
calls_by_id[call_id] = call_name
res = []
for _, value in sorted(calls_by_id.items()):
res.append(value)
calls_by_id = res
return calls, calls_by_id
class FbsObject(object):
def __init__(self,
repository: T_FbsRepository,
name: str,
fields: Dict[str, FbsField],
fields_by_id: Dict[int, str],
is_struct: bool,
min_align: int,
bytesize: int,
attrs: Dict[str, FbsAttribute],
docs: str):
self._repository = repository
self._name = name
self._fields = fields
self._fields_by_id = fields_by_id
self._is_struct = is_struct
self._min_align = min_align
self._bytesize = bytesize
self._attrs = attrs
self._docs = docs
def map(self, language: str) -> str:
if language == 'python':
klass = self._name.split('.')[-1]
return klass
else:
raise NotImplementedError()
def map_import(self, language: str) -> str:
if language == 'python':
base = self._name.split('.')[-2]
klass = self._name.split('.')[-1]
return 'from {} import {}'.format(base, klass)
else:
raise NotImplementedError()
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def fields(self):
return self._fields
@property
def fields_by_id(self):
return self._fields_by_id
@property
def is_struct(self):
return self._is_struct
@property
def min_align(self):
return self._min_align
@property
def bytesize(self):
return self._bytesize
@property
def attrs(self):
return self._attrs
@property
def docs(self):
return self._docs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'fields': {},
'is_struct': self._is_struct,
'min_align': self._min_align,
'bytesize': self._bytesize,
'attrs': {},
'docs': self._docs,
}
if self._fields:
for k, v in self._fields.items():
obj['fields'][k] = v.marshal() if v else None
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
@staticmethod
def parse(repository, fbs_obj, objs_lst=None):
obj_name = fbs_obj.Name()
if obj_name:
obj_name = obj_name.decode('utf8')
obj_docs = parse_docs(fbs_obj)
obj_attrs = parse_attr(fbs_obj)
obj_fields, obj_fields_by_id = parse_fields(repository, fbs_obj, objs_lst=objs_lst)
obj = FbsObject(repository=repository,
name=obj_name,
fields=obj_fields,
fields_by_id=obj_fields_by_id,
is_struct=fbs_obj.IsStruct(),
min_align=fbs_obj.Minalign(),
bytesize=fbs_obj.Bytesize(),
attrs=obj_attrs,
docs=obj_docs)
return obj
class FbsRPCCall(object):
def __init__(self,
repository: T_FbsRepository,
name: str,
id: int,
request: FbsObject,
response: FbsObject,
docs: str,
attrs: Dict[str, FbsAttribute]):
self._repository = repository
self._name = name
self._id = id
self._request = request
self._response = response
self._docs = docs
self._attrs = attrs
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def id(self):
return self._id
@property
def request(self):
return self._request
@property
def response(self):
return self._response
@property
def docs(self):
return self._docs
@property
def attrs(self):
return self._attrs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'request': self._request.marshal() if self._request else None,
'response': self._response.marshal() if self._response else None,
'attrs': {},
'docs': self._docs,
}
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
class FbsService(object):
def __init__(self,
repository: T_FbsRepository,
name: str,
calls: Dict[str, FbsRPCCall],
calls_by_id: Dict[int, str],
attrs: Dict[str, FbsAttribute],
docs: str):
self._repository = repository
self._name = name
self._calls = calls
self._calls_by_id = calls_by_id
self._attrs = attrs
self._docs = docs
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def calls(self):
return self._calls
@property
def calls_by_id(self):
return self._calls_by_id
@property
def attrs(self):
return self._attrs
@property
def docs(self):
return self._docs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'calls': {},
'attrs': {},
'docs': self._docs,
}
if self._calls:
for k, v in self._calls.items():
obj['calls'][k] = v.marshal()
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
class FbsEnumValue(object):
def __init__(self, repository, name, value, docs):
self._repository = repository
self._name = name
self._value = value
self._attrs = {}
self._docs = docs
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def value(self):
return self._value
@property
def attrs(self):
return self._attrs
@property
def docs(self):
return self._docs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'attrs': self._attrs,
'docs': self._docs,
'value': self._value,
}
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
class FbsEnum(object):
"""
FlatBuffers enum type.
"""
def __init__(self,
repository: T_FbsRepository,
name: str,
values: Dict[str, FbsEnumValue],
is_union: bool,
underlying_type: int,
attrs: Dict[str, FbsAttribute],
docs: str):
self._repository = repository
self._name = name
self._values = values
self._is_union = is_union
# zlmdb.flatbuffers.reflection.Type.Type
self._underlying_type = underlying_type
self._attrs = attrs
self._docs = docs
@property
def repository(self):
return self._repository
@property
def name(self):
return self._name
@property
def values(self):
return self._values
@property
def is_union(self):
return self._is_union
@property
def underlying_type(self):
return self._underlying_type
@property
def attrs(self):
return self._attrs
@property
def docs(self):
return self._docs
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal()))
def marshal(self):
obj = {
'name': self._name,
'values': {},
'is_union': self._is_union,
'underlying_type': FbsType.FBS2STR.get(self._underlying_type, None),
'attrs': {},
'docs': self._docs,
}
if self._values:
for k, v in self._values.items():
obj['values'][k] = v.marshal()
if self._attrs:
for k, v in self._attrs.items():
obj['attrs'][k] = v
return obj
class FbsSchema(object):
"""
"""
def __init__(self,
repository: T_FbsRepository,
file_name: str,
file_sha256: str,
file_size: int,
file_ident: str,
file_ext: str,
root_table: FbsObject,
root: _Schema,
objs: Dict[str, FbsObject],
enums: Dict[str, FbsEnum],
services: Dict[str, FbsService]):
"""
:param repository:
:param file_name:
:param file_sha256:
:param file_size:
:param file_ident:
:param file_ext:
:param root_table:
:param root:
:param objs:
:param enums:
:param services:
"""
self._repository = repository
self._file_name = file_name
self._file_sha256 = file_sha256
self._file_size = file_size
self._file_ident = file_ident
self._file_ext = file_ext
self._root_table = root_table
self._root = root
self._objs = objs
self._enums = enums
self._services = services
@property
def repository(self):
return self._repository
@property
def file_name(self):
return self._file_name
@property
def file_sha256(self):
return self._file_sha256
@property
def file_size(self):
return self._file_size
@property
def file_ident(self):
return self._file_ident
@property
def file_ext(self):
return self._file_ext
@property
def root_table(self):
return self._root_table
@property
def root(self):
return self._root
@property
def objs(self):
return self._objs
@property
def enums(self):
return self._enums
@property
def services(self):
return self._services
def __str__(self):
return '\n{}\n'.format(pprint.pformat(self.marshal(), width=255))
def marshal(self) -> Dict[str, object]:
"""
:return:
"""
obj = {
'schema': {
'ident': self._file_ident,
'ext': self._file_ext,
'name': os.path.basename(self._file_name) if self._file_name else None,
'sha256': self._file_sha256,
'size': self._file_size,
'objects': len(self._objs),
'enums': len(self._enums),
'services': len(self._services),
},
'root_table': self._root_table.marshal() if self._root_table else None,
'enums': {},
'objects': {},
'services': {},
}
if self._enums:
for k, v in self._enums.items():
obj['enums'][k] = v.marshal()
if self._objs:
for k, v in self._objs.items():
obj['objects'][k] = v.marshal()
if self._services:
for k, v in self._services.items():
obj['services'][k] = v.marshal()
return obj
@staticmethod
def load(repository, filename) -> object:
"""
:param filename:
:return:
"""
if not os.path.isfile(filename):
raise RuntimeError('cannot open schema file {}'.format(filename))
with open(filename, 'rb') as fd:
data = fd.read()
print('processing schema {} ({} bytes) ..'.format(filename, len(data)))
root = _Schema.GetRootAsSchema(data, 0)
file_ident = root.FileIdent()
if file_ident is not None:
file_ident = file_ident.decode('utf8')
file_ext = root.FileExt()
if file_ext is not None:
file_ext = file_ext.decode('utf8')
root_table = root.RootTable()
if root_table is not None:
root_table = FbsObject.parse(repository, root_table)
objs = {}
objs_lst = []
services = {}
enums = {}
for i in range(root.EnumsLength()):
fbs_enum = root.Enums(i)
enum_name = fbs_enum.Name()
if enum_name:
enum_name = enum_name.decode('utf8')
enum_underlying_type = fbs_enum.UnderlyingType()
enum_values = {}
for j in range(fbs_enum.ValuesLength()):
fbs_enum_value = fbs_enum.Values(j)
enum_value_name = fbs_enum_value.Name()
if enum_value_name:
enum_value_name = enum_value_name.decode('utf8')
enum_value_value = fbs_enum_value.Value()
enum_value_docs = parse_docs(fbs_enum_value)
enum_value = FbsEnumValue(repository=repository,
name=enum_value_name,
value=enum_value_value,
docs=enum_value_docs)
assert enum_value_name not in enum_values
enum_values[enum_value_name] = enum_value
enum = FbsEnum(repository=repository,
name=enum_name,
values=enum_values,
is_union=fbs_enum.IsUnion(),
underlying_type=enum_underlying_type,
attrs=parse_attr(fbs_enum),
docs=parse_docs(fbs_enum))
assert enum_name not in enums
enums[enum_name] = enum
for i in range(root.ObjectsLength()):
fbs_obj = root.Objects(i)
obj = FbsObject.parse(repository, fbs_obj, objs_lst=objs_lst)
assert obj.name not in objs
objs[obj.name] = obj
objs_lst.append(obj)
for i in range(root.ServicesLength()):
svc_obj = root.Services(i)
svc_name = svc_obj.Name()
if svc_name:
svc_name = svc_name.decode('utf8')
docs = parse_docs(svc_obj)
attrs = parse_attr(svc_obj)
calls, calls_by_id = parse_calls(repository, svc_obj, objs_lst=objs_lst)
service = FbsService(repository=repository,
name=svc_name,
calls=calls,
calls_by_id=calls_by_id,
attrs=attrs,
docs=docs)
assert svc_name not in services
services[svc_name] = service
m = hashlib.sha256()
m.update(data)
schema = FbsSchema(repository=repository,
file_name=filename,
file_size=len(data),
file_sha256=m.hexdigest(),
file_ident=file_ident,
file_ext=file_ext,
root_table=root_table,
root=root,
objs=objs,
enums=enums,
services=services)
return schema
class FbsRepository(object):
"""
"""
def __init__(self, render_to_basemodule):
self._render_to_basemodule = render_to_basemodule
self._schemata = {}
# Dict[str, FbsObject]
self._objs = {}
# Dict[str, FbsEnum]
self._enums = {}
# Dict[str, FbsService]
self._services = {}
def summary(self, keys=False):
if keys:
return {
'schemata': sorted(self._schemata.keys()),
'objs': sorted(self._objs.keys()),
'enums': sorted(self._enums.keys()),
'services': sorted(self._services.keys()),
}
else:
return {
'schemata': len(self._schemata),
'objs': len(self._objs),
'enums': len(self._enums),
'services': len(self._services),
}
@property
def render_to_basemodule(self):
return self._render_to_basemodule
@property
def objs(self):
return self._objs
@property
def enums(self):
return self._enums
@property
def services(self):
return self._services
def load(self, dirname) -> object:
if not os.path.isdir(dirname):
raise RuntimeError('cannot open schema directory {}'.format(dirname))
found = []
for path in Path(dirname).rglob('*.bfbs'):
fn = os.path.abspath(os.path.join(dirname, path.name))
if fn not in self._schemata:
found.append(fn)
else:
print('duplicate schema: {} already loaded'.format(fn))
# iterate over all schema files found
for fn in found:
# load this schema file
schema = FbsSchema.load(self, fn)
# add enum types
for enum in schema.enums.values():
if enum.name in self._enums:
print('duplicate enum for name "{}"'.format(enum.name))
else:
self._enums[enum.name] = enum
# add object types
for obj in schema.objs.values():
if obj.name in self._objs:
print('duplicate object for name "{}"'.format(obj.name))
else:
self._objs[obj.name] = obj
# add service definitions ("APIs")
for svc in schema.services.values():
if svc.name in self._services:
print('duplicate service for name "{}"'.format(svc.name))
else:
self._services[svc.name] = svc
self._schemata[fn] = schema