Update preferred method for reading/writing messages from files

This commit is contained in:
Jason Paryani 2013-09-01 02:13:19 -07:00
parent eb9e7511d0
commit 6fcdf841e4
7 changed files with 103 additions and 124 deletions

View file

@ -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)

View file

@ -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

View file

@ -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
----------------

View file

@ -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 <https://github.com/jparyani/pycapnp/tree/master/examples>`_::
# 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 <https://github.com/
this_dir = os.path.dirname(__file__)
addressbook = capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
def writeAddressBook(fd):
message = capnp.MallocMessageBuilder()
addressBook = message.initRoot(addressbook.AddressBook)
people = addressBook.init('people', 2)
def writeAddressBook(file):
addresses = addressbook.AddressBook.newMessage()
people = addresses.init('people', 2)
alice = people[0]
alice.id = 123
@ -249,14 +234,13 @@ Here is a full example reproduced from `examples/example.py <https://github.com/
bobPhones[1].type = 'work'
bob.employment.unemployed = None
capnp.writePackedMessageToFd(fd, message)
addresses.writeTo(file)
def printAddressBook(fd):
message = capnp.PackedFdMessageReader(f.fileno())
addressBook = message.getRoot(addressbook.AddressBook)
def printAddressBook(file):
addresses = addressbook.AddressBook.readFrom(file)
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)
@ -277,7 +261,7 @@ Here is a full example reproduced from `examples/example.py <https://github.com/
if __name__ == '__main__':
f = open('example', 'w')
writeAddressBook(f.fileno())
writeAddressBook(f)
f = open('example', 'r')
printAddressBook(f.fileno())
printAddressBook(f)

View file

@ -5,10 +5,9 @@ import capnp
this_dir = os.path.dirname(__file__)
addressbook = capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
def writeAddressBook(fd):
message = capnp.MallocMessageBuilder()
addressBook = message.initRoot(addressbook.AddressBook)
people = addressBook.init('people', 2)
def writeAddressBook(file):
addresses = addressbook.AddressBook.newMessage()
people = addresses.init('people', 2)
alice = people[0]
alice.id = 123
@ -30,14 +29,13 @@ def writeAddressBook(fd):
bobPhones[1].type = 'work'
bob.employment.unemployed = None
capnp.writePackedMessageToFd(fd, message)
addresses.writeTo(file)
def printAddressBook(fd):
message = capnp.PackedFdMessageReader(f.fileno())
addressBook = message.getRoot(addressbook.AddressBook)
def printAddressBook(file):
addresses = addressbook.AddressBook.readFrom(file)
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)
@ -58,7 +56,7 @@ def printAddressBook(fd):
if __name__ == '__main__':
f = open('example', 'w')
writeAddressBook(f.fileno())
writeAddressBook(f)
f = open('example', 'r')
printAddressBook(f.fileno())
printAddressBook(f)

View file

@ -61,7 +61,7 @@ setup(
author_email="pypi-contact@jparyani.com",
url = 'https://github.com/jparyani/pycapnp',
download_url = 'https://github.com/jparyani/pycapnp/archive/v%s.zip' % VERSION,
keywords = ['capnp', 'capnproto'],
keywords = ['capnp', 'capnproto', "Cap'n Proto"],
classifiers = [
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',

View file

@ -9,7 +9,7 @@ this_dir = os.path.dirname(__file__)
def addressbook():
return capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
def test_addressbook(addressbook):
def test_addressbook_message_classes(addressbook):
def writeAddressBook(fd):
message = capnp.MallocMessageBuilder()
addressBook = message.initRoot(addressbook.AddressBook)
@ -67,6 +67,63 @@ def test_addressbook(addressbook):
f = open('example', 'r')
printAddressBook(f.fileno())
def test_addressbook(addressbook):
def writeAddressBook(file):
addresses = addressbook.AddressBook.newMessage()
people = addresses.init('people', 2)
alice = people[0]
alice.id = 123
alice.name = 'Alice'
alice.email = 'alice@example.com'
alicePhones = alice.init('phones', 1)
alicePhones[0].number = "555-1212"
alicePhones[0].type = 'mobile'
alice.employment.school = "MIT"
bob = people[1]
bob.id = 456
bob.name = 'Bob'
bob.email = 'bob@example.com'
bobPhones = bob.init('phones', 2)
bobPhones[0].number = "555-4567"
bobPhones[0].type = 'home'
bobPhones[1].number = "555-7654"
bobPhones[1].type = 'work'
bob.employment.unemployed = None
addresses.writeTo(file)
def printAddressBook(file):
addresses = addressbook.AddressBook.readFrom(file)
for person in addresses.people:
print(person.name, ':', person.email)
for phone in person.phones:
print(phone.type, ':', phone.number)
which = person.employment.which()
print(which)
if which == 'unemployed':
print('unemployed')
elif which == 'employer':
print('employer:', person.employment.employer)
elif which == 'school':
print('student at:', person.employment.school)
elif which == 'selfEmployed':
print('self employed')
print()
f = open('example', 'w')
writeAddressBook(f)
f = open('example', 'r')
printAddressBook(f)
@pytest.fixture
def all_types():
return capnp.load(os.path.join(this_dir, 'all-types.capnp'))