python-lua-1.0.orig/0000755000175000017500000000000015021656144013751 5ustar shevekshevekpython-lua-1.0.orig/.README.md.swp0000644000175000017500000004000015021544572016111 0ustar shevekshevekb0VIM 9.1(hB 4shevekhelios~shevek/src/python-lua/README.mdutf-8 U3210#"! Utp\H]2ad\uRQ@?5,!nmT z y K 2 1 < ] 3 2 n B .    O !   z2wm[PB43G7-- io: enable the io library. This library allows arbitrary file access on the host system and should only be enabled for trusted Lua code.- doloadfile: enable the dofile and loadfile functions. Allows running lua code from external files. Should not pose risks to the system, but does contain a privacy risk, because lua can find out what other lua files are available on the system. Users should also normally expect that an embedded language cannot access external files, so disabling it follows the Principle of Least Astonishment.- searchers: enable the default contents of package.searchers. This allows loading lua modules using require. When disabled, only standard modules and modules that have been explicitly provided by the host can be accessed.- loadlib: enable the loadlib function. Allows running code on host system and thus cause damage and violate privacy.- debug: enable the debugging library. Not actually unsafe (probably?), but should only be enabled explicitly.setting the corresponding constructor parameter to True. The features are:a risk for security or user privacy. Each of these features can be enabled byWhen creating an instance, the default is to disable all Lua features that are```# INCORRECT CODE!B.set('table_from_A', table)table = A.run('return {}')B = lua.Lua()A = lua.Lua()import lua# INCORRECT CODE!```Pythonexampe, this should not be done:Lua variables from different instances results in undefined behavior. Forenvironments, which do not share any state with each other. Note that combiningmultiple instances of this class allows you to have completely separateTo use the module, an instance of the Lua class must be created. Creating## Creating a Lua instance```import lua# Import Lua module.```Python## Module Loadingdescribed here, please check the source code.this may not be kept entirely up to date. If things don't seem to work asThe module API is described below. Note that due to limited time available,# API description```b'Binary''Unicode'Python function called with arg 'from Lua'.Lua function called with arg 'from Python'.```This generates the following output:```print(repr(code.run('return python.bytes("Binary")')))# A bytes object can be created with python.bytes.print(repr(code.run('return "Unicode"')))# Lua strings that are passed to Python must be UTF-8 encoded and are treated as str.print(type(code.run('return python.dict{foo = "bar"}'))) # Python dictprint(type(code.run('return python.list{1,2,3}'))) # Python listprint(type(code.run('return {1, 2, 3}'))) # Lua table (not a Python object)# Create Python compound objects from Lua.code.run('pyfun("from Lua")')# Run the Python function from Lua.luafun('from Python')# Run the Lua function from Python.code.set('pyfun', pyfun)# Make the Python function accessible to Lua.luafun = code.run('return luafun')# Make the Lua function accessible to Python (this could have been done in one step).code.run('''function luafun(arg) print("Lua function called with arg '" .. arg .. "'.") end''')# Define a Lua function. print("Python function called with arg '%s'." % arg)def pyfun(arg):# Define a Python function.code.run('python = require "python"') # If you want to use it.code = lua.Lua()import lua# Setup.```Python# Simple ExampleLua objects in the Python program.The interface allows passing Python objects into the Lua environment and usingThis module allows Lua code to be used in Python programs.add 8 P  x w  ```print('element 2 in the lua table is "two":', lua_table[2])code.run('''print('element 2 in the python list is "two": ' .. list[2])''', var = 'list', value = python_list)lua_table = code.run('return {"one", "two", "three"}')python_list = ['zero', 'one', 'two']```PythonHere's an example to show this:Python.When indexing a Python list from Lua, the first index is 0, just like inindexing a Lua table from Python code, the first index is 1, just like in Lua.this means is that you must be aware of which language owns the object. Whenother language can be confusing. The module does not account for this. Whatin Python (0) and that of a table in Lua (1), indexing such structures from theBecause there is a difference between the index of the first element of a list## First elements- `luatable.pop(index = -1)`: Removes the last index (or the given index) from the table and shifts the contents of the table to fill its place using `table.remove`. The index must be an integer. Negative indices count from the back of the list, with -1 being the last element.python-lua-1.0.orig/pyproject.toml0000644000175000017500000000012514252035332016656 0ustar shevekshevek[build-system] requires = ['setuptools>=42'] build-backend = 'setuptools.build_meta' python-lua-1.0.orig/Makefile0000664000175000017500000000014515012112430015374 0ustar shevekshevekall: python3 setup.py build test: cd build/lib* && python3 < ../../test.py clean: rm -rf build/ python-lua-1.0.orig/lua.c0000664000175000017500000006061215012431770014701 0ustar shevekshevek// lua.c - Use Lua in Python programs /* Copyright 2012-2023 Bas Wijnen {{{ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * }}} */ #include "module.h" struct OperatorMap operators[NUM_OPERATORS] = { // {{{ { "__add__", "__add", "+" }, { "__sub__", "__sub", "-" }, { "__mul__", "__mul", "*" }, { "__truediv__", "__div", "/" }, { "__mod__", "__mod", "%" }, { "__pow__", "__pow", "^" }, { "__floordiv__", "__idiv", "//" }, { "__and__", "__band", "&" }, { "__or__", "__bor", "|" }, { "__xor__", "__bxor", "~" }, { "__lshift__", "__shl", "<<" }, { "__rshift__", "__shr", ">>" }, { "__matmul__", "__concat", ".." }, { "__eq__", "__eq", "==" }, { "__lt__", "__lt", "<" }, { "__le__", "__le", "<=" }, { "__close__", "__close", NULL }, { "__neg__", "__unm", NULL }, { "__invert__", "__bnot", NULL }, { "__len__", "__len", NULL }, { "__getitem__", "__index", NULL }, { "__setitem__", "__newindex", NULL }, //{ "__call__", "__call", NULL }, { "__repr__", "__totring", NULL } }; // }}} // Lua callback for all userdata metamethods. (Method selection is done via operator name stored in upvalue.) // Userdata metamethods are called when Lua code uses an operator on a Python-owned object. static int metamethod(lua_State *state) { // {{{ // This function is called from Lua through a metatable. // Get self pointer. lua_getfield(state, LUA_REGISTRYINDEX, "self"); Lua *lua = (Lua *)lua_touserdata(state, -1); lua_pop(state, 1); // 1 upvalue: python method index. int method_index = lua_tointeger(state, lua_upvalueindex(1)); fprintf(stderr, "calling method %s\n", operators[method_index].python_name); PyObject *target = Lua_to_python(lua, 1); PyObject *method = PyMapping_GetItemString(target, operators[method_index].python_name); if (!method) { PyErr_Clear(); lua_pop(state, 1); return 0; } // Create arguments tuple int nargs = lua_gettop(state); PyObject *args = PyTuple_New(nargs - 1); for (int i = 2; i <= nargs; ++i) { PyObject *arg = Lua_to_python(lua, i); // New reference. // fill tuple PyTuple_SET_ITEM(args, i - 2, arg); // Steals reference to arg. } // Call function. PyObject *ret = PyObject_CallObject(method, args); Lua_push(lua, ret); // Unpack returned sequence? Py_DECREF(ret); Py_DECREF(args); return 1; } // }}} static int gc(lua_State *state) { // {{{ lua_getfield(state, LUA_REGISTRYINDEX, "self"); PyObject *lua = (PyObject *)lua_touserdata(state, -1); lua_pop(state, 1); // Drop (this reference to) the object. Py_DECREF(lua); return 0; } // }}} static lua_Integer build_wrapper(Lua *self, char const *code, char const *desc, bool make_ref) { // {{{ if (luaL_loadbuffer(self->state, code, strlen(code), desc ? desc : code) != LUA_OK) { lua_close(self->state); PyErr_SetString(PyExc_ValueError, lua_tostring(self->state, -1)); return LUA_NOREF; } lua_call(self->state, 0, 1); if (!make_ref) return LUA_REFNIL; return luaL_ref(self->state, LUA_REGISTRYINDEX); } // }}} // Create (native) Lua table on Lua stack from items in Python list or dict. static void push_luatable_dict(Lua *self, PyObject *obj) { // {{{ Py_ssize_t ppos = 0; PyObject *key; PyObject *value; while (PyDict_Next(obj, &ppos, &key, &value)) { // Key and value become borrowed references. Lua_push(self, key); Lua_push(self, value); lua_rawset(self->state, -3); // Pops key and value from the stack. } } // }}} static void push_luatable_list(Lua *self, PyObject *obj) { // {{{ Py_ssize_t size = PySequence_Size(obj); for (Py_ssize_t i = 0; i < size; ++i) { PyObject *value = PySequence_GetItem(obj, i); // New reference. Lua_push(self, value); Py_DECREF(value); lua_rawseti(self->state, -2, i + 1); } } // }}} static PyObject *construct_bytes_impl(PyObject * /*self*/, PyObject *args) // {{{ { // Implementation of python.bytes() PyObject *arg; if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (PyObject_IsInstance(arg, (PyObject*)table_type)) { PyObject *list = table_list_method((Table *)arg, NULL); return PyBytes_FromObject(list); } if (PyUnicode_Check(arg)) return PyUnicode_AsUTF8String(arg); return PyBytes_FromObject(arg); } static PyMethodDef construct_bytes_method = { "constuct_bytes", (PyCFunction)construct_bytes_impl, METH_VARARGS, "Convert argument to bytes" }; // }}} static int call_method(lua_State *state) // {{{ { lua_getfield(state, LUA_REGISTRYINDEX, "self"); Lua *lua = (Lua *)lua_touserdata(state, -1); lua_pop(state, 1); PyObject *target = Lua_to_python(lua, 1); if (!PyCallable_Check(target)) { fprintf(stderr, "Called object is not callable: "); PyObject_Print(target, stderr, 0); fprintf(stderr, "\n"); lua_settop(state, 0); return 0; } // Create arguments tuple int nargs = lua_gettop(state); PyObject *args = PyTuple_New(nargs - 1); for (int i = 2; i <= nargs; ++i) { PyObject *arg = Lua_to_python(lua, i); // New reference. // fill tuple PyTuple_SET_ITEM(args, i - 2, arg); // Steals reference to arg. } // Call function. PyObject *ret = PyObject_CallObject(target, args); Py_DECREF(args); if (ret == NULL) return 0; Lua_push(lua, ret); // Unpack returned sequence? Py_DECREF(ret); return 1; } // }}} // Python class Lua __new__ function. static PyObject *Lua_new(PyTypeObject *type, PyObject *args, PyObject *keywords) { // {{{ Lua *self = (Lua *)type->tp_alloc(type, 0); if (!self) return NULL; // Create a new lua object. // This object provides the interface into the lua library. // It also provides access to all the symbols that lua owns. // Parse arguments. {{{ bool debug = false; bool loadlib = false; bool searchers = false; bool doloadfile = false; bool io = false; bool os = false; bool python_module = true; char const *keywordnames[] = {"debug", "loadlib", "searchers", "doloadfile", "io", "os", "python_module", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywords, "|ppppppp", (char **)keywordnames, &debug, &loadlib, &searchers, &doloadfile, &io, &os, &python_module)) return NULL; // }}} // Create new state and store back pointer to self. {{{ self->state = luaL_newstate(); lua_pushlightuserdata(self->state, self); lua_setfield(self->state, LUA_REGISTRYINDEX, "self"); // }}} // Open standard libraries. Many of them are closed again below. luaL_openlibs(self->state); // Create metatable and store operators (because the API does not define them). {{{ lua_createtable(self->state, 0, NUM_OPERATORS + 1); for (int i = 0; i < NUM_OPERATORS; ++i) { // Create metatable for table wrappers. lua_pushinteger(self->state, i); lua_pushcclosure(self->state, metamethod, 1); lua_setfield(self->state, -2, operators[i].lua_name); // Store binary operators. (Others are stored below this loop.) if (!operators[i].lua_operator) continue; char const *template = "return function(a, b) return a %s b end"; char buffer[strlen(template) + 2]; sprintf(buffer, template, operators[i].lua_operator); lua_Integer function_id = build_wrapper(self, buffer, NULL, true); if (function_id == LUA_NOREF) return NULL; lua_pushinteger(self->state, function_id); self->lua_operator[i] = Function_create(self); } //Lua_dump_stack(self); // Unary operators. lua_Integer index = build_wrapper(self, "return function(a) return -a end", NULL, true); if (index == LUA_NOREF) return NULL; lua_pushinteger(self->state, index); self->lua_operator[NEG] = Function_create(self); index = build_wrapper(self, "return function(a) return ~a end", NULL, true); if (index == LUA_NOREF) return NULL; lua_pushinteger(self->state, index); self->lua_operator[NOT] = Function_create(self); index = build_wrapper(self, "return function(a) return tostring(a) end", NULL, true); if (index == LUA_NOREF) return NULL; lua_pushinteger(self->state, index); self->lua_operator[STR] = Function_create(self); // Skipped: len, getitem, setitem, delitem, because they have API calls which are used. // Set call metamethod. lua_pushcclosure(self->state, call_method, 0); lua_setfield(self->state, -2, "__call"); // Set gc metamethod. This is not passed through to Python, but instead cleans up the PyObject * reference. lua_pushcclosure(self->state, gc, 0); lua_setfield(self->state, -2, "__gc"); // Set the metatable in the registry, for use by new userdata objects. lua_setfield(self->state, LUA_REGISTRYINDEX, "metatable"); // }}} // Store a copy of some initial values, so they still work if the original value is replaced. self->table_remove = build_wrapper(self, "return table.remove", "get table.remove", true); if (self->table_remove == LUA_NOREF) return NULL; self->table_concat = build_wrapper(self, "return table.concat", "get table.concat", true); if (self->table_concat == LUA_NOREF) return NULL; self->table_insert = build_wrapper(self, "return table.insert", "get table.insert", true); if (self->table_insert == LUA_NOREF) return NULL; self->table_unpack = build_wrapper(self, "return table.unpack", "get table.unpack", true); if (self->table_unpack == LUA_NOREF) return NULL; self->table_move = build_wrapper(self, "return table.move", "get table.move", true); if (self->table_move == LUA_NOREF) return NULL; self->table_sort = build_wrapper(self, "return table.sort", "get table.sort", true); if (self->table_sort == LUA_NOREF) return NULL; self->package_loaded = build_wrapper(self, "return package.loaded", "get package.loaded", true); if (self->package_loaded == LUA_NOREF) return NULL; // Disable optional features that have not been requested. {{{ if (!debug) { if (build_wrapper(self, "debug = nil package.loaded.debug = nil", "disabling debug", false) == LUA_NOREF) return NULL; } if (!loadlib) { if (build_wrapper(self, "package.loadlib = nil", "disabling loadlib", false) == LUA_NOREF) return NULL; } if (!searchers) { if (build_wrapper(self, "package.searchers = {}", "disabling searchers", false) == LUA_NOREF) return NULL; } if (!doloadfile) { if (build_wrapper(self, "loadfile = nil dofile = nil", "disabling loadfile and dofile", false) == LUA_NOREF) return NULL; } if (!os) { if (build_wrapper(self, "os = {clock = os.clock, date = os.date, difftime = os.difftime, setlocale = os.setlocale, time = os.time} package.loaded.os = os", "disabling some of os", false) == LUA_NOREF) return NULL; } if (!io) { if (build_wrapper(self, "io = nil package.loaded.io = nil", "disabling io", false) == LUA_NOREF) return NULL; } // }}} /* Add access to Python object constructors from Lua (unless disabled). TODO when Table interface has been implemented.*/ if (python_module) { // Lua module for accessing some Python parts from Lua. This is prepared as a "python" module unless disabled. {{{ PyObject *table_list = PyCMethod_New(&Table_methods[0], NULL, NULL, NULL); PyObject *table_dict = PyCMethod_New(&Table_methods[1], NULL, NULL, NULL); PyObject *construct_bytes = PyCMethod_New(&construct_bytes_method, NULL, NULL, NULL); lua_load_module(self, "python", Py_BuildValue("{sO sO sO}", "list", table_list, "dict", table_dict, "bytes", construct_bytes)); // }}} } return (PyObject *)self; } // }}} // Destructor. static void Lua_dealloc(Lua *self) { // {{{ lua_close(self->state); Lua_type->tp_free((PyObject *)self); } // }}} // set variable in Lua. static void set(Lua *self, char const *name, PyObject *value) { // {{{ lua_rawgeti(self->state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); Lua_push(self, value); lua_setfield(self->state, -2, name); lua_settop(self->state, -2); } // }}} static PyObject *return_stack(Lua *self, int pos, bool keep_single) { // {{{ int size = lua_gettop(self->state) - pos; PyObject *ret; if (keep_single || size > 1) { ret = PyTuple_New(size); if (ret == NULL) return NULL; for (int i = 0; i < size; ++i) PyTuple_SET_ITEM(ret, i, Lua_to_python(self, -size + i)); lua_settop(self->state, pos); return ret; } else if (size == 1) { ret = Lua_to_python(self, -1); lua_settop(self->state, pos); return ret; } else { lua_settop(self->state, pos); Py_RETURN_NONE; } } // }}} // run code after having loaded the buffer (internal use only). static PyObject *run_code(Lua *self, int pos, bool keep_single) { // {{{ lua_call(self->state, 0, LUA_MULTRET); if (PyErr_Occurred()) { lua_settop(self->state, 0); return NULL; } return return_stack(self, pos, keep_single); } // }}} // run string in lua. static PyObject *run(Lua *self, char const *cmd, char const *description, bool keep_single) { // {{{ int pos = lua_gettop(self->state); if (luaL_loadbuffer(self->state, cmd, strlen(cmd), description) != LUA_OK) { PyErr_SetString(PyExc_ValueError, lua_tostring(self->state, -1)); return NULL; } return run_code(self, pos, keep_single); } // }}} // run file in lua. static PyObject *do_run_file(Lua *self, char const *filename, bool keep_single) { // {{{ int pos = lua_gettop(self->state); if (luaL_loadfilex(self->state, filename, NULL) != LUA_OK) { PyErr_SetString(PyExc_ValueError, lua_tostring(self->state, -1)); return NULL; } return run_code(self, pos, keep_single); } // }}} // load module into lua. void lua_load_module(Lua *self, char const *name, PyObject *dict) { // {{{ if (!PyDict_Check(dict)) { PyObject *new_dict = PyDict_New(); // new reference PyObject *dir = PyObject_Dir(dict); // new reference Py_ssize_t size = PyList_Size(dir); for (Py_ssize_t i = 0; i < size; ++i) { PyObject *key = PyList_GetItem(dir, i); // borrowed reference Py_ssize_t len; char const *byteskey = PyUnicode_AsUTF8AndSize(key, &len); if (len > 0 && byteskey[0] == '_') { if (strncmp(byteskey, "_lua_", 5) != 0) continue; byteskey = &byteskey[4]; len -= 4; } PyObject *value = PyObject_GetAttr(dict, key); // new reference PyDict_SetItemString(new_dict, byteskey, value); // no stealing Py_DECREF(value); } Py_DECREF(dir); Py_DECREF(dict); dict = new_dict; } lua_geti(self->state, LUA_REGISTRYINDEX, self->package_loaded); lua_createtable(self->state, 0, PyDict_Size(dict)); // Pushes new table on the stack. push_luatable_dict(self, dict); // Fills it with contents. lua_setfield(self->state, -2, name); lua_settop(self->state, -2); } // }}} // load variable from lua stack into python. PyObject *Lua_to_python(Lua *self, int index) { // {{{ int type = lua_type(self->state, index); switch(type) { case LUA_TNIL: Py_RETURN_NONE; case LUA_TBOOLEAN: return PyBool_FromLong(lua_toboolean(self->state, index)); //case LUA_TLIGHTUSERDATA: // Not used. // return lua_touserdata(self->state, index) case LUA_TNUMBER: { // If this is exactly an integer, return it as an integer. lua_Integer iret = lua_tointeger(self->state, index); lua_Number ret = lua_tonumber(self->state, index); if ((lua_Number)iret == ret) return PyLong_FromLongLong(iret); return PyFloat_FromDouble(ret); } case LUA_TSTRING: { size_t len; char const *str = lua_tolstring(self->state, index, &len); return PyUnicode_DecodeUTF8(str, len, NULL); } case LUA_TTABLE: lua_pushvalue(self->state, index); return Table_create(self); case LUA_TFUNCTION: lua_pushvalue(self->state, index); return Function_create(self); case LUA_TUSERDATA: // This is a Python-owned object that was used by Lua. // The data block of the userdata stores the PyObject *. return *(PyObject **)lua_touserdata(self->state, index); //case LUA_TTHREAD: // Not used. // return lua_tothread(self->state, index) default: //if (lua_iscfunction(self->state, index)) { // return lua_tocfunction(self->state, index) //} return PyErr_Format(PyExc_ValueError, "Invalid type %x passed to Lua_to_python", type); } } // }}} // load variable from python onto lua stack. void Lua_push(Lua *self, PyObject *obj) { // {{{ if (obj == Py_None) lua_pushnil(self->state); else if (PyBool_Check(obj)) lua_pushboolean(self->state, obj == Py_True); else if (PyLong_Check(obj)) lua_pushinteger(self->state, PyLong_AsLongLong(obj)); else if (PyUnicode_Check(obj)) { // A str is encoded as bytes in Lua; bytes is wrapped as an object. Py_ssize_t len; char const *str = PyUnicode_AsUTF8AndSize(obj, &len); lua_pushlstring(self->state, str, len); } else if (PyFloat_Check(obj)) lua_pushnumber(self->state, PyFloat_AsDouble(obj)); else if (PyObject_TypeCheck(obj, table_type)) lua_rawgeti(self->state, LUA_REGISTRYINDEX, ((Table *)obj)->id); else if (PyObject_TypeCheck(obj, function_type)) lua_rawgeti(self->state, LUA_REGISTRYINDEX, ((Function *)obj)->id); else { *(PyObject **)lua_newuserdatauv(self->state, sizeof(PyObject *), 0) = obj; Py_INCREF(obj); // FIXME: use gc metamethod to DECREF the object. lua_getfield(self->state, LUA_REGISTRYINDEX, "metatable"); lua_setmetatable(self->state, -2); } } // }}} // Type definition. {{{ // Python-accessible methods. {{{ static PyObject *Lua_set(Lua *self, PyObject *args) { // {{{ char const *name; PyObject *value; if (!PyArg_ParseTuple(args, "sO", &name, &value)) return NULL; set(self, name, value); Py_RETURN_NONE; } // }}} static PyObject *Lua_run(Lua *self, PyObject *args, PyObject *keywords) { // {{{ char const *code; char const *description = NULL; bool keep_single = false; char const *var = NULL; PyObject *value = NULL; char const *keywordnames[] = {"code", "description", "var", "value", "keep_single", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywords, "s|ssOp", (char **)keywordnames, &code, &description, &var, &value, &keep_single)) return NULL; if (!description) description = code; if (var || value) { if (!var || !value) { PyErr_SetString(PyExc_ValueError, "var and value must both be present, or both be missing"); return NULL; } set(self, var, value); } return run(self, code, description, keep_single); } // }}} static PyObject *Lua_run_file(Lua *self, PyObject *args, PyObject *keywords) { // {{{ char const *filename; bool keep_single = false; char const *var = NULL; PyObject *value = NULL; char const *keywordnames[] = {"filename", "keep_single", "var", "value", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywords, "s|sOp", (char **)keywordnames, &filename, &var, &value, &keep_single)) return NULL; if (var) set(self, var, value); return do_run_file(self, filename, keep_single); } // }}} static PyObject *Lua_module(Lua *self, PyObject *args) { // {{{ char const *name; PyObject *dict; if (!PyArg_ParseTuple(args, "sO", &name, &dict)) return NULL; lua_load_module(self, name, dict); Py_RETURN_NONE; } // }}} static PyObject *Lua_table(Lua *self, PyObject *args, PyObject *kwargs) { // {{{ int pos = lua_gettop(self->state); size_t kw_size = kwargs == NULL ? 0 : PyDict_Size(kwargs); lua_createtable(self->state, PySequence_Size(args), kw_size); // Pushes new table on the stack. push_luatable_list(self, args); if (kwargs != NULL) push_luatable_dict(self, kwargs); return return_stack(self, pos, false); } // }}} // }}} // }}} void Lua_dump_stack(Lua *self) { // {{{ int n = lua_gettop(self->state); fprintf(stderr, "***** Lua stack dump *****\n"); for (int i = 1; i <= n; ++i) { fprintf(stderr, "%d\t", i); PyObject *value = Lua_to_python(self, i); PyObject_Print(value, stderr, 0); Py_DECREF(value); fprintf(stderr, "\n"); } fprintf(stderr, "**************************\n"); } // }}} // Module registration. PyMODINIT_FUNC PyInit_lua() { // {{{ static PyModuleDef Module = { // {{{ PyModuleDef_HEAD_INIT, .m_name = "lua", .m_doc = PyDoc_STR("Use Lua code and object from Python and vice versa."), .m_size = 0, // There is no module state. .m_methods = NULL, // There are no functions. .m_slots = NULL, // This must be NULL for PyModule_Create() .m_traverse = NULL, .m_clear = NULL, .m_free = NULL, }; // }}} static PyMethodDef Lua_methods[] = { // {{{ {"set", (PyCFunction)Lua_set, METH_VARARGS, "Set a variable"}, {"run", (PyCFunction)Lua_run, METH_VARARGS | METH_KEYWORDS, "Run a Lua script"}, {"run_file", (PyCFunction)Lua_run_file, METH_VARARGS | METH_KEYWORDS, "Run a Lua script from a file"}, {"module", (PyCFunction)Lua_module, METH_VARARGS, "Import a module into Lua"}, {"table", (PyCFunction)Lua_table, METH_VARARGS | METH_KEYWORDS, "Create a Lua Table"}, {NULL, NULL, 0, NULL} }; // }}} static PyType_Slot lua_slots[] = { // {{{ { Py_tp_new, Lua_new }, { Py_tp_dealloc, Lua_dealloc }, { Py_tp_doc, "Hold Lua object state" }, { Py_tp_methods, Lua_methods }, { 0, NULL }, }; // }}} static PyType_Spec *LuaType; // {{{ LuaType = malloc(sizeof(*LuaType)); memset(LuaType, 0, sizeof(*LuaType)); LuaType->name = "lua.Lua"; LuaType->basicsize = sizeof(Lua); LuaType->itemsize = 0; LuaType->flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; LuaType->slots = lua_slots; // }}} static PyType_Slot function_slots[] = { // {{{ //{ Py_tp_new, NULL }, { Py_tp_dealloc, Function_dealloc }, { Py_tp_doc, "Access a Lua-owned function from Python" }, { Py_tp_call, Function_call }, { Py_tp_repr, Function_repr }, { 0, NULL }, }; // }}} static PyType_Spec *FunctionType; // {{{ FunctionType = malloc(sizeof(*FunctionType)); memset(FunctionType, 0, sizeof(*FunctionType)); FunctionType->name = "lua.Function"; FunctionType->basicsize = sizeof(Function); FunctionType->itemsize = 0; FunctionType->flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; FunctionType->slots = function_slots; // }}} static PyType_Slot table_slots[] = { // {{{ { Py_tp_dealloc, Table_dealloc }, { Py_tp_doc, "Access a Lua-owned table from Python" }, { Py_tp_repr, Table_repr }, { Py_tp_str, Table_repr }, { Py_mp_length, Table_len }, { Py_mp_subscript, Table_getitem }, { Py_mp_ass_subscript, Table_setitem }, { Py_tp_methods, Table_methods }, { 0, NULL }, }; // }}} static PyType_Spec *TableType; // {{{ TableType = malloc(sizeof(*TableType)); memset(TableType, 0, sizeof(*TableType)); TableType->name = "lua.Table"; TableType->basicsize = sizeof(Table); TableType->itemsize = 0; TableType->flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; TableType->slots = table_slots; // }}} static PyType_Slot table_iter_slots[] = { // {{{ { Py_tp_dealloc, Table_iter_dealloc }, { Py_tp_doc, "iterator for lua.table" }, { Py_tp_repr, Table_iter_repr }, { Py_tp_str, Table_iter_repr }, { Py_tp_iter, Table_iter_iter }, { Py_tp_iternext, Table_iter_iternext }, { 0, NULL }, }; // }}} static PyType_Spec *TableIterType; // {{{ TableIterType = malloc(sizeof(*TableIterType)); memset(TableIterType, 0, sizeof(*TableIterType)); TableIterType->name = "lua.Table.iterator"; TableIterType->basicsize = sizeof(TableIter); TableIterType->itemsize = 0; TableIterType->flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; TableIterType->slots = table_iter_slots; // }}} //fprintf(stderr, "creating module.\n"); PyObject *m = PyModule_Create(&Module); if (!m) return NULL; //fprintf(stderr, "creating type lua.\n"); Lua_type = (PyTypeObject *)PyType_FromModuleAndSpec(m, LuaType, NULL); if (!Lua_type || PyModule_AddObject(m, "Lua", (PyObject *)Lua_type) < 0) { Py_DECREF(m); return NULL; } Py_INCREF(Lua_type); //fprintf(stderr, "creating type function.\n"); function_type = (PyTypeObject *)PyType_FromModuleAndSpec(m, FunctionType, NULL); if (!function_type || PyModule_AddObject(m, "Function", (PyObject *)function_type) < 0) { Py_DECREF(Lua_type); Py_DECREF(m); return NULL; } Py_INCREF(function_type); //fprintf(stderr, "creating type table.\n"); table_type = (PyTypeObject *)PyType_FromModuleAndSpec(m, TableType, NULL); if (!table_type || PyModule_AddObject(m, "Table", (PyObject *)table_type) < 0) { Py_DECREF(Lua_type); Py_DECREF(function_type); Py_DECREF(m); return NULL; } Py_INCREF(table_type); //fprintf(stderr, "creating type table_iter.\n"); table_iter_type = (PyTypeObject *)PyType_FromModuleAndSpec(m, TableIterType, NULL); if (!table_iter_type || PyModule_AddObject(m, "TableIter", (PyObject *)table_iter_type) < 0) { Py_DECREF(Lua_type); Py_DECREF(function_type); Py_DECREF(table_type); Py_DECREF(m); return NULL; } Py_INCREF(table_iter_type); //fprintf(stderr, "done.\n"); return m; } // }}} PyTypeObject *Lua_type; // vim: set foldmethod=marker : python-lua-1.0.orig/README.md0000664000175000017500000002765315012175032015237 0ustar shevekshevekThis module allows Lua code to be used in Python programs. The interface allows passing Python objects into the Lua environment and using Lua objects in the Python program. # Simple Example ```Python # Setup. import lua code = lua.Lua() code.run('python = require("python")') # If you want to use it. # Define a Python function. def pyfun(arg): print("Python function called with arg '%s'." % arg) # Define a Lua function. code.run('''function luafun(arg) print("Lua function called with arg '" .. arg .. "'.") end''') # Make the Lua function accessible to Python (this could have been done in one step). luafun = code.run('return luafun') # Make the Python function accessible to Lua. code.set('pyfun', pyfun) # Run the Lua function from Python. luafun('from Python') # Run the Python function from Lua. code.run('pyfun("from Lua")') # Create Python compound objects from Lua. print(type(code.run('return {1, 2, 3}'))) # Lua table (not a Python object) print(type(code.run('return python.list{1,2,3}'))) # Python list print(type(code.run('return python.dict{foo = "bar"}'))) # Python dict # Lua strings that are passed to Python must be UTF-8 encoded and are treated as str. print(repr(code.run('return "Unicode"'))) # A bytes object can be created with python.bytes. print(repr(code.run('return python.bytes("Binary")'))) ``` This generates the following output: ``` Lua function called with arg 'from Python'. Python function called with arg 'from Lua'. 'Unicode' b'Binary' ``` # API description The module API is described below. Note that due to limited time available, this may not be kept entirely up to date. If things don't seem to work as described here, please check the source code. ## Module Loading ```Python # Import Lua module. import lua ``` ## Creating a Lua instance To use the module, an instance of the Lua class must be created. Creating multiple instances of this class allows you to have completely separate environments, which do not share any state with each other. Note that combining Lua variables from different instances results in undefined behavior. For exampe, this should not be done: ```Python # INCORRECT CODE! import lua A = lua.Lua() B = lua.Lua() table = A.run('return {}') B.set('table_from_A', table) # INCORRECT CODE! ``` When creating an instance, the default is to disable all Lua features that are a risk for security or user privacy. Each of these features can be enabled by setting the corresponding constructor parameter to True. The features are: - debug: enable the debugging library. Not actually unsafe (probably?), but should only be enabled explicitly. - loadlib: enable the loadlib function. Allows running code on host system and thus cause damage and violate privacy. - searchers: enable the default contents of package.searchers. This allows loading lua modules using require. When disabled, only standard modules and modules that have been explicitly provided by the host can be accessed. - doloadfile: enable the dofile and loadfile functions. Allows running lua code from external files. Should not pose risks to the system, but does contain a privacy risk, because lua can find out what other lua files are available on the system. Users should also normally expect that an embedded language cannot access external files, so disabling it follows the Principle of Least Astonishment. - io: enable the io library. This library allows arbitrary file access on the host system and should only be enabled for trusted Lua code. - os: enable all of the os module. Even when not enabled, `clock`, `date`, `difftime`, `setlocale` and `time` are still available. The rest of os allows running executables on the host system. This should only be enabled for trusted Lua code. Additionally, the 'python' module, which contains list and dict constructors, can be disabled by setting `python_module` to False. It is enabled by default. Note that this setting does not create the `python` variable; it only allows lua code to `require` it. An example instance that allows access of the host filesystem through `io` is: ```Python code = lua.Lua(io = True) ``` ## Setting up the Lua environment Lua code will usually require access to some variables or functions from Python. There are two methods for granting this access: setting variables, and providing modules. ### Setting variables To set a Lua variable to a Python value, the `set` method is used. It takes two arguments: a string which is the name of the (possibly new) Lua variable, and its value. For convenience, if `run` is passed keyword arguments `var` and `value`, it calls `set` with those parameters before running the Lua code. If the variable is mutable (for example, it is a `list`), then changing the contents of the value will result in a changed value in Python. In other words, mutable variables are passed by reference and remain owned by Python. ```Python my_list = [1, 2, 3] code.run('foo[2] = "bar"', var = 'foo', value = my_list) # my_list is now [1, 'bar', 3]. Note that Lua-indexing is 1-based. ``` ### Providing modules The more common way to provide access to host functionality is through a module that can be loaded using the `require` function in Lua. This is done by calling the `module` function. The argument is a dict, or a Python module. The contents of the argument are provided to Lua. Note that the object itself is not, so if new items are added to the dict, these changes will not show up in Lua. Changes to the _values_ of the items will show up, however. ```Python import my_custom_module code.module('my_module', my_custom_module) code.run('mod = require "my_module"; mod.custom_function()') ``` ### The builtin `python` module Unless `python_module` is set to `False` in the Lua constructor, the `python` module is accessible to Lua code. It contains three functions: 1. `list(lua_table)` converts a lua table to a Python list, as when calling `lua_table.list()` from Python. 1. `dict(lua_table)` converts a lua table to a Python dict, as when calling `lua_table.dict()` from Python. 1. `bytes(obj)` converts an object to a Python bytes object. #### The `python.bytes` function This function converts its argument to a Python `bytes` object. There are several cases: - If the argument is a Lua Table, it converts it to a Python list using its `list` method. This is then passed to Python's `bytes` constructor. It should therefore contain only integers in the range from 0 to 255. - If the argument is a Lua string, it encodes it as UTF-8. - Otherwise, the object is passed to Python's `bytes` constructor. ## Running Lua code There are two ways to run Lua code. Using the `run()` function demonstrated in the previous section, and using the `run_file()` function. The `run_file()` function works very similar to `run()`. There are two differences: 1. The parameter of `run_file` is a file name, for `run()` it is Lua code. 1. `run()` allows setting a variable to a Python value. `run_file()` does not. The intent of both functions is slightly different: `run()` is meant to be used mostly for situations where `eval` might have been used if it was Python code; `run_file()` is meant to be used when `exec` is more appropriate. In other words, `run()` would be used for a short computation, while `run_file()` would be more likely to be used for running a potentially large chunk of code. Because of this, it is convenient for `run()` to pass a Python value, or perhaps a list or dict, to be used directly, while `run_file()` will normally need much more, and the `module()` function will be used to provide it in a more elegant way. However, while this is the consideration behind the functions, there is nothing to enforce it. If you want to run large amounts of code with `run()`, or a single line computation with `run_file()`, it will work without any problems. The `run` method accept a `description` argument, which must be a string. It is used in error messages. If not provided, the lua code is used as a description. ### Return values Both `run()` and `run_file()` can return a value. This is the primary method for accessing Lua values from Python. (The other option is to provide a container from Python and injecting a value from Lua.) Because Lua functions can return multiple values, this leads to a usability issue: the return value could always be a sequence, but that is inconvenient for the usual case when zero or one values are returned. This is solved by converting the result in those cases: - When 0 values are returned, the functions return None instead. - When 1 value is returned, the functions return that value. - When 2 or more values are returned, the functions return a list of return values. This means that Python cannot see the difference between returning 0 values, and returning 1 value which is `nil`, or between multiple values and a single list. Normally this will not be a problem, but if this is important to the Python program, it can pass the parameter `keep_single = True`. In that case the returned value is always a list of values, even if it has 0 or 1 elements. ## Operators Most Python operators have obvious behavior when applied to Lua objects and vice versa. For example, when using `*` the objects will be multiplied. However, there are some exceptions. 1. In Lua there is a difference between adding numbers (which is done with `+`) and strings (which is done with `..`). Unfortunately there is no appropriate operator available in Python, so `..` has been mapped to `@`, the matrix multiplication operator. This means that running `lua_table @ python_object` from Python will call the `__concat` method in `lua_table`'s metatable, and that running `python_object .. lua_object` from Lua will call the `__matmul__` method on `python_object`. Note that this does not apply to simple objects like numbers and strings, because these are converted into native objects on both sides (so a Lua string that is passed to Python becomes a Python `str` object, and using the @ operator on that does not involve Lua). 1. Lua uses the `__close` metatable method for to-be-closed variables. Python does not have this concept. The module will call the `__close__` method (which despite its name, does not have a special meaning in Python) on the Python object when the underlying Lua object is closed by Lua. ## Using Lua tables from Python code In addition to all the obvious operators (including `@` as concat) working normally on Lua tables, there are a few extra members defined on them. Note that attributes cannot be used to access table contents, so `luatable.foo` does not work, but `luatable['foo']` does. - `luatable.dict()`: Converts the table to a Python dict, containing all items. - `luatable.list()`: Converts the table to a Python list. Only the values with integer keys are inserted, and the list stops at the first missing element. In other words, this is only useful for lists that don't contain nil "values". Also note that the indexing changes: luatable[1] is the first element, and it is the same as luatable.list()[0]. - `luatable.pop(index = -1)`: Removes the last index (or the given index) from the table and shifts the contents of the table to fill its place using `table.remove`. The index must be an integer. Negative indices count from the back of the list, with -1 being the last element. ## First elements Because there is a difference between the index of the first element of a list in Python (0) and that of a table in Lua (1), indexing such structures from the other language can be confusing. The module does not account for this. What this means is that you must be aware of which language owns the object. When indexing a Lua table from Python code, the first index is 1, just like in Lua. When indexing a Python list from Lua, the first index is 0, just like in Python. Here's an example to show this: ```Python python_list = ['zero', 'one', 'two'] lua_table = code.run('return {"one", "two", "three"}') code.run('''print('element 2 in the python list is "two": ' .. list[2])''', var = 'list', value = python_list) print('element 2 in the lua table is "two":', lua_table[2]) ``` python-lua-1.0.orig/function.c0000664000175000017500000000670315012112347015742 0ustar shevekshevek// lua.py - Use Lua in Python programs /* Copyright 2012-2023 Bas Wijnen {{{ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * }}} */ #include "module.h" // Create new Function from value at top of stack. PyObject *Function_create(Lua *context) { // {{{ Function *self = (Function *)(function_type->tp_alloc(function_type, 0)); if (!self) return NULL; self->lua = (Lua *)context; Py_INCREF((PyObject *)(self->lua)); // Wrap function at top of lua stack, which must have been pushed before calling this. self->id = luaL_ref(self->lua->state, LUA_REGISTRYINDEX); return (PyObject *)self; }; // }}} // Destructor. void Function_dealloc(Function *self) { // {{{ luaL_unref(self->lua->state, LUA_REGISTRYINDEX, self->id); Py_DECREF(self->lua); function_type->tp_free((PyObject *)self); } // }}} PyObject *Function_call(Function *self, PyObject *args, PyObject *keywords) { // {{{ // Parse keep_single argument. {{{ bool keep_single = false; if (keywords) { PyObject *kw = PyDict_GetItemString(keywords, "keep_single"); // Returns borrowed reference. Py_ssize_t num_keywords = PyDict_Size(keywords); if (kw) { if (!PyBool_Check(kw)) { PyErr_SetString(PyExc_ValueError, "keep_single argument must be of bool type"); return NULL; } keep_single = kw == Py_True; num_keywords -= 1; } if (num_keywords > 0) { PyErr_SetString(PyExc_ValueError, "only keep_single is supported as a keyword argument"); return NULL; } } // }}} int pos = lua_gettop(self->lua->state); // Push target function to stack. lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); // Push arguments to stack. {{{ assert(PyTuple_Check(args)); Py_ssize_t nargs = PyTuple_Size(args); for (Py_ssize_t a = 0; a < nargs; ++a) { PyObject *arg = PyTuple_GetItem(args, a); // Borrowed reference. Lua_push(self->lua, arg); } // }}} // Call function. lua_call(self->lua->state, nargs, LUA_MULTRET); // Build return tuple. {{{ Py_ssize_t size = lua_gettop(self->lua->state) - pos; if (!keep_single && size < 2) { if (size == 0) Py_RETURN_NONE; // Size is 1. return Lua_to_python(self->lua, -1); } PyObject *ret = PyTuple_New(size); for (int i = 0; i < size; ++i) { PyObject *value = Lua_to_python(self->lua, -size + i); // New reference. PyTuple_SET_ITEM(ret, i, value); // Steal reference. } // }}} lua_settop(self->lua->state, pos); return ret; } // }}} PyObject *Function_repr(PyObject *self) { // {{{ char str[100]; snprintf(str, sizeof(str), "", self); return PyUnicode_DecodeUTF8(str, strlen(str), NULL); } // }}} // }}} // Python-accessible methods. //PyMethodDef function_methods[] = { // {{{ // {"__call__", (PyCFunction)Function_call, METH_VARARGS | METH_KEYWORDS, "Call the Lua function"}, // {NULL, NULL, 0, NULL} //}; // }}} PyTypeObject *function_type; // vim: set foldmethod=marker : python-lua-1.0.orig/module.h0000664000175000017500000001431415012435636015415 0ustar shevekshevek// lua.h - Use Lua in Python programs, (internal) header file. /* Copyright 2012-2023 Bas Wijnen {{{ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * }}} */ /* Module documentation {{{ This module provides an interface between Python and Lua. Objects of the Lua class contain the state of a Lua environment. Lua instances do not share variables. Interactions of Lua objects from different environments may not work; Lua's documentation isn't clear on that. By default, the Lua constructor disables all potentially insecure features. To enable them, set the corresponding argument to True. The features are: debug: debug library Not unsafe, but this should be disabled for production code, so it should only be enabled explicitly. Jailbreak: no. System damage: no. Privacy issue: no. loadlib: package.loadlib function It can load shared libraries from the system. Jailbreak: yes. System damage: yes. Privacy issue: yes. doloadfile: dofile and loadfile functions They access files on the file system. Jailbreak: no. System damage: no. Privacy issue: yes. (Very limited; only lua source can be run.) io: file read and write module The module accesses files on the file system. Jailbreak: no. System damage: yes. Privacy issue: yes. os: the os module, except for os.clock, os.date, os.difftime, os.setlocale and os.time It allows access to the os. Jailbreak: yes. System damage: yes. Privacy issue: yes. lua = Lua() After creating a Lua instance, it can be used to run a script either from a string, or from a file. The script may be lua source, or compiled lua code. Lua().run(source) Lua().run_file(filename) A variable in the Lua environment can be given a value using: Lua().run(var = 'name', value = 'value') When using run() to both set a variable and run code, the variable is set before running the code. While it is possible to access external code from Lua by setting a variable to a function, the normal way to do it is through a module which is loaded with a require statement. For this to work, the module must first be made available to Lua. This is done using: lua.module(name, object) }}} */ // Includes. {{{ #define PY_SSIZE_T_CLEAN #include #include #include #include #include // }}} typedef int bool; #define true 1 #define false 0 enum Operator { // {{{ // Operators using the default metamethod. ADD, SUB, MUL, DIV, MOD, POW, IDIV, AND, OR, XOR, LSHIFT, RSHIFT, CONCAT, EQ, LT, LE, // Treated as a normal metamethod, but not special for Python. CLOSE, #define LAST_DEFAULT_METAMETHOD_OPERATOR CLOSE // Operators with a custom metamethod NEG, NOT, LEN, GETITEM, SETITEM, // Not a typical operator, but useful to define as such. STR, NUM_OPERATORS }; // }}} struct OperatorMap { // {{{ char const *python_name; char const *lua_name; char const *lua_operator; }; // }}} extern struct OperatorMap operators[NUM_OPERATORS]; extern PyTypeObject *Lua_type; extern PyTypeObject *function_type; extern PyTypeObject *table_type; extern PyTypeObject *table_iter_type; typedef struct Lua { // {{{ PyObject_HEAD // Context for Lua environment. lua_State *state; // Copies of initial values of some globals, to use later regardless of them changing in Lua. lua_Integer table_remove; lua_Integer table_concat; lua_Integer table_insert; lua_Integer table_unpack; lua_Integer table_move; lua_Integer table_sort; lua_Integer package_loaded; // Stored Lua functions of all operators, to be used from Python calls on Lua-owned objects. // Values are Function objects. PyObject *lua_operator[NUM_OPERATORS]; } Lua; // }}} #ifdef __cplusplus extern "C" { #endif void lua_load_module(Lua *self, const char *name, PyObject *dict); #ifdef __cplusplus } #endif // Load variable from Lua stack into Python. PyObject *Lua_to_python(Lua *self, int index); // Load variable from Python onto Lua stack. void Lua_push(Lua *self, PyObject *obj); void Lua_dump_stack(Lua *self); typedef struct Function { // {{{ PyObject_HEAD // Context in which this object is defined. Lua *lua; // Registry index holding the Lua function. lua_Integer id; } Function; // }}} // Construct new function from value at top of stack. PyObject *Function_create(Lua *context); void Function_dealloc(Function *self); PyObject *Function_call(Function *self, PyObject *args, PyObject *keywords); PyObject *Function_repr(PyObject *self); typedef struct Table { // {{{ PyObject_HEAD // Registry index holding the Lua table for this object. lua_Integer id; // Context in which this object is defined. Lua *lua; } Table; // }}} // Construct new table from value at top of stack. PyObject *Table_create(Lua *context); void Table_dealloc(Table *self); PyObject *Table_repr(PyObject *self); Py_ssize_t Table_len(Table *self); PyObject *Table_getitem(Table *self, PyObject *key); int Table_setitem(Table *self, PyObject *key, PyObject *value); PyObject *table_list_method(Table *self, PyObject *args); extern PyMethodDef Table_methods[]; // Class to iterate over table elements. typedef struct TableIter { // {{{ PyObject_HEAD // Object that we are iterating over, or NULL. PyObject *target; // Iteration type: pairs or ipairs. bool is_ipairs; // Current key for pairs iteration. PyObject *current; // Current key for ipairs iteration. int icurrent; } TableIter; // }}} PyObject *Table_iter_create(PyObject *target, bool is_ipairs); void Table_iter_dealloc(TableIter *self); PyObject *Table_iter_repr(PyObject *self); PyObject *Table_iter_iter(PyObject *self); PyObject *Table_iter_iternext(TableIter *self); // vim: set foldmethod=marker : python-lua-1.0.orig/table.c0000664000175000017500000004317415013166506015216 0ustar shevekshevek// lua.py - Use Lua in Python programs /* Copyright 2012-2023 Bas Wijnen {{{ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * }}} */ #include "module.h" PyObject *Table_create(Lua *context) { // {{{ Table *self = (Table *)(table_type->tp_alloc(table_type, 0)); if (!self) return NULL; self->lua = context; Py_INCREF(self->lua); self->id = luaL_ref(self->lua->state, LUA_REGISTRYINDEX); //fprintf(stderr, "alloc table %p\n", self); return (PyObject *)self; }; // }}} // Destructor. void Table_dealloc(Table *self) { // {{{ //fprintf(stderr, "dealloc %p refcnt %ld\n", self, Py_REFCNT((PyObject *)self)); luaL_unref(self->lua->state, LUA_REGISTRYINDEX, self->id); Py_DECREF(self->lua); table_type->tp_free((PyObject *)self); } // }}} // Methods. static PyObject *iadd_method(Table *self, PyObject *args) { // {{{ PyObject *iterator; if (!PyArg_ParseTuple(args, "O", &iterator)) // borrowed reference. return NULL; if (!PyIter_Check(iterator)) { PyErr_SetString(PyExc_TypeError, "argument must be iterable"); return NULL; } lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); // Table pushed. lua_len(self->lua->state, -1); // Length pushed. Py_ssize_t length = lua_tointeger(self->lua->state, -1); lua_pop(self->lua->state, 1); // Length popped. PyObject *item; while ((item = PyIter_Next(iterator))) { Lua_push(self->lua, item); lua_seti(self->lua->state, -2, length + 1); length += 1; Py_DECREF(item); } lua_pop(self->lua->state, 1); return (PyObject *)self; } // }}} static PyObject *contains_method(Table *self, PyObject *args) { // {{{ // Check if the argument exists in the table as a value (not a key). PyObject *value; if (!PyArg_ParseTuple(args, "O", &value)) // borrowed reference. return NULL; lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); // Table pushed. lua_pushnil(self->lua->state); while (lua_next(self->lua->state, -2) != 0) { PyObject *candidate = Lua_to_python(self->lua, -1); if (PyObject_RichCompareBool(value, candidate, Py_EQ)) { lua_pop(self->lua->state, 3); Py_DECREF(candidate); Py_RETURN_TRUE; } Py_DECREF(candidate); lua_pop(self->lua->state, 1); } lua_pop(self->lua->state, 2); Py_RETURN_FALSE; } // }}} static PyObject *ne_method(Table *self, PyObject *args) { // {{{ PyObject *other; if (!PyArg_ParseTuple(args, "O", &other)) // borrowed reference. return NULL; Lua_push(self->lua, self->lua->lua_operator[EQ]); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); Lua_push(self->lua, other); lua_call(self->lua->state, 2, 1); PyObject *inverted = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 1); PyObject *ret = PyBool_FromLong(PyObject_Not(inverted)); Py_DECREF(inverted); return ret; } // }}} static PyObject *gt_method(Table *self, PyObject *args) { // {{{ PyObject *other; if (!PyArg_ParseTuple(args, "O", &other)) // borrowed reference. return NULL; Lua_push(self->lua, self->lua->lua_operator[LT]); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); Lua_push(self->lua, other); lua_call(self->lua->state, 2, 1); PyObject *ret = Lua_to_python(self->lua, -1); lua_settop(self->lua->state, -1); return ret; } // }}} static PyObject *ge_method(Table *self, PyObject *args) { // {{{ PyObject *other; if (!PyArg_ParseTuple(args, "O", &other)) // borrowed reference. return NULL; Lua_push(self->lua, self->lua->lua_operator[LE]); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); Lua_push(self->lua, other); lua_call(self->lua->state, 2, 1); PyObject *ret = Lua_to_python(self->lua, -1); lua_settop(self->lua->state, -1); return ret; } // }}} static PyObject *dict_method(Table *self, PyObject *args) { // {{{ // Get a copy of the table as a dict // This function can be called as a method on self, or standalone. // If called standalone, the target object is in args. Table *target; if (self == NULL) { if (!PyArg_ParseTuple(args, "O", (PyObject **)&target)) return NULL; if (!PyObject_IsInstance((PyObject *)target, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)target); return NULL; } } else { if (!PyArg_ParseTuple(args, "")) return NULL; target = self; } PyObject *ret = PyDict_New(); lua_rawgeti(target->lua->state, LUA_REGISTRYINDEX, target->id); lua_pushnil(target->lua->state); while (lua_next(target->lua->state, -2) != 0) { PyObject *key = Lua_to_python(target->lua, -2); PyObject *value = Lua_to_python(target->lua, -1); bool fail = PyDict_SetItem(ret, key, value) < 0; Py_DECREF(key); Py_DECREF(value); if (fail) { //fprintf(stderr, "Fail\n"); //fprintf(stderr, "decref %p\n", ret); Py_DECREF(ret); return NULL; } lua_pop(target->lua->state, 1); } lua_pop(target->lua->state, 1); return ret; } // }}} PyObject *table_list_method(Table *self, PyObject *args) { // {{{ // Get a copy of (the sequence elements of) the table, as a list. note that table[1] becomes list[0]. // This function can be called as a method on self, or standalone. // If called standalone, the target object is in args. Table *target; if (self == NULL) { if (!PyArg_ParseTuple(args, "O", (PyObject **)&target)) return NULL; if (!PyObject_IsInstance((PyObject *)target, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)target); return NULL; } } else { if (!PyArg_ParseTuple(args, "")) return NULL; target = self; } lua_rawgeti(target->lua->state, LUA_REGISTRYINDEX, target->id); lua_len(target->lua->state, -1); Py_ssize_t length = lua_tointeger(target->lua->state, -1); lua_pop(target->lua->state, 1); PyObject *ret = PyList_New(length); for (Py_ssize_t i = 1; i <= length; ++i) { lua_rawgeti(target->lua->state, -1, i); PyObject *value = Lua_to_python(target->lua, -1); PyList_SET_ITEM(ret, i - 1, value); lua_pop(target->lua->state, 1); } lua_pop(target->lua->state, 1); return ret; } // }}} static PyObject *remove_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } Py_ssize_t index = Table_len(self); if (!PyArg_ParseTuple(args, "|n", &index)) return NULL; lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_remove); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_pushinteger(self->lua->state, index); lua_call(self->lua->state, 2, 1); PyObject *ret = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 1); return ret; } // }}} static PyObject *concat_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } const char *sep = ""; Py_ssize_t seplen = 0, i = 1, j = Table_len(self); if (!PyArg_ParseTuple(args, "|s#nn", &sep, &seplen, &i, &j)) return NULL; lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_concat); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_pushlstring(self->lua->state, sep, seplen); lua_pushinteger(self->lua->state, i); lua_pushinteger(self->lua->state, j); lua_call(self->lua->state, 4, 1); PyObject *ret = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 1); return ret; } // }}} static PyObject *insert_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } Py_ssize_t length = PySequence_Length(args); Py_ssize_t pos; PyObject *value; switch (length) { case 1: if (!PyArg_ParseTuple(args, "O", &value)) return NULL; pos = Table_len(self) + 1; break; case 2: if (!PyArg_ParseTuple(args, "nO", &pos, &value)) return NULL; break; default: PyErr_SetString(PyExc_ValueError, "invalid argument for lua.Table.insert"); return NULL; } lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_insert); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_pushinteger(self->lua->state, pos); Lua_push(self->lua, value); lua_call(self->lua->state, 3, 0); lua_pop(self->lua->state, 1); Py_RETURN_NONE; } // }}} static PyObject *unpack_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } Py_ssize_t i = 1, j = Table_len(self); if (!PyArg_ParseTuple(args, "|nn", &i, &j)) return NULL; int oldtop = lua_gettop(self->lua->state); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_unpack); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_pushinteger(self->lua->state, i); lua_pushinteger(self->lua->state, j); lua_call(self->lua->state, 3, LUA_MULTRET); int newtop = lua_gettop(self->lua->state); PyObject *ret = PyTuple_New(newtop - oldtop); if (ret == NULL) { lua_settop(self->lua->state, oldtop); return NULL; } int p; for (p = oldtop + 1; p <= newtop; ++p) PyTuple_SET_ITEM(ret, p - oldtop - 1, Lua_to_python(self->lua, p)); lua_settop(self->lua->state, oldtop); return ret; } // }}} static PyObject *move_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } PyObject *other = NULL; Py_ssize_t f, e, t; if (!PyArg_ParseTuple(args, "nnn|O!", &f, &e, &t, (PyObject *)table_type, &other)) return NULL; lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_move); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_pushinteger(self->lua->state, f); lua_pushinteger(self->lua->state, e); lua_pushinteger(self->lua->state, t); if (other != NULL) { Lua_push(self->lua, other); lua_call(self->lua->state, 5, 1); } else { lua_call(self->lua->state, 4, 1); } PyObject *ret = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 1); return ret; } // }}} static PyObject *sort_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } PyObject *comp = NULL; if (!PyArg_ParseTuple(args, "|O", &comp)) return NULL; lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->lua->table_sort); lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); if (comp != NULL) { Lua_push(self->lua, comp); lua_call(self->lua->state, 2, 1); } else { lua_call(self->lua->state, 1, 1); } PyObject *ret = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 1); return ret; } // }}} static PyObject *pairs_method(Table *self, PyObject *args) { // {{{ if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } return Table_iter_create((PyObject *)self, false); } // }}} static PyObject *ipairs_method(Table *self, PyObject *args) { // {{{ //fprintf(stderr, "ipairs\n"); if (!PyObject_IsInstance((PyObject *)self, (PyObject *)table_type)) { PyErr_Format(PyExc_ValueError, "argument is not a Lua Table: %S", (PyObject *)self); return NULL; } return Table_iter_create((PyObject *)self, true); } // }}} Py_ssize_t Table_len(Table *self) { // {{{ lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); lua_len(self->lua->state, -1); lua_Number ret = lua_tonumber(self->lua->state, -1); lua_pop(self->lua->state, 2); return ret; } // }}} PyObject *Table_getitem(Table *self, PyObject *key) { // {{{ lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); // Table pushed. Lua_push(self->lua, key); // Key pushed. lua_gettable(self->lua->state, -2); // Key replaced by value. PyObject *ret = Lua_to_python(self->lua, -1); lua_pop(self->lua->state, 2); if (ret == Py_None) { PyErr_Format(PyExc_IndexError, "Key %S does not exist in Lua table", key); return NULL; } return ret; } // }}} int Table_setitem(Table *self, PyObject *key, PyObject *value) { // {{{ lua_rawgeti(self->lua->state, LUA_REGISTRYINDEX, self->id); // Table pushed. Lua_push(self->lua, key); if (value == NULL) lua_pushnil(self->lua->state); else Lua_push(self->lua, value); lua_settable(self->lua->state, -3); // Pops key and value from stack lua_pop(self->lua->state, 1); return 0; } // }}} PyObject *Table_repr(PyObject *self) { // {{{ char str[100]; snprintf(str, sizeof(str), "", self); return PyUnicode_DecodeUTF8(str, strlen(str), NULL); } // }}} // Iterator methods. {{{ PyObject *Table_iter_create(PyObject *target, bool is_ipairs) { // {{{ TableIter *self = (TableIter *) (table_iter_type->tp_alloc(table_iter_type, 0)); if (!self) return NULL; self->target = target; Py_INCREF(self); Py_INCREF(target); //fprintf(stderr, "new iter %p %p refcnt %ld, %ld\n", target, self, // Py_REFCNT((PyObject *)self), Py_REFCNT(target)); self->is_ipairs = is_ipairs; if (is_ipairs) { self->current = NULL; self->icurrent = 0; } else { self->current = Py_None; self->icurrent = -1; } return (PyObject *)self; } // }}} void Table_iter_dealloc(TableIter *self) { // {{{ //fprintf(stderr, "dealloc iter %p / %p\n", self, self->target); if (self->target != NULL) Py_DECREF(self->target); if (self->current != NULL) Py_DECREF(self->current); table_iter_type->tp_free((PyObject *)self); } // }}} PyObject *Table_iter_repr(PyObject *self) { // {{{ char str[100]; snprintf(str, sizeof(str), "", self); return PyUnicode_DecodeUTF8(str, strlen(str), NULL); } // }}} PyObject *Table_iter_iter(PyObject *self) { // {{{ //fprintf(stderr, "iter %p\n", self); return self; } // }}} PyObject *Table_iter_iternext(TableIter *self) { // {{{ //fprintf(stderr, "next iter %p: %ld\n", self, Py_REFCNT(self)); //if (!PyObject_IsInstance((PyObject *)self, // (PyObject *)&table_iter_type)) { // PyErr_SetString(PyExc_ValueError, // "self must be a table iterator"); // return NULL; //} Lua *lua = ((Table *)(self->target))->lua; if (self->is_ipairs) { if (self->icurrent < 0) return NULL; self->icurrent += 1; Lua_push(lua, self->target); lua_geti(lua->state, -1, self->icurrent); if (lua_isnil(lua->state, -1)) { self->icurrent = -1; lua_pop(lua->state, 2); return NULL; } PyObject *ret = Lua_to_python(lua, -1); lua_pop(lua->state, 2); return Py_BuildValue("iN", self->icurrent, ret); } // pairs iterator. if (self->current == NULL) return NULL; Lua_push(lua, self->target); Lua_push(lua, self->current); if (lua_next(lua->state, -2) == 0) { lua_pop(lua->state, 1); Py_DECREF(self->current); self->current = NULL; return NULL; } PyObject *value = Lua_to_python(lua, -1); Py_DECREF(self->current); self->current = Lua_to_python(lua, -2); lua_pop(lua->state, 3); return Py_BuildValue("ON", self->current, value); } // }}} // }}} // Python-accessible methods. PyMethodDef Table_methods[] = { // {{{ {"list", (PyCFunction)table_list_method, METH_VARARGS, "Create list from Lua table"}, {"dict", (PyCFunction)dict_method, METH_VARARGS, "Create dict from Lua table"}, //{"__len__", (PyCFunction)len_method, METH_VARARGS, "Call the # operator on the Lua table"}, {"__iadd__", (PyCFunction)iadd_method, METH_VARARGS, "Call the += operator on the Lua table"}, //{"__getitem__", (PyCFunction)getitem_method, METH_VARARGS, "Get item from Lua table"}, //{"__setitem__", (PyCFunction)setitem_method, METH_VARARGS, "Set item in Lua table"}, //{"__delitem__", (PyCFunction)delitem_method, METH_VARARGS, "Remove item from Lua table"}, {"__contains__", (PyCFunction)contains_method, METH_VARARGS, "Check if Lua table contains item"}, //{"__iter__", (PyCFunction)iter_method, METH_VARARGS, "Loop over the Lua table"}, {"__ne__", (PyCFunction)ne_method, METH_VARARGS, "Call the ~= operator on the Lua table"}, {"__gt__", (PyCFunction)gt_method, METH_VARARGS, "Call the > operator on the Lua table"}, {"__ge__", (PyCFunction)ge_method, METH_VARARGS, "Call the >= operator on the Lua table"}, {"remove", (PyCFunction)remove_method, METH_VARARGS, "Remove item from Lua table"}, {"concat", (PyCFunction)concat_method, METH_VARARGS, "Join items from Lua table into string"}, {"insert", (PyCFunction)insert_method, METH_VARARGS, "Insert item into Lua table"}, {"unpack", (PyCFunction)unpack_method, METH_VARARGS, "Extract items from Lua table into tuple"}, {"move", (PyCFunction)move_method, METH_VARARGS, "Move item from Lua table to other index or table"}, {"sort", (PyCFunction)sort_method, METH_VARARGS, "Sort item in Lua table"}, {"pairs", (PyCFunction)pairs_method, METH_VARARGS, "Iterate over key-value pairs"}, {"ipairs", (PyCFunction)ipairs_method, METH_VARARGS, "Iterate over index-value pairs"}, {NULL, NULL, 0, NULL} }; // }}} PyTypeObject *table_type; PyTypeObject *table_iter_type; // vim: set foldmethod=marker : python-lua-1.0.orig/.gitignore0000664000175000017500000000007215012112347015732 0ustar shevekshevek__pycache__/ *.egg-info /dist/ /build/ /lua.cpython-*.so python-lua-1.0.orig/setup.py0000664000175000017500000000064415021655777015504 0ustar shevekshevek#!/usr/bin/python3 from setuptools import setup, Extension module = Extension('lua', sources = [ 'lua.c', 'function.c', 'table.c', ], depends = [ 'module.h', 'setup.py', ], language = 'c', libraries = ['lua5.4'], extra_compile_args = ['-I/usr/include/lua5.4'], ) setup(name = 'lua-wrapper', version = '1.0', description = 'Allow Lua and Python scripts to work together', ext_modules = [module], ) python-lua-1.0.orig/test.py0000775000175000017500000000341415012727674015320 0ustar shevekshevek#!/usr/bin/python3 import lua # Setup. import lua code = lua.Lua() code.run('python = require("python")') # If you want to use it. # Define a Python function. def pyfun(arg): print("Python function called with arg '%s'." % arg) # Define a Lua function. code.run('''function luafun(arg) print("Lua function called with arg '" .. arg .. "'.") end''') # Make the Lua function accessible to Python (this could have been done in one step). luafun = code.run('return luafun') # Make the Python function accessible to Lua. code.set('pyfun', pyfun) # Run the Lua function from Python. luafun('from Python') # Run the Python function from Lua. code.run('pyfun("from Lua")') # Create Python compound objects from Lua. print(type(code.run('return {1, 2, 3}'))) # Lua table (not a Python object) print(type(code.run('return python.list{1,2,3}'))) # Python list print(type(code.run('return python.dict{foo = "bar"}'))) # Python dict # Lua strings that are passed to Python must be UTF-8 encoded and are treated as str. print(repr(code.run('return "Unicode"'))) # A bytes object can be created with python.bytes. print(repr(code.run('return python.bytes("Binary")'))) t = code.run('return {"grape", 24, x = 12, ["-"] = "+"}') print(t.concat('/')) t.insert("new 1") t.insert(2, "new 2") print(t.unpack()) print(t.unpack(3)) print(t.unpack(2, 3)) print(t.move(2, 3, 4, code.table()).dict()) comp = lambda i, j: str(i) < str(j) c = code.run('return function (i, j) return foo(i, j) end', var = 'foo', value = comp) t.sort(c) print(t.dict()) t = code.table(12, 7, 333, -18) t.sort() print(t.dict()) t = code.table('12', '7', '333', '-18', foo = 27) t.sort() print(t.dict()) for k, v in t.pairs(): print('pair item', k, '=', v) for k, v in t.ipairs(): print('ipair item', k, '=', v)