Merge pull request #307 from Zentren/dynamic_schema_loading
Schema loading from the wire
This commit is contained in:
12
capnp/helpers/deserialize.h
Normal file
12
capnp/helpers/deserialize.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "capnp/dynamic.h"
|
||||||
|
#include "capnp/schema.capnp.h"
|
||||||
|
|
||||||
|
/// @brief Convert the dynamic struct to a Node::Reader
|
||||||
|
::capnp::schema::Node::Reader toReader(capnp::DynamicStruct::Reader reader)
|
||||||
|
{
|
||||||
|
// requires an intermediate step to AnyStruct before going directly to Node::Reader,
|
||||||
|
// since there exists no direct conversion from DynamicStruct::Reader to Node::Reader.
|
||||||
|
return reader.as<capnp::AnyStruct>().as<capnp::schema::Node>();
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from capnp.includes.capnp_cpp cimport (
|
from capnp.includes.capnp_cpp cimport (
|
||||||
Maybe, PyPromise, VoidPromise, RemotePromise,
|
Maybe, PyPromise, VoidPromise, RemotePromise,
|
||||||
DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability,
|
DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability,
|
||||||
RpcSystem, MessageBuilder, Own, PyRefCounter
|
RpcSystem, MessageBuilder, Own, PyRefCounter, Node, DynamicStruct
|
||||||
)
|
)
|
||||||
|
|
||||||
from capnp.includes.schema_cpp cimport ByteArray
|
from capnp.includes.schema_cpp cimport ByteArray
|
||||||
@@ -30,3 +30,7 @@ cdef extern from "capnp/helpers/rpcHelper.h":
|
|||||||
|
|
||||||
cdef extern from "capnp/helpers/serialize.h":
|
cdef extern from "capnp/helpers/serialize.h":
|
||||||
ByteArray messageToPackedBytes(MessageBuilder &, size_t wordCount)
|
ByteArray messageToPackedBytes(MessageBuilder &, size_t wordCount)
|
||||||
|
|
||||||
|
cdef extern from "capnp/helpers/deserialize.h":
|
||||||
|
Node.Reader toReader(DynamicStruct.Reader reader) except +reraise_kj_exception
|
||||||
|
|
||||||
|
|||||||
@@ -446,6 +446,12 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
|||||||
DynamicStruct.Pipeline asStruct"releaseAs< ::capnp::DynamicStruct>"()
|
DynamicStruct.Pipeline asStruct"releaseAs< ::capnp::DynamicStruct>"()
|
||||||
Type getType()
|
Type getType()
|
||||||
|
|
||||||
|
cdef extern from "capnp/schema-loader.h" namespace " ::capnp":
|
||||||
|
cdef cppclass SchemaLoader:
|
||||||
|
SchemaLoader()
|
||||||
|
Schema load(Node.Reader reader) except +reraise_kj_exception
|
||||||
|
Schema get(uint64_t id_) except +reraise_kj_exception
|
||||||
|
|
||||||
cdef extern from "capnp/schema-parser.h" namespace " ::capnp":
|
cdef extern from "capnp/schema-parser.h" namespace " ::capnp":
|
||||||
cdef cppclass ParsedSchema(Schema) nogil:
|
cdef cppclass ParsedSchema(Schema) nogil:
|
||||||
ParsedSchema getNested(char * name) except +reraise_kj_exception
|
ParsedSchema getNested(char * name) except +reraise_kj_exception
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from capnp.includes cimport schema_cpp
|
|||||||
from capnp.includes.capnp_cpp cimport (
|
from capnp.includes.capnp_cpp cimport (
|
||||||
Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema,
|
Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema,
|
||||||
EnumSchema as C_EnumSchema, ListSchema as C_ListSchema, DynamicStruct as C_DynamicStruct,
|
EnumSchema as C_EnumSchema, ListSchema as C_ListSchema, DynamicStruct as C_DynamicStruct,
|
||||||
DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList,
|
DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, SchemaLoader as C_SchemaLoader,
|
||||||
SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr,
|
SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr,
|
||||||
String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject,
|
String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject,
|
||||||
DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, Promise,
|
DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, Promise,
|
||||||
@@ -30,6 +30,9 @@ cdef class _StringArrayPtr:
|
|||||||
cdef size_t size
|
cdef size_t size
|
||||||
cdef ArrayPtr[StringPtr] asArrayPtr(self) except +reraise_kj_exception
|
cdef ArrayPtr[StringPtr] asArrayPtr(self) except +reraise_kj_exception
|
||||||
|
|
||||||
|
cdef class SchemaLoader:
|
||||||
|
cdef C_SchemaLoader * thisptr
|
||||||
|
|
||||||
cdef class SchemaParser:
|
cdef class SchemaParser:
|
||||||
cdef C_SchemaParser * thisptr
|
cdef C_SchemaParser * thisptr
|
||||||
cdef public dict modules_by_id
|
cdef public dict modules_by_id
|
||||||
|
|||||||
@@ -363,6 +363,11 @@ cdef class _NodeReader:
|
|||||||
def __get__(self):
|
def __get__(self):
|
||||||
return self.thisptr.isEnum()
|
return self.thisptr.isEnum()
|
||||||
|
|
||||||
|
property node:
|
||||||
|
"""A property that returns the NodeReader as a DynamicStructReader."""
|
||||||
|
def __get__(self):
|
||||||
|
return _DynamicStructReader()._init(self.thisptr, self)
|
||||||
|
|
||||||
|
|
||||||
cdef class _NestedNodeReader:
|
cdef class _NestedNodeReader:
|
||||||
cdef C_Node.NestedNode.Reader thisptr
|
cdef C_Node.NestedNode.Reader thisptr
|
||||||
@@ -3231,6 +3236,34 @@ cdef class _StringArrayPtr:
|
|||||||
return ArrayPtr[StringPtr](self.thisptr, self.size)
|
return ArrayPtr[StringPtr](self.thisptr, self.size)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class SchemaLoader:
|
||||||
|
""" Class which can be used to construct Schema objects from schema::Nodes as defined in
|
||||||
|
schema.capnp.
|
||||||
|
|
||||||
|
This class wraps capnproto/c++/src/capnp/schema-loader.h directly."""
|
||||||
|
def __cinit__(self):
|
||||||
|
self.thisptr = new C_SchemaLoader()
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
del self.thisptr
|
||||||
|
|
||||||
|
def load(self, _NodeReader reader):
|
||||||
|
"""Loads the given schema node. Validates the node and throws an exception if invalid. This
|
||||||
|
makes a copy of the schema, so the object passed in can be destroyed after this returns.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return _Schema()._init(self.thisptr.load(reader.thisptr))
|
||||||
|
|
||||||
|
def load_dynamic(self, _DynamicStructReader reader):
|
||||||
|
"""Loads the given schema node with self.load, but converts from a _DynamicStructReader
|
||||||
|
first."""
|
||||||
|
return _Schema()._init(self.thisptr.load(helpers.toReader(reader.thisptr)))
|
||||||
|
|
||||||
|
def get(self, id_):
|
||||||
|
"""Gets the schema for the given ID, throwing an exception if it isn't present."""
|
||||||
|
return _Schema()._init(self.thisptr.get(<uint64_t>id_))
|
||||||
|
|
||||||
|
|
||||||
cdef class SchemaParser:
|
cdef class SchemaParser:
|
||||||
"""A class for loading Cap'n Proto schema files.
|
"""A class for loading Cap'n Proto schema files.
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ Miscellaneous
|
|||||||
:undoc-members:
|
:undoc-members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
.. autoclass:: SchemaLoader
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
---------
|
---------
|
||||||
|
|||||||
@@ -5,3 +5,16 @@ struct Foo {
|
|||||||
name @1 :Text;
|
name @1 :Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Baz{
|
||||||
|
text @0 :Text;
|
||||||
|
qux @1 :Qux;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Qux{
|
||||||
|
id @0 :UInt64;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Wrapper {
|
||||||
|
wrapped @0 (object :AnyPointer);
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,3 +119,31 @@ def test_bundled_import_hook():
|
|||||||
# stream.capnp should be bundled, or provided by the system capnproto
|
# stream.capnp should be bundled, or provided by the system capnproto
|
||||||
capnp.add_import_hook()
|
capnp.add_import_hook()
|
||||||
import stream_capnp # noqa: F401
|
import stream_capnp # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
|
async def test_load_capnp(foo):
|
||||||
|
# test dynamically loading
|
||||||
|
loader = capnp.SchemaLoader()
|
||||||
|
loader.load(foo.Baz.schema.get_proto())
|
||||||
|
loader.load_dynamic(foo.Qux.schema.get_proto().node)
|
||||||
|
|
||||||
|
schema = loader.get(foo.Baz.schema.get_proto().node.id).as_struct()
|
||||||
|
assert "text" in schema.fieldnames
|
||||||
|
assert "qux" in schema.fieldnames
|
||||||
|
assert schema.fields["qux"].proto.slot.type.which == "struct"
|
||||||
|
|
||||||
|
class Wrapper(foo.Wrapper.Server):
|
||||||
|
async def wrapped(self, object, **kwargs):
|
||||||
|
assert isinstance(object, capnp.lib.capnp._DynamicObjectReader)
|
||||||
|
baz_ = object.as_struct(schema)
|
||||||
|
assert baz_.text == "test"
|
||||||
|
assert baz_.qux.id == 2
|
||||||
|
|
||||||
|
# test calling into the wrapper with a Baz message.
|
||||||
|
baz_ = foo.Baz.new_message()
|
||||||
|
baz_.text = "test"
|
||||||
|
baz_.qux.id = 2
|
||||||
|
|
||||||
|
wrapper = foo.Wrapper._new_client(Wrapper())
|
||||||
|
remote = wrapper.wrapped(baz_)
|
||||||
|
await remote
|
||||||
|
|||||||
Reference in New Issue
Block a user