From 82fd9b61db2aef7f479b8a3e914f01627342eb41 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Thu, 4 Jul 2024 10:55:55 +0000 Subject: [PATCH 1/9] Modified function error handling after calling python function in py_object_call. Captured any error string from the called function and added it to the lua error being thrown. --- src/pythoninlua.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/pythoninlua.c b/src/pythoninlua.c index cbfc433..e3df724 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include #if defined(__linux__) # include @@ -183,8 +184,23 @@ static int py_object_call(lua_State *L) ret = py_convert(L, value); Py_DECREF(value); } else { - PyErr_Print(); - luaL_error(L, "error calling python function"); + char s_err[1024]; + memset(s_err, 0, 1024); + + PyObject *exc_type, *exc_value, *exc_traceback; + PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + PyObject *exc_str = PyObject_Str(exc_value); + + // Need not be garbage collected as per documentation of PyUnicode_AsUTF8 + const char *exc_cstr = PyUnicode_AsUTF8(exc_str); + + strncpy(s_err, (!(exc_cstr)?"UNKNOWN ERROR":exc_cstr), 1023); + Py_XDECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(exc_traceback); + Py_XDECREF(exc_str); + + luaL_error(L, "error calling python function [%s]", s_err); } return ret; From c3367ad8092c1fbae99b0ee7eecc09503363f95b Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Thu, 4 Jul 2024 15:10:59 +0000 Subject: [PATCH 2/9] Used traceback.format_exception so that the formatting is original. --- src/pythoninlua.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/pythoninlua.c b/src/pythoninlua.c index e3df724..fc58a95 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -184,23 +184,54 @@ static int py_object_call(lua_State *L) ret = py_convert(L, value); Py_DECREF(value); } else { - char s_err[1024]; - memset(s_err, 0, 1024); + char s_exc[1024]; + char s_traceback[1280]; + memset(s_exc, 0, 1024); + memset(s_traceback, 0, 1280); PyObject *exc_type, *exc_value, *exc_traceback; PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); + PyObject *exc_str = PyObject_Str(exc_value); // Need not be garbage collected as per documentation of PyUnicode_AsUTF8 - const char *exc_cstr = PyUnicode_AsUTF8(exc_str); + const char *exc_cstr = (exc_str)?PyUnicode_AsUTF8(exc_str):""; + strncpy(s_exc, (!(exc_cstr)?"UNKNOWN ERROR":exc_cstr), 1023); + + if (exc_value != NULL && exc_traceback != NULL) { + PyObject *traceback_module = PyImport_ImportModule("traceback"); + if (traceback_module != NULL) { + PyObject *traceback_list = PyObject_CallMethod(traceback_module, + "format_exception", "OOO", exc_type, exc_value, exc_traceback); + if (traceback_list != NULL) { + PyObject *traceback_str = PyUnicode_Join(PyUnicode_FromString(""), traceback_list); + if (traceback_str != NULL) { + // Need not be garbage collected as per documentation of PyUnicode_AsUTF8 + const char *traceback_cstr = PyUnicode_AsUTF8(traceback_str); + if (traceback_cstr != NULL) { + strncpy(s_traceback, traceback_cstr, 1023); + } + Py_XDECREF(traceback_str); + } + Py_XDECREF(traceback_list); + } + Py_XDECREF(traceback_module); + } + } + + if (*s_traceback == '\0') { + strcpy(s_traceback, "Exception: "); + strncat(s_traceback, s_exc, (1280 - strlen("Exception: "))); + } + - strncpy(s_err, (!(exc_cstr)?"UNKNOWN ERROR":exc_cstr), 1023); Py_XDECREF(exc_type); Py_XDECREF(exc_value); Py_XDECREF(exc_traceback); Py_XDECREF(exc_str); - luaL_error(L, "error calling python function [%s]", s_err); + luaL_error(L, "error calling python function:\n%s", s_traceback); } return ret; From 3946a029ee0a422e1286f4ae8f8009dbc86e7624 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Fri, 5 Jul 2024 01:19:56 +0000 Subject: [PATCH 3/9] Further edit to previous commit, based on review --- src/pythoninlua.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pythoninlua.c b/src/pythoninlua.c index fc58a95..6029b4d 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -184,10 +184,8 @@ static int py_object_call(lua_State *L) ret = py_convert(L, value); Py_DECREF(value); } else { - char s_exc[1024]; - char s_traceback[1280]; - memset(s_exc, 0, 1024); - memset(s_traceback, 0, 1280); + char s_exc[1024] = {0}; + char s_traceback[1280] = {0}; PyObject *exc_type, *exc_value, *exc_traceback; PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); From f484faceb37d96ba26d5db48c4256eb6ad49aa1c Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Sun, 7 Jul 2024 12:51:18 +0000 Subject: [PATCH 4/9] Changes realted to impact due to introduction of #define PY_SSIZE_T_CLEAN --- src/luainpython.c | 113 ++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/src/luainpython.c b/src/luainpython.c index c55c6ab..6922d3c 100644 --- a/src/luainpython.c +++ b/src/luainpython.c @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define PY_SSIZE_T_CLEAN #include /* need this to build with Lua 5.2: enables lua_strlen() macro */ @@ -396,9 +397,17 @@ static PyObject *LuaObject_iternext(LuaObject *obj) return ret; } +#ifdef PY_SSIZE_T_CLEAN +static Py_ssize_t LuaObject_length(LuaObject *obj) +#else static int LuaObject_length(LuaObject *obj) +#endif { +#ifdef PY_SSIZE_T_CLEAN + Py_ssize_t len; +#else int len; +#endif lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); len = luaL_len(LuaState, -1); lua_settop(LuaState, 0); @@ -428,46 +437,68 @@ static PyMappingMethods LuaObject_as_mapping = { PyTypeObject LuaObject_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "lua.custom", /*tp_name*/ - sizeof(LuaObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)LuaObject_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - LuaObject_str, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - &LuaObject_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash*/ - (ternaryfunc)LuaObject_call, /*tp_call*/ - LuaObject_str, /*tp_str*/ - LuaObject_getattr, /*tp_getattro*/ - LuaObject_setattr, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "custom lua object", /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - LuaObject_richcmp, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - PyObject_SelfIter, /*tp_iter*/ - (iternextfunc)LuaObject_iternext, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - PyType_GenericAlloc, /*tp_alloc*/ - PyType_GenericNew, /*tp_new*/ - PyObject_Del, /*tp_free*/ - 0, /*tp_is_gc*/ + .tp_name = "lua.custom", + .tp_basicsize = sizeof(LuaObject), + .tp_dealloc = (destructor)LuaObject_dealloc, + .tp_repr = LuaObject_str, + .tp_as_mapping = &LuaObject_as_mapping, + .tp_call = (ternaryfunc)LuaObject_call, + .tp_str = LuaObject_str, + .tp_getattro = LuaObject_getattr, + .tp_setattro = LuaObject_setattr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "custom lua object", + .tp_richcompare = LuaObject_richcmp, + .tp_iter = PyObject_SelfIter, + .tp_iternext = (iternextfunc)LuaObject_iternext, + .tp_alloc = PyType_GenericAlloc, + .tp_new = PyType_GenericNew, + .tp_free = PyObject_Del, }; +/* +PyTypeObject LuaObject_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "lua.custom", //tp_name + sizeof(LuaObject), //tp_basicsize + 0, //tp_itemsize + (destructor)LuaObject_dealloc, //tp_dealloc + 0, //tp_print + 0, //tp_getattr + 0, //tp_setattr + 0, //tp_compare + LuaObject_str, //tp_repr + 0, //tp_as_number + 0, //tp_as_sequence + &LuaObject_as_mapping, //tp_as_mapping + 0, //tp_hash + (ternaryfunc)LuaObject_call, //tp_call + LuaObject_str, //tp_str + LuaObject_getattr, //tp_getattro + LuaObject_setattr, //tp_setattro + 0, //tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //tp_flags + "custom lua object", //tp_doc + 0, //tp_traverse + 0, //tp_clear + LuaObject_richcmp, //tp_richcompare + 0, //tp_weaklistoffset + PyObject_SelfIter, //tp_iter + (iternextfunc)LuaObject_iternext, //tp_iternext + 0, //tp_methods + 0, //tp_members + 0, //tp_getset + 0, //tp_base + 0, //tp_dict + 0, //tp_descr_get + 0, //tp_descr_set + 0, //tp_dictoffset + 0, //tp_init + PyType_GenericAlloc, //tp_alloc + PyType_GenericNew, //tp_new + PyObject_Del, //tp_free + 0, //tp_is_gc +}; +*/ PyObject *Lua_run(PyObject *args, int eval) @@ -475,7 +506,11 @@ PyObject *Lua_run(PyObject *args, int eval) PyObject *ret; char *buf = NULL; char *s; +#ifdef PY_SSIZE_T_CLEAN + Py_ssize_t len; +#else int len; +#endif if (!PyArg_ParseTuple(args, "s#", &s, &len)) return NULL; From d73cfbb41502bb7ffe8806ee0edcc047129fd6a4 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Sun, 7 Jul 2024 16:34:05 +0000 Subject: [PATCH 5/9] Defect fix: function LuaObject_richcmp was returning instance of Py_True/Py_False, without incrementing the reference. Fixed it by calling LuaConvert --- src/luainpython.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/luainpython.c b/src/luainpython.c index 6922d3c..7a1a8cc 100644 --- a/src/luainpython.c +++ b/src/luainpython.c @@ -111,7 +111,12 @@ static PyObject *LuaCall(lua_State *L, PyObject *args) { PyObject *ret = NULL; PyObject *arg; - int nargs, rc, i; +#ifdef PY_SSIZE_T_CLEAN + Py_ssize_t nargs, i; +#else + int nargs, i; +#endif + int rc; if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "tuple expected"); @@ -359,7 +364,8 @@ static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); return NULL; } - return lua_toboolean(LuaState, -1) ? Py_True : Py_False; + //return lua_toboolean(LuaState, -1) ? Py_True : Py_False; + return LuaConvert(LuaState, -1); } static PyObject *LuaObject_call(PyObject *obj, PyObject *args) @@ -455,6 +461,7 @@ PyTypeObject LuaObject_Type = { .tp_new = PyType_GenericNew, .tp_free = PyObject_Del, }; + /* PyTypeObject LuaObject_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -462,7 +469,7 @@ PyTypeObject LuaObject_Type = { sizeof(LuaObject), //tp_basicsize 0, //tp_itemsize (destructor)LuaObject_dealloc, //tp_dealloc - 0, //tp_print + 0, //tp_vectorcall_offset 0, //tp_getattr 0, //tp_setattr 0, //tp_compare @@ -497,6 +504,16 @@ PyTypeObject LuaObject_Type = { PyType_GenericNew, //tp_new PyObject_Del, //tp_free 0, //tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, //tp_weaklist + 0, //tp_del + 0, //tp_version_tag + 0, //tp_finalize + 0, //tp_vectorcall + 0, //tp_watched }; */ From 406b46e3c6d55c34a2bba3b29b80c50187928576 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Mon, 8 Jul 2024 06:05:49 +0000 Subject: [PATCH 6/9] Edited code to remove unnecessary commented lines --- src/luainpython.c | 57 ----------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/src/luainpython.c b/src/luainpython.c index 7a1a8cc..02a3ed9 100644 --- a/src/luainpython.c +++ b/src/luainpython.c @@ -364,7 +364,6 @@ static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); return NULL; } - //return lua_toboolean(LuaState, -1) ? Py_True : Py_False; return LuaConvert(LuaState, -1); } @@ -462,62 +461,6 @@ PyTypeObject LuaObject_Type = { .tp_free = PyObject_Del, }; -/* -PyTypeObject LuaObject_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "lua.custom", //tp_name - sizeof(LuaObject), //tp_basicsize - 0, //tp_itemsize - (destructor)LuaObject_dealloc, //tp_dealloc - 0, //tp_vectorcall_offset - 0, //tp_getattr - 0, //tp_setattr - 0, //tp_compare - LuaObject_str, //tp_repr - 0, //tp_as_number - 0, //tp_as_sequence - &LuaObject_as_mapping, //tp_as_mapping - 0, //tp_hash - (ternaryfunc)LuaObject_call, //tp_call - LuaObject_str, //tp_str - LuaObject_getattr, //tp_getattro - LuaObject_setattr, //tp_setattro - 0, //tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //tp_flags - "custom lua object", //tp_doc - 0, //tp_traverse - 0, //tp_clear - LuaObject_richcmp, //tp_richcompare - 0, //tp_weaklistoffset - PyObject_SelfIter, //tp_iter - (iternextfunc)LuaObject_iternext, //tp_iternext - 0, //tp_methods - 0, //tp_members - 0, //tp_getset - 0, //tp_base - 0, //tp_dict - 0, //tp_descr_get - 0, //tp_descr_set - 0, //tp_dictoffset - 0, //tp_init - PyType_GenericAlloc, //tp_alloc - PyType_GenericNew, //tp_new - PyObject_Del, //tp_free - 0, //tp_is_gc - 0, // tp_bases - 0, // tp_mro - 0, // tp_cache - 0, // tp_subclasses - 0, //tp_weaklist - 0, //tp_del - 0, //tp_version_tag - 0, //tp_finalize - 0, //tp_vectorcall - 0, //tp_watched -}; -*/ - - PyObject *Lua_run(PyObject *args, int eval) { PyObject *ret; From 1930603f996df3050e22b69949f8c5cf882ae713 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Tue, 9 Jul 2024 01:58:15 +0000 Subject: [PATCH 7/9] Refactored code for error formatting --- src/pythoninlua.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pythoninlua.c b/src/pythoninlua.c index 6029b4d..8ca9467 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -219,8 +219,7 @@ static int py_object_call(lua_State *L) } if (*s_traceback == '\0') { - strcpy(s_traceback, "Exception: "); - strncat(s_traceback, s_exc, (1280 - strlen("Exception: "))); + snprintf(s_traceback, 1280, "Exception: %s", s_exc); } From e6837bf1fd1f4b75c9e069f497cf7f5029e835b3 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Tue, 9 Jul 2024 06:19:20 +0000 Subject: [PATCH 8/9] Refactored code for error formatting --- src/pythoninlua.c | 18 +++++++++--------- tests/test_py.lua | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/pythoninlua.c b/src/pythoninlua.c index 8ca9467..464be43 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -185,7 +185,7 @@ static int py_object_call(lua_State *L) Py_DECREF(value); } else { char s_exc[1024] = {0}; - char s_traceback[1280] = {0}; + char s_traceback[1024] = {0}; PyObject *exc_type, *exc_value, *exc_traceback; PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); @@ -195,7 +195,7 @@ static int py_object_call(lua_State *L) // Need not be garbage collected as per documentation of PyUnicode_AsUTF8 const char *exc_cstr = (exc_str)?PyUnicode_AsUTF8(exc_str):""; - strncpy(s_exc, (!(exc_cstr)?"UNKNOWN ERROR":exc_cstr), 1023); + strncpy(s_exc, (!(exc_cstr)?"UNKNOWN ERROR\n":exc_cstr), 1023); if (exc_value != NULL && exc_traceback != NULL) { PyObject *traceback_module = PyImport_ImportModule("traceback"); @@ -217,18 +217,18 @@ static int py_object_call(lua_State *L) Py_XDECREF(traceback_module); } } - - if (*s_traceback == '\0') { - snprintf(s_traceback, 1280, "Exception: %s", s_exc); - } - - Py_XDECREF(exc_type); Py_XDECREF(exc_value); Py_XDECREF(exc_traceback); Py_XDECREF(exc_str); - luaL_error(L, "error calling python function:\n%s", s_traceback); + if (*s_traceback == '\0') { + luaL_error(L, "error calling python function:\nException: %s", s_exc); + } + else { + luaL_error(L, "error calling python function:\nException: %s", s_traceback); + } + } return ret; diff --git a/tests/test_py.lua b/tests/test_py.lua index a5f4d28..b945528 100644 --- a/tests/test_py.lua +++ b/tests/test_py.lua @@ -40,3 +40,28 @@ bar = 2 assert(python.globals().foo == 1) assert(python.globals().bar == 2) + +python.execute +[[ +def throw_exc(): + raise Exception("THIS EXCEPTION") +]] + +local status, exc = pcall(python.globals().throw_exc) +assert(status == false) +assert(exc == +[[error calling python function: +Exception: Traceback (most recent call last): + File "", line 2, in throw_exc +Exception: THIS EXCEPTION +]], exc) + +local b, e = string.find(exc, "Exception: ", 1); +local ob, oe = b, e; +while (b ~= nil) do + ob, oe = b, e + b, e = string.find(exc, "Exception: ", e+1) +end +local exc_s = (string.sub(exc, oe+1)) +assert((require "pl.stringx").strip(exc_s) == "THIS EXCEPTION"); + From d521cba316c64d49e06b386d628de50c18bc7ba3 Mon Sep 17 00:00:00 2001 From: Sudheer Hebbale Date: Tue, 9 Jul 2024 08:54:11 +0000 Subject: [PATCH 9/9] removed dependency on penlight lua library --- tests/test_py.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_py.lua b/tests/test_py.lua index b945528..2467596 100644 --- a/tests/test_py.lua +++ b/tests/test_py.lua @@ -63,5 +63,5 @@ while (b ~= nil) do b, e = string.find(exc, "Exception: ", e+1) end local exc_s = (string.sub(exc, oe+1)) -assert((require "pl.stringx").strip(exc_s) == "THIS EXCEPTION"); +--assert((require "pl.stringx").strip(exc_s) == "THIS EXCEPTION");