Merge branch 'feature/v0.5' into develop

Conflicts:
	capnp/helpers/helpers.pxd
	capnp/helpers/rpcHelper.h
	capnp/lib/capnp.pxd
	capnp/lib/capnp.pyx
	requirements.txt
	setup.py
	test/test_struct.py
This commit is contained in:
Jason Paryani 2014-12-15 12:20:26 -08:00
commit 2ee4498318
40 changed files with 2620 additions and 416 deletions

View file

@ -132,7 +132,7 @@ An error like:
capnp/capnp.cpp:312:10: fatal error: 'capnp/dynamic.h' file not found
#include "capnp/dynamic.h"
Means you haven't installed the Cap'n Proto C++ library. Please follow the directions at the [official installation docs](http://kentonv.github.io/capnproto/install.html)
Means your sytem can't find the installed the Cap'n Proto C++ library. If you haven't installed it yet, please follow the directions at the [official installation docs](http://kentonv.github.io/capnproto/install.html). Otherwise trying `LDFLAGS=-L/usr/local/lib CPPFLAGS=-I/usr/local/include pip install pycapnp` may fix the problem for you.
[![Build Status](https://travis-ci.org/jparyani/pycapnp.png?branch=develop)](https://travis-ci.org/jparyani/pycapnp)

View file

@ -2,12 +2,16 @@ from __future__ import print_function
import os
import capnp
try:
profile
except:
profile = lambda func: func
this_dir = os.path.dirname(__file__)
addressbook = capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
print = lambda *x: x
@profile
def writeAddressBook():
addressBook = addressbook.AddressBook.new_message()
people = addressBook.init('people', 2)
@ -32,20 +36,57 @@ def writeAddressBook():
msg_bytes = addressBook.to_bytes()
return msg_bytes
@profile
def printAddressBook(msg_bytes):
addressBook = addressbook.AddressBook.from_bytes(msg_bytes)
for person in addressBook.people:
print(person.name, ':', person.email)
person.name, person.email
for phone in person.phones:
print(phone.type, ':', phone.number)
print()
phone.type, phone.number
@profile
def writeAddressBookDict():
addressBook = addressbook.AddressBook.new_message()
people = addressBook.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'
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'
msg = addressBook.to_dict()
return msg
@profile
def printAddressBookDict(msg):
addressBook = addressbook.AddressBook.new_message(**msg)
for person in addressBook.people:
person.name, person.email
for phone in person.phones:
phone.type, phone.number
if __name__ == '__main__':
# for i in range(10000):
# msg_bytes = writeAddressBook()
# printAddressBook(msg_bytes)
for i in range(10000):
msg_bytes = writeAddressBook()
msg = writeAddressBookDict()
printAddressBook(msg_bytes)
printAddressBookDict(msg)

View file

@ -35,7 +35,7 @@ pjoin = os.path.join
# Constants
#-----------------------------------------------------------------------------
bundled_version = (0,4,1)
bundled_version = (0,5,0)
libcapnp = "capnproto-c++-%i.%i.%i.tar.gz" % (bundled_version)
libcapnp_url = "https://capnproto.org/" + libcapnp

View file

@ -1,7 +1,7 @@
"""A python library wrapping the Cap'n Proto C++ library
Example Usage::
import capnp
addressbook = capnp.load('addressbook.capnp')

64
capnp/_gen.py Normal file
View file

@ -0,0 +1,64 @@
from __future__ import print_function
import capnp
import schema_capnp
import sys
from jinja2 import Environment, PackageLoader
import os
def find_type(code, id):
for node in code['nodes']:
if node['id'] == id:
return node
return None
def main():
env = Environment(loader=PackageLoader('capnp', 'templates'))
env.filters['format_name'] = lambda name: name[name.find(':')+1:]
code = schema_capnp.CodeGeneratorRequest.read(sys.stdin)
code=code.to_dict()
code['nodes'] = [node for node in code['nodes'] if 'struct' in node and node['scopeId'] != 0]
for node in code['nodes']:
displayName = node['displayName']
parent, path = displayName.split(':')
node['module_path'] = parent.replace('.', '_') + '.' + '.'.join([x[0].upper() + x[1:] for x in path.split('.')])
node['module_name'] = path.replace('.', '_')
node['c_module_path'] = '::'.join([x[0].upper() + x[1:] for x in path.split('.')])
node['schema'] = '_{}_Schema'.format(node['module_name'])
is_union = False
for field in node['struct']['fields']:
if field['discriminantValue'] != 65535:
is_union = True
field['c_name'] = field['name'][0].upper() + field['name'][1:]
if 'slot' in field:
field['type'] = field['slot']['type'].keys()[0]
if not isinstance(field['slot']['type'][field['type']], dict):
continue
sub_type = field['slot']['type'][field['type']].get('typeId', None)
if sub_type:
field['sub_type'] = find_type(code, sub_type)
sub_type = field['slot']['type'][field['type']].get('elementType', None)
if sub_type:
field['sub_type'] = sub_type
else:
field['type'] = find_type(code, field['group']['typeId'])
node['is_union'] = is_union
include_dir = os.path.abspath(os.path.join(os.path.dirname(capnp.__file__), '..'))
module = env.get_template('module.pyx')
for f in code['requestedFiles']:
filename = f['filename'].replace('.', '_') + '_cython.pyx'
file_code = dict(code)
file_code['nodes'] = [node for node in file_code['nodes'] if node['displayName'].startswith(f['filename'])]
with open(filename, 'w') as out:
out.write(module.render(code=file_code, file=f, include_dir=include_dir))
setup = env.get_template('setup.py.tmpl')
with open('setup_capnp.py', 'w') as out:
out.write(setup.render(code=code))
print('You now need to build the cython module by running `python setup_capnp.py build_ext --inplace`.')
print()

View file

@ -1,27 +1,26 @@
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xbdf87d7bb8304e81;
$namespace("capnp::annotations");
annotation namespace(file): Text;
annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text;

View file

@ -2,6 +2,7 @@
#include "kj/async.h"
#include "Python.h"
#include "capabilityHelper.h"
class PyEventPort: public kj::EventPort {
public:
@ -9,15 +10,18 @@ public:
// We don't need to incref/decref, since this C++ class will be owned by the Python wrapper class, and we'll make sure the python class doesn't refcount to 0 elsewhere.
// Py_INCREF(py_event_port);
}
virtual void wait() {
virtual bool wait() {
GILAcquire gil;
PyObject_CallMethod(py_event_port, const_cast<char *>("wait"), NULL);
}
virtual void poll() {
virtual bool poll() {
GILAcquire gil;
PyObject_CallMethod(py_event_port, const_cast<char *>("poll"), NULL);
}
virtual void setRunnable(bool runnable) {
GILAcquire gil;
PyObject * arg = Py_False;
if (runnable)
arg = Py_True;
@ -29,5 +33,25 @@ private:
};
void waitNeverDone(kj::WaitScope & scope) {
GILRelease gil;
kj::NEVER_DONE.wait(scope);
}
kj::Timer * getTimer(kj::AsyncIoContext * context) {
return &context->lowLevelProvider->getTimer();
}
void waitVoidPromise(kj::Promise<void> * promise, kj::WaitScope & scope) {
GILRelease gil;
promise->wait(scope);
}
PyObject * waitPyPromise(kj::Promise<PyObject *> * promise, kj::WaitScope & scope) {
GILRelease gil;
return promise->wait(scope);
}
capnp::Response< ::capnp::DynamicStruct> * waitRemote(capnp::RemotePromise< ::capnp::DynamicStruct> * promise, kj::WaitScope & scope) {
GILRelease gil;
return new capnp::Response< ::capnp::DynamicStruct>(promise->wait(scope));
}

View file

@ -3,7 +3,6 @@
#include "capnp/dynamic.h"
#include <stdexcept>
#include "Python.h"
#include <iostream>
extern "C" {
PyObject * wrap_remote_call(PyObject * func, capnp::Response<capnp::DynamicStruct> &);
@ -17,12 +16,38 @@ extern "C" {
::capnp::RemotePromise< ::capnp::DynamicStruct> * extract_remote_promise(PyObject *);
}
class GILAcquire {
public:
GILAcquire() : gstate(PyGILState_Ensure()) {}
~GILAcquire() {
PyGILState_Release(gstate);
}
PyGILState_STATE gstate;
};
class GILRelease {
public:
GILRelease() {
Py_UNBLOCK_THREADS
}
~GILRelease() {
Py_BLOCK_THREADS
}
PyThreadState *_save; // The macros above read/write from this variable
};
::kj::Promise<PyObject *> convert_to_pypromise(capnp::RemotePromise<capnp::DynamicStruct> & promise) {
return promise.then([](capnp::Response<capnp::DynamicStruct>&& response) { return wrap_dynamic_struct_reader(response); } );
}
::kj::Promise<PyObject *> convert_to_pypromise(kj::Promise<void> & promise) {
return promise.then([]() { Py_RETURN_NONE;} );
return promise.then([]() {
GILAcquire gil;
Py_INCREF( Py_None );
return Py_None;
});
}
template<class T>
@ -31,6 +56,7 @@ template<class T>
}
void reraise_kj_exception() {
GILAcquire gil;
try {
if (PyErr_Occurred())
; // let the latest Python exn pass through and ignore the current one
@ -51,12 +77,13 @@ void reraise_kj_exception() {
}
void check_py_error() {
GILAcquire gil;
PyObject * err = PyErr_Occurred();
if(err) {
PyObject * ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
if(ptype == NULL || pvalue == NULL || ptraceback == NULL)
throw kj::Exception(kj::Exception::Nature::OTHER, kj::Exception::Durability::PERMANENT, kj::heapString("capabilityHelper.h"), 44, kj::heapString("Unknown error occurred"));
throw kj::Exception(kj::Exception::Type::FAILED, kj::heapString("capabilityHelper.h"), 44, kj::heapString("Unknown error occurred"));
PyObject * info = get_exception_info(ptype, pvalue, ptraceback);
@ -75,11 +102,12 @@ void check_py_error() {
Py_DECREF(info);
PyErr_Clear();
throw kj::Exception(kj::Exception::Nature::OTHER, kj::Exception::Durability::PERMANENT, kj::mv(filename), line, kj::mv(description));
throw kj::Exception(kj::Exception::Type::FAILED, kj::mv(filename), line, kj::mv(description));
}
}
kj::Promise<PyObject *> wrapPyFunc(PyObject * func, PyObject * arg) {
GILAcquire gil;
auto arg_promise = extract_promise(arg);
if(arg_promise == NULL) {
@ -102,6 +130,7 @@ kj::Promise<PyObject *> wrapPyFunc(PyObject * func, PyObject * arg) {
}
kj::Promise<PyObject *> wrapPyFuncNoArg(PyObject * func) {
GILAcquire gil;
PyObject * result = PyObject_CallFunctionObjArgs(func, NULL);
check_py_error();
@ -116,6 +145,7 @@ kj::Promise<PyObject *> wrapPyFuncNoArg(PyObject * func) {
}
kj::Promise<PyObject *> wrapRemoteCall(PyObject * func, capnp::Response<capnp::DynamicStruct> & arg) {
GILAcquire gil;
PyObject * ret = wrap_remote_call(func, arg);
check_py_error();
@ -133,7 +163,7 @@ kj::Promise<PyObject *> wrapRemoteCall(PyObject * func, capnp::Response<capnp::D
if(error_func == Py_None)
return promise.then([func](PyObject * arg) { return wrapPyFunc(func, arg); } );
else
return promise.then([func](PyObject * arg) { return wrapPyFunc(func, arg); }
return promise.then([func](PyObject * arg) { return wrapPyFunc(func, arg); }
, [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } );
}
@ -141,7 +171,7 @@ kj::Promise<PyObject *> wrapRemoteCall(PyObject * func, capnp::Response<capnp::D
if(error_func == Py_None)
return promise.then([func](capnp::Response<capnp::DynamicStruct>&& arg) { return wrapRemoteCall(func, arg); } );
else
return promise.then([func](capnp::Response<capnp::DynamicStruct>&& arg) { return wrapRemoteCall(func, arg); }
return promise.then([func](capnp::Response<capnp::DynamicStruct>&& arg) { return wrapRemoteCall(func, arg); }
, [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } );
}
@ -149,7 +179,7 @@ kj::Promise<PyObject *> wrapRemoteCall(PyObject * func, capnp::Response<capnp::D
if(error_func == Py_None)
return promise.then([func]() { return wrapPyFuncNoArg(func); } );
else
return promise.then([func]() { return wrapPyFuncNoArg(func); }
return promise.then([func]() { return wrapPyFuncNoArg(func); }
, [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } );
}
@ -163,10 +193,12 @@ public:
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema, PyObject * _py_server)
: capnp::DynamicCapability::Server(schema), py_server(_py_server) {
GILAcquire gil;
Py_INCREF(_py_server);
}
~PythonInterfaceDynamicImpl() {
GILAcquire gil;
Py_DECREF(py_server);
}
@ -192,14 +224,17 @@ public:
PyObject * obj;
PyRefCounter(PyObject * o) : obj(o) {
GILAcquire gil;
Py_INCREF(obj);
}
PyRefCounter(const PyRefCounter & ref) : obj(ref.obj) {
GILAcquire gil;
Py_INCREF(obj);
}
~PyRefCounter() {
GILAcquire gil;
Py_DECREF(obj);
}
};
@ -213,4 +248,4 @@ capnp::DynamicValue::Reader new_server(capnp::InterfaceSchema & schema, PyObject
capnp::Capability::Client server_to_client(capnp::InterfaceSchema & schema, PyObject * server) {
return kj::heap<PythonInterfaceDynamicImpl>(schema, server);
}
}

View file

@ -8,4 +8,4 @@
#include "capnp/dynamic.h"
static_assert(CAPNP_VERSION >= 4000, "Version of Cap'n Proto C++ Library is too old. Please upgrade to a version >= 0.4 and then re-install this python library");
static_assert(CAPNP_VERSION >= 5000, "Version of Cap'n Proto C++ Library is too old. Please upgrade to a version >= 0.5 and then re-install this python library");

View file

@ -1,4 +1,4 @@
from capnp.includes.capnp_cpp cimport Maybe, DynamicStruct, Request, PyPromise, VoidPromise, PyPromiseArray, RemotePromise, DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability, RpcSystem, MessageBuilder, MessageReader, TwoPartyVatNetwork, PyRestorer, AnyPointer, DynamicStruct_Builder, WaitScope, AsyncIoContext, StringPtr, TaskSet
from capnp.includes.capnp_cpp cimport Maybe, DynamicStruct, Request, Response, PyPromise, VoidPromise, PyPromiseArray, RemotePromise, DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability, RpcSystem, MessageBuilder, MessageReader, TwoPartyVatNetwork, PyRestorer, AnyPointer, DynamicStruct_Builder, WaitScope, AsyncIoContext, StringPtr, TaskSet, Timer
from capnp.includes.schema_cpp cimport ByteArray
@ -6,11 +6,11 @@ from non_circular cimport reraise_kj_exception
from cpython.ref cimport PyObject
cdef extern from "../helpers/fixMaybe.h":
cdef extern from "capnp/helpers/fixMaybe.h":
EnumSchema.Enumerant fixMaybe(Maybe[EnumSchema.Enumerant]) except +reraise_kj_exception
StructSchema.Field fixMaybe(Maybe[StructSchema.Field]) except +reraise_kj_exception
cdef extern from "../helpers/capabilityHelper.h":
cdef extern from "capnp/helpers/capabilityHelper.h":
# PyPromise evalLater(EventLoop &, PyObject * func)
# PyPromise there(EventLoop & loop, PyPromise & promise, PyObject * func, PyObject * error_func)
PyPromise then(PyPromise & promise, PyObject * func, PyObject * error_func)
@ -24,7 +24,7 @@ cdef extern from "../helpers/capabilityHelper.h":
PyPromise convert_to_pypromise(VoidPromise&)
VoidPromise convert_to_voidpromise(PyPromise&)
cdef extern from "../helpers/rpcHelper.h":
cdef extern from "capnp/helpers/rpcHelper.h":
Capability.Client restoreHelper(RpcSystem&)
Capability.Client restoreHelper(RpcSystem&, MessageBuilder&)
Capability.Client restoreHelper(RpcSystem&, MessageReader&)
@ -33,8 +33,12 @@ cdef extern from "../helpers/rpcHelper.h":
RpcSystem makeRpcClientWithRestorer(TwoPartyVatNetwork&, PyRestorer&)
PyPromise connectServer(TaskSet &, PyRestorer &, AsyncIoContext *, StringPtr)
cdef extern from "../helpers/serialize.h":
cdef extern from "capnp/helpers/serialize.h":
ByteArray messageToPackedBytes(MessageBuilder &, size_t wordCount)
cdef extern from "../helpers/asyncHelper.h":
cdef extern from "capnp/helpers/asyncHelper.h":
void waitNeverDone(WaitScope&)
Response * waitRemote(RemotePromise *, WaitScope&)
PyObject * waitPyPromise(PyPromise *, WaitScope&)
void waitVoidPromise(VoidPromise *, WaitScope&)
Timer * getTimer(AsyncIoContext *) except +reraise_kj_exception

View file

@ -1,20 +1,20 @@
from cpython.ref cimport PyObject
cdef extern from "../helpers/capabilityHelper.h":
cdef extern from "capnp/helpers/capabilityHelper.h":
cppclass PythonInterfaceDynamicImpl:
PythonInterfaceDynamicImpl(PyObject *)
cdef extern from "../helpers/capabilityHelper.h":
cdef extern from "capnp/helpers/capabilityHelper.h":
void reraise_kj_exception()
cdef cppclass PyRefCounter:
PyRefCounter(PyObject *)
cdef extern from "../helpers/rpcHelper.h":
cdef extern from "capnp/helpers/rpcHelper.h":
cdef cppclass PyRestorer:
PyRestorer(PyObject *)
cdef cppclass ErrorHandler:
pass
cdef extern from "../helpers/asyncHelper.h":
cdef extern from "capnp/helpers/asyncHelper.h":
cdef cppclass PyEventPort:
PyEventPort(PyObject *)
PyEventPort(PyObject *)

View file

@ -20,8 +20,9 @@ public:
// ~PyRestorer() {
// Py_DECREF(py_restorer);
// }
capnp::Capability::Client restore(capnp::AnyPointer::Reader objectId) override {
GILAcquire gil;
capnp::Capability::Client * ret = call_py_restorer(py_restorer, objectId);
check_py_error();
capnp::Capability::Client stack_ret(*ret);
@ -63,11 +64,13 @@ capnp::Capability::Client restoreHelper(capnp::RpcSystem<capnp::rpc::twoparty::S
}
capnp::Capability::Client restoreHelper(capnp::RpcSystem<capnp::rpc::twoparty::SturdyRefHostId>& client) {
capnp::MallocMessageBuilder message;
capnp::rpc::SturdyRef::Builder ref = message.getRoot<capnp::rpc::SturdyRef>();
auto hostId = ref.getHostId().initAs<capnp::rpc::twoparty::SturdyRefHostId>();
capnp::MallocMessageBuilder hostIdMessage(8);
auto hostId = hostIdMessage.initRoot<capnp::rpc::twoparty::SturdyRefHostId>();
hostId.setSide(capnp::rpc::twoparty::Side::SERVER);
return client.restore(hostId, ref.getObjectId());
capnp::MallocMessageBuilder blankMessage(8);
auto objectId = blankMessage.getRoot<capnp::AnyPointer>();
return client.restore(hostId, objectId);
}
template <typename SturdyRefHostId, typename ProvisionId,
@ -76,8 +79,7 @@ capnp::RpcSystem<SturdyRefHostId> makeRpcClientWithRestorer(
capnp::VatNetwork<SturdyRefHostId, ProvisionId, RecipientId, ThirdPartyCapId, JoinAnswer>& network,
PyRestorer& restorer) {
using namespace capnp;
return RpcSystem<SturdyRefHostId>(network,
kj::Maybe<SturdyRefRestorer<AnyPointer>&>(restorer));
return RpcSystem<SturdyRefHostId>(network, restorer);
}
struct ServerContext {
@ -113,17 +115,17 @@ void acceptLoop(kj::TaskSet & tasks, PyRestorer & restorer, kj::Own<kj::Connecti
}
kj::Promise<PyObject *> connectServer(kj::TaskSet & tasks, PyRestorer & restorer, kj::AsyncIoContext * context, kj::StringPtr bindAddress) {
auto paf = kj::newPromiseAndFulfiller<uint32_t>();
auto paf = kj::newPromiseAndFulfiller<unsigned int>();
auto portPromise = paf.promise.fork();
tasks.add(context->provider->getNetwork().parseAddress(bindAddress)
.then(kj::mvCapture(paf.fulfiller,
[&](kj::Own<kj::PromiseFulfiller<uint32_t>>&& portFulfiller,
[&](kj::Own<kj::PromiseFulfiller<unsigned int>>&& portFulfiller,
kj::Own<kj::NetworkAddress>&& addr) {
auto listener = addr->listen();
portFulfiller->fulfill(listener->getPort());
acceptLoop(tasks, restorer, kj::mv(listener));
})));
return portPromise.addBranch().then([&](uint32_t port) { return PyLong_FromUnsignedLong(port); });
return portPromise.addBranch().then([&](unsigned int port) { return PyLong_FromUnsignedLong(port); });
}

View file

@ -1,7 +1,7 @@
# schema.capnp.cpp.pyx
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11
cdef extern from "../helpers/checkCompiler.h":
cdef extern from "capnp/helpers/checkCompiler.h":
pass
from schema_cpp cimport Node, Data, StructNode, EnumNode, InterfaceNode, MessageBuilder, MessageReader
@ -39,8 +39,7 @@ cdef extern from "kj/exception.h" namespace " ::kj":
Exception(Exception)
char* getFile()
int getLine()
int getNature()
int getDurability()
int getType()
StringPtr getDescription()
cdef extern from "kj/memory.h" namespace " ::kj":
@ -51,7 +50,7 @@ cdef extern from "kj/memory.h" namespace " ::kj":
Own[PyRefCounter] makePyRefCounter" ::kj::heap< PyRefCounter >"(PyObject *)
cdef extern from "kj/async.h" namespace " ::kj":
cdef cppclass Promise[T]:
cdef cppclass Promise[T] nogil:
Promise()
Promise(Promise)
Promise(T)
@ -100,6 +99,16 @@ cdef extern from "kj/array.h" namespace " ::kj":
ctypedef Promise[PyArray] PyPromiseArray
cdef extern from "kj/time.h" namespace " ::kj":
cdef cppclass Duration:
Duration(int64_t)
# cdef cppclass TimePoint:
# TimePoint(Duration)
cdef cppclass Timer:
# int64_t now()
# VoidPromise atTime(TimePoint time)
VoidPromise afterDelay(Duration delay)
cdef extern from "kj/async-io.h" namespace " ::kj":
cdef cppclass AsyncIoStream:
pass
@ -107,6 +116,7 @@ cdef extern from "kj/async-io.h" namespace " ::kj":
# Own[AsyncInputStream] wrapInputFd(int)
# Own[AsyncOutputStream] wrapOutputFd(int)
Own[AsyncIoStream] wrapSocketFd(int)
Timer& getTimer() except +reraise_kj_exception
cdef cppclass AsyncIoProvider:
pass
cdef cppclass WaitScope:
@ -122,7 +132,42 @@ cdef extern from "kj/async-io.h" namespace " ::kj":
AsyncIoContext setupAsyncIo()
cdef extern from "capnp/schema.capnp.h" namespace " ::capnp":
enum TypeWhich" ::capnp::schema::Type::Which":
TypeWhichVOID " ::capnp::schema::Type::Which::VOID"
TypeWhichBOOL " ::capnp::schema::Type::Which::BOOL"
TypeWhichINT8 " ::capnp::schema::Type::Which::INT8"
TypeWhichINT16 " ::capnp::schema::Type::Which::INT16"
TypeWhichINT32 " ::capnp::schema::Type::Which::INT32"
TypeWhichINT64 " ::capnp::schema::Type::Which::INT64"
TypeWhichUINT8 " ::capnp::schema::Type::Which::UINT8"
TypeWhichUINT16 " ::capnp::schema::Type::Which::UINT16"
TypeWhichUINT32 " ::capnp::schema::Type::Which::UINT32"
TypeWhichUINT64 " ::capnp::schema::Type::Which::UINT64"
TypeWhichFLOAT32 " ::capnp::schema::Type::Which::FLOAT32"
TypeWhichFLOAT64 " ::capnp::schema::Type::Which::FLOAT64"
TypeWhichTEXT " ::capnp::schema::Type::Which::TEXT"
TypeWhichDATA " ::capnp::schema::Type::Which::DATA"
TypeWhichLIST " ::capnp::schema::Type::Which::LIST"
TypeWhichENUM " ::capnp::schema::Type::Which::ENUM"
TypeWhichSTRUCT " ::capnp::schema::Type::Which::STRUCT"
TypeWhichINTERFACE " ::capnp::schema::Type::Which::INTERFACE"
TypeWhichANY_POINTER " ::capnp::schema::Type::Which::ANY_POINTER"
cdef extern from "capnp/schema.h" namespace " ::capnp":
cdef cppclass SchemaType" ::capnp::Type":
SchemaType()
SchemaType(TypeWhich)
cbool isList()
cbool isEnum()
cbool isStruct()
cbool isInterface()
StructSchema asStruct() except +reraise_kj_exception
EnumSchema asEnum() except +reraise_kj_exception
InterfaceSchema asInterface() except +reraise_kj_exception
ListSchema asList() except +reraise_kj_exception
cdef cppclass Schema:
Node.Reader getProto() except +reraise_kj_exception
StructSchema asStruct() except +reraise_kj_exception
@ -132,11 +177,17 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
InterfaceSchema asInterface() except +reraise_kj_exception
cdef cppclass InterfaceSchema(Schema):
cppclass SuperclassList:
uint size()
InterfaceSchema operator[](uint index)
cppclass Method:
InterfaceNode.Method.Reader getProto()
InterfaceSchema getContainingInterface()
uint16_t getOrdinal()
uint getIndex()
StructSchema getParamType()
StructSchema getResultType()
cppclass MethodList:
uint size()
@ -146,6 +197,7 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
Maybe[Method] findMethodByName(StringPtr name)
Method getMethodByName(StringPtr name)
bint extends(InterfaceSchema other)
SuperclassList getSuperclasses()
# kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const;
cdef cppclass StructSchema(Schema):
@ -153,6 +205,7 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
StructNode.Member.Reader getProto()
StructSchema getContainingStruct()
uint getIndex()
SchemaType getType()
cppclass FieldList:
uint size()
@ -184,6 +237,15 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
Enumerant getEnumerantByName(char * name)
Node.Reader getProto()
cdef cppclass ListSchema:
SchemaType getElementType()
ListSchema listSchemaOfStruct" ::capnp::ListSchema::of"(StructSchema)
ListSchema listSchemaOfEnum" ::capnp::ListSchema::of"(EnumSchema)
ListSchema listSchemaOfInterface" ::capnp::ListSchema::of"(InterfaceSchema)
ListSchema listSchemaOfList" ::capnp::ListSchema::of"(ListSchema)
ListSchema listSchemaOfType" ::capnp::ListSchema::of"(SchemaType)
cdef cppclass ConstSchema:
pass
@ -214,8 +276,11 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicStruct:
cppclass Reader:
DynamicValueForward.Reader get(char *) except +reraise_kj_exception
DynamicValueForward.Reader getByField"get"(StructSchema.Field) except +reraise_kj_exception
bint has(char *) except +reraise_kj_exception
bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema()
uint64_t getId"getSchema().getProto().getId"()
Maybe[StructSchema.Field] which()
MessageSize totalSize()
cppclass Pipeline:
@ -228,11 +293,17 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicStruct_Builder()
DynamicStruct_Builder(DynamicStruct_Builder &)
DynamicValueForward.Builder get(char *) except +reraise_kj_exception
DynamicValueForward.Builder getByField"get"(StructSchema.Field) except +reraise_kj_exception
bint has(char *) except +reraise_kj_exception
bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception
void set(char *, DynamicValueForward.Reader) except +reraise_kj_exception
void setByField"set"(StructSchema.Field, DynamicValueForward.Reader) except +reraise_kj_exception
DynamicValueForward.Builder init(char *, uint size) except +reraise_kj_exception
DynamicValueForward.Builder init(char *) except +reraise_kj_exception
DynamicValueForward.Builder initByField"init"(StructSchema.Field, uint size) except +reraise_kj_exception
DynamicValueForward.Builder initByField"init"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema()
uint64_t getId"getSchema().getProto().getId"()
Maybe[StructSchema.Field] which()
void adopt(char *, DynamicOrphan) except +reraise_kj_exception
DynamicOrphan disown(char *)
@ -263,9 +334,12 @@ cdef extern from "capnp/capability.h" namespace " ::capnp":
cdef extern from "capnp/rpc-twoparty.h" namespace " ::capnp":
cdef cppclass RpcSystem" ::capnp::RpcSystem<capnp::rpc::twoparty::SturdyRefHostId>":
RpcSystem(RpcSystem&&)
enum Side" ::capnp::rpc::twoparty::Side":
CLIENT" ::capnp::rpc::twoparty::Side::CLIENT"
SERVER" ::capnp::rpc::twoparty::Side::SERVER"
cdef cppclass Side" ::capnp::rpc::twoparty::Side":
pass
cdef Side CLIENT" ::capnp::rpc::twoparty::Side::CLIENT"
cdef Side SERVER" ::capnp::rpc::twoparty::Side::SERVER"
cdef cppclass TwoPartyVatNetwork:
TwoPartyVatNetwork(EventLoop &, AsyncIoStream& stream, Side)
VoidPromise onDisconnect()
@ -286,30 +360,11 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
Maybe[StructSchema.Field] which()
RemotePromise send()
cdef extern from "capnp/any.h" namespace " ::capnp":
cdef cppclass AnyPointer:
cppclass Reader:
DynamicStruct.Reader getAs"getAs< ::capnp::DynamicStruct>"(StructSchema)
StringPtr getAsText"getAs< ::capnp::Text>"()
cppclass Builder:
Builder(Builder)
DynamicStruct_Builder getAs"getAs< ::capnp::DynamicStruct>"(StructSchema)
StringPtr getAsText"getAs< ::capnp::Text>"()
void setAsStruct"setAs< ::capnp::DynamicStruct>"(DynamicStruct.Reader&) except +reraise_kj_exception
void setAsText"setAs< ::capnp::Text>"(char*) except +reraise_kj_exception
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicEnum:
uint16_t getRaw()
Maybe[EnumSchema.Enumerant] getEnumerant()
cdef cppclass DynamicObject:
cppclass Reader:
DynamicStruct.Reader as(StructSchema schema)
cppclass Builder:
DynamicObject.Reader asReader()
# DynamicList::Reader as(ListSchema schema) const;
cdef cppclass DynamicList:
cppclass Reader:
DynamicValueForward.Reader operator[](uint) except +reraise_kj_exception
@ -325,6 +380,28 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicOrphan disown(uint)
StructSchema getStructElementType'getSchema().getStructElementType'()
cdef extern from "capnp/any.h" namespace " ::capnp":
cdef cppclass AnyPointer:
cppclass Reader:
DynamicStruct.Reader getAs"getAs< ::capnp::DynamicStruct>"(StructSchema) except +reraise_kj_exception
DynamicCapability.Client getAsCapability"getAs< ::capnp::DynamicCapability>"(InterfaceSchema) except +reraise_kj_exception
DynamicList.Reader getAsList"getAs< ::capnp::DynamicList>"(ListSchema) except +reraise_kj_exception
StringPtr getAsText"getAs< ::capnp::Text>"() except +reraise_kj_exception
cppclass Builder:
Builder(Builder)
DynamicStruct_Builder getAs"getAs< ::capnp::DynamicStruct>"(StructSchema) except +reraise_kj_exception
DynamicCapability.Client getAsCapability"getAs< ::capnp::DynamicCapability>"(InterfaceSchema) except +reraise_kj_exception
DynamicList.Builder getAsList"getAs< ::capnp::DynamicList>"(ListSchema) except +reraise_kj_exception
StringPtr getAsText"getAs< ::capnp::Text>"() except +reraise_kj_exception
void setAsStruct"setAs< ::capnp::DynamicStruct>"(DynamicStruct.Reader&) except +reraise_kj_exception
void setAsText"setAs< ::capnp::Text>"(char*) except +reraise_kj_exception
AnyPointer.Reader asReader() except +reraise_kj_exception
void set(AnyPointer.Reader) except +reraise_kj_exception
DynamicStruct_Builder initAsStruct"initAs< ::capnp::DynamicStruct>"(StructSchema) except +reraise_kj_exception
DynamicList.Builder initAsList"initAs< ::capnp::DynamicList>"(ListSchema, uint) except +reraise_kj_exception
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicValue:
cppclass Reader:
Reader()

View file

@ -1,5 +1,4 @@
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
from cpython.exc cimport PyErr_Clear
from libc.stdint cimport *
ctypedef unsigned int uint
ctypedef uint8_t byte

View file

@ -1,19 +1,123 @@
from capnp.includes cimport capnp_cpp as capnp
from capnp.includes cimport schema_cpp
from capnp.includes.capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, EnumSchema as C_EnumSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject, DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, PyPromise, VoidPromise, CallContext, PyRestorer, RpcSystem, makeRpcServer, makeRpcClient, Capability as C_Capability, TwoPartyVatNetwork as C_TwoPartyVatNetwork, Side, AsyncIoStream, Own, makeTwoPartyVatNetwork, PromiseFulfillerPair as C_PromiseFulfillerPair, copyPromiseFulfillerPair, newPromiseAndFulfiller, PyArray, DynamicStruct_Builder
from capnp.includes.capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, EnumSchema as C_EnumSchema, ListSchema as C_ListSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject, DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, PyPromise, VoidPromise, CallContext, PyRestorer, RpcSystem, makeRpcServer, makeRpcClient, Capability as C_Capability, TwoPartyVatNetwork as C_TwoPartyVatNetwork, Side, AsyncIoStream, Own, makeTwoPartyVatNetwork, PromiseFulfillerPair as C_PromiseFulfillerPair, copyPromiseFulfillerPair, newPromiseAndFulfiller, PyArray, DynamicStruct_Builder
from capnp.includes.schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode
from capnp.includes.types cimport *
from capnp.helpers.non_circular cimport reraise_kj_exception
from capnp.helpers cimport helpers
cdef class _StructSchemaField:
cdef C_StructSchema.Field thisptr
cdef object _parent
cdef _init(self, C_StructSchema.Field other, parent=?)
cdef class _DynamicOrphan:
cdef C_DynamicOrphan thisptr
cdef public object _parent
cdef _init(self, C_DynamicOrphan other, object parent)
cdef C_DynamicOrphan move(self)
cpdef get(self)
cdef class _DynamicStructReader:
cdef C_DynamicStruct.Reader thisptr
cdef public object _parent
cdef public bint is_root
cdef object _obj_to_pin
cdef object _schema
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?)
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?, bint tryRegistry=?)
cpdef _which(self)
cpdef _get(self, field)
cpdef _has(self, field)
cpdef _DynamicEnumField _which(self)
cpdef _which_str(self)
cpdef _get_by_field(self, _StructSchemaField field)
cpdef _has_by_field(self, _StructSchemaField field)
cpdef as_builder(self, num_first_segment_words=?)
cdef class _DynamicStructBuilder:
cdef DynamicStruct_Builder thisptr
cdef public object _parent
cdef public bint is_root
cdef bint _is_written
cdef object _schema
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?, bint tryRegistry=?)
cdef _check_write(self)
cpdef to_bytes(_DynamicStructBuilder self)
cpdef _to_bytes_packed_helper(_DynamicStructBuilder self, word_count)
cpdef to_bytes_packed(_DynamicStructBuilder self)
cpdef _get(self, field)
cpdef _set(self, field, value)
cpdef _has(self, field)
cpdef init(self, field, size=?)
cpdef _get_by_field(self, _StructSchemaField field)
cpdef _set_by_field(self, _StructSchemaField field, value)
cpdef _has_by_field(self, _StructSchemaField field)
cpdef _init_by_field(self, _StructSchemaField field, size=?)
cpdef init_resizable_list(self, field)
cpdef _DynamicEnumField _which(self)
cpdef _which_str(self)
cpdef adopt(self, field, _DynamicOrphan orphan)
cpdef disown(self, field)
cpdef as_reader(self)
cpdef copy(self, num_first_segment_words=?)
cdef class _DynamicEnumField:
cdef object thisptr
cdef _init(self, proto)
cpdef _str(self)
cdef class _Schema:
cdef C_Schema thisptr
cdef _init(self, C_Schema other)
cpdef as_const_value(self)
cpdef as_struct(self)
cpdef as_interface(self)
cpdef as_enum(self)
cpdef get_dependency(self, id)
cpdef get_proto(self)
cdef class _InterfaceSchema:
cdef C_InterfaceSchema thisptr
cdef object __method_names, __method_names_inherited, __methods, __methods_inherited
cdef _init(self, C_InterfaceSchema other)
cpdef get_dependency(self, id)
cdef class _DynamicEnum:
cdef capnp.DynamicEnum thisptr
cdef public object _parent
cdef _init(self, capnp.DynamicEnum other, object parent)
cpdef _as_str(self)
cdef class _DynamicListBuilder:
cdef C_DynamicList.Builder thisptr
cdef public object _parent
cdef _init(self, C_DynamicList.Builder other, object parent)
cpdef _get(self, int64_t index)
cpdef _set(self, index, value)
cpdef adopt(self, index, _DynamicOrphan orphan)
cpdef disown(self, index)
cdef to_python_reader(C_DynamicValue.Reader self, object parent)
cdef to_python_builder(C_DynamicValue.Builder self, object parent)
cdef _to_dict(msg, bint verbose, bint ordered)
cdef _from_list(_DynamicListBuilder msg, list d)
cdef _setDynamicFieldWithField(DynamicStruct_Builder thisptr, _StructSchemaField field, value, parent)
cdef _setDynamicFieldStatic(DynamicStruct_Builder thisptr, field, value, parent)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
import capnp
def _struct_reducer(schema_id, data):
'Hack to deal with pypy not allowing reduce functions to be "built-in" methods (ie. compiled from a .pyx)'
return capnp._global_schema_parser.modules_by_id[schema_id].from_bytes(data)

383
capnp/schema.capnp Normal file
View file

@ -0,0 +1,383 @@
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using Cxx = import "c++.capnp";
@0xa93fc509624c72d9;
$Cxx.namespace("capnp::schema");
using Id = UInt64;
# The globally-unique ID of a file, type, or annotation.
struct Node {
id @0 :Id;
displayName @1 :Text;
# Name to present to humans to identify this Node. You should not attempt to parse this. Its
# format could change. It is not guaranteed to be unique.
#
# (On Zooko's triangle, this is the node's nickname.)
displayNamePrefixLength @2 :UInt32;
# If you want a shorter version of `displayName` (just naming this node, without its surrounding
# scope), chop off this many characters from the beginning of `displayName`.
scopeId @3 :Id;
# ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back
# at this node, but robust code should avoid relying on this (and, in fact, group nodes are not
# listed in the outer struct's nestedNodes, since they are listed in the fields). `scopeId` is
# zero if the node has no parent, which is normally only the case with files, but should be
# allowed for any kind of node (in order to make runtime type generation easier).
nestedNodes @4 :List(NestedNode);
# List of nodes nested within this node, along with the names under which they were declared.
struct NestedNode {
name @0 :Text;
# Unqualified symbol name. Unlike Node.name, this *can* be used programmatically.
#
# (On Zooko's triangle, this is the node's petname according to its parent scope.)
id @1 :Id;
# ID of the nested node. Typically, the target node's scopeId points back to this node, but
# robust code should avoid relying on this.
}
annotations @5 :List(Annotation);
# Annotations applied to this node.
union {
# Info specific to each kind of node.
file @6 :Void;
struct :group {
dataWordCount @7 :UInt16;
# Size of the data section, in words.
pointerCount @8 :UInt16;
# Size of the pointer section, in pointers (which are one word each).
preferredListEncoding @9 :ElementSize;
# The preferred element size to use when encoding a list of this struct. If this is anything
# other than `inlineComposite` then the struct is one word or less in size and is a candidate
# for list packing optimization.
isGroup @10 :Bool;
# If true, then this "struct" node is actually not an independent node, but merely represents
# some named union or group within a particular parent struct. This node's scopeId refers
# to the parent struct, which may itself be a union/group in yet another struct.
#
# All group nodes share the same dataWordCount and pointerCount as the top-level
# struct, and their fields live in the same ordinal and offset spaces as all other fields in
# the struct.
#
# Note that a named union is considered a special kind of group -- in fact, a named union
# is exactly equivalent to a group that contains nothing but an unnamed union.
discriminantCount @11 :UInt16;
# Number of fields in this struct which are members of an anonymous union, and thus may
# overlap. If this is non-zero, then a 16-bit discriminant is present indicating which
# of the overlapping fields is active. This can never be 1 -- if it is non-zero, it must be
# two or more.
#
# Note that the fields of an unnamed union are considered fields of the scope containing the
# union -- an unnamed union is not its own group. So, a top-level struct may contain a
# non-zero discriminant count. Named unions, on the other hand, are equivalent to groups
# containing unnamed unions. So, a named union has its own independent schema node, with
# `isGroup` = true.
discriminantOffset @12 :UInt32;
# If `discriminantCount` is non-zero, this is the offset of the union discriminant, in
# multiples of 16 bits.
fields @13 :List(Field);
# Fields defined within this scope (either the struct's top-level fields, or the fields of
# a particular group; see `isGroup`).
#
# The fields are sorted by ordinal number, but note that because groups share the same
# ordinal space, the field's index in this list is not necessarily exactly its ordinal.
# On the other hand, the field's position in this list does remain the same even as the
# protocol evolves, since it is not possible to insert or remove an earlier ordinal.
# Therefore, for most use cases, if you want to identify a field by number, it may make the
# most sense to use the field's index in this list rather than its ordinal.
}
enum :group {
enumerants@14 :List(Enumerant);
# Enumerants ordered by numeric value (ordinal).
}
interface :group {
methods @15 :List(Method);
# Methods ordered by ordinal.
extends @31 :List(Id);
# Superclasses of this interface.
}
const :group {
type @16 :Type;
value @17 :Value;
}
annotation :group {
type @18 :Type;
targetsFile @19 :Bool;
targetsConst @20 :Bool;
targetsEnum @21 :Bool;
targetsEnumerant @22 :Bool;
targetsStruct @23 :Bool;
targetsField @24 :Bool;
targetsUnion @25 :Bool;
targetsGroup @26 :Bool;
targetsInterface @27 :Bool;
targetsMethod @28 :Bool;
targetsParam @29 :Bool;
targetsAnnotation @30 :Bool;
}
}
}
struct Field {
# Schema for a field of a struct.
name @0 :Text;
codeOrder @1 :UInt16;
# Indicates where this member appeared in the code, relative to other members.
# Code ordering may have semantic relevance -- programmers tend to place related fields
# together. So, using code ordering makes sense in human-readable formats where ordering is
# otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so the maximum
# value is count(members) - 1. Fields that are members of a union are only ordered relative to
# the other members of that union, so the maximum value there is count(union.members).
annotations @2 :List(Annotation);
const noDiscriminant :UInt16 = 0xffff;
discriminantValue @3 :UInt16 = Field.noDiscriminant;
# If the field is in a union, this is the value which the union's discriminant should take when
# the field is active. If the field is not in a union, this is 0xffff.
union {
slot :group {
# A regular, non-group, non-fixed-list field.
offset @4 :UInt32;
# Offset, in units of the field's size, from the beginning of the section in which the field
# resides. E.g. for a UInt32 field, multiply this by 4 to get the byte offset from the
# beginning of the data section.
type @5 :Type;
defaultValue @6 :Value;
hadExplicitDefault @10 :Bool;
# Whether the default value was specified explicitly. Non-explicit default values are always
# zero or empty values. Usually, whether the default value was explicit shouldn't matter.
# The main use case for this flag is for structs representing method parameters:
# explicitly-defaulted parameters may be allowed to be omitted when calling the method.
}
group :group {
# A group.
typeId @7 :Id;
# The ID of the group's node.
}
}
ordinal :union {
implicit @8 :Void;
explicit @9 :UInt16;
# The original ordinal number given to the field. You probably should NOT use this; if you need
# a numeric identifier for a field, use its position within the field array for its scope.
# The ordinal is given here mainly just so that the original schema text can be reproduced given
# the compiled version -- i.e. so that `capnp compile -ocapnp` can do its job.
}
}
struct Enumerant {
# Schema for member of an enum.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the enumerants were declared in the code.
# Like Struct.Field.codeOrder.
annotations @2 :List(Annotation);
}
struct Method {
# Schema for method of an interface.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the methods were declared in the code.
# Like Struct.Field.codeOrder.
paramStructType @2 :Id;
# ID of the parameter struct type. If a named parameter list was specified in the method
# declaration (rather than a single struct parameter type) then a corresponding struct type is
# auto-generated. Such an auto-generated type will not be listed in the interface's
# `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace.
resultStructType @3 :Id;
# ID of the return struct type; similar to `paramStructType`.
annotations @4 :List(Annotation);
}
struct Type {
# Represents a type expression.
union {
# The ordinals intentionally match those of Value.
void @0 :Void;
bool @1 :Void;
int8 @2 :Void;
int16 @3 :Void;
int32 @4 :Void;
int64 @5 :Void;
uint8 @6 :Void;
uint16 @7 :Void;
uint32 @8 :Void;
uint64 @9 :Void;
float32 @10 :Void;
float64 @11 :Void;
text @12 :Void;
data @13 :Void;
list :group {
elementType @14 :Type;
}
enum :group {
typeId @15 :Id;
}
struct :group {
typeId @16 :Id;
}
interface :group {
typeId @17 :Id;
}
anyPointer @18 :Void;
}
}
struct Value {
# Represents a value, e.g. a field default value, constant value, or annotation value.
union {
# The ordinals intentionally match those of Type.
void @0 :Void;
bool @1 :Bool;
int8 @2 :Int8;
int16 @3 :Int16;
int32 @4 :Int32;
int64 @5 :Int64;
uint8 @6 :UInt8;
uint16 @7 :UInt16;
uint32 @8 :UInt32;
uint64 @9 :UInt64;
float32 @10 :Float32;
float64 @11 :Float64;
text @12 :Text;
data @13 :Data;
list @14 :AnyPointer;
enum @15 :UInt16;
struct @16 :AnyPointer;
interface @17 :Void;
# The only interface value that can be represented statically is "null", whose methods always
# throw exceptions.
anyPointer @18 :AnyPointer;
}
}
struct Annotation {
# Describes an annotation applied to a declaration. Note AnnotationNode describes the
# annotation's declaration, while this describes a use of the annotation.
id @0 :Id;
# ID of the annotation node.
value @1 :Value;
}
enum ElementSize {
# Possible element sizes for encoded lists. These correspond exactly to the possible values of
# the 3-bit element size component of a list pointer.
empty @0; # aka "void", but that's a keyword.
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
struct CodeGeneratorRequest {
nodes @0 :List(Node);
# All nodes parsed by the compiler, including for the files on the command line and their
# imports.
requestedFiles @1 :List(RequestedFile);
# Files which were listed on the command line.
struct RequestedFile {
id @0 :Id;
# ID of the file.
filename @1 :Text;
# Name of the file as it appeared on the command-line (minus the src-prefix). You may use
# this to decide where to write the output.
imports @2 :List(Import);
# List of all imported paths seen in this file.
struct Import {
id @0 :Id;
# ID of the imported file.
name @1 :Text;
# Name which *this* file used to refer to the foreign file. This may be a relative name.
# This information is provided because it might be useful for code generation, e.g. to
# generate #include directives in C++. We don't put this in Node.file because this
# information is only meaningful at compile time anyway.
#
# (On Zooko's triangle, this is the import's petname according to the importing file.)
}
}
}

284
capnp/templates/module.pyx Normal file
View file

@ -0,0 +1,284 @@
# addressbook_fast.pyx
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11
# distutils: include_dirs = {{include_dir}}
# distutils: libraries = capnpc capnp capnp-rpc
# distutils: sources = {{file.filename}}.cpp
# cython: c_string_type = str
# cython: c_string_encoding = default
# cython: embedsignature = True
{% macro getter(field, type) -%}
{% if 'uint' in field['type'] -%}
uint64_t get{{field.c_name}}() except +reraise_kj_exception
{% elif 'int' in field['type'] -%}
int64_t get{{field.c_name}}() except +reraise_kj_exception
{% elif 'void' == field['type'] -%}
void get{{field.c_name}}() except +reraise_kj_exception
{% elif 'bool' == field['type'] -%}
cbool get{{field.c_name}}() except +reraise_kj_exception
{% elif 'text' == field['type'] -%}
StringPtr get{{field.c_name}}() except +reraise_kj_exception
{% elif 'data' == field['type'] -%}
Data.{{type}} get{{field.c_name}}() except +reraise_kj_exception
{% else -%}
DynamicValue.{{type}} get{{field.c_name}}() except +reraise_kj_exception
{%- endif %}
{%- endmacro %}
# TODO: add struct/enum/list types
{% macro getfield(field, type) -%}
cpdef _get_{{field.name}}(self):
{% if 'int' in field['type'] -%}
return self.thisptr_child.get{{field.c_name}}()
{% elif 'void' == field['type'] -%}
self.thisptr_child.get{{field.c_name}}()
return None
{% elif 'bool' == field['type'] -%}
return self.thisptr_child.get{{field.c_name}}()
{% elif 'text' == field['type'] -%}
temp = self.thisptr_child.get{{field.c_name}}()
return (<char*>temp.begin())[:temp.size()]
{% elif 'data' == field['type'] -%}
temp = self.thisptr_child.get{{field.c_name}}()
return <bytes>((<char*>temp.begin())[:temp.size()])
{% else -%}
cdef DynamicValue.{{type}} temp = self.thisptr_child.get{{field.c_name}}()
return to_python_{{type | lower}}(temp, self._parent)
{% endif -%}
{%- endmacro %}
{% macro setter(field) -%}
{% if 'int' in field['type'] -%}
void set{{field.c_name}}({{field.type}}_t) except +reraise_kj_exception
{% elif 'bool' == field['type'] -%}
void set{{field.c_name}}(cbool) except +reraise_kj_exception
{% elif 'text' == field['type'] -%}
void set{{field.c_name}}(StringPtr) except +reraise_kj_exception
{% elif 'data' == field['type'] -%}
void set{{field.c_name}}(ArrayPtr[byte]) except +reraise_kj_exception
{% else -%}
void set{{field.c_name}}(DynamicValue.Reader) except +reraise_kj_exception
{%- endif %}
{%- endmacro %}
{% macro setfield(field) -%}
{% if 'int' in field['type'] -%}
cpdef _set_{{field.name}}(self, {{field.type}}_t value):
self.thisptr_child.set{{field.c_name}}(value)
{% elif 'void' == field['type'] -%}
cpdef _set_{{field.name}}(self, value=None):
pass
{% elif 'bool' == field['type'] -%}
cpdef _set_{{field.name}}(self, bool value):
self.thisptr_child.set{{field.c_name}}(value)
{% elif 'list' == field['type'] -%}
cpdef _set_{{field.name}}(self, list value):
cdef uint i = 0
self.init("{{field.name}}", len(value))
cdef _DynamicListBuilder temp = self._get_{{field.name}}()
for elem in value:
{% if 'struct' in field['sub_type'] -%}
temp._get(i).from_dict(elem)
{% else -%}
temp[i] = elem
{% endif -%}
i += 1
{% elif 'text' == field['type'] -%}
cpdef _set_{{field.name}}(self, value):
cdef StringPtr temp_string
if type(value) is bytes:
temp_string = StringPtr(<char*>value, len(value))
else:
encoded_value = value.encode()
temp_string = StringPtr(<char*>encoded_value, len(encoded_value))
self.thisptr_child.set{{field.c_name}}(temp_string)
{% elif 'data' == field['type'] -%}
cpdef _set_{{field.name}}(self, value):
cdef StringPtr temp_string
if type(value) is bytes:
temp_string = StringPtr(<char*>value, len(value))
else:
encoded_value = value.encode()
temp_string = StringPtr(<char*>encoded_value, len(encoded_value))
self.thisptr_child.set{{field.c_name}}(ArrayPtr[byte](<byte *>temp_string.begin(), temp_string.size()))
{% else -%}
cpdef _set_{{field.name}}(self, value):
_setDynamicFieldStatic(self.thisptr, "{{field.name}}", value, self._parent)
{% endif -%}
{%- endmacro %}
import capnp
import {{file.filename | replace('.', '_')}}
from capnp.includes.types cimport *
from capnp cimport helpers
from capnp.includes.capnp_cpp cimport DynamicValue, Schema, VOID, StringPtr, ArrayPtr, Data
from capnp.lib.capnp cimport _DynamicStructReader, _DynamicStructBuilder, _DynamicListBuilder, _DynamicEnum, _StructSchemaField, to_python_builder, to_python_reader, _to_dict, _setDynamicFieldStatic, _Schema, _InterfaceSchema
from capnp.helpers.non_circular cimport reraise_kj_exception
cdef DynamicValue.Reader _extract_dynamic_struct_builder(_DynamicStructBuilder value):
return DynamicValue.Reader(value.thisptr.asReader())
cdef DynamicValue.Reader _extract_dynamic_struct_reader(_DynamicStructReader value):
return DynamicValue.Reader(value.thisptr)
cdef DynamicValue.Reader _extract_dynamic_enum(_DynamicEnum value):
return DynamicValue.Reader(value.thisptr)
cdef _from_list(_DynamicListBuilder msg, list d):
cdef size_t count = 0
for val in d:
msg._set(count, val)
count += 1
cdef DynamicValue.Reader to_dynamic_value(value):
cdef DynamicValue.Reader temp
cdef StringPtr temp_string
value_type = type(value)
if value_type is int or value_type is long:
if value < 0:
temp = DynamicValue.Reader(<long long>value)
else:
temp = DynamicValue.Reader(<unsigned long long>value)
elif value_type is float:
temp = DynamicValue.Reader(<double>value)
elif value_type is bool:
temp = DynamicValue.Reader(<cbool>value)
elif value_type is bytes:
temp_string = StringPtr(<char*>value, len(value))
temp = DynamicValue.Reader(temp_string)
elif isinstance(value, basestring):
encoded_value = value.encode()
temp_string = StringPtr(<char*>encoded_value, len(encoded_value))
temp = DynamicValue.Reader(temp_string)
elif value is None:
temp = DynamicValue.Reader(VOID)
elif value_type is _DynamicStructBuilder:
temp = _extract_dynamic_struct_builder(value)
elif value_type is _DynamicStructReader:
temp = _extract_dynamic_struct_reader(value)
elif value_type is _DynamicEnum:
temp = _extract_dynamic_enum(value)
else:
raise ValueError("Tried to convert value of: '{}' which is an unsupported type: '{}'".format(str(value), str(type(value))))
return temp
cdef extern from "{{file.filename}}.h":
{%- for node in code.nodes %}
Schema get{{node.module_name}}Schema"capnp::Schema::from<{{node.c_module_path}}>"()
cdef cppclass {{node.module_name}}"{{node.c_module_path}}":
cppclass Reader:
{%- for field in node.struct.fields %}
{{ getter(field, "Reader")|indent(12)}}
{%- endfor %}
cppclass Builder:
{%- for field in node.struct.fields %}
{{ getter(field, "Builder")|indent(12)}}
{{ setter(field)|indent(12)}}
{%- endfor %}
{%- endfor %}
cdef cppclass C_DynamicStruct_Reader" ::capnp::DynamicStruct::Reader":
{%- for node in code.nodes %}
{{node.module_name}}.Reader as{{node.module_name}}"as<{{node.c_module_path}}>"()
{%- endfor %}
cdef cppclass C_DynamicStruct_Builder" ::capnp::DynamicStruct::Builder":
{%- for node in code.nodes %}
{{node.module_name}}.Builder as{{node.module_name}}"as<{{node.c_module_path}}>"()
{%- endfor %}
{%- for node in code.nodes %}
{{node.schema}} = _Schema()._init(get{{node.module_name}}Schema()).as_struct()
{{node.module_path}}.schema = {{node.schema}}
cdef class {{node.module_name}}_Reader(_DynamicStructReader):
cdef {{node.module_name}}.Reader thisptr_child
def __init__(self, _DynamicStructReader struct):
self._init(struct.thisptr, struct._parent, struct.is_root, False)
self.thisptr_child = (<C_DynamicStruct_Reader>struct.thisptr).as{{node.module_name}}()
{% for field in node.struct.fields %}
{{ getfield(field, "Reader")|indent(4) }}
property {{field.name}}:
def __get__(self):
return self._get_{{field.name}}()
{%- endfor %}
def to_dict(self, verbose=False, ordered=False):
ret = {
{% for field in node.struct.fields %}
{% if field.discriminantValue == 65535 %}
'{{field.name}}': _to_dict(self.{{field.name}}, verbose, ordered),
{% endif %}
{%- endfor %}
}
{% if node.is_union %}
which = self._which_str()
ret[which] = getattr(self, which)
{% endif %}
return ret
cdef class {{node.module_name}}_Builder(_DynamicStructBuilder):
cdef {{node.module_name}}.Builder thisptr_child
def __init__(self, _DynamicStructBuilder struct):
self._init(struct.thisptr, struct._parent, struct.is_root, False)
self.thisptr_child = (<C_DynamicStruct_Builder>struct.thisptr).as{{node.module_name}}()
{% for field in node.struct.fields %}
{{ getfield(field, "Builder")|indent(4) }}
{{ setfield(field)|indent(4) }}
property {{field.name}}:
def __get__(self):
return self._get_{{field.name}}()
def __set__(self, value):
self._set_{{field.name}}(value)
{%- endfor %}
def to_dict(self, verbose=False, ordered=False):
ret = {
{% for field in node.struct.fields %}
{% if field.discriminantValue == 65535 %}
'{{field.name}}': _to_dict(self.{{field.name}}, verbose, ordered),
{% endif %}
{%- endfor %}
}
{% if node.is_union %}
which = self._which_str()
ret[which] = getattr(self, which)
{% endif %}
return ret
def from_dict(self, dict d):
cdef str key
for key, val in d.iteritems():
if False: pass
{% for field in node.struct.fields %}
elif key == "{{field.name}}":
try:
self._set_{{field.name}}(val)
except Exception as e:
if 'expected isSetInUnion(field)' in str(e):
self.init(key)
self._set_{{field.name}}(val)
else:
raise
{%- endfor %}
else:
raise ValueError('Key not found in struct: ' + key)
capnp.register_type({{node.id}}, ({{node.module_name}}_Reader, {{node.module_name}}_Builder))
{% endfor %}

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python
from distutils.core import setup
from Cython.Build import cythonize
import os
import re
files = [{% for f in code.requestedFiles %}"{{f.filename}}", {% endfor %}]
for f in files:
cpp_file = f + '.cpp'
cplus_file = f + '.c++'
cpp_mod = os.path.getmtime(cpp_file)
cplus_mod = 0
try:
cplus_mod = os.path.getmtime(cplus_file)
except:
pass
if not os.path.exists(cpp_file) or cpp_mod < cplus_mod:
if not os.path.exists(cplus_file):
raise RuntimeError("You need to run `capnp compile -oc++` in addition to `-ocython` first.")
os.rename(cplus_file, cpp_file)
with open(f + '.h', "r") as file:
lines = file.readlines()
with open(f + '.h', "w") as file:
for line in lines:
file.write(re.sub(r'Builder\(\)\s*=\s*delete;', 'Builder() = default;', line))
setup(
name="{{code.requestedFiles[0] | replace('.', '_')}}",
ext_modules=cythonize('*_capnp_cython.pyx', language="c++")
)

View file

@ -18,7 +18,6 @@ struct Person {
work @2;
}
}
employment :union {
unemployed @4 :Void;
employer @5 :Text;
@ -26,6 +25,13 @@ struct Person {
selfEmployed @7 :Void;
# We assume that a person is only one of these.
}
testGroup :group {
field1 @8 :UInt32;
field2 @9 :UInt32;
field3 @10 :UInt32;
}
extraData @11 :Data;
}
struct AddressBook {

11
examples/thread.capnp Normal file
View file

@ -0,0 +1,11 @@
@0xf5745ea9c82baa3a;
interface Example {
interface StatusSubscriber {
status @0 (value: Bool);
# Call the function on the given parameters.
}
longRunning @0 () -> (value: Bool);
subscribeStatus @1 (subscriber: StatusSubscriber);
}

58
examples/thread_client.py Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
from __future__ import print_function
import argparse
import threading
import time
import capnp
import thread_capnp
capnp.remove_event_loop()
capnp.create_event_loop(threaded=True)
def parse_args():
parser = argparse.ArgumentParser(usage='Connects to the Example thread server \
at the given address and does some RPCs')
parser.add_argument("host", help="HOST:PORT")
return parser.parse_args()
class StatusSubscriber(thread_capnp.Example.StatusSubscriber.Server):
'''An implementation of the StatusSubscriber interface'''
def status(self, value, **kwargs):
print('status: {}'.format(time.time()))
def start_status_thread(host):
client = capnp.TwoPartyClient(host)
cap = client.ez_restore('example').cast_as(thread_capnp.Example)
subscriber = StatusSubscriber()
promise = cap.subscribeStatus(subscriber)
promise.wait()
def main(host):
client = capnp.TwoPartyClient(host)
cap = client.ez_restore('example').cast_as(thread_capnp.Example)
status_thread = threading.Thread(target=start_status_thread, args=(host,))
status_thread.daemon = True
status_thread.start()
print('main: {}'.format(time.time()))
cap.longRunning().wait()
print('main: {}'.format(time.time()))
cap.longRunning().wait()
print('main: {}'.format(time.time()))
cap.longRunning().wait()
print('main: {}'.format(time.time()))
if __name__ == '__main__':
main(parse_args().host)

49
examples/thread_server.py Executable file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env python
from __future__ import print_function
import argparse
import capnp
import thread_capnp
class ExampleImpl(thread_capnp.Example.Server):
"Implementation of the Example threading Cap'n Proto interface."
def subscribeStatus(self, subscriber, **kwargs):
return capnp.getTimer().after_delay(10**9) \
.then(lambda: subscriber.status(True)) \
.then(lambda _: self.subscribeStatus(subscriber))
def longRunning(self, **kwargs):
return capnp.getTimer().after_delay(3 * 10**9)
def parse_args():
parser = argparse.ArgumentParser(usage='''Runs the server bound to the\
given address/port ADDRESS may be '*' to bind to all local addresses.\
:PORT may be omitted to choose a port automatically. ''')
parser.add_argument("address", help="ADDRESS[:PORT]")
return parser.parse_args()
impl = ExampleImpl()
def restore(ref):
assert ref.as_text() == 'example'
return impl
def main():
address = parse_args().address
server = capnp.TwoPartyServer(address, restore)
server.run_forever()
if __name__ == '__main__':
main()

View file

@ -1,3 +1,4 @@
jinja2 >= 2.7.3
cython >= 0.21
setuptools >= 0.8
pytest

View file

@ -25,8 +25,8 @@ from Cython.Distutils import build_ext as build_ext_c
_this_dir = os.path.dirname(__file__)
MAJOR = 0
MINOR = 4
MICRO = 6
MINOR = 5
MICRO = 0
VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO)
# Write version info
@ -114,7 +114,7 @@ setup(
name="pycapnp",
packages=["capnp"],
version=VERSION,
package_data={'capnp': ['*.pxd', '*.h', '*.capnp', 'helpers/*.pxd', 'helpers/*.h', 'includes/*.pxd', 'lib/*.pxd', 'lib/*.py', 'lib/*.pyx']},
package_data={'capnp': ['*.pxd', '*.h', '*.capnp', 'helpers/*.pxd', 'helpers/*.h', 'includes/*.pxd', 'lib/*.pxd', 'lib/*.py', 'lib/*.pyx', 'templates/*']},
ext_modules=cythonize('capnp/lib/*.pyx'),
cmdclass = {
'clean': clean,
@ -123,6 +123,9 @@ setup(
install_requires=[
'cython >= 0.21',
'setuptools >= 0.8'],
entry_points={
'console_scripts' : ['capnpc-cython = capnp._gen:main']
},
# PyPi info
description="A cython wrapping of the C++ Cap'n Proto library",
long_description=long_description,

View file

@ -0,0 +1,39 @@
@0xc39aee9191aedcf3;
const qux :UInt32 = 123;
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 :union {
unemployed @4 :Void;
employer @5 :Employer;
school @6 :Text;
selfEmployed @7 :Void;
# We assume that a person is only one of these.
}
}
struct Employer {
name @0 :Text;
boss @1 :Person;
}
struct AddressBook {
people @0 :List(Person);
}

View file

@ -0,0 +1,39 @@
@0xd33206731939e03b;
const qux :UInt32 = 123;
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 :union {
unemployed @4 :Void;
employer @5 :Employer;
school @6 :Text;
selfEmployed @7 :Void;
# We assume that a person is only one of these.
}
}
struct Employer {
name @0 :Text;
boss @1 :Person;
}
struct AddressBook {
people @0 :List(Person);
}

17
test/annotations.capnp Normal file
View file

@ -0,0 +1,17 @@
@0xfb9a160831eee9bb;
struct AnnotationStruct {
test @0: Int32;
}
annotation test1(*): Text;
annotation test2(*): AnnotationStruct;
annotation test3(*): List(AnnotationStruct);
annotation test4(*): List(UInt16);
$test1("TestFile");
struct TestAnnotationOne $test1("Test") { }
struct TestAnnotationTwo $test2(test = 100) { }
struct TestAnnotationThree $test3([(test=100), (test=101)]) { }
struct TestAnnotationFour $test4([200, 201]) { }

View file

@ -1,21 +0,0 @@
@0x8186ddb142b58556;
struct Person {
id @0 :UInt32;
name @1 :Text;
}
struct Place {
id @0 :UInt32;
name @1 :Text;
}
struct Thing {
id @0 :UInt64;
value @1 :UInt64;
}
struct TestObject {
object @0 :AnyPointer;
}

View file

@ -1,6 +1,7 @@
import pytest
import capnp
import os
import time
import test_capability_capnp as capability
@ -31,7 +32,7 @@ class PipelineServer(capability.TestPipeline.Server):
def test_client():
client = capability.TestInterface._new_client(Server())
req = client._request('foo')
req.i = 5
@ -39,7 +40,7 @@ def test_client():
response = remote.wait()
assert response.x == '26'
req = client.foo_request()
req.i = 5
@ -48,12 +49,12 @@ def test_client():
assert response.x == '26'
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
client.foo2_request()
req = client.foo_request()
with pytest.raises(ValueError):
with pytest.raises(Exception):
req.i = 'foo'
req = client.foo_request()
@ -63,18 +64,18 @@ def test_client():
def test_simple_client():
client = capability.TestInterface._new_client(Server())
remote = client._send('foo', i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5, j=True)
response = remote.wait()
@ -106,19 +107,19 @@ def test_simple_client():
assert response.x == '5_test'
assert response.i == 5
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, 10)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, True, 100)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(i='foo')
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
remote = client.foo2(i=5)
with pytest.raises(AttributeError):
with pytest.raises(Exception):
remote = client.foo(baz=5)
def test_pipeline():
@ -148,7 +149,7 @@ class BadServer(capability.TestInterface.Server):
def test_exception_client():
client = capability.TestInterface._new_client(BadServer())
remote = client._send('foo', i=5)
with pytest.raises(capnp.KjException):
remote.wait()
@ -254,4 +255,91 @@ def test_tail_call():
assert result.n == 2
assert callee_server.count == 1
assert caller_server.count == 1
assert caller_server.count == 1
def test_cancel():
client = capability.TestInterface._new_client(Server())
req = client._request('foo')
req.i = 5
remote = req.send()
remote.cancel()
with pytest.raises(Exception):
remote.wait()
def test_timer():
global test_timer_var
test_timer_var = False
def set_timer_var():
global test_timer_var
test_timer_var = True
capnp.getTimer().after_delay(1).then(set_timer_var).wait()
assert test_timer_var is True
test_timer_var = False
promise = capnp.Promise(0).then(lambda x: time.sleep(.1)).then(lambda x: time.sleep(.1)).then(lambda x: set_timer_var())
canceller = capnp.getTimer().after_delay(1).then(lambda: promise.cancel())
joined = capnp.join_promises([canceller, promise])
joined.wait()
# faling for now, not sure why...
# assert test_timer_var is False
def test_double_send():
client = capability.TestInterface._new_client(Server())
req = client._request('foo')
req.i = 5
req.send()
with pytest.raises(Exception):
req.send()
def test_then_args():
capnp.Promise(0).then(lambda x: 1)
with pytest.raises(Exception):
capnp.Promise(0).then(lambda: 1)
with pytest.raises(Exception):
capnp.Promise(0).then(lambda x, y: 1)
capnp.getTimer().after_delay(1).then(lambda: 1) # after_delay is a VoidPromise
with pytest.raises(Exception):
capnp.getTimer().after_delay(1).then(lambda x: 1)
client = capability.TestInterface._new_client(Server())
client.foo(i=5).then(lambda x: 1)
with pytest.raises(Exception):
client.foo(i=5).then(lambda: 1)
with pytest.raises(Exception):
client.foo(i=5).then(lambda x, y: 1)
class ExtendsServer(Server):
def qux(self, **kwargs):
pass
def test_inheritance():
client = capability.TestExtends._new_client(ExtendsServer())
client.qux().wait()
remote = client.foo(i=5)
response = remote.wait()
assert response.x == '26'

View file

@ -31,7 +31,7 @@ class PipelineServer:
def test_client_context(capability):
client = capability.TestInterface._new_client(Server())
req = client._request('foo')
req.i = 5
@ -39,7 +39,7 @@ def test_client_context(capability):
response = remote.wait()
assert response.x == '26'
req = client.foo_request()
req.i = 5
@ -48,12 +48,12 @@ def test_client_context(capability):
assert response.x == '26'
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
client.foo2_request()
req = client.foo_request()
with pytest.raises(ValueError):
with pytest.raises(Exception):
req.i = 'foo'
req = client.foo_request()
@ -63,18 +63,18 @@ def test_client_context(capability):
def test_simple_client_context(capability):
client = capability.TestInterface._new_client(Server())
remote = client._send('foo', i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5, j=True)
response = remote.wait()
@ -100,19 +100,19 @@ def test_simple_client_context(capability):
assert response.x == 'localhost_test'
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, 10)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, True, 100)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(i='foo')
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
remote = client.foo2(i=5)
with pytest.raises(AttributeError):
with pytest.raises(Exception):
remote = client.foo(baz=5)
def test_pipeline_context(capability):
@ -140,7 +140,7 @@ class BadServer:
def test_exception_client_context(capability):
client = capability.TestInterface._new_client(BadServer())
remote = client._send('foo', i=5)
with pytest.raises(capnp.KjException):
remote.wait()

View file

@ -32,7 +32,7 @@ class PipelineServer:
def test_client(capability):
client = capability.TestInterface._new_client(Server())
req = client._request('foo')
req.i = 5
@ -40,7 +40,7 @@ def test_client(capability):
response = remote.wait()
assert response.x == '26'
req = client.foo_request()
req.i = 5
@ -49,12 +49,12 @@ def test_client(capability):
assert response.x == '26'
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
client.foo2_request()
req = client.foo_request()
with pytest.raises(ValueError):
with pytest.raises(Exception):
req.i = 'foo'
req = client.foo_request()
@ -64,18 +64,18 @@ def test_client(capability):
def test_simple_client(capability):
client = capability.TestInterface._new_client(Server())
remote = client._send('foo', i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5)
response = remote.wait()
assert response.x == '26'
remote = client.foo(i=5, j=True)
response = remote.wait()
@ -101,19 +101,19 @@ def test_simple_client(capability):
assert response.x == 'localhost_test'
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, 10)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(5, True, 100)
with pytest.raises(ValueError):
with pytest.raises(Exception):
remote = client.foo(i='foo')
with pytest.raises(ValueError):
with pytest.raises(AttributeError):
remote = client.foo2(i=5)
with pytest.raises(AttributeError):
with pytest.raises(Exception):
remote = client.foo(baz=5)
def test_pipeline(capability):
@ -143,7 +143,7 @@ class BadServer:
def test_exception_client(capability):
client = capability.TestInterface._new_client(BadServer())
remote = client._send('foo', i=5)
with pytest.raises(capnp.KjException):
remote.wait()
@ -249,4 +249,4 @@ def test_tail_call(capability):
assert result.n == 2
assert callee_server.count == 1
assert caller_server.count == 1
assert caller_server.count == 1

View file

@ -52,12 +52,18 @@ def test_failed_import():
foo.name = 'foo'
with pytest.raises(ValueError):
with pytest.raises(Exception):
bar.foo = foo
def test_defualt_import_hook():
import addressbook_capnp
def test_dash_import():
import addressbook_with_dashes_capnp
def test_spaces_import():
import addressbook_with_spaces_capnp
def test_add_import_hook():
capnp.add_import_hook([this_dir])

View file

@ -4,21 +4,48 @@ import os
this_dir = os.path.dirname(__file__)
@pytest.fixture
def object():
return capnp.load(os.path.join(this_dir, 'object.capnp'))
def test_object_basic(object):
obj = object.TestObject.new_message()
person = obj.object.as_struct(object.Person)
@pytest.fixture
def addressbook():
return capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
def test_object_basic(addressbook):
obj = capnp._MallocMessageBuilder().get_root_as_any()
person = obj.as_struct(addressbook.Person)
person.name = 'test'
person.id = 1000
same_person = obj.object.as_struct(object.Person)
same_person = obj.as_struct(addressbook.Person)
assert same_person.name == 'test'
assert same_person.id == 1000
obj_r = obj.as_reader()
same_person = obj_r.object.as_struct(object.Person)
same_person = obj_r.as_struct(addressbook.Person)
assert same_person.name == 'test'
assert same_person.id == 1000
def test_object_list(addressbook):
obj = capnp._MallocMessageBuilder().get_root_as_any()
listSchema = capnp.ListSchema(addressbook.Person)
people = obj.init_as_list(listSchema, 2)
person = people[0]
person.name = 'test'
person.id = 1000
person = people[1]
person.name = 'test2'
person.id = 1001
same_person = obj.as_list(listSchema)
assert same_person[0].name == 'test'
assert same_person[0].id == 1000
assert same_person[1].name == 'test2'
assert same_person[1].id == 1001
obj_r = obj.as_reader()
same_person = obj_r.as_list(listSchema)
assert same_person[0].name == 'test'
assert same_person[0].id == 1000
assert same_person[1].name == 'test2'
assert same_person[1].id == 1001

View file

@ -156,7 +156,7 @@ def test_addressbook_resizable(addressbook):
bob.employment.unemployed = None
people.finish()
addresses.write(file)
@ -192,6 +192,75 @@ def test_addressbook_resizable(addressbook):
f = open('example', 'r')
printAddressBook(f)
def test_addressbook_explicit_fields(addressbook):
def writeAddressBook(file):
addresses = addressbook.AddressBook.new_message()
address_fields = addressbook.AddressBook.schema.fields
person_fields = addressbook.Person.schema.fields
phone_fields = addressbook.Person.PhoneNumber.schema.fields
people = addresses._init_by_field(address_fields['people'], 2)
alice = people[0]
alice._set_by_field(person_fields['id'], 123)
alice._set_by_field(person_fields['name'], 'Alice')
alice._set_by_field(person_fields['email'], 'alice@example.com')
alicePhones = alice._init_by_field(person_fields['phones'], 1)
alicePhones[0]._set_by_field(phone_fields['number'], "555-1212")
alicePhones[0]._set_by_field(phone_fields['type'], 'mobile')
employment = alice._get_by_field(person_fields['employment'])
employment._set_by_field(addressbook.Person.Employment.schema.fields['school'], "MIT")
bob = people[1]
bob._set_by_field(person_fields['id'], 456)
bob._set_by_field(person_fields['name'], 'Bob')
bob._set_by_field(person_fields['email'], 'bob@example.com')
bobPhones = bob._init_by_field(person_fields['phones'], 2)
bobPhones[0]._set_by_field(phone_fields['number'], "555-4567")
bobPhones[0]._set_by_field(phone_fields['type'], 'home')
bobPhones[1]._set_by_field(phone_fields['number'], "555-7654")
bobPhones[1]._set_by_field(phone_fields['type'], 'work')
employment = bob._get_by_field(person_fields['employment'])
employment._set_by_field(addressbook.Person.Employment.schema.fields['unemployed'], None)
addresses.write(file)
def printAddressBook(file):
addresses = addressbook.AddressBook.read(file)
address_fields = addressbook.AddressBook.schema.fields
person_fields = addressbook.Person.schema.fields
phone_fields = addressbook.Person.PhoneNumber.schema.fields
people = addresses._get_by_field(address_fields['people'])
alice = people[0]
assert alice._get_by_field(person_fields['id']) == 123
assert alice._get_by_field(person_fields['name']) == 'Alice'
assert alice._get_by_field(person_fields['email']) == 'alice@example.com'
alicePhones = alice._get_by_field(person_fields['phones'])
assert alicePhones[0]._get_by_field(phone_fields['number']) == "555-1212"
assert alicePhones[0]._get_by_field(phone_fields['type']) == 'mobile'
employment = alice._get_by_field(person_fields['employment'])
employment._get_by_field(addressbook.Person.Employment.schema.fields['school']) == "MIT"
bob = people[1]
assert bob._get_by_field(person_fields['id']) == 456
assert bob._get_by_field(person_fields['name']) == 'Bob'
assert bob._get_by_field(person_fields['email']) == 'bob@example.com'
bobPhones = bob._get_by_field(person_fields['phones'])
assert bobPhones[0]._get_by_field(phone_fields['number']) == "555-4567"
assert bobPhones[0]._get_by_field(phone_fields['type']) == 'home'
assert bobPhones[1]._get_by_field(phone_fields['number']) == "555-7654"
assert bobPhones[1]._get_by_field(phone_fields['type']) == 'work'
employment = bob._get_by_field(person_fields['employment'])
employment._get_by_field(addressbook.Person.Employment.schema.fields['unemployed']) == None
f = open('example', 'w')
writeAddressBook(f)
f = open('example', 'r')
printAddressBook(f)
@pytest.fixture
def all_types():

50
test/test_schema.py Normal file
View file

@ -0,0 +1,50 @@
import pytest
import capnp
import os
this_dir = os.path.dirname(__file__)
@pytest.fixture
def addressbook():
return capnp.load(os.path.join(this_dir, 'addressbook.capnp'))
@pytest.fixture
def annotations():
return capnp.load(os.path.join(this_dir, 'annotations.capnp'))
def test_basic_schema(addressbook):
assert addressbook.Person.schema.fieldnames[0] == 'id'
def test_list_schema(addressbook):
peopleField = addressbook.AddressBook.schema.fields['people']
personType = peopleField.schema.elementType
assert personType.node.id == addressbook.Person.schema.node.id
personListSchema = capnp.ListSchema(addressbook.Person)
assert personListSchema.elementType.node.id == addressbook.Person.schema.node.id
def test_annotations(annotations):
assert annotations.schema.node.annotations[0].value.text == 'TestFile'
annotation = annotations.TestAnnotationOne.schema.node.annotations[0]
assert annotation.value.text == 'Test'
annotation = annotations.TestAnnotationTwo.schema.node.annotations[0]
assert annotation.value.struct.as_struct(annotations.AnnotationStruct).test == 100
annotation = annotations.TestAnnotationThree.schema.node.annotations[0]
annotation_list = annotation.value.list.as_list(capnp.ListSchema(annotations.AnnotationStruct))
assert annotation_list[0].test == 100
assert annotation_list[1].test == 101
annotation = annotations.TestAnnotationFour.schema.node.annotations[0]
annotation_list = annotation.value.list.as_list(capnp.ListSchema(capnp.types.UInt16))
assert annotation_list[0] == 200
assert annotation_list[1] == 201

View file

@ -4,6 +4,7 @@ import os
import platform
import test_regression
import tempfile
import pickle
this_dir = os.path.dirname(__file__)
@ -98,3 +99,11 @@ def test_file_and_bytes_packed(all_types):
f.seek(0)
assert f.read() == msg.to_bytes_packed()
def test_pickle(all_types):
msg = all_types.TestAllTypes.new_message()
test_regression.init_all_types(msg)
data = pickle.dumps(msg)
msg2 = pickle.loads(data)
test_regression.check_all_types(msg2)

View file

@ -37,9 +37,9 @@ def test_which_builder(addressbook):
assert bob.employment.which == addressbook.Person.Employment.unemployed
assert bob.employment.which == "unemployed"
with pytest.raises(ValueError):
with pytest.raises(Exception):
addresses.which
with pytest.raises(ValueError):
with pytest.raises(Exception):
addresses.which
@ -71,9 +71,9 @@ def test_which_reader(addressbook):
bob = people[1]
assert bob.employment.which == "unemployed"
with pytest.raises(ValueError):
with pytest.raises(Exception):
addresses.which
with pytest.raises(ValueError):
with pytest.raises(Exception):
addresses.which
@ -187,3 +187,34 @@ def test_to_dict_enum(addressbook):
field = person.to_dict()['phones'][0]['type']
assert isstr(field)
assert field == 'mobile'
def test_explicit_field(addressbook):
person = addressbook.Person.new_message(**{'name': 'Test'})
name_field = addressbook.Person.schema.fields['name']
assert person.name == person._get_by_field(name_field)
assert person.name == person.as_reader()._get_by_field(name_field)
def test_to_dict_verbose(addressbook):
person = addressbook.Person.new_message(**{'name': 'Test'})
assert person.to_dict(verbose=True)['phones'] == []
if sys.version_info >= (2, 7):
assert person.to_dict(verbose=True, ordered=True)['phones'] == []
with pytest.raises(KeyError):
assert person.to_dict()['phones'] == []
def test_to_dict_ordered(addressbook):
person = addressbook.Person.new_message(**{'name': 'Alice', 'phones': [{'type': 'mobile', 'number': '555-1212'}], 'id': 123, 'employment': {'school': 'MIT'}, 'email': 'alice@example.com'})
if sys.version_info >= (2, 7):
assert list(person.to_dict(ordered=True).keys()) == ['id', 'name', 'email', 'phones', 'employment']
else:
with pytest.raises(Exception):
person.to_dict(ordered=True)

66
test/test_threads.py Normal file
View file

@ -0,0 +1,66 @@
import capnp
import pytest
import test_capability_capnp
import socket
import threading
import platform
@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="pycapnp's GIL handling isn't working properly at the moment for PyPy")
def test_making_event_loop():
capnp.remove_event_loop(True)
capnp.create_event_loop()
capnp.remove_event_loop()
capnp.create_event_loop()
@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="pycapnp's GIL handling isn't working properly at the moment for PyPy")
def test_making_threaded_event_loop():
capnp.remove_event_loop(True)
capnp.create_event_loop(True)
capnp.remove_event_loop()
capnp.create_event_loop(True)
class Server(test_capability_capnp.TestInterface.Server):
def __init__(self, val=1):
self.val = val
def foo(self, i, j, **kwargs):
return str(i * 5 + self.val)
class SimpleRestorer(test_capability_capnp.TestSturdyRefObjectId.Restorer):
def restore(self, ref_id):
assert ref_id.tag == 'testInterface'
return Server(100)
@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="pycapnp's GIL handling isn't working properly at the moment for PyPy")
def test_using_threads():
capnp.remove_event_loop(True)
capnp.create_event_loop(True)
read, write = socket.socketpair(socket.AF_UNIX)
def run_server():
restorer = SimpleRestorer()
server = capnp.TwoPartyServer(write, restorer)
capnp.wait_forever()
server_thread = threading.Thread(target=run_server)
server_thread.daemon = True
server_thread.start()
client = capnp.TwoPartyClient(read)
ref = test_capability_capnp.TestSturdyRefObjectId.new_message(tag='testInterface')
cap = client.restore(ref)
cap = cap.cast_as(test_capability_capnp.TestInterface)
remote = cap.foo(i=5)
response = remote.wait()
assert response.x == '125'