Fix exception propogation in Promises

This commit is contained in:
Jason Paryani
2013-11-13 20:54:57 -08:00
parent 72c36c3997
commit c1952a62ae
3 changed files with 29 additions and 5 deletions

View File

@@ -11,6 +11,7 @@ extern "C" {
::kj::Promise<void> * call_server_method(PyObject * py_server, char * name, capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> & context); ::kj::Promise<void> * 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(kj::Exception &);
PyObject * wrap_kj_exception_for_reraise(kj::Exception &); PyObject * wrap_kj_exception_for_reraise(kj::Exception &);
PyObject * get_exception_info(PyObject *, PyObject *, PyObject *);
} }
void reraise_kj_exception() { void reraise_kj_exception() {
@@ -36,8 +37,22 @@ void reraise_kj_exception() {
void check_py_error() { void check_py_error() {
PyObject * err = PyErr_Occurred(); PyObject * err = PyErr_Occurred();
if(err) { if(err) {
// PyErr_Clear(); // TODO: decref references
throw std::exception(); PyObject * ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
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)));
throw kj::Exception(kj::Exception::Nature::OTHER, kj::Exception::Durability::PERMANENT, kj::mv(filename), line, kj::mv(description));
} }
} }

View File

@@ -16,6 +16,7 @@ from cython.operator cimport dereference as deref
cimport async_cpp cimport async_cpp
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
from cpython.exc cimport PyErr_Clear
from libc.stdint cimport * from libc.stdint cimport *
ctypedef unsigned int uint ctypedef unsigned int uint
ctypedef uint8_t UInt8 ctypedef uint8_t UInt8
@@ -181,6 +182,7 @@ class KjException(Exception):
return self.message return self.message
cdef public object wrap_kj_exception(capnp.Exception & exception): cdef public object wrap_kj_exception(capnp.Exception & exception):
PyErr_Clear()
wrapper = _KjExceptionWrapper()._init(exception) wrapper = _KjExceptionWrapper()._init(exception)
ret = KjException(wrapper=wrapper) ret = KjException(wrapper=wrapper)
@@ -204,6 +206,13 @@ cdef public object wrap_kj_exception_for_reraise(capnp.Exception & exception):
ret = KjException(wrapper=wrapper) ret = KjException(wrapper=wrapper)
return ret return ret
cdef public object get_exception_info(object exc_type, object exc_obj, object exc_tb):
try:
return (exc_tb.tb_frame.f_code.co_filename, exc_tb.tb_lineno, repr(exc_type) + ':' + str(exc_obj))
except:
return ('', 0, "Couldn't determine python exception")
ctypedef fused _DynamicStructReaderOrBuilder: ctypedef fused _DynamicStructReaderOrBuilder:
_DynamicStructReader _DynamicStructReader
_DynamicStructBuilder _DynamicStructBuilder

View File

@@ -113,7 +113,7 @@ def test_exception_client(capability):
client = capability.TestInterface.new_client(BadServer(), loop) client = capability.TestInterface.new_client(BadServer(), loop)
remote = client._send('foo', i=5) remote = client._send('foo', i=5)
with pytest.raises(ValueError): with pytest.raises(capnp.KjException):
loop.wait(remote) loop.wait(remote)
class BadPipelineServer: class BadPipelineServer:
@@ -122,7 +122,7 @@ class BadPipelineServer:
context.results.s = response.x + '_foo' context.results.s = response.x + '_foo'
context.results.outBox.cap = capability().TestInterface.new_server(Server(100)) context.results.outBox.cap = capability().TestInterface.new_server(Server(100))
def _error(error): def _error(error):
raise Exception('test') raise Exception('test was a success')
return context.params.inCap.foo(i=context.params.n).then(_then, _error) return context.params.inCap.foo(i=context.params.n).then(_then, _error)
@@ -137,7 +137,7 @@ def test_exception_chain(capability):
try: try:
loop.wait(remote) loop.wait(remote)
except Exception as e: except Exception as e:
assert str(e) == 'test' assert 'test was a success' in str(e)
def test_pipeline_exception(capability): def test_pipeline_exception(capability):
loop = capnp.EventLoop() loop = capnp.EventLoop()