Skip to content

Commit d5fbf0d

Browse files
authored
Merge pull request #128 from Distributive-Network/feat/catch-py-exceptions
Handle nested exceptions in different languages
2 parents 10feaa6 + d844346 commit d5fbf0d

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

src/jsTypeFactory.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,21 @@ JS::Value jsTypeFactorySafe(JSContext *cx, PyObject *object) {
196196
return v;
197197
}
198198

199+
void setPyException(JSContext *cx) {
200+
// Python `exit` and `sys.exit` only raise a SystemExit exception to end the program
201+
// We definitely don't want to catch it in JS
202+
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
203+
return;
204+
}
205+
206+
PyObject *type, *value, *traceback;
207+
PyErr_Fetch(&type, &value, &traceback); // also clears the error indicator
208+
209+
JSObject *jsException = ExceptionType(value).toJsError(cx);
210+
JS::RootedValue jsExceptionValue(cx, JS::ObjectValue(*jsException));
211+
JS_SetPendingException(cx, jsExceptionValue);
212+
}
213+
199214
bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) {
200215
JS::CallArgs callargs = JS::CallArgsFromVp(argc, vp);
201216

@@ -213,6 +228,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) {
213228
PyObject *pyRval = _PyObject_CallNoArg(pyFunc); // in Python 3.8, the API is only available under the name with a leading underscore
214229
#endif
215230
if (PyErr_Occurred()) { // Check if an exception has already been set in Python error stack
231+
setPyException(cx);
216232
return false;
217233
}
218234
// @TODO (Caleb Aikens) need to check for python exceptions here
@@ -233,11 +249,13 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) {
233249

234250
PyObject *pyRval = PyObject_Call(pyFunc, pyArgs, NULL);
235251
if (PyErr_Occurred()) {
252+
setPyException(cx);
236253
return false;
237254
}
238255
// @TODO (Caleb Aikens) need to check for python exceptions here
239256
callargs.rval().set(jsTypeFactory(cx, pyRval));
240257
if (PyErr_Occurred()) {
258+
setPyException(cx);
241259
return false;
242260
}
243261

tests/python/test_pythonmonkey_eval.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,30 @@ def test_eval_exceptions():
119119
with pytest.raises(pm.SpiderMonkeyError, match="Error: Python BaseException: 123"):
120120
js_rethrow(BaseException("123"))
121121

122+
def test_eval_exceptions_nested_py_js_py():
123+
def c():
124+
raise Exception('this is an exception')
125+
b = pm.eval('''(x) => {
126+
try {
127+
x()
128+
} catch(e) {
129+
return "Caught in JS " + e;
130+
}
131+
}''')
132+
assert b(c) == "Caught in JS Error: Python Exception: this is an exception"
133+
134+
def test_eval_exceptions_nested_js_py_js():
135+
c = pm.eval("() => { throw TypeError('this is an exception'); }")
136+
137+
def b(x):
138+
try:
139+
x()
140+
return ""
141+
except Exception as e:
142+
return "Caught in Py " + str(e)
143+
ret = b(c)
144+
assert ("Caught in Py Error in" in ret) and ("TypeError: this is an exception" in ret)
145+
122146
def test_eval_undefined():
123147
x = pm.eval("undefined")
124148
assert x == None

0 commit comments

Comments
 (0)