Lupa: Python Wrapper around Lua and LuaJIT
Lupa is a Python library that seamlessly integrates the runtimes of Lua or LuaJIT2 into CPython. It enables Python developers to embed Lua code, call Lua functions from Python, and interact with Python objects from within Lua. Key features include separate Lua runtime states, Python coroutine wrappers for Lua coroutines, and robust iteration support between the two languages. Currently at version 2.6, Lupa is actively maintained, with releases focusing on cross-version compatibility and feature enhancements.
Warnings
- breaking In Lupa 2.0, Lua stack traces within Python exception messages were reversed to align with Python's stack trace order. If you relied on the previous ordering, your error parsing logic may break.
- gotcha The behavior of Lua's `#` (length) operator, and thus Lupa's `len()` for wrapped Lua tables, can be unpredictable for tables containing `nil` values ('holes') or primarily acting as mappings. It generally stops at the first `nil` element, making it unsuitable for non-sequence tables. It's best not to rely on `len()` for mappings in Lupa.
- gotcha When passing Python objects to Lua, Lupa employs a heuristic for indexing: if the Python object has a `__getitem__` method, it's preferred for Lua's `obj[x]` and `obj.x` operations. Otherwise, attribute access is used. This can lead to unexpected behavior if an object has both attributes and `__getitem__` and you expect a specific access method from Lua.
- gotcha Prior to Lupa 2.1, recursive mapping of complex Python data structures (like nested lists/dicts) to Lua tables was not straightforward. Since Lupa 2.1, explicit `recursive=True` is needed in conversion functions (e.g., `LuaRuntime.table_from`) to enable deep conversion.
- gotcha Importing Lua binary modules (C modules) within a Lupa runtime typically requires CPython to enable global symbol visibility for shared libraries by calling `sys.setdlopenflags`. Lupa attempts to set these flags automatically, but it might fail on some platforms or configurations, leading to module import errors in Lua.
Install
-
pip install lupa -
pip install lupa --global-option="--with-luajit"
Imports
- LuaRuntime
from lupa import LuaRuntime
- lupa
import lupa
Quickstart
import os
from lupa import LuaRuntime
# Create a Lua runtime instance
lua = LuaRuntime(unpack_returned_tuples=True)
# Evaluate Lua code directly
result_eval = lua.eval('1 + 2')
print(f'Lua eval("1 + 2"): {result_eval}')
# Define a Lua function and call it from Python
lua_add_func = lua.eval('function(x, y) return x + y end')
result_lua_call = lua_add_func(5, 7)
print(f'Called Lua function (5, 7): {result_lua_call}')
# Pass a Python function into Lua and call it
def py_multiply(a, b):
return a * b
lua.globals().py_multiply = py_multiply
result_python_call_from_lua = lua.eval('py_multiply(3, 4)')
print(f'Called Python function from Lua (3, 4): {result_python_call_from_lua}')
# Access Python builtins from Lua
python_str_from_lua = lua.eval('python.builtins.str(123)')
print(f'Python str from Lua: {python_str_from_lua}')