From 97c1eb2fd306dcf3740d3316a99de52614a2d181 Mon Sep 17 00:00:00 2001 From: Nick Franken Date: Mon, 30 Sep 2019 11:37:28 -0500 Subject: [PATCH] Exceptions (#14) * can get single line as exception * returns exceptions now * remove stream code --- src/main.cpp | 36 +++++++++++++++++++++++++++++++++++- test.js | 11 +++++++++++ test_files/tools.py | 5 +++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 6cf9be5..e42ef01 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,6 @@ +#include #include +#include // #include #ifdef COMPILER #undef COMPILER @@ -196,9 +198,41 @@ class CallWorker : public Nan::AsyncWorker { Py_DECREF(pValue); } else { + std::string error; + PyObject * errOccurred = PyErr_Occurred(); + if (errOccurred != NULL) { + PyObject *pType, *pValue, *pTraceback, *pTypeString; + PyErr_Fetch(&pType, &pValue, &pTraceback); + const char * value = PyUnicode_AsUTF8(pValue); + pTypeString = PyObject_Str(pType); + const char * type = PyUnicode_AsUTF8(pTypeString); + + PyTracebackObject * tb = (PyTracebackObject *)pTraceback; + std::ostringstream stream; + _frame * frame = tb->tb_frame; + + while (frame != NULL) { + int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); + const char * filename = PyUnicode_AsUTF8(frame->f_code->co_filename); + const char * funcname = PyUnicode_AsUTF8(frame->f_code->co_name); + stream << "File \"" << filename << "\", line " << line << ", in " << funcname << "\n"; + stream << type << ": " << value; + frame = frame->f_back; + } + + error.append(stream.str()); + + Py_DecRef(errOccurred); + Py_DecRef(pTypeString); + PyErr_Restore(pType, pValue, pTraceback); + } else { + error.append("Function call failed"); + } + Py_DecRef(pFunc); PyErr_Print(); - argv[0] = Nan::Error("Function call failed"); + + argv[0] = Nan::Error(error.c_str()); } callback->Call(2, argv); diff --git a/test.js b/test.js index d07b59d..ba2d638 100644 --- a/test.js +++ b/test.js @@ -25,6 +25,17 @@ describe('nodePython', () => { }) describe('#call', () => { + it('should return the stack trace', done => { + call('causes_runtime_error') + .then(result => console.log('should not see this : ', result)) + .catch(err => { + expect(err.message.includes('tools.py')).to.equal(true) + expect(err.message.includes('in causes_runtime_error')).to.equal(true) + expect(err.message.includes('name \'secon\' is not defined')).to.equal(true) + done() + }) + }) + it('should return the time series data', done => { call('time_series_data') .then(result => { diff --git a/test_files/tools.py b/test_files/tools.py index f112f10..d3dfb8d 100644 --- a/test_files/tools.py +++ b/test_files/tools.py @@ -38,3 +38,8 @@ def return_dict(): def return_tuple(): x = (1, 2, 3) return x + +def causes_runtime_error(): + first = 1 + second = 2 + return first + secon