diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a59d666 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +language: c + +sudo: required +dist: trusty + +compiler: + - gcc + +env: + - LUA_ENV=lua5.1 PY_ENV=python2.7 m_SUFFIX= + - LUA_ENV=lua5.3 PY_ENV=python2.7 m_SUFFIX= + - LUA_ENV=lua5.1 PY_ENV=python3.6 m_SUFFIX=m + - LUA_ENV=lua5.3 PY_ENV=python3.6 m_SUFFIX=m + + +before_install: + - sudo add-apt-repository -y "deb http://ppa.launchpad.net/grilo-team/travis/ubuntu trusty main" + - sudo add-apt-repository -y "deb http://ppa.launchpad.net/fkrull/deadsnakes/ubuntu trusty main" + - sudo apt-get update -qq + +install: + - sudo apt-get install -qq --force-yes ${LUA_ENV} + - sudo apt-get install -qq --force-yes ${PY_ENV} + - sudo apt-get install -qq --force-yes lib${LUA_ENV}-dev + - sudo apt-get install -qq --force-yes lib${PY_ENV}-dev + - ${LUA_ENV} -v + - ${PY_ENV} --version + +script: + - cmake -B./build -H. -DPYTHON_INCLUDE_DIR=/usr/include/${PY_ENV}${m_SUFFIX} -DPYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/lib${PY_ENV}${m_SUFFIX}.so + - cmake --build ./build + - cd ./build/bin + - ${PY_ENV} ../../tests/test_lua.py + - ${LUA_ENV} ../../tests/test_py.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index d273ade..1422914 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,13 +4,12 @@ set(CMAKE_BUILD_TYPE_INIT "Release") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH}) + project(Lunatic) -find_package(Lua 5.1 REQUIRED) -find_package(PythonLibs REQUIRED) +find_package(Lua 5.1 REQUIRED) +find_package(PythonLibs 2.7 REQUIRED) -include_directories(${LUA_INCLUDE_DIR}) -include_directories(${PYTHON_INCLUDE_DIR}) add_subdirectory(src) @@ -19,14 +18,14 @@ set_target_properties(python PROPERTIES PREFIX "") add_library(lua MODULE $) -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") +if (WIN32) set_target_properties(lua PROPERTIES PREFIX "" SUFFIX ".pyd") -else () +else (WIN32) set_target_properties(lua PROPERTIES PREFIX "") -endif () +endif (WIN32) -target_link_libraries(lua ${LUA_LIBRARIES} ${PYTHON_LIBRARIES}) -target_link_libraries(python ${LUA_LIBRARIES} ${PYTHON_LIBRARIES}) +target_link_libraries(lua ${LUA_LIBRARIES} ${PYTHON_LIBRARIES}) +target_link_libraries(python ${LUA_LIBRARIES} ${PYTHON_LIBRARIES}) diff --git a/README.md b/README.md index ce66561..a525e2f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/bastibe/lunatic-python.svg?branch=master)](https://travis-ci.org/bastibe/lunatic-python) + Details ======= @@ -5,7 +7,7 @@ This is a fork of Lunatic Python, which can be found on the 'net at http://labix Sadly, Lunatic Python is very much outdated and won't work with either a current Python or Lua. -This is an updated version of lunatic-python that works with Python 2.7 and Lua 5.1. +This is an updated version of lunatic-python that works with Python 2.7-3.x and Lua 5.1-5.3. I tried contacting the original author of Lunatic Python, but got no response. Installing diff --git a/setup.py b/setup.py index 6501557..9f0496d 100755 --- a/setup.py +++ b/setup.py @@ -18,8 +18,15 @@ if os.path.isfile("MANIFEST"): os.unlink("MANIFEST") +presult, poutput = commands.getstatusoutput("pkg-config --exists lua5.2") +HAS_LUA5_2 = (presult == 0) + # You may have to change these -LUAVERSION = "5.2" +if HAS_LUA5_2: + LUAVERSION = "5.2" +else: + LUAVERSION = "5.1" + PYTHONVERSION = get_python_version() PYLIBS = ["python" + get_python_version(), "pthread", "util"] PYLIBDIR = [get_python_lib(standard_lib=True) + "/config"] @@ -30,18 +37,19 @@ def pkgconfig(*packages): # map pkg-config output to kwargs for distutils.core.Extension flag_map = {'-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries'} + combined_pcoutput = '' for package in packages: (pcstatus, pcoutput) = commands.getstatusoutput( "pkg-config --libs --cflags %s" % package) if pcstatus == 0: - break - else: - sys.exit("pkg-config failed for %s; " - "most recent output was:\n%s" % - (", ".join(packages), pcoutput)) + combined_pcoutput += ' ' + pcoutput + else: + sys.exit("pkg-config failed for %s; " + "most recent output was:\n%s" % + (", ".join(packages), pcoutput)) kwargs = {} - for token in pcoutput.split(): + for token in combined_pcoutput.split(): if token[:2] in flag_map: kwargs.setdefault(flag_map.get(token[:2]), []).append(token[2:]) else: # throw others to extra_link_args @@ -56,7 +64,7 @@ def pkgconfig(*packages): return kwargs -lua_pkgconfig = pkgconfig('lua' + LUAVERSION, 'lua' + LUAVERSION,'python-' + PYTHONVERSION) +lua_pkgconfig = pkgconfig('lua' + LUAVERSION, 'python-' + PYTHONVERSION) lua_pkgconfig['extra_compile_args'] = ['-I/usr/include/lua'+LUAVERSION] setup(name="lunatic-python", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 586b66b..b4664b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,14 @@ -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - add_definitions(-DLUA_BUILD_AS_DLL) -endif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") -add_definitions(-DLUA_LIB) - add_library(src OBJECT luainpython.c pythoninlua.c) set_target_properties(src PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +target_include_directories(src PRIVATE ${LUA_INCLUDE_DIR} ${PYTHON_INCLUDE_DIR}) + +target_compile_definitions(src PRIVATE LUA_LIB) +if (WIN32) + target_compile_definitions(src PRIVATE LUA_BUILD_AS_DLL) +endif (WIN32) + +if (CMAKE_COMPILER_IS_GNUCC) + target_compile_options(src PUBLIC -Wall -pedantic -std=c99) +endif (CMAKE_COMPILER_IS_GNUCC) diff --git a/src/luainpython.c b/src/luainpython.c index 0c2647b..c55c6ab 100644 --- a/src/luainpython.c +++ b/src/luainpython.c @@ -35,7 +35,17 @@ lua_State *LuaState = NULL; -static PyObject *LuaObject_New(int n); +static PyObject *LuaObject_New(lua_State *L, int n) +{ + LuaObject *obj = PyObject_New(LuaObject, &LuaObject_Type); + if (obj) + { + lua_pushvalue(L, n); + obj->ref = luaL_ref(L, LUA_REGISTRYINDEX); + obj->refiter = 0; + } + return (PyObject*) obj; +} PyObject *LuaConvert(lua_State *L, int n) { @@ -50,9 +60,14 @@ PyObject *LuaConvert(lua_State *L, int n) break; case LUA_TSTRING: { - const char *s = lua_tostring(L, n); - int len = lua_strlen(L, n); + size_t len; + const char *s = lua_tolstring(L, n, &len); ret = PyUnicode_FromStringAndSize(s, len); + if (!ret) + { + PyErr_Clear(); + ret = PyBytes_FromStringAndSize(s, len); + } break; } @@ -67,13 +82,8 @@ PyObject *LuaConvert(lua_State *L, int n) } case LUA_TBOOLEAN: - if (lua_toboolean(L, n)) { - Py_INCREF(Py_True); - ret = Py_True; - } else { - Py_INCREF(Py_False); - ret = Py_False; - } + ret = lua_toboolean(L, n) ? Py_True : Py_False; + Py_INCREF(ret); break; case LUA_TUSERDATA: { @@ -89,7 +99,7 @@ PyObject *LuaConvert(lua_State *L, int n) } default: - ret = LuaObject_New(n); + ret = LuaObject_New(L, n); break; } @@ -117,7 +127,7 @@ static PyObject *LuaCall(lua_State *L, PyObject *args) lua_settop(L, 0); return NULL; } - rc = py_convert(L, arg, 0); + rc = py_convert(L, arg); if (!rc) { PyErr_Format(PyExc_TypeError, "failed to convert argument #%d", i); @@ -171,17 +181,6 @@ static PyObject *LuaCall(lua_State *L, PyObject *args) return ret; } -static PyObject *LuaObject_New(int n) -{ - LuaObject *obj = PyObject_New(LuaObject, &LuaObject_Type); - if (obj) { - lua_pushvalue(LuaState, n); - obj->ref = luaL_ref(LuaState, LUA_REGISTRYINDEX); - obj->refiter = 0; - } - return (PyObject*) obj; -} - static void LuaObject_dealloc(LuaObject *self) { luaL_unref(LuaState, LUA_REGISTRYINDEX, self->ref); @@ -209,7 +208,7 @@ static PyObject *LuaObject_getattr(PyObject *obj, PyObject *attr) } PyObject *ret = NULL; - int rc = py_convert(LuaState, attr, 0); + int rc = py_convert(LuaState, attr); if (rc) { lua_gettable(LuaState, -2); ret = LuaConvert(LuaState, -1); @@ -235,13 +234,13 @@ static int LuaObject_setattr(PyObject *obj, PyObject *attr, PyObject *value) PyErr_SetString(PyExc_TypeError, "Lua object is not a table"); return -1; } - rc = py_convert(LuaState, attr, 0); + rc = py_convert(LuaState, attr); if (rc) { if (NULL == value) { lua_pushnil(LuaState); rc = 1; } else { - rc = py_convert(LuaState, value, 0); + rc = py_convert(LuaState, value); } if (rc) { @@ -302,6 +301,66 @@ static PyObject *LuaObject_str(PyObject *obj) return ret; } +#if LUA_VERSION_NUM == 501 +enum +{ + LUA_OK, LUA_OPEQ, LUA_OPLT, LUA_OPLE, +}; +static int lua_compare(lua_State *L, int lhs, int rhs, int op) +{ + switch(op) + { + case LUA_OPEQ: + return lua_equal(L, lhs, rhs); + case LUA_OPLT: + return lua_lessthan(L, lhs, rhs); + case LUA_OPLE: + return lua_lessthan(L, lhs, rhs) || lua_equal(L, lhs, rhs); + } + return 0; +} +#endif +static int LuaObject_pcmp(lua_State *L) +{ + int op = lua_tointeger(L, -3); + switch(op) + { + case Py_EQ: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPEQ)); + break; + case Py_NE: + lua_pushboolean(L, !lua_compare(L, -2, -1, LUA_OPEQ)); + break; + case Py_GT: + lua_insert(LuaState, -2); + case Py_LT: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLT)); + break; + case Py_GE: + lua_insert(LuaState, -2); + case Py_LE: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLE)); + } + + return 1; +} + +static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) +{ + if (!LuaObject_Check(rhs)) return Py_False; + + lua_pushcfunction(LuaState, LuaObject_pcmp); + lua_pushinteger(LuaState, op); + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)lhs)->ref); + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)rhs)->ref); + if (lua_pcall(LuaState, 3, 1, 0) != LUA_OK) + { + PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); + return NULL; + } + return lua_toboolean(LuaState, -1) ? Py_True : Py_False; +} + static PyObject *LuaObject_call(PyObject *obj, PyObject *args) { lua_settop(LuaState, 0); @@ -341,7 +400,7 @@ static int LuaObject_length(LuaObject *obj) { int len; lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - len = lua_objlen(LuaState, -1); + len = luaL_len(LuaState, -1); lua_settop(LuaState, 0); return len; } @@ -391,7 +450,7 @@ PyTypeObject LuaObject_Type = { "custom lua object", /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ - 0, /*tp_richcompare*/ + LuaObject_richcmp, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ PyObject_SelfIter, /*tp_iter*/ (iternextfunc)LuaObject_iternext, /*tp_iternext*/ @@ -433,6 +492,7 @@ PyObject *Lua_run(PyObject *args, int eval) PyErr_Format(PyExc_RuntimeError, "error loading code: %s", lua_tostring(LuaState, -1)); + free(buf); return NULL; } @@ -473,7 +533,7 @@ PyObject *Lua_globals(PyObject *self, PyObject *args) ret = LuaConvert(LuaState, -1); if (!ret) PyErr_Format(PyExc_TypeError, - "failed to convert globals table"); + "failed to convert globals table"); lua_settop(LuaState, 0); return ret; } @@ -489,7 +549,8 @@ static PyObject *Lua_require(PyObject *self, PyObject *args) return LuaCall(LuaState, args); } -static PyMethodDef lua_methods[] = { +static PyMethodDef lua_methods[] = +{ {"execute", Lua_execute, METH_VARARGS, NULL}, {"eval", Lua_eval, METH_VARARGS, NULL}, {"globals", Lua_globals, METH_NOARGS, NULL}, @@ -498,7 +559,8 @@ static PyMethodDef lua_methods[] = { }; #if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef lua_module = { +static struct PyModuleDef lua_module = +{ PyModuleDef_HEAD_INIT, "lua", "Lunatic-Python Python-Lua bridge", @@ -509,22 +571,19 @@ static struct PyModuleDef lua_module = { PyMODINIT_FUNC PyInit_lua(void) { - PyObject *m; - + PyObject *m; + if (PyType_Ready(&LuaObject_Type) < 0 || #if PY_MAJOR_VERSION >= 3 - if (PyType_Ready(&LuaObject_Type) < 0) return NULL; - m = PyModule_Create(&lua_module); - if (m == NULL) return NULL; + (m = PyModule_Create(&lua_module)) == NULL) + return NULL; #else - if (PyType_Ready(&LuaObject_Type) < 0) return; - m = Py_InitModule3("lua", lua_methods, - "Lunatic-Python Python-Lua bridge"); - if (m == NULL) return; + (m = Py_InitModule3("lua", lua_methods, + "Lunatic-Python Python-Lua bridge")) == NULL) + return; #endif - Py_INCREF(&LuaObject_Type); - - if (!LuaState) { + if (!LuaState) + { LuaState = luaL_newstate(); luaL_openlibs(LuaState); luaopen_python(LuaState); diff --git a/src/luainpython.h b/src/luainpython.h index bf9d88c..73ea65c 100644 --- a/src/luainpython.h +++ b/src/luainpython.h @@ -23,7 +23,14 @@ #ifndef LUAINPYTHON_H #define LUAINPYTHON_H -typedef struct { +#if LUA_VERSION_NUM == 501 + #define luaL_len lua_objlen + #define luaL_setfuncs(L, l, nup) luaL_register(L, NULL, (l)) + #define luaL_newlib(L, l) (lua_newtable(L), luaL_register(L, NULL, (l))) +#endif + +typedef struct +{ PyObject_HEAD int ref; int refiter; @@ -33,7 +40,7 @@ extern PyTypeObject LuaObject_Type; #define LuaObject_Check(op) PyObject_TypeCheck(op, &LuaObject_Type) -PyObject *LuaConvert(lua_State *L, int n); +PyObject* LuaConvert(lua_State *L, int n); extern lua_State *LuaState; diff --git a/src/pythoninlua.c b/src/pythoninlua.c index 0069201..f5b5bd5 100644 --- a/src/pythoninlua.c +++ b/src/pythoninlua.c @@ -21,6 +21,9 @@ */ #include +#if defined(__linux__) +# include +#endif /* need this to build with Lua 5.2: defines luaL_register() macro */ #define LUA_COMPAT_MODULE @@ -31,59 +34,48 @@ #include "pythoninlua.h" #include "luainpython.h" -static int py_asfunc_call(lua_State *L); +static int py_asfunc_call(lua_State *); +static int py_eval(lua_State *); static int py_convert_custom(lua_State *L, PyObject *o, int asindx) { - int ret = 0; py_object *obj = (py_object*) lua_newuserdata(L, sizeof(py_object)); - if (obj) { - Py_INCREF(o); - obj->o = o; - obj->asindx = asindx; - luaL_getmetatable(L, POBJECT); - lua_setmetatable(L, -2); - ret = 1; - } else { + if (!obj) luaL_error(L, "failed to allocate userdata object"); - } - return ret; + + Py_INCREF(o); + obj->o = o; + obj->asindx = asindx; + luaL_getmetatable(L, POBJECT); + lua_setmetatable(L, -2); + + return 1; } -int py_convert(lua_State *L, PyObject *o, int withnone) +int py_convert(lua_State *L, PyObject *o) { int ret = 0; - if (o == Py_None) { - if (withnone) { - lua_pushliteral(L, "Py_None"); - lua_rawget(L, LUA_REGISTRYINDEX); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - luaL_error(L, "lost none from registry"); - } - } else { - /* Not really needed, but this way we may check - * for errors with ret == 0. */ - lua_pushnil(L); - ret = 1; - } + if (o == Py_None) + { + /* Not really needed, but this way we may check + * for errors with ret == 0. */ + lua_pushnil(L); + ret = 1; } else if (o == Py_True) { lua_pushboolean(L, 1); ret = 1; } else if (o == Py_False) { lua_pushboolean(L, 0); ret = 1; -#if PY_MAJOR_VERSION >= 3 - } else if (PyUnicode_Check(o)) { - Py_ssize_t len; - char *s = PyUnicode_AsUTF8AndSize(o, &len); -#else - } else if (PyString_Check(o)) { + } else if (PyUnicode_Check(o) || PyBytes_Check(o)) { + PyObject *bstr = PyUnicode_AsEncodedString(o, "utf-8", NULL); Py_ssize_t len; char *s; - PyString_AsStringAndSize(o, &s, &len); -#endif + + PyErr_Clear(); + PyBytes_AsStringAndSize(bstr ? bstr : o, &s, &len); lua_pushlstring(L, s, len); + if (bstr) Py_DECREF(bstr); ret = 1; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(o)) { @@ -100,33 +92,25 @@ int py_convert(lua_State *L, PyObject *o, int withnone) lua_rawgeti(L, LUA_REGISTRYINDEX, ((LuaObject*)o)->ref); ret = 1; } else { - int asindx = 0; - if (PyDict_Check(o) || PyList_Check(o) || PyTuple_Check(o)) - asindx = 1; + int asindx = PyDict_Check(o) || PyList_Check(o) || PyTuple_Check(o); ret = py_convert_custom(L, o, asindx); - if (ret && !asindx && - (PyFunction_Check(o) || PyCFunction_Check(o))) - lua_pushcclosure(L, py_asfunc_call, 1); } return ret; } static int py_object_call(lua_State *L) { - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); PyObject *args; PyObject *value; - PyObject* pKywdArgs = NULL; + PyObject *pKywdArgs = NULL; int nargs = lua_gettop(L)-1; int ret = 0; int i; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); - if (!obj) { - return luaL_argerror(L, 1, "not a python object"); - } - if (!PyCallable_Check(obj->o)) { + if (!PyCallable_Check(obj->o)) return luaL_error(L, "object is not callable"); - } // passing a single table forces named keyword call style, e.g. plt.plot{x, y, c='red'} if (nargs==1 && lua_istable(L, 2)) { @@ -195,47 +179,7 @@ static int py_object_call(lua_State *L) if (pKywdArgs) Py_DECREF(pKywdArgs); if (value) { - ret = py_convert(L, value, 0); - Py_DECREF(value); - } else { - PyErr_Print(); - luaL_error(L, "error calling python function"); - } - - return ret; -} - -static int py_object_call2(lua_State *L, PyObject *obj) -{ - PyObject *args; - PyObject *value; - int nargs = lua_gettop(L); - int ret = 0; - int i; - - if (!PyCallable_Check(obj)) { - return luaL_error(L, "object is not callable"); - } - - args = PyTuple_New(nargs); - if (!args) { - PyErr_Print(); - return luaL_error(L, "failed to create arguments tuple"); - } - - for (i = 0; i != nargs; i++) { - PyObject *arg = LuaConvert(L, i+1); - if (!arg) { - Py_DECREF(args); - return luaL_error(L, "failed to convert argument #%d", i+1); - } - PyTuple_SetItem(args, i, arg); - } - - value = PyObject_Call(obj, args, NULL); - Py_DECREF(args); - if (value) { - ret = py_convert(L, value, 0); + ret = py_convert(L, value); Py_DECREF(value); } else { PyErr_Print(); @@ -281,38 +225,32 @@ static int _p_object_newindex_set(lua_State *L, py_object *obj, static int py_object_newindex_set(lua_State *L) { - py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), - POBJECT); - if (lua_gettop(L) != 2) { + py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); + if (lua_gettop(L) != 2) return luaL_error(L, "invalid arguments"); - } + return _p_object_newindex_set(L, obj, 1, 2); } static int py_object_newindex(lua_State *L) { - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - const char *attr; PyObject *value; + const char *attr; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); - if (!obj) { - return luaL_argerror(L, 1, "not a python object"); - } - - if (obj->asindx || lua_type(L, 2)!=LUA_TSTRING) + if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) return _p_object_newindex_set(L, obj, 2, 3); attr = luaL_checkstring(L, 2); - if (!attr) { - return luaL_argerror(L, 2, "string needed"); - } + assert(attr); value = LuaConvert(L, 3); if (!value) { return luaL_argerror(L, 1, "failed to convert value"); } - if (PyObject_SetAttrString(obj->o, (char*)attr, value) == -1) { + if (PyObject_SetAttrString(obj->o, attr, value) == -1) { Py_DECREF(value); PyErr_Print(); return luaL_error(L, "failed to set value"); @@ -337,7 +275,7 @@ static int _p_object_index_get(lua_State *L, py_object *obj, int keyn) Py_DECREF(key); if (item) { - ret = py_convert(L, item, 0); + ret = py_convert(L, item); Py_DECREF(item); } else { PyErr_Clear(); @@ -352,52 +290,47 @@ static int _p_object_index_get(lua_State *L, py_object *obj, int keyn) static int py_object_index_get(lua_State *L) { - py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), - POBJECT); + py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); int top = lua_gettop(L); - if (top < 1 || top > 2) { + if (top < 1 || top > 2) return luaL_error(L, "invalid arguments"); - } + return _p_object_index_get(L, obj, 1); } static int py_object_index(lua_State *L) { - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - const char *attr; - PyObject *value; int ret = 0; + PyObject *value; + const char *attr; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); - if (!obj) { - return luaL_argerror(L, 1, "not a python object"); - } - - if (obj->asindx || lua_type(L, 2)!=LUA_TSTRING) + if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) return _p_object_index_get(L, obj, 2); attr = luaL_checkstring(L, 2); - if (!attr) { - return luaL_argerror(L, 2, "string needed"); - } + assert(attr); - if (attr[0] == '_' && strcmp(attr, "__get") == 0) { + if (strcmp(attr, "__get") == 0) { lua_pushvalue(L, 1); lua_pushcclosure(L, py_object_index_get, 1); return 1; - } else if (attr[0] == '_' && strcmp(attr, "__set") == 0) { + } else if (strcmp(attr, "__set") == 0) { lua_pushvalue(L, 1); lua_pushcclosure(L, py_object_newindex_set, 1); return 1; } - value = PyObject_GetAttrString(obj->o, (char*)attr); + value = PyObject_GetAttrString(obj->o, attr); if (value) { - ret = py_convert(L, value, 0); + ret = py_convert(L, value); Py_DECREF(value); } else { PyErr_Clear(); - luaL_error(L, "unknown attribute in python object"); + lua_pushnil(L); + ret = 1; } return ret; @@ -406,182 +339,110 @@ static int py_object_index(lua_State *L) static int py_object_gc(lua_State *L) { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - if (obj) { - Py_DECREF(obj->o); - } + assert(obj); + + Py_DECREF(obj->o); return 0; } static int py_object_tostring(lua_State *L) { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - if (obj) { - PyObject *repr = PyObject_Str(obj->o); - if (!repr) { - char buf[256]; - snprintf(buf, 256, "python object: %p", obj->o); - lua_pushstring(L, buf); - PyErr_Clear(); - } else { - py_convert(L, repr, 0); - assert(lua_type(L, -1) == LUA_TSTRING); - Py_DECREF(repr); - } - } - return 1; -} - -static int py_object_mul(lua_State *L) -{ - int r=0; - PyObject *value; - if (lua_isuserdata(L, 1)) - { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - lua_remove(L, 1); - value = PyObject_GetAttrString(obj->o, "__mul__"); - } - else - { py_object *obj = (py_object*) luaL_checkudata(L, 2, POBJECT); - lua_remove(L, 2); - value = PyObject_GetAttrString(obj->o, "__rmul__"); - } - r = py_object_call2(L, value); - Py_DECREF(value); - return r; -} - -static int py_object_div(lua_State *L) -{ - int r=0; - PyObject *value; - if (lua_isuserdata(L, 1)) - { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - lua_remove(L, 1); - value = PyObject_GetAttrString(obj->o, "__div__"); - } - else - { py_object *obj = (py_object*) luaL_checkudata(L, 2, POBJECT); - lua_remove(L, 2); - value = PyObject_GetAttrString(obj->o, "__rdiv__"); - } - r = py_object_call2(L, value); - Py_DECREF(value); - return r; -} - -static int py_object_add(lua_State *L) -{ - int r=0; - PyObject *value; - if (lua_isuserdata(L, 1)) - { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - lua_remove(L, 1); - value = PyObject_GetAttrString(obj->o, "__add__"); + assert(obj); + + PyObject *repr = PyObject_Str(obj->o); + if (!repr) + { + char buf[256]; + snprintf(buf, 256, "python object: %p", obj->o); + lua_pushstring(L, buf); + PyErr_Clear(); } else - { py_object *obj = (py_object*) luaL_checkudata(L, 2, POBJECT); - lua_remove(L, 2); - value = PyObject_GetAttrString(obj->o, "__radd__"); + { + py_convert(L, repr); + assert(lua_type(L, -1) == LUA_TSTRING); + Py_DECREF(repr); } - r = py_object_call2(L, value); - Py_DECREF(value); - return r; + return 1; } -static int py_object_sub(lua_State *L) +static int py_operator_lambda(lua_State *L, const char *op) { - int r=0; - PyObject *value; - if (lua_isuserdata(L, 1)) - { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - lua_remove(L, 1); - value = PyObject_GetAttrString(obj->o, "__sub__"); - } - else - { py_object *obj = (py_object*) luaL_checkudata(L, 2, POBJECT); - lua_remove(L, 2); - value = PyObject_GetAttrString(obj->o, "__rsub__"); - } - r = py_object_call2(L, value); - Py_DECREF(value); - return r; + static char script_buff[] = "lambda a, b: a b"; + static size_t len = sizeof(script_buff) / sizeof(script_buff[0]); + snprintf(script_buff, len, "lambda a, b: a %s b", op); + + lua_pushcfunction(L, py_eval); + lua_pushlstring(L, script_buff, len); + lua_call(L, 1, 1); + return luaL_ref(L, LUA_REGISTRYINDEX); } -static int py_object_pow(lua_State *L) +#define make_pyoperator(opname, opliteral) \ + static int py_object_ ## opname(lua_State *L) \ + { \ + static int op_ref = LUA_REFNIL; \ + if (op_ref == LUA_REFNIL) op_ref = py_operator_lambda(L, opliteral); \ + \ + lua_rawgeti(L, LUA_REGISTRYINDEX, op_ref); \ + lua_insert(L, 1); \ + return py_object_call(L); \ + } \ + struct opname ## __LINE__ // force semi + +make_pyoperator(_pow, "**"); +make_pyoperator(_mul, "*"); +make_pyoperator(_div, "/"); +make_pyoperator(_add, "+"); +make_pyoperator(_sub, "-"); + +static const luaL_Reg py_object_mt[] = { - int r=0; - PyObject *value; - if (lua_isuserdata(L, 1)) - { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - lua_remove(L, 1); - value = PyObject_GetAttrString(obj->o, "__pow__"); - } - else - { py_object *obj = (py_object*) luaL_checkudata(L, 2, POBJECT); - lua_remove(L, 2); - value = PyObject_GetAttrString(obj->o, "__rpow__"); - } - r = py_object_call2(L, value); - Py_DECREF(value); - return r; -} - -static const luaL_Reg py_object_lib[] = { {"__call", py_object_call}, {"__index", py_object_index}, {"__newindex", py_object_newindex}, {"__gc", py_object_gc}, {"__tostring", py_object_tostring}, - {"__mul", py_object_mul}, - {"__div", py_object_div}, - {"__add", py_object_add}, - {"__sub", py_object_sub}, - {"__pow", py_object_pow}, + {"__pow", py_object__pow}, + {"__mul", py_object__mul}, + {"__div", py_object__div}, + {"__add", py_object__add}, + {"__sub", py_object__sub}, {NULL, NULL} }; static int py_run(lua_State *L, int eval) { - const char *s; - char *buffer = NULL; + const char *s = luaL_checkstring(L, 1); PyObject *m, *d, *o; int ret = 0; - int len; - s = luaL_checkstring(L, 1); - if (!s) - return 0; + lua_settop(L, 1); - if (!eval) { - len = strlen(s)+1; - buffer = (char *) malloc(len+1); - if (!buffer) { - return luaL_error(L, "Failed allocating buffer for execution"); - } - strcpy(buffer, s); - buffer[len-1] = '\n'; - buffer[len] = '\0'; - s = buffer; + if (!eval) + { + lua_pushliteral(L, "\n"); + lua_concat(L, 2); + + s = luaL_checkstring(L, 1); } m = PyImport_AddModule("__main__"); - if (!m) { - free(buffer); + if (!m) return luaL_error(L, "Can't get __main__ module"); - } - d = PyModule_GetDict(m); - - o = PyRun_StringFlags(s, eval ? Py_eval_input : Py_single_input, - d, d, NULL); - free(buffer); + d = PyModule_GetDict(m); - if (!o) { + o = PyRun_String(s, eval ? Py_eval_input : Py_file_input, + d, d); + if (!o) + { PyErr_Print(); return 0; } - if (py_convert(L, o, 0)) + if (py_convert(L, o)) ret = 1; Py_DECREF(o); @@ -607,8 +468,7 @@ static int py_eval(lua_State *L) static int py_asindx(lua_State *L) { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - if (!obj) - return luaL_argerror(L, 1, "not a python object"); + assert(obj); return py_convert_custom(L, obj->o, 1); } @@ -616,8 +476,7 @@ static int py_asindx(lua_State *L) static int py_asattr(lua_State *L) { py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - if (!obj) - return luaL_argerror(L, 1, "not a python object"); + assert(obj); return py_convert_custom(L, obj->o, 0); } @@ -631,15 +490,14 @@ static int py_asfunc_call(lua_State *L) static int py_asfunc(lua_State *L) { - int ret = 0; - if (luaL_checkudata(L, 1, POBJECT)) { - lua_pushcclosure(L, py_asfunc_call, 1); - ret = 1; - } else { - luaL_argerror(L, 1, "not a python object"); - } + py_object *obj = luaL_checkudata(L, 1, POBJECT); + if (!PyCallable_Check(obj->o)) + return luaL_error(L, "object is not callable"); - return ret; + lua_settop(L, 1); + lua_pushcclosure(L, py_asfunc_call, 1); + + return 1; } static int py_globals(lua_State *L) @@ -702,16 +560,11 @@ static int py_builtins(lua_State *L) static int py_import(lua_State *L) { const char *name = luaL_checkstring(L, 1); - PyObject *module; + PyObject *module = PyImport_ImportModule((char*)name); int ret; - if (!name) { - return luaL_argerror(L, 1, "module name expected"); - } - - module = PyImport_ImportModule((char*)name); - - if (!module) { + if (!module) + { PyErr_Print(); return luaL_error(L, "failed importing '%s'", name); } @@ -731,7 +584,8 @@ py_object* luaPy_to_pobject(lua_State *L, int n) return is_pobject ? (py_object *) lua_touserdata(L, n) : NULL; } -static const luaL_Reg py_lib[] = { +static const luaL_Reg py_lib[] = +{ {"execute", py_execute}, {"eval", py_eval}, {"asindx", py_asindx}, @@ -749,18 +603,19 @@ LUA_API int luaopen_python(lua_State *L) int rc; /* Register module */ - luaL_register(L, "python", py_lib); + luaL_newlib(L, py_lib); /* Register python object metatable */ luaL_newmetatable(L, POBJECT); - luaL_register(L, NULL, py_object_lib); + luaL_setfuncs(L, py_object_mt, 0); lua_pop(L, 1); /* Initialize Lua state in Python territory */ if (!LuaState) LuaState = L; /* Initialize Python interpreter */ - if (!Py_IsInitialized()) { + if (!Py_IsInitialized()) + { PyObject *luam, *mainm, *maind; #if PY_MAJOR_VERSION >= 3 wchar_t *argv[] = {L"", 0}; @@ -769,36 +624,48 @@ LUA_API int luaopen_python(lua_State *L) #endif Py_SetProgramName(argv[0]); PyImport_AppendInittab("lua", PyInit_lua); + + /* Loading python library symbols so that dynamic extensions don't throw symbol not found error. + Ref Link: http://stackoverflow.com/questions/29880931/importerror-and-pyexc-systemerror-while-embedding-python-script-within-c-for-pam + */ +#if defined(__linux__) +# define STR(s) #s +#if PY_MAJOR_VERSION < 3 +# define PYLIB_STR(major, minor) "libpython" STR(major) "." STR(minor) ".so" +#else +# define PYLIB_STR(major, minor) "libpython" STR(major) "." STR(minor) "m.so" +#endif + dlopen(PYLIB_STR(PY_MAJOR_VERSION, PY_MINOR_VERSION), RTLD_NOW | RTLD_GLOBAL); +#endif + Py_Initialize(); PySys_SetArgv(1, argv); /* Import 'lua' automatically. */ luam = PyImport_ImportModule("lua"); - if (!luam) { - luaL_error(L, "Can't import lua module"); - } else { - mainm = PyImport_AddModule("__main__"); - if (!mainm) { - luaL_error(L, "Can't get __main__ module"); - } else { - maind = PyModule_GetDict(mainm); - PyDict_SetItemString(maind, "lua", luam); - Py_DECREF(luam); - } + if (!luam) + return luaL_error(L, "Can't import lua module"); + + mainm = PyImport_AddModule("__main__"); + if (!mainm) + { + Py_DECREF(luam); + return luaL_error(L, "Can't get __main__ module"); } + + maind = PyModule_GetDict(mainm); + PyDict_SetItemString(maind, "lua", luam); + Py_DECREF(luam); } /* Register 'none' */ - lua_pushliteral(L, "Py_None"); rc = py_convert_custom(L, Py_None, 0); - if (rc) { - lua_pushliteral(L, "none"); - lua_pushvalue(L, -2); - lua_rawset(L, -5); /* python.none */ - lua_rawset(L, LUA_REGISTRYINDEX); /* registry.Py_None */ - } else { - lua_pop(L, 1); - luaL_error(L, "failed to convert none object"); - } + if (!rc) + return luaL_error(L, "failed to convert none object"); - return 0; + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "Py_None"); /* registry.Py_None */ + + lua_setfield(L, -2, "none"); /* python.none */ + + return 1; } diff --git a/src/pythoninlua.h b/src/pythoninlua.h index 61171ca..dada102 100644 --- a/src/pythoninlua.h +++ b/src/pythoninlua.h @@ -25,14 +25,20 @@ #define POBJECT "POBJECT" -int py_convert(lua_State *L, PyObject *o, int withnone); +#if PY_MAJOR_VERSION < 3 + #define PyBytes_Check PyString_Check + #define PyBytes_AsStringAndSize PyString_AsStringAndSize +#endif + +int py_convert(lua_State *L, PyObject *o); -typedef struct { +typedef struct +{ PyObject *o; int asindx; } py_object; -py_object* luaPy_to_pobject(lua_State *L, int n); -LUA_API int luaopen_python(lua_State *L); +py_object* luaPy_to_pobject(lua_State *L, int n); +LUA_API int luaopen_python(lua_State *L); #endif diff --git a/test.py b/test.py deleted file mode 100644 index 354b010..0000000 --- a/test.py +++ /dev/null @@ -1,29 +0,0 @@ -print "----- import lua -----" -import lua -print "----- lg = lua.globals() -----" -lg = lua.globals() -print "lg:", lg -print "lg._G:", lg._G -print "lg['_G']:", lg['_G'] -print "----- lg.foo = \"bar\" -----" -lg.foo = 'bar' -print "----- lg.tmp = [] -----" -lg.tmp = [] -print "----- print lg.tmp -----" -print lg.tmp -print "----- lua.execute(\"xxx = {1,2,3,foo={4,5}}\") -----" -lua.execute("xxx = {1,2,3,foo={4,5}}") -print "----- print lg.xxx[1] -----" -print lg.xxx[1] -print "----- print lg.xxx[2] -----" -print lg.xxx[2] -print "----- print lg.xxx[3] -----" -print lg.xxx[3] -print "----- print lg.xxx['foo'][1] -----" -print lg.xxx['foo'][1] -print "lua.require =", lua.require -try: - lua.require("foo") -except: - print "lua.require('foo') raised an exception" - diff --git a/tests/test_lua.py b/tests/test_lua.py new file mode 100644 index 0000000..a8eb916 --- /dev/null +++ b/tests/test_lua.py @@ -0,0 +1,91 @@ +""" +>>> lg = lua.globals() +>>> lg == lg._G +True +>>> lg._G == lg._G +True +>>> lg._G == lg['_G'] +True + +>>> lg.foo = 'bar' +>>> lg.foo == u'bar' +True + +>>> lg.tmp = [] +>>> lg.tmp +[] + +>>> lua.execute("x = {1, 2, 3, foo = {4, 5}}") +>>> lg.x[1], lg.x[2], lg.x[3] +(1..., 2..., 3...) +>>> lg.x['foo'][1], lg.x['foo'][2] +(4..., 5...) + +>>> lua.require + + +>>> lg.string + +>>> lg.string.lower + +>>> lg.string.lower("Hello world!") == u'hello world!' +True + +>>> d = {} +>>> lg.d = d +>>> lua.execute("d['key'] = 'value'") +>>> d +{...'key': ...'value'} + +>>> d2 = lua.eval("d") +>>> d is d2 +True + +>>> lua.execute("python = require 'python'") +>>> lua.eval("python") + + +>>> obj + + +>>> lua.eval("python.eval 'obj'") + + +>>> lua.eval(\"\"\"python.eval([[lua.eval('python.eval("obj")')]])\"\"\") + + +>>> lua.execute("pg = python.globals()") +>>> lua.eval("pg.obj") + + +>>> def show(key, value): +... print("key is %s and value is %s" % (repr(key), repr(value))) +... +>>> asfunc = lua.eval("python.asfunc") +>>> asfunc + + +>>> l = ['a', 'b', 'c'] +>>> t = lua.eval("{a=1, b=2, c=3}") +>>> for k in l: +... show(k, t[k]) +key is 'a' and value is 1... +key is 'b' and value is 2... +key is 'c' and value is 3... + +""" + +import sys, os +sys.path.append(os.getcwd()) + + +class MyClass: + def __repr__(self): return '' + +obj = MyClass() + + +if __name__ == '__main__': + import lua + import doctest + doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/tests/test_py.lua b/tests/test_py.lua new file mode 100644 index 0000000..a5f4d28 --- /dev/null +++ b/tests/test_py.lua @@ -0,0 +1,42 @@ +python = require 'python' + +assert(nil == python.eval "None") +assert(true == python.eval "True") +assert(false == python.eval "False") + +assert(1 == python.eval "1") +assert(1.5 == python.eval "1.5") + +assert("foo" == python.eval "b'foo'") +assert("bar" == python.eval "u'bar'") + +pyglob = python.globals() +d = {} +pyglob.d = d +-- This crashes if you've compiled the python.so to include another +-- Lua interpreter, i.e., with -llua. +python.execute "d['key'] = 'value'" +assert(d.key == 'value', d.key) + +python.execute "d.key = 'newvalue'" +assert(d.key == 'newvalue', d.key) + +-- Test operators on py containers +l = python.eval "['hello']" +assert(tostring(l * 3) == "['hello', 'hello', 'hello']") +assert(tostring(l + python.eval "['bye']") == "['hello', 'bye']") + +-- Test that Python C module can access Py Runtime symbols +ctypes = python.import 'ctypes' +assert(tostring(ctypes):match "module 'ctypes'") + +-- Test multiple Python statement execution +pytype = python.eval "type" +python.execute +[[ +foo = 1 +bar = 2 +]] + +assert(python.globals().foo == 1) +assert(python.globals().bar == 2)