mirror of
https://github.com/capnproto/pycapnp.git
synced 2025-03-04 08:24:43 +01:00
Update preferred method for reading/writing messages from files
This commit is contained in:
parent
eb9e7511d0
commit
6fcdf841e4
7 changed files with 103 additions and 124 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
----------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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',
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Add table
Reference in a new issue