diff --git a/capnp/capnp.pyx b/capnp/capnp.pyx index 8cfac42..9dc092e 100644 --- a/capnp/capnp.pyx +++ b/capnp/capnp.pyx @@ -9,7 +9,7 @@ cimport cython cimport capnp_cpp as capnp cimport schema_cpp -from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, WordArrayPtr +from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, DynamicObject as C_DynamicObject, WordArrayPtr from schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode from cython.operator cimport dereference as deref @@ -352,7 +352,7 @@ cdef to_python_reader(C_DynamicValue.Reader self, object parent): elif type == capnp.TYPE_VOID: return None elif type == capnp.TYPE_OBJECT: - raise ValueError("Cannot convert type to Python. Object type is not supported in pycapnp yet") + return _DynamicObjectReader()._init(self.asObject(), parent) elif type == capnp.TYPE_UNKNOWN: raise ValueError("Cannot convert type to Python. Type is unknown by capnproto library") else: @@ -382,7 +382,7 @@ cdef to_python_builder(C_DynamicValue.Builder self, object parent): elif type == capnp.TYPE_VOID: return None elif type == capnp.TYPE_OBJECT: - raise ValueError("Cannot convert type to Python. Object type is not supported in pycapnp yet") + raise ValueError("Cannot convert type to Python. Type is 'Object', but is being used improperly. You can only get 'Object' types from a struct") elif type == capnp.TYPE_UNKNOWN: raise ValueError("Cannot convert type to Python. Type is unknown by capnproto library") else: @@ -618,7 +618,21 @@ cdef class _DynamicStructBuilder: return ret cdef _get(self, field): - return to_python_builder(self.thisptr.get(field), self._parent) + cdef C_DynamicValue.Builder value = self.thisptr.get(field) + + if value.getType() == capnp.TYPE_OBJECT: + return _DynamicObjectBuilder(field, self) + else: + return to_python_builder(value, self._parent) + + cpdef _get_object(self, field, schema): + cdef _StructSchema s + if hasattr(schema, 'schema'): + s = schema.schema + else: + s = schema + + return _DynamicStructBuilder()._init(self.thisptr.getObject(field, s.thisptr), self._parent) def __getattr__(self, field): return self._get(field) @@ -771,6 +785,32 @@ cdef class _DynamicOrphan: def __repr__(self): return repr(self.get()) +cdef class _DynamicObjectReader: + cdef C_DynamicObject.Reader thisptr + cdef public object _parent + cdef _init(self, C_DynamicObject.Reader other, object parent): + self.thisptr = other + self._parent = parent + return self + + cpdef as_struct(self, schema): + cdef _StructSchema s + if hasattr(schema, 'schema'): + s = schema.schema + else: + s = schema + + return _DynamicStructReader()._init(self.thisptr.as(s.thisptr), self._parent) + +cdef class _DynamicObjectBuilder: + cdef public object _field, _parent_struct + def __init__(self, field, parent_struct): + self._field = field + self._parent_struct = parent_struct + + cpdef as_struct(self, schema): + return self._parent_struct._get_object(self._field, schema) + cdef class _Schema: cdef C_Schema thisptr cdef _init(self, C_Schema other): diff --git a/capnp/capnp_cpp.pxd b/capnp/capnp_cpp.pxd index 8fe49b6..9f27652 100644 --- a/capnp/capnp_cpp.pxd +++ b/capnp/capnp_cpp.pxd @@ -138,6 +138,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp": void adopt(char *, DynamicOrphan) except +ValueError DynamicOrphan disown(char *) DynamicStruct.Reader asReader() + DynamicStruct.Builder getObject(char *, StructSchema) cdef extern from "fixMaybe.h": EnumSchema.Enumerant fixMaybe(Maybe[EnumSchema.Enumerant]) except +ValueError @@ -149,6 +150,13 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp": uint16_t getRaw() Maybe[EnumSchema.Enumerant] getEnumerant() + cdef cppclass DynamicObject: + cppclass Reader: + DynamicStruct.Reader as(StructSchema schema) + cppclass Builder: + DynamicObject.Reader asReader() + # DynamicList::Reader as(ListSchema schema) const; + cdef cppclass DynamicList: cppclass Reader: DynamicValueForward.Reader operator[](uint) except +ValueError @@ -193,6 +201,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp": char * asText"as< ::capnp::Text>().cStr"() DynamicList.Reader asList"as< ::capnp::DynamicList>"() DynamicStruct.Reader asStruct"as< ::capnp::DynamicStruct>"() + DynamicObject.Reader asObject"as< ::capnp::DynamicObject>"() DynamicEnum asEnum"as< ::capnp::DynamicEnum>"() Data.Reader asData"as< ::capnp::Data>"() diff --git a/test/object.capnp b/test/object.capnp new file mode 100644 index 0000000..bd074c1 --- /dev/null +++ b/test/object.capnp @@ -0,0 +1,21 @@ +@0x8186ddb142b58556; + +struct Person { + id @0 :UInt32; + name @1 :Text; +} + +struct Place { + id @0 :UInt32; + name @1 :Text; +} + +struct Thing { + id @0 :UInt64; + value @1 :UInt64; +} + +struct TestObject { + object @0 :Object; +} + diff --git a/test/test_object.py b/test/test_object.py new file mode 100644 index 0000000..5d7af08 --- /dev/null +++ b/test/test_object.py @@ -0,0 +1,25 @@ +import pytest +import capnp +import os +import math + +this_dir = os.path.dirname(__file__) + +@pytest.fixture +def object(): + return capnp.load(os.path.join(this_dir, 'object.capnp')) + +def test_object_basic(object): + obj = object.TestObject.new_message() + person = obj.object.as_struct(object.Person) + person.name = 'test' + person.id = 1000 + + same_person = obj.object.as_struct(object.Person) + assert same_person.name == 'test' + assert same_person.id == 1000 + + obj_r = obj.as_reader() + same_person = obj_r.object.as_struct(object.Person) + assert same_person.name == 'test' + assert same_person.id == 1000 \ No newline at end of file