From 6fcdf841e43bb6e65396e6934ec65e84909545d3 Mon Sep 17 00:00:00 2001 From: Jason Paryani Date: Sun, 1 Sep 2013 02:13:19 -0700 Subject: [PATCH] Update preferred method for reading/writing messages from files --- capnp/__init__.py | 12 ++++----- capnp/capnp.pyx | 60 +++++++++++------------------------------ docs/capnp.rst | 28 ------------------- docs/quickstart.rst | 46 +++++++++++-------------------- examples/example.py | 20 +++++++------- setup.py | 2 +- test/test_regression.py | 59 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 103 insertions(+), 124 deletions(-) diff --git a/capnp/__init__.py b/capnp/__init__.py index d66dcb7..669fb4a 100644 --- a/capnp/__init__.py +++ b/capnp/__init__.py @@ -7,9 +7,8 @@ Example Usage:: addressbook = capnp.load('addressbook.capnp') # Building - message = capnp.MallocMessageBuilder() - addressBook = message.initRoot(addressbook.AddressBook) - people = addressBook.init('people', 1) + addresses = addressbook.AddressBook.newMessage() + people = addresses.init('people', 1) alice = people[0] alice.id = 123 @@ -19,16 +18,15 @@ Example Usage:: alicePhone.type = 'mobile' f = open('example.bin', 'w') - capnp.writePackedMessageToFd(f.fileno(), message) + addresses.writeTo(f) f.close() # Reading f = open('example.bin') - message = capnp.PackedFdMessageReader(f.fileno()) - addressBook = message.getRoot(addressbook.AddressBook) + addresses = addressbook.AddressBook.readFrom(f) - for person in addressBook.people: + for person in addresses.people: print(person.name, ':', person.email) for phone in person.phones: print(phone.type, ':', phone.number) diff --git a/capnp/capnp.pyx b/capnp/capnp.pyx index 53b2918..6531ff6 100644 --- a/capnp/capnp.pyx +++ b/capnp/capnp.pyx @@ -32,7 +32,6 @@ ctypedef double Float64 from libc.stdlib cimport malloc, free from libcpp cimport bool as cbool -_MAX_INT = 2**63 - 1 ctypedef fused _DynamicStructReaderOrBuilder: _DynamicStructReader _DynamicStructBuilder @@ -131,7 +130,7 @@ cdef class _DynamicListReader: This class thinly wraps the C++ Cap'n Proto DynamicList::Reader class. __getitem__ and __len__ have been defined properly, so you can treat this class mostly like any other iterable class:: ... - person = message.getRoot(addressbook.Person) + person = addressbook.Person.readFrom(file) phones = person.phones # This returns a _DynamicListReader @@ -170,10 +169,10 @@ cdef class _DynamicResizableListBuilder: .. warning:: You need to call :meth:`finish` on this object before serializing the Cap'n Proto message. Failure to do so will cause your objects not to be written out as well as leaking orphan structs into your message. - This class works much like :class:`_DynamicListBuilder`, but it allows growing the list dynamically. It is meant for lists of structs, since for primitive types like int or float, you're much better off using a normal python list and then serializing straight to a Cap'n Proto list. It has __getitem__ and __len__ defined, but not __setitem__. + This class works much like :class:`_DynamicListBuilder`, but it allows growing the list dynamically. It is meant for lists of structs, since for primitive types like int or float, you're much better off using a normal python list and then serializing straight to a Cap'n Proto list. It has __getitem__ and __len__ defined, but not __setitem__:: ... - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() phones = person.initResizableList('phones') # This returns a _DynamicResizableListBuilder @@ -184,7 +183,8 @@ cdef class _DynamicResizableListBuilder: people.finish() - capnp.writePackedMessageToFd(fd, message) + f = open('example', 'w') + person.writeTo(f) """ cdef public object _parent, _message, _field, _schema cdef public list _list @@ -234,7 +234,7 @@ cdef class _DynamicListBuilder: This class thinly wraps the C++ Cap'n Proto DynamicList::Bulder class. __getitem__, __setitem__, and __len__ have been defined properly, so you can treat this class mostly like any other iterable class:: ... - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() phones = person.init('phones', 2) # This returns a _DynamicListBuilder @@ -278,21 +278,7 @@ cdef class _DynamicListBuilder: cpdef adopt(self, index, _DynamicOrphan orphan): """A method for adopting Cap'n Proto orphans - Don't use this method unless you know what you're doing. Orphans are useful for dynamically allocating objects for an unkown sized list, ie:: - - message = capnp.MallocMessageBuilder() - - alice = m.newOrphan(addressbook.Person) - alice.get().name = 'alice' - - bob = m.newOrphan(addressbook.Person) - bob.get().name = 'bob' - - addressBook = message.initRoot(addressbook.AddressBook) - people = addressBook.init('people', 2) - - people.adopt(0, alice) - people.adopt(1, bob) + Don't use this method unless you know what you're doing. Orphans are useful for dynamically allocating objects for an unkown sized list. :type index: int :param index: The index of the element in the list to replace with the newly adopted object @@ -439,7 +425,7 @@ cdef class _DynamicStructReader: This class is almost a 1 for 1 wrapping of the Cap'n Proto C++ DynamicStruct::Reader. The only difference is that instead of a `get` method, __getattr__ is overloaded and the field name is passed onto the C++ equivalent `get`. This means you just use . syntax to access any field. For field names that don't follow valid python naming convention for fields, use the global function :py:func:`getattr`:: - person = message.getRoot(addressbook.Person) # This returns a _DynamicStructReader + person = addressbook.Person.readFrom(file) # This returns a _DynamicStructReader print person.name # using . syntax print getattr(person, 'field-with-hyphens') # for names that are invalid for python, use getattr """ @@ -462,7 +448,7 @@ cdef class _DynamicStructReader: Enums are just strings in the python Cap'n Proto API, so this function will either return a string equal to the field name of the active field in the union, or throw a ValueError if this isn't a union, or a struct with an unnamed union:: - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() person.which() # ValueError: member was null @@ -497,7 +483,7 @@ cdef class _DynamicStructBuilder: This class is almost a 1 for 1 wrapping of the Cap'n Proto C++ DynamicStruct::Builder. The only difference is that instead of a `get`/`set` method, __getattr__/__setattr__ is overloaded and the field name is passed onto the C++ equivalent function. This means you just use . syntax to access or set any field. For field names that don't follow valid python naming convention for fields, use the global functions :py:func:`getattr`/:py:func:`setattr`:: - person = message.initRoot(addressbook.Person) # This returns a _DynamicStructBuilder + person = addressbook.Person.newMessage() # This returns a _DynamicStructBuilder person.name = 'foo' # using . syntax print person.name # using . syntax @@ -601,7 +587,7 @@ cdef class _DynamicStructBuilder: Enums are just strings in the python Cap'n Proto API, so this function will either return a string equal to the field name of the active field in the union, or throw a ValueError if this isn't a union, or a struct with an unnamed union:: - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() person.which() # ValueError: member was null @@ -620,21 +606,7 @@ cdef class _DynamicStructBuilder: cpdef adopt(self, field, _DynamicOrphan orphan): """A method for adopting Cap'n Proto orphans - Don't use this method unless you know what you're doing. Orphans are useful for dynamically allocating objects for an unkown sized list, ie:: - - message = capnp.MallocMessageBuilder() - - alice = m.newOrphan(addressbook.Person) - alice.get().name = 'alice' - - bob = m.newOrphan(addressbook.Person) - bob.get().name = 'bob' - - addressBook = message.initRoot(addressbook.AddressBook) - people = addressBook.init('people', 2) - - people.adopt(0, alice) - people.adopt(1, bob) + Don't use this method unless you know what you're doing. Orphans are useful for dynamically allocating objects for an unkown sized list. :type field: str :param field: The field name in the struct @@ -814,8 +786,7 @@ cdef class SchemaParser: addressbook = parser.load('addressbook.capnp') print addressbook.qux # qux is a top level constant # 123 - message = capnp.MallocMessageBuilder() - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() :type file_name: str :param file_name: A relative or absolute path to a Cap'n Proto schema @@ -956,7 +927,7 @@ cdef class MessageBuilder: Don't use this method unless you know what you're doing. Orphans are useful for dynamically allocating objects for an unkown sized list, ie:: addressbook = capnp.load('addressbook.capnp') - + m = capnp.MallocMessageBuilder() alice = m.newOrphan(addressbook.Person) :type schema: Schema @@ -1129,8 +1100,7 @@ def load(file_name, display_name=None, imports=[]): addressbook = capnp.load('addressbook.capnp') print addressbook.qux # qux is a top level constant in the addressbook.capnp schema # 123 - message = capnp.MallocMessageBuilder() - person = message.initRoot(addressbook.Person) + person = addressbook.Person.newMessage() :type file_name: str :param file_name: A relative or absolute path to a Cap'n Proto schema diff --git a/docs/capnp.rst b/docs/capnp.rst index 04774fa..354e911 100644 --- a/docs/capnp.rst +++ b/docs/capnp.rst @@ -10,34 +10,6 @@ API Reference Functions ------------- .. autofunction:: load -.. autofunction:: writeMessageToFd -.. autofunction:: writePackedMessageToFd - -Readers -------------- - -.. autoclass:: StreamFdMessageReader - :members: - :undoc-members: - :inherited-members: - -.. autoclass:: PackedFdMessageReader - :members: - :undoc-members: - :inherited-members: - -Builders -------------- - -.. autoclass:: MessageBuilder - :members: - :undoc-members: - :inherited-members: - -.. autoclass:: MallocMessageBuilder - :members: - :undoc-members: - :inherited-members: Internal Classes ---------------- diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 67ee283..e11273d 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -22,7 +22,7 @@ Note that we assign the loaded module to a variable, named `addressbook`. We'll You can also provide an absolute path to the Cap'n Proto schema you wish to load. Otherwise, it will only look in the current working directory. -For future reference, here is the Cap'n Proto schema. Also available in the github repository under examples/addressbook.capnp:: +For future reference, here is the Cap'n Proto schema. Also available in the github repository under `examples/addressbook.capnp `_:: # addressbook.capnp 0x934efea7f017fff0; @@ -70,19 +70,12 @@ Const values show up just as you'd expect under the loaded schema. For example:: Build a message ------------------ -Message Builder -~~~~~~~~~~~~~~~~~~~ - -First you need to allocate a MessageBuilder for your message to go in. There is only 1 message allocator available at the moment (Malloc), although there may be more varied kinds in the future:: - - message = capnp.MallocMessageBuilder() - Initialize a New Cap'n Proto Object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now that you have a message buffer, you need to allocate an actual object that is from your schema. In this case, we will allocate an `AddressBook`:: - addressBook = message.initRoot(addressbook.AddressBook) + addresses = addressbook.AddressBook.newMessage() Notice that we used `addressbook` from the previous section: `Load a Cap'n Proto Schema`_. @@ -153,7 +146,7 @@ Writing to a File For now, the only way to serialize a message is to write it directly to a file descriptor (expect serializing to strings at some point soon):: f = open('example.bin', 'w') - capnp.writePackedMessageToFd(f.fileno(), message) + addresses.writeTo(f) Note the call to fileno(), since it expects a raw file descriptor. There is also `writeMessageToFd` instead of `writePackedMessageToFd`. Make sure your reader uses the same packing type. @@ -166,16 +159,9 @@ Reading from a file Much like before, you will have to de-serialize the message from a file descriptor:: f = open('example.bin') - message = capnp.PackedFdMessageReader(f.fileno()) + addresses = addressbook.AddressBook.readFrom(f -Initialize a New Cap'n Proto Object -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Just like when building, you have to actually specify which message you want to read out of buffer:: - - addressBook = message.getRoot(addressbook.AddressBook) - -Note that this very much needs to match the type you wrote out. In general, you will always be sending the same message types out over a given channel, wrap all your types in an unnamed enum, or you need some out of band method for communicating what type a message is. Unnamed unions are defined in the .capnp file like so:: +Note that this very much needs to match the type you wrote out. In general, you will always be sending the same message types out over a given channel or you should wrap all your types in an unnamed union. Unnamed unions are defined in the .capnp file like so:: struct Message { union { @@ -189,7 +175,7 @@ Reading Fields Fields are very easy to read. You just use the `.` syntax as before. Lists behave just like normal Python lists:: - for person in addressBook.people: + for person in addresses.people: print(person.name, ':', person.email) for phone in person.phones: print(phone.type, ':', phone.number) @@ -224,10 +210,9 @@ Here is a full example reproduced from `examples/example.py