#pragma once #include "capnp/dynamic.h" #include #include "Python.h" #include extern "C" { PyObject * wrap_remote_call(PyObject * func, capnp::Response &); PyObject * wrap_dynamic_struct_reader(capnp::Response &); ::kj::Promise * call_server_method(PyObject * py_server, char * name, capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> & context); PyObject * wrap_kj_exception(kj::Exception &); PyObject * wrap_kj_exception_for_reraise(kj::Exception &); PyObject * get_exception_info(PyObject *, PyObject *, PyObject *); PyObject * convert_array_pyobject(kj::Array&); ::kj::Promise * extract_promise(PyObject *); ::capnp::RemotePromise< ::capnp::DynamicStruct> * extract_remote_promise(PyObject *); } ::kj::Promise convert_to_pypromise(capnp::RemotePromise & promise) { return promise.then([](capnp::Response&& response) { return wrap_dynamic_struct_reader(response); } ); } ::kj::Promise convert_to_pypromise(kj::Promise & promise) { return promise.then([]() { Py_RETURN_NONE;} ); } template ::kj::Promise convert_to_voidpromise(kj::Promise & promise) { return promise.then([](T) { } ); } void reraise_kj_exception() { try { if (PyErr_Occurred()) ; // let the latest Python exn pass through and ignore the current one else throw; } catch (kj::Exception& exn) { auto obj = wrap_kj_exception_for_reraise(exn); PyErr_SetObject((PyObject*)obj->ob_type, obj); } catch (const std::exception& exn) { PyErr_SetString(PyExc_RuntimeError, exn.what()); } catch (...) { PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); } } void check_py_error() { 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")); PyObject * info = get_exception_info(ptype, pvalue, ptraceback); PyObject * py_filename = PyTuple_GetItem(info, 0); kj::String filename(kj::heapString(PyBytes_AsString(py_filename))); PyObject * py_line = PyTuple_GetItem(info, 1); int line = PyInt_AsLong(py_line); PyObject * py_description = PyTuple_GetItem(info, 2); kj::String description(kj::heapString(PyBytes_AsString(py_description))); Py_DECREF(ptype); Py_DECREF(pvalue); Py_DECREF(ptraceback); Py_DECREF(info); PyErr_Clear(); throw kj::Exception(kj::Exception::Nature::OTHER, kj::Exception::Durability::PERMANENT, kj::mv(filename), line, kj::mv(description)); } } kj::Promise wrapPyFunc(PyObject * func, PyObject * arg) { auto arg_promise = extract_promise(arg); if(arg_promise == NULL) { PyObject * result = PyObject_CallFunctionObjArgs(func, arg, NULL); Py_DECREF(arg); check_py_error(); auto promise = extract_promise(result); if(promise != NULL) return kj::mv(*promise); // TODO: delete promise, see incref of containing promise in capnp.pyx auto remote_promise = extract_remote_promise(result); if(remote_promise != NULL) return convert_to_pypromise(*remote_promise); // TODO: delete promise, see incref of containing promise in capnp.pyx return result; } else { return arg_promise->then([&](PyObject * new_arg){ return wrapPyFunc(func, new_arg); });// TODO: delete arg_promise? } } kj::Promise wrapPyFuncNoArg(PyObject * func) { PyObject * result = PyObject_CallFunctionObjArgs(func, NULL); check_py_error(); auto promise = extract_promise(result); if(promise != NULL) return kj::mv(*promise); auto remote_promise = extract_remote_promise(result); if(remote_promise != NULL) return convert_to_pypromise(*remote_promise); // TODO: delete promise, see incref of containing promise in capnp.pyx return result; } kj::Promise wrapRemoteCall(PyObject * func, capnp::Response & arg) { PyObject * ret = wrap_remote_call(func, arg); check_py_error(); auto promise = extract_promise(ret); if(promise != NULL) return kj::mv(*promise); auto remote_promise = extract_remote_promise(ret); if(remote_promise != NULL) return convert_to_pypromise(*remote_promise); // TODO: delete promise, see incref of containing promise in capnp.pyx return ret; } ::kj::Promise then(kj::Promise & promise, PyObject * func, PyObject * error_func) { 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); } , [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } ); } ::kj::Promise then(::capnp::RemotePromise< ::capnp::DynamicStruct> & promise, PyObject * func, PyObject * error_func) { if(error_func == Py_None) return promise.then([func](capnp::Response&& arg) { return wrapRemoteCall(func, arg); } ); else return promise.then([func](capnp::Response&& arg) { return wrapRemoteCall(func, arg); } , [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } ); } ::kj::Promise then(kj::Promise & promise, PyObject * func, PyObject * error_func) { if(error_func == Py_None) return promise.then([func]() { return wrapPyFuncNoArg(func); } ); else return promise.then([func]() { return wrapPyFuncNoArg(func); } , [error_func](kj::Exception arg) { return wrapPyFunc(error_func, wrap_kj_exception(arg)); } ); } ::kj::Promise then(kj::Promise > && promise) { return promise.then([](kj::Array&& arg) { return convert_array_pyobject(arg); } ); } 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) { Py_INCREF(_py_server); } ~PythonInterfaceDynamicImpl() { Py_DECREF(py_server); } kj::Promise call(capnp::InterfaceSchema::Method method, capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> context) { auto methodName = method.getProto().getName(); kj::Promise * promise = call_server_method(py_server, const_cast(methodName.cStr()), context); check_py_error(); if(promise == nullptr) return kj::READY_NOW; kj::Promise ret(kj::mv(*promise)); delete promise; return ret; } }; class PyRefCounter { public: PyObject * obj; PyRefCounter(PyObject * o) : obj(o) { Py_INCREF(obj); } PyRefCounter(const PyRefCounter & ref) : obj(ref.obj) { Py_INCREF(obj); } ~PyRefCounter() { Py_DECREF(obj); } }; capnp::DynamicCapability::Client new_client(capnp::InterfaceSchema & schema, PyObject * server) { return capnp::DynamicCapability::Client(kj::heap(schema, server)); } capnp::DynamicValue::Reader new_server(capnp::InterfaceSchema & schema, PyObject * server) { return capnp::DynamicValue::Reader(kj::heap(schema, server)); } capnp::Capability::Client server_to_client(capnp::InterfaceSchema & schema, PyObject * server) { return kj::heap(schema, server); }