From 1cfea9c846e2566657cd302e4af0e9432f880a6a Mon Sep 17 00:00:00 2001 From: Kenton Varda Date: Sat, 31 Aug 2013 18:19:02 -0700 Subject: [PATCH] Add shortcuts for reading from / writing to files. In Python, it doesn't make much sense to force people to muck around with MessageReaders and MessageBuilders since everything is landing on the heap anyway. Instead, let's make it easy: MyType.read[Packed]From(file) reads a file and returns a MyType reader. MyType.newMessage() returns a MyType builder representing the root of a new message. You can call this builder's write[Packed]To(file) method to write it to a file. --- capnp/capnp.pyx | 54 ++++++++++++++++++++++++++++++++++++++--- test/test_regression.py | 23 +++++++++++++----- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/capnp/capnp.pyx b/capnp/capnp.pyx index 8ef44ca..12d10b9 100644 --- a/capnp/capnp.pyx +++ b/capnp/capnp.pyx @@ -525,10 +525,46 @@ cdef class _DynamicStructBuilder: """ cdef C_DynamicStruct.Builder thisptr cdef public object _parent - cdef _init(self, C_DynamicStruct.Builder other, object parent): + cdef bint _isRoot + cdef _init(self, C_DynamicStruct.Builder other, object parent, bint isRoot = False): self.thisptr = other self._parent = parent + self._isRoot = isRoot return self + + def writeTo(self, file): + """Writes the struct's containing message to the given file object in unpacked binary format. + + This is a shortcut for calling capnp.writeMessageToFd(). This can only be called on the + message's root struct. + + :type file: file + :param file: A file or socket object (or anything with a fileno() method), open for write. + + :rtype: void + + :Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct. + """ + if not self._isRoot: + raise ValueError("You can only call writeTo() on the message's root struct.") + writeMessageToFd(file.fileno(), self._parent) + + def writePackedTo(self, file): + """Writes the struct's containing message to the given file object in packed binary format. + + This is a shortcut for calling capnp.writePackedMessageToFd(). This can only be called on + the message's root struct. + + :type file: file + :param file: A file or socket object (or anything with a fileno() method), open for write. + + :rtype: void + + :Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct. + """ + if not self._isRoot: + raise ValueError("You can only call writeTo() on the message's root struct.") + writePackedMessageToFd(file.fileno(), self._parent) cdef _get(self, field) except +ValueError: return toPython(self.thisptr.get(field), self._parent) @@ -892,6 +928,18 @@ cdef class SchemaParser: proto = schema.getProto() if proto.isStruct: local_module.schema = schema.asStruct() + def readFrom(file): + reader = StreamFdMessageReader(file.fileno()) + return reader.getRoot(local_module) + def readPackedFrom(file): + reader = PackedFdMessageReader(file.fileno()) + return reader.getRoot(local_module) + def newMessage(): + builder = MallocMessageBuilder() + return builder.initRoot(local_module) + local_module.readFrom = readFrom + local_module.readPackedFrom = readPackedFrom + local_module.newMessage = newMessage elif proto.isConst: module.__dict__[node.name] = schema.asConstValue() @@ -943,7 +991,7 @@ cdef class MessageBuilder: s = schema.schema else: s = schema - return _DynamicStructBuilder()._init(self.thisptr.initRootDynamicStruct(s.thisptr), self) + return _DynamicStructBuilder()._init(self.thisptr.initRootDynamicStruct(s.thisptr), self, True) cpdef getRoot(self, schema): """A method for instantiating Cap'n Proto structs, from an already pre-written buffer @@ -968,7 +1016,7 @@ cdef class MessageBuilder: s = schema.schema else: s = schema - return _DynamicStructBuilder()._init(self.thisptr.getRootDynamicStruct(s.thisptr), self) + return _DynamicStructBuilder()._init(self.thisptr.getRootDynamicStruct(s.thisptr), self, True) cpdef setRoot(self, value): """A method for instantiating Cap'n Proto structs by copying from an existing struct diff --git a/test/test_regression.py b/test/test_regression.py index f19683e..1a896ad 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -266,16 +266,14 @@ def check_all_types(reader): check_list(reader.enumList, ["foo", "garply"]) def test_build(all_types): - builder = capnp.MallocMessageBuilder() - root = builder.getRoot(all_types.TestAllTypes) + root = all_types.TestAllTypes.newMessage() init_all_types(root) expectedText = open(os.path.join(this_dir, 'all-types.txt'), 'r').read() assert str(root) + '\n' == expectedText def test_binary_read(all_types): f = open(os.path.join(this_dir, 'all-types.binary'), 'r') - message = capnp.StreamFdMessageReader(f.fileno()) - root = message.getRoot(all_types.TestAllTypes) + root = all_types.TestAllTypes.readFrom(f) check_all_types(root) expectedText = open(os.path.join(this_dir, 'all-types.txt'), 'r').read() @@ -292,9 +290,22 @@ def test_binary_read(all_types): def test_packed_read(all_types): f = open(os.path.join(this_dir, 'all-types.packed'), 'r') - message = capnp.PackedFdMessageReader(f.fileno()) - root = message.getRoot(all_types.TestAllTypes) + root = all_types.TestAllTypes.readPackedFrom(f) check_all_types(root) expectedText = open(os.path.join(this_dir, 'all-types.txt'), 'r').read() assert str(root) + '\n' == expectedText + +def test_binary_write(all_types): + root = all_types.TestAllTypes.newMessage() + init_all_types(root) + root.writeTo(open('example', 'w')) + + check_all_types(all_types.TestAllTypes.readFrom(open('example', 'r'))) + +def test_packed_write(all_types): + root = all_types.TestAllTypes.newMessage() + init_all_types(root) + root.writePackedTo(open('example', 'w')) + + check_all_types(all_types.TestAllTypes.readPackedFrom(open('example', 'r')))