First working version of capability interfaces

This commit is contained in:
Jason Paryani
2013-10-15 22:36:14 -07:00
parent 9391ed6759
commit a5d0abb49f
12 changed files with 576 additions and 140 deletions

View File

@@ -1,25 +0,0 @@
#ifndef __PYX_HAVE__capnp__async
#define __PYX_HAVE__capnp__async
#ifndef __PYX_HAVE_API__capnp__async
#ifndef __PYX_EXTERN_C
#ifdef __cplusplus
#define __PYX_EXTERN_C extern "C"
#else
#define __PYX_EXTERN_C extern
#endif
#endif
__PYX_EXTERN_C DL_IMPORT(PyObject) *wrap_kj_exception( ::kj::Exception &);
#endif /* !__PYX_HAVE_API__capnp__async */
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC initasync(void);
#else
PyMODINIT_FUNC PyInit_async(void);
#endif
#endif /* !__PYX_HAVE__capnp__async */

View File

@@ -1,102 +0,0 @@
# capnp.pyx
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11 -fpermissive
# distutils: libraries = kj
# cython: c_string_type = str
# cython: c_string_encoding = default
# cython: embedsignature = True
cimport cython
cimport async_cpp as async
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
from cython.operator cimport dereference as deref
cdef extern from "<utility>" namespace "std":
async.PyPromise movePromise"std::move"(async.PyPromise)
# By making it public, we'll be able to call it from asyncHelper.h
cdef public object wrap_kj_exception(async.Exception & exception):
return None # TODO
cdef class EventLoop:
cdef async.SimpleEventLoop thisptr
cpdef evalLater(self, func):
Py_INCREF(func)
return Promise()._init(async.evalLater(self.thisptr, <PyObject *>func))
cpdef wait(self, Promise promise) except+:
if promise.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = self.thisptr.wait(movePromise(deref(promise.thisptr)))
promise.is_consumed = True
return ret
cpdef there(self, Promise promise, object func, object error_func=None):
if promise.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
Py_INCREF(func)
Py_INCREF(error_func)
return Promise()._init(async.there(self.thisptr, deref(promise.thisptr), <PyObject *>func, <PyObject *>error_func))
cdef class Promise:
cdef async.PyPromise * thisptr
cdef public bint is_consumed
def __init__(self):
self.is_consumed = True
cdef _init(self, async.PyPromise other):
self.is_consumed = False
self.thisptr = new async.PyPromise(movePromise(other))
return self
def __dealloc__(self):
del self.thisptr
cpdef wait(self) except+:
if self.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = <object>self.thisptr.wait()
self.is_consumed = True
return ret
cpdef then(self, func, error_func=None) except+:
if self.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
Py_INCREF(func)
Py_INCREF(error_func)
return Promise()._init(async.then(deref(self.thisptr), <PyObject *>func, <PyObject *>error_func))
# from gevent.event import Event, AsyncResult
# import gevent
# cdef object _event = Event()
# cdef object _start_loop = AsyncResult()
# def _start_event_loop():
# loop = EventLoop()
# _start_loop.set(loop)
# _event.wait()
# _event.clear()
# _event_loop_greenlet = gevent.spawn(_start_event_loop)
# event_loop = _start_loop.get()
# _event.set()
# cdef public void _gevent_eventloop_prepare_to_sleep():
# _event.clear()
# cdef public void _gevent_eventloop_sleep():
# _event.wait()
# cdef public void _gevent_eventloop_wake():
# _event.set()

14
capnp/asyncHelper_cpp.pxd Normal file
View File

@@ -0,0 +1,14 @@
# schema.capnp.cpp.pyx
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11
from cpython.ref cimport PyObject
from capnp_cpp cimport PyPromise, EventLoop
cdef extern from "asyncHelper.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)
# cdef cppclass PyEventLoop(EventLoop):
# pass

View File

@@ -10,6 +10,7 @@ cdef extern from "kj/exception.h" namespace " ::kj":
cdef extern from "kj/async.h" namespace " ::kj":
cdef cppclass Promise[T]:
Promise()
Promise(Promise)
T wait()
@@ -25,11 +26,3 @@ cdef extern from "kj/async.h" namespace " ::kj":
PyPromise there(PyPromise, PyObject * func)
cdef cppclass SimpleEventLoop(EventLoop):
pass
cdef extern from "asyncHelper.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)
# cdef cppclass PyEventLoop(EventLoop):
# pass

