2013-08-18 21:49:12 -07:00
.. _quickstart:
Quickstart
===================
This assumes you already have the capnp library installed. If you don't, please follow the instructions at :ref: `Installation <install>` first.
2013-08-20 00:58:34 -07:00
In general, this library is a very light wrapping of the `Cap'n Proto C++ library <http://kentonv.github.io/capnproto/cxx.html> `_ . You can refer to its docs for more advanced concepts, or just to get a basic idea of how the python library is structured.
2013-08-18 21:49:12 -07:00
2013-08-20 00:58:34 -07:00
Load a Cap'n Proto Schema
2013-08-21 17:46:50 -07:00
-------------------------
2013-08-18 21:49:12 -07:00
First you need to import the library::
import capnp
2013-08-20 00:58:34 -07:00
Then you can load the Cap'n Proto schema with::
2013-08-18 21:49:12 -07:00
2013-09-03 01:16:48 -07:00
import addressbook_capnp
2013-08-25 17:51:23 -07:00
2013-09-03 01:16:48 -07:00
This will look all through all the directories in your sys.path/PYTHONPATH, and try to find a file of the form 'addressbook.capnp'. If you want to disable the import hook magic that `import capnp` adds, and load manually, here's how::
2013-08-21 17:18:02 -07:00
2013-09-03 01:16:48 -07:00
capnp.remove_import_hook()
addressbook_capnp = capnp.load('addressbook.capnp')
2013-09-03 01:24:16 -07:00
2013-09-01 02:13:19 -07:00
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> `_ ::
2013-08-18 21:49:12 -07:00
# addressbook.capnp
0x934efea7f017fff0;
2013-08-25 17:51:23 -07:00
const qux :UInt32 = 123;
2013-08-18 21:49:12 -07:00
struct Person {
id @0 :UInt32;
name @1 :Text;
email @2 :Text;
phones @3 :List(PhoneNumber);
struct PhoneNumber {
number @0 :Text;
type @1 :Type;
enum Type {
mobile @0;
home @1;
work @2;
}
}
employment @4 union {
unemployed @5 :Void;
employer @6 :Text;
school @7 :Text;
selfEmployed @8 :Void;
# We assume that a person is only one of these.
}
}
struct AddressBook {
people @0 :List(Person);
}
2013-08-25 17:51:23 -07:00
Const values
~~~~~~~~~~~~~~
Const values show up just as you'd expect under the loaded schema. For example::
print addressbook.qux
# 123
2013-08-18 21:49:12 -07:00
Build a message
2013-08-18 22:43:40 -07:00
------------------
2013-08-18 21:49:12 -07:00
2013-08-20 00:58:34 -07:00
Initialize a New Cap'n Proto Object
2013-08-21 17:46:50 -07:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2013-08-18 21:49:12 -07:00
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` ::
2013-09-01 20:10:57 -07:00
addresses = addressbook.AddressBook.new_message()
2013-08-18 21:49:12 -07:00
2013-08-20 00:58:34 -07:00
Notice that we used `addressbook` from the previous section: `Load a Cap'n Proto Schema`_ .
2013-08-18 21:49:12 -07:00
List
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allocating a list inside of an object requires use of the `init` function::
people = addressBook.init('people', 2)
For now, let's grab the first element out of this list and assign it to a variable named `alice` ::
alice = people[0]
Primitive Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~
2013-08-20 00:58:34 -07:00
For all primitive types, from the Cap'n Proto docs:
2013-08-18 21:49:12 -07:00
- Boolean: Bool
- Integers: Int8, Int16, Int32, Int64
- Unsigned integers: UInt8, UInt16, UInt32, UInt64
- Floating-point: Float32, Float64
- Blobs: Text, Data
2013-08-30 23:13:20 -07:00
You can assign straight to the variable with the corresponding Python type. For Blobs, you use strings. Assignment happens just by using the `.` syntax on the object you contstructed above::
2013-08-18 21:49:12 -07:00
alice.id = 123
alice.name = 'Alice'
alice.email = 'alice@example.com'
Enums
~~~~~~~~~~~~~~
First we'll allocate a length one list of phonenumbers for `alice` ::
alicePhone = alice.init('phones', 1)[0]
Note that even though it was a length 1 list, it was still a list that was returned, and we extracted the first (and only) element with `[0]` .
Now, enums are treated like strings, and you just assign to them like there were a Text field::
alicePhone.type = 'mobile'
If you assign an invalid value to one, you will get a ValueError::
alicePhone.type = 'foo'
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: src/capnp/schema.c++:326: requirement not met: enum has no such enumerant; name = foo
Unions
~~~~~~~~~~~~~~~~~~
For the most part, you just treat them like structs::
alice.employment.school = "MIT"
Now the `school` field is the active part of the union, and we've assigned `'MIT'` to it. You can query which field is set in a union with `which()`, shown in `Reading Unions`_
Also, one weird case is for Void types in Unions (and in general, but Void is really only used in Unions). For these, you will have to assign `None` to them::
2013-08-18 23:49:07 -07:00
2013-08-18 21:49:12 -07:00
bob.employment.unemployed = None
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')
2013-09-01 20:10:57 -07:00
addresses.write(f)
2013-08-18 21:49:12 -07:00
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.
Read a message
2013-08-18 22:43:40 -07:00
-----------------
2013-08-18 21:49:12 -07:00
Reading from a file
~~~~~~~~~~~~~~~~~~~~~~
Much like before, you will have to de-serialize the message from a file descriptor::
f = open('example.bin')
2013-09-01 20:10:57 -07:00
addresses = addressbook.AddressBook.read(f)
2013-08-18 21:49:12 -07:00
2013-09-01 02:13:19 -07:00
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::
2013-08-18 21:49:12 -07:00
struct Message {
union {
person @0 :Person;
addressbook @1 :AddressBook;
}
}
Reading Fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fields are very easy to read. You just use the `.` syntax as before. Lists behave just like normal Python lists::
2013-09-01 02:13:19 -07:00
for person in addresses.people:
2013-08-18 21:49:12 -07:00
print(person.name, ':', person.email)
for phone in person.phones:
print(phone.type, ':', phone.number)
Reading Unions
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The only tricky one is unions, where you need to call `.which()` to determine the union type. The `.which()` call returns an enum, ie. a string, corresponding to the field name::
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()
2013-09-03 22:10:37 -07:00
Serializing/Deserializing
2013-09-03 01:24:16 -07:00
--------------
2013-09-03 22:10:37 -07:00
Files
2013-09-03 01:24:16 -07:00
~~~~~~~~~~~~~~~~~~~~~~~~~~
2013-09-03 22:10:37 -07:00
As shown in the examples above, there is file serialization with `write()` ::
addresses = addressbook.AddressBook.new_message()
...
f = open('example.bin', 'w')
addresses.write(f)
And similarly for reading::
f = open('example.bin')
addresses = addressbook.AddressBook.read(f)
Dictionaries
~~~~~~~~~~~~~~
2013-09-03 01:24:16 -07:00
There is a convenience method for converting Cap'n Proto messages to a dictionary. This works for both Builder and Reader type messages::
alice.to_dict()
2013-09-03 22:10:37 -07:00
There is also a convenience method for reading for reading a dict in and building a Builder message out of it. This the inverse of the above::
2013-09-03 01:24:16 -07:00
my_dict = {'name' : 'alice'}
alice = addressbook.Person.from_dict(my_dict)
2013-09-03 22:10:37 -07:00
Byte Strings/Buffers
~~~~~~~~~~~~~~~~~~~~~
There is serialization to a byte string available::
encoded_message = alice.to_bytes()
And a corresponding from_bytes function::
alice addressbook.Person.from_bytes(encoded_message)
2013-08-18 21:49:12 -07:00
Full Example
------------------
2013-09-01 01:15:31 -07:00
Here is a full example reproduced from `examples/example.py <https://github.com/jparyani/pycapnp/blob/master/examples/example.py> `_ ::
2013-08-18 21:49:12 -07:00
from __future__ import print_function
import os
import capnp
2013-09-03 01:16:48 -07:00
import addressbook_capnp
2013-08-18 21:49:12 -07:00
2013-09-01 02:13:19 -07:00
def writeAddressBook(file):
2013-09-03 01:16:48 -07:00
addresses = addressbook_capnp.AddressBook.new_message()
2013-09-01 02:13:19 -07:00
people = addresses.init('people', 2)
2013-08-18 21:49:12 -07:00
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
2013-09-01 20:10:57 -07:00
addresses.write(file)
2013-08-18 21:49:12 -07:00
2013-09-01 02:13:19 -07:00
def printAddressBook(file):
2013-09-03 01:16:48 -07:00
addresses = addressbook_capnp.AddressBook.read(file)
2013-08-18 21:49:12 -07:00
2013-09-01 02:13:19 -07:00
for person in addresses.people:
2013-08-18 21:49:12 -07:00
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()
if __name__ == '__main__':
f = open('example', 'w')
2013-09-01 02:13:19 -07:00
writeAddressBook(f)
2013-08-18 21:49:12 -07:00
f = open('example', 'r')
2013-09-01 02:13:19 -07:00
printAddressBook(f)
2013-09-01 20:10:57 -07:00