diff --git a/src/luainpython.c b/src/luainpython.c index c55c6ab..02a3ed9 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 */ @@ -110,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"); @@ -358,7 +364,7 @@ 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); } static PyObject *LuaObject_call(PyObject *obj, PyObject *args) @@ -396,9 +402,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,54 +442,35 @@ 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, }; - 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; diff --git a/src/pythoninlua.c b/src/pythoninlua.c index cbfc433..464be43 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,51 @@ 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_exc[1024] = {0}; + char s_traceback[1024] = {0}; + + 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 = (exc_str)?PyUnicode_AsUTF8(exc_str):""; + strncpy(s_exc, (!(exc_cstr)?"UNKNOWN ERROR\n":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); + } + } + Py_XDECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(exc_traceback); + Py_XDECREF(exc_str); + + 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..2467596 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"); +