32
capnp/capabilityHelper.h Normal file
View File

@@ -0,0 +1,32 @@
#include "capnp/dynamic.h"
#include <stdexcept>
#include "Python.h"
#include <iostream>
extern "C" {
PyObject * wrap_dynamic_struct_reader(capnp::DynamicStruct::Reader &);
void call_server_method(PyObject * py_server, char * name, capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> & context);
}
class PythonInterfaceDynamicImpl final: public capnp::DynamicCapability::Server {
public:
PyObject * py_server;
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema, PyObject * py_server)
: capnp::DynamicCapability::Server(schema), py_server(py_server) {}
kj::Promise<void> call(capnp::InterfaceSchema::Method method,
capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> context) {
auto methodName = method.getProto().getName();
call_server_method(py_server, const_cast<char *>(methodName.cStr()), context);
return kj::READY_NOW;
}
};
capnp::DynamicCapability::Client new_client(capnp::InterfaceSchema & schema, PyObject * server, kj::EventLoop & loop) {
return capnp::DynamicCapability::Client(kj::heap<PythonInterfaceDynamicImpl>(schema, server), loop);
}
::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); } );
}

View File

@@ -9,11 +9,13 @@
cimport cython
cimport capnp_cpp as capnp
cimport schema_cpp
from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, ObjectPointer as C_DynamicObject, WordArrayPtr
from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, ObjectPointer as C_DynamicObject, WordArrayPtr, DynamicCapability as C_DynamicCapability, new_client, Request, RemotePromise, convert_to_pypromise, SimpleEventLoop, PyPromise, CallContext
from schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode
from cython.operator cimport dereference as deref
cimport async_cpp
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
from libc.stdint cimport *
ctypedef unsigned int uint
ctypedef uint8_t UInt8
@@ -32,6 +34,21 @@ ctypedef double Float64
from libc.stdlib cimport malloc, free
from libcpp cimport bool as cbool
# By making it public, we'll be able to call it from capabilityHelper.h
cdef public object wrap_dynamic_struct_reader(C_DynamicStruct.Reader & reader):
return _DynamicStructReader()._init(reader, None)
cdef public void call_server_method(PyObject * _server, char * _method_name, CallContext & _context):
server = <object>_server
method_name = <object>_method_name
context = _CallContext()._init(_context)
getattr(server, method_name)(context)
# By making it public, we'll be able to call it from asyncHelper.h
cdef public object wrap_kj_exception(capnp.Exception & exception):
return None # TODO
ctypedef fused _DynamicStructReaderOrBuilder:
_DynamicStructReader
_DynamicStructBuilder
@@ -39,6 +56,7 @@ ctypedef fused _DynamicStructReaderOrBuilder:
ctypedef fused _DynamicSetterClasses:
C_DynamicList.Builder
C_DynamicStruct.Builder
Request
cdef extern from "Python.h":
cdef int PyObject_AsReadBuffer(object, void** b, Py_ssize_t* c)
@@ -61,7 +79,7 @@ _Type = _make_enum('DynamicValue.Type',
LIST = capnp.TYPE_LIST,
ENUM = capnp.TYPE_ENUM,
STRUCT = capnp.TYPE_STRUCT,
# INTERFACE = capnp.TYPE_INTERFACE,
CAPABILITY = capnp.TYPE_CAPABILITY,
OBJECT = capnp.TYPE_OBJECT)
# Templated classes are weird in cython. I couldn't put it in a pxd header for some reason
@@ -76,16 +94,22 @@ cdef extern from "capnp/list.h" namespace " ::capnp":
cdef extern from "<utility>" namespace "std":
C_DynamicOrphan moveOrphan"std::move"(C_DynamicOrphan)
Request moveRequest"std::move"(Request)
PyPromise movePromise"std::move"(PyPromise)
RemotePromise moveRemotePromise"std::move"(RemotePromise)
CallContext moveCallContext"std::move"(CallContext)
cdef extern from "<capnp/pretty-print.h>" namespace " ::capnp":
StringTree printStructReader" ::capnp::prettyPrint"(C_DynamicStruct.Reader)
StringTree printStructBuilder" ::capnp::prettyPrint"(C_DynamicStruct.Builder)
StringTree printRequest" ::capnp::prettyPrint"(Request &)
StringTree printListReader" ::capnp::prettyPrint"(C_DynamicList.Reader)
StringTree printListBuilder" ::capnp::prettyPrint"(C_DynamicList.Builder)
cdef extern from "<kj/string.h>" namespace " ::kj":
String strStructReader" ::kj::str"(C_DynamicStruct.Reader)
String strStructBuilder" ::kj::str"(C_DynamicStruct.Builder)
String strRequest" ::kj::str"(Request &)
String strListReader" ::kj::str"(C_DynamicList.Reader)
String strListBuilder" ::kj::str"(C_DynamicList.Builder)
@@ -430,6 +454,39 @@ cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent):
else:
raise ValueError("Non primitive type")
cdef _setDynamicFieldPtr(_DynamicSetterClasses * thisptr, field, value, parent):
cdef C_DynamicValue.Reader temp
value_type = type(value)
if value_type is int or value_type is long:
if value < 0:
temp = C_DynamicValue.Reader(<long long>value)
else:
temp = C_DynamicValue.Reader(<unsigned long long>value)
thisptr.set(field, temp)
elif value_type is float:
temp = C_DynamicValue.Reader(<double>value)
thisptr.set(field, temp)
elif value_type is bool:
temp = C_DynamicValue.Reader(<cbool>value)
thisptr.set(field, temp)
elif isinstance(value, basestring):
temp = C_DynamicValue.Reader(<char*>value)
thisptr.set(field, temp)
elif value_type is list:
builder = to_python_builder(thisptr.init(field, len(value)), parent)
for (i, v) in enumerate(value):
builder[i] = v
elif value is None:
temp = C_DynamicValue.Reader(VOID)
thisptr.set(field, temp)
elif value_type is _DynamicStructBuilder:
thisptr.set(field, _extract_dynamic_struct_builder(value))
elif value_type is _DynamicStructReader:
thisptr.set(field, _extract_dynamic_struct_reader(value))
else:
raise ValueError("Non primitive type")
cdef _to_dict(msg):
msg_type = type(msg)
if msg_type is _DynamicListBuilder or msg_type is _DynamicListReader or msg_type is _DynamicResizableListBuilder:
@@ -811,6 +868,253 @@ cdef class _DynamicObjectBuilder:
return _DynamicStructBuilder()._init(self.thisptr.getAs(s.thisptr), self._parent)
cdef class _CallContext:
cdef CallContext * thisptr
cdef _init(self, CallContext other):
self.thisptr = new CallContext(moveCallContext(other))
return self
def __dealloc__(self):
del self.thisptr
property params:
def __get__(self):
return _DynamicStructReader()._init(self.thisptr.getParams(), self)
cpdef _get_results(self, uint firstSegmentWordSize=0):
return _DynamicStructBuilder()._init(self.thisptr.getResults(firstSegmentWordSize), self)
property results:
def __get__(self):
return self._get_results()
cdef class Promise:
cdef PyPromise * thisptr
def __init__(self):
self.is_consumed = True
cdef _init(self, PyPromise other):
self.is_consumed = False
self.thisptr = new PyPromise(movePromise(other))
return self
def __dealloc__(self):
del self.thisptr
cpdef wait(self) except+:
if self.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = <object>self.thisptr.wait()
self.is_consumed = True
return ret
cpdef then(self, func, error_func=None) except+:
if self.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
Py_INCREF(func)
Py_INCREF(error_func)
return Promise()._init(capnp.then(deref(self.thisptr), <PyObject *>func, <PyObject *>error_func))
cdef class _RemotePromise:
cdef RemotePromise * thisptr
cdef public bint is_consumed
cdef public object _parent
def __init__(self):
self.is_consumed = True
cdef _init(self, RemotePromise other, parent):
self.is_consumed = False
self.thisptr = new RemotePromise(moveRemotePromise(other))
self._parent = parent
return self
def __dealloc__(self):
del self.thisptr
cpdef wait(self) except+:
if self.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = _DynamicStructReader()._init(self.thisptr.wait(), self._parent)
self.is_consumed = True
return ret
cpdef as_pypromise(self) except +:
Promise()._init(convert_to_pypromise(deref(self.thisptr)))
# cpdef then(self, func, error_func=None) except+:
# if self.is_consumed:
# raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
# Py_INCREF(func)
# Py_INCREF(error_func)
# return _RemotePromise()._init(capnp.then(deref(self.thisptr), <PyObject *>func, <PyObject *>error_func))
cdef class EventLoop:
cdef SimpleEventLoop thisptr
cpdef evalLater(self, func):
Py_INCREF(func)
return Promise()._init(capnp.evalLater(self.thisptr, <PyObject *>func))
cpdef wait_remote(self, _RemotePromise promise) except +:
if promise.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = _DynamicStructReader()._init(self.thisptr.wait_remote(moveRemotePromise(deref(promise.thisptr))), promise._parent)
promise.is_consumed = True
return ret
# cpdef there(self, Promise promise, object func, object error_func=None):
# if promise.is_consumed:
# raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
# Py_INCREF(func)
# Py_INCREF(error_func)
# return Promise()._init(capnp.there(self.thisptr, deref(promise.thisptr), <PyObject *>func, <PyObject *>error_func))
cdef class _Request:
cdef Request * thisptr
cdef public object _parent
cdef _init(self, Request other, parent):
self.thisptr = new Request(moveRequest(other))
self._parent = parent
return self
cpdef send(self):
return _RemotePromise()._init(self.thisptr.send(), self._parent)
cdef _get(self, field):
cdef C_DynamicValue.Builder value = self.thisptr.get(field)
return to_python_builder(value, self._parent)
def __getattr__(self, field):
return self._get(field)
def __setattr__(self, field, value):
_setDynamicFieldPtr(self.thisptr, field, value, self._parent)
def _has(self, field):
return self.thisptr.has(field)
cpdef init(self, field, size=None):
"""Method for initializing fields that are of type union/struct/list
Typically, you don't have to worry about initializing structs/unions, so this method is mainly for lists.
:type field: str
:param field: The field name to initialize
:type size: int
:param size: The size of the list to initiialize. This should be None for struct/union initialization.
:rtype: :class:`_DynamicStructBuilder` or :class:`_DynamicListBuilder`
:Raises: :exc:`exceptions.ValueError` if the field isn't in this struct
"""
if size is None:
return to_python_builder(self.thisptr.init(field), self._parent)
else:
return to_python_builder(self.thisptr.init(field, size), self._parent)
cpdef init_resizable_list(self, field):
"""Method for initializing fields that are of type list (of structs)
This version of init returns a :class:`_DynamicResizableListBuilder` that allows you to add members one at a time (ie. if you don't know the size for sure). This is only meant for lists of Cap'n Proto objects, since for primitive types you can just define a normal python list and fill it yourself.
.. warning:: You need to call :meth:`_DynamicResizableListBuilder.finish` on the list object before serializing the Cap'n Proto message. Failure to do so will cause your objects not to be written out as well as leaking orphan structs into your message.
:type field: str
:param field: The field name to initialize
:rtype: :class:`_DynamicResizableListBuilder`
:Raises: :exc:`exceptions.ValueError` if the field isn't in this struct
"""
return _DynamicResizableListBuilder(self, field, _StructSchema()._init((<C_DynamicValue.Builder>self.thisptr.get(field)).asList().getStructElementType()))
cpdef which(self):
"""Returns the enum corresponding to the union in this struct
Enums are just strings in the python Cap'n Proto API, so this function will either return a string equal to the field name of the active field in the union, or throw a ValueError if this isn't a union, or a struct with an unnamed union::
person = addressbook.Person.new_message()
person.which()
# ValueError: member was null
a.employment.employer = 'foo'
print employment.which()
# 'employer'
:rtype: str
:return: A string/enum corresponding to what field is set in the union
:Raises: :exc:`exceptions.ValueError` if this struct doesn't contain a union
"""
cdef object which = getEnumString(deref(self.thisptr))
if len(which) == 0:
raise ValueError("Attempted to call which on a non-union type")
return which
property schema:
"""A property that returns the _StructSchema object matching this writer"""
def __get__(self):
return _StructSchema()._init(self.thisptr.getSchema())
def __dir__(self):
return list(self.schema.fieldnames)
def __str__(self):
return printRequest(deref(self.thisptr)).flatten().cStr()
def __repr__(self):
return '<%s builder %s>' % (self.schema.node.displayName, strRequest(deref(self.thisptr)).cStr())
def to_dict(self):
return _to_dict(self)
cdef class _DynamicCapabilityClient:
cdef C_DynamicCapability.Client thisptr
cdef public object _event_loop, _server
def __init__(self, schema, server, event_loop):
cdef _InterfaceSchema s
if hasattr(schema, 'schema'):
s = schema.schema
else:
s = schema
cdef EventLoop loop = event_loop
self._event_loop = event_loop
self.thisptr = new_client(s.thisptr, <PyObject *>server, loop.thisptr)
self._server = server
cpdef _new_request_helper(self, name, firstSegmentWordSize, kwargs) except +ValueError:
cdef Request * request = new Request(self.thisptr.newRequest(name, firstSegmentWordSize))
for key, val in kwargs.items():
_setDynamicFieldPtr(request, key, val, self)
return _RemotePromise()._init(request.send(), self)
cpdef request(self, name, firstSegmentWordSize=0) except +ValueError:
return _Request()._init(self.thisptr.newRequest(name, firstSegmentWordSize), self)
def send(self, name, firstSegmentWordSize=0, **kwargs):
return self._new_request_helper(name, firstSegmentWordSize, kwargs)
cdef class _Schema:
cdef C_Schema thisptr
cdef _init(self, C_Schema other):
@@ -1049,7 +1353,12 @@ cdef class SchemaParser:
elif proto.isConst:
module.__dict__[node.name] = schema.as_const_value()
elif proto.isInterface:
def new_client(bound_local_module):
def helper(server, loop):
return _DynamicCapabilityClient(bound_local_module, server, loop)
return helper
local_module.schema = schema.as_interface()
local_module.new_client = new_client(local_module)
_load(schema, local_module)
if not _os.path.isfile(file_name):

View File

@@ -2,7 +2,9 @@
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11
from schema_cpp cimport Node, Data, StructNode, EnumNode
from async_cpp cimport PyPromise, Promise
from cpython.ref cimport PyObject
from libc.stdint cimport *
ctypedef unsigned int uint
from libcpp cimport bool as cbool
@@ -13,6 +15,10 @@ cdef extern from "capnp/common.h" namespace " ::capnp":
cdef cppclass word:
pass
cdef extern from "kj/exception.h" namespace " ::kj":
cdef cppclass Exception:
pass
cdef extern from "kj/string.h" namespace " ::kj":
cdef cppclass StringPtr:
StringPtr(char *)
@@ -119,7 +125,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
TYPE_LIST " ::capnp::DynamicValue::LIST"
TYPE_ENUM " ::capnp::DynamicValue::ENUM"
TYPE_STRUCT " ::capnp::DynamicValue::STRUCT"
# TYPE_INTERFACE " ::capnp::DynamicValue::INTERFACE"
TYPE_CAPABILITY " ::capnp::DynamicValue::CAPABILITY"
TYPE_OBJECT " ::capnp::DynamicValue::OBJECT"
cdef cppclass DynamicStruct:
@@ -142,6 +148,31 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicOrphan disown(char *)
DynamicStruct.Reader asReader()
cdef extern from "capnp/capability.h" namespace " ::capnp":
cdef cppclass Response" ::capnp::Response< ::capnp::DynamicStruct>"(DynamicStruct.Reader):
pass
cdef cppclass RemotePromise" ::capnp::RemotePromise< ::capnp::DynamicStruct>"(Promise[Response]):
RemotePromise(RemotePromise)
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass Request" ::capnp::Request< ::capnp::DynamicStruct, ::capnp::DynamicStruct>":
Request()
Request(Request &)
DynamicValueForward.Builder get(char *) except +ValueError
bint has(char *) except +ValueError
void set(char *, DynamicValueForward.Reader) except +ValueError
DynamicValueForward.Builder init(char *, uint size) except +ValueError
DynamicValueForward.Builder init(char *) except +ValueError
StructSchema getSchema()
Maybe[StructSchema.Field] which()
RemotePromise send()
cdef cppclass DynamicCapability:
cppclass Client:
Client upcast(InterfaceSchema requestedSchema)
InterfaceSchema getSchema()
Request newRequest(char * methodName, uint firstSegmentWordSize)
cdef extern from "capnp/object.h" namespace " ::capnp":
cdef cppclass ObjectPointer:
cppclass Reader:
@@ -154,6 +185,14 @@ cdef extern from "fixMaybe.h":
EnumSchema.Enumerant fixMaybe(Maybe[EnumSchema.Enumerant]) except +ValueError
char * getEnumString(DynamicStruct.Reader val)
char * getEnumString(DynamicStruct.Builder val)
char * getEnumString(Request val)
cdef extern from "capabilityHelper.h":
cppclass PythonInterfaceDynamicImpl:
pass
DynamicCapability.Client new_client(InterfaceSchema&, PyObject *, EventLoop&)
PyPromise convert_to_pypromise(RemotePromise&)
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicEnum:
@@ -232,3 +271,34 @@ cdef extern from "capnp/orphan.h" namespace " ::capnp":
cdef cppclass DynamicOrphan" ::capnp::Orphan< ::capnp::DynamicValue>":
DynamicValue.Builder get()
DynamicValue.Reader getReader()
cdef extern from "capnp/capability.h" namespace " ::capnp":
cdef cppclass CallContext' ::capnp::CallContext< ::capnp::DynamicStruct, ::capnp::DynamicStruct>':
CallContext(CallContext&)
DynamicStruct.Reader getParams() except +
void releaseParams()
DynamicStruct.Builder getResults(uint firstSegmentWordSize)
DynamicStruct.Builder initResults(uint firstSegmentWordSize)
void setResults(DynamicStruct.Reader value)
# void adoptResults(Orphan<Results>&& value);
# Orphanage getResultsOrphanage(uint firstSegmentWordSize = 0);
void allowAsyncCancellation(bint allow = true)
bint isCanceled()
cdef extern from "kj/async.h" namespace " ::kj":
cdef cppclass EventLoop:
EventLoop()
# Promise[void] yield_end'yield'()
object wait(PyPromise) except+
DynamicStruct.Reader wait_remote'wait'(RemotePromise) except+
object there(PyPromise) except+
PyPromise evalLater(PyObject * func)
PyPromise there(PyPromise, PyObject * func)
cdef cppclass SimpleEventLoop(EventLoop):
pass
cdef extern from "asyncHelper.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)

View File

@@ -13,7 +13,7 @@ T fixMaybe(::kj::Maybe<T> val) {
}
template<typename T>
const char * getEnumString(T val) {
const char * getEnumString(T & val) {
auto maybe_val = val.which();
KJ_IF_MAYBE(new_val, maybe_val) {

View File

@@ -0,0 +1,45 @@
# 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.
@0xd508eefec2dc42b8;
interface TestInterface {
foo @0 (i :UInt32, j :Bool) -> (x: Text);
bar @1 () -> ();
# baz @2 (s: TestAllTypes);
}
# interface TestExtends extends(TestInterface) {
# qux @0 ();
# corge @1 TestAllTypes -> ();
# grault @2 () -> TestAllTypes;
# }
# interface TestPipeline {
# getCap @0 (n: UInt32, inCap :TestInterface) -> (s: Text, outBox :Box);
# testPointers @1 (cap :TestInterface, obj :Object, list :List(TestInterface)) -> ();
# struct Box {
# cap @0 :TestInterface;
# }
# }

View File

@@ -0,0 +1,25 @@
from __future__ import print_function
import capnp
import example_capability_capnp
class Server:
def foo(self, context):
context.results.x = str(context.params.i * 5 + 1)
def example_client():
loop = capnp.EventLoop()
client = example_capability_capnp.TestInterface.new_client(Server(), loop)
req = client.request('foo')
req = client.request('foo2')
req.i = 5
remote = req.send()
response = loop.wait_remote(remote)
print(response.x)
if __name__ == '__main__':
example_client()

View File

@@ -0,0 +1,45 @@
# 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.
@0xd508eefec2dc42b8;
interface TestInterface {
foo @0 (i :UInt32, j :Bool) -> (x: Text);
bar @1 () -> ();
# baz @2 (s: TestAllTypes);
}
# interface TestExtends extends(TestInterface) {
# qux @0 ();
# corge @1 TestAllTypes -> ();
# grault @2 () -> TestAllTypes;
# }
# interface TestPipeline {
# getCap @0 (n: UInt32, inCap :TestInterface) -> (s: Text, outBox :Box);
# testPointers @1 (cap :TestInterface, obj :Object, list :List(TestInterface)) -> ();
# struct Box {
# cap @0 :TestInterface;
# }
# }

30
test/test_capability.py Normal file
View File

@@ -0,0 +1,30 @@
import pytest
import capnp
import os
this_dir = os.path.dirname(__file__)
@pytest.fixture
def capability():
return capnp.load(os.path.join(this_dir, 'test_capability.capnp'))
class Server:
def foo(self, context):
context.results.x = str(context.params.i * 5 + 1)
def test_basic_client(capability):
loop = capnp.EventLoop()
client = capability.TestInterface.new_client(Server(), loop)
req = client.request('foo')
req.i = 5
remote = req.send()
remote = client.send('foo', i=10)
response = loop.wait_remote(remote)
# assert response.x == '26'
with pytest.raises(ValueError):
client.request('foo2')