|
| 1 | +# PyLua API |
| 2 | + |
| 3 | +This guide explains the public API users interact with when embedding Python in Luau. It reflects the current v0.3.0‑dev implementation. |
| 4 | + |
| 5 | +## Importing |
| 6 | + |
| 7 | +From this repository when running with Lute: |
| 8 | + |
| 9 | +```lua |
| 10 | +-- From docs/examples/* the relative import is: |
| 11 | +local PyLua = require("../../src/PyLua") |
| 12 | + |
| 13 | +-- From the repo root or your own project layout, adjust the path accordingly, |
| 14 | +-- e.g. local PyLua = require("src/PyLua"). |
| 15 | +``` |
| 16 | + |
| 17 | +## Creating a runtime |
| 18 | + |
| 19 | +```lua |
| 20 | +local py = PyLua.new({ |
| 21 | + debug = false, -- optional; enables extra diagnostics in some subsystems |
| 22 | + timeout = 0, -- optional; planned, not enforced yet in v0.3.0‑dev |
| 23 | + maxRecursion = 0, -- optional; planned guard, not enforced yet |
| 24 | + builtins = nil, -- optional; override built-ins |
| 25 | +}) |
| 26 | +``` |
| 27 | + |
| 28 | +Notes: |
| 29 | + |
| 30 | +- A PyLua runtime holds its own globals and built-ins table. |
| 31 | +- The globals table is pre-populated with `__pylua_version__`, `__name__ = "__main__"`, and a few standard dunders. |
| 32 | + |
| 33 | +## Running code |
| 34 | + |
| 35 | +### execute(code) |
| 36 | + |
| 37 | +Run Python statements. Returns no value; exceptions raise Luau errors. |
| 38 | + |
| 39 | +```lua |
| 40 | +py:execute([[ |
| 41 | + x = [1, 2, 3] |
| 42 | + y = { 'name': 'test', 'value': 42 } |
| 43 | + print("sum:", sum(x)) |
| 44 | +]]) |
| 45 | +``` |
| 46 | + |
| 47 | +### eval(code) -> any |
| 48 | + |
| 49 | +Evaluate a single Python expression and return its value (converted when reasonable). |
| 50 | + |
| 51 | +```lua |
| 52 | +local v = py:eval("1 + 2 * 3") -- 7 |
| 53 | +``` |
| 54 | + |
| 55 | +Edge cases: |
| 56 | + |
| 57 | +- `eval` requires an expression (not statements); use `execute` otherwise. |
| 58 | +- Errors are thrown as Luau `error(...)`; use `pcall` to handle them. |
| 59 | + |
| 60 | +### compile(code) -> CodeObject |
| 61 | + |
| 62 | +Compile Python source to a code object (bytecode + pools). |
| 63 | + |
| 64 | +```lua |
| 65 | +local co = py:compile("1 + 2") |
| 66 | +-- co has fields: constants, names, varnames, bytecode, ... |
| 67 | +``` |
| 68 | + |
| 69 | +### runBytecode(codeObject) -> any |
| 70 | + |
| 71 | +Execute a previously compiled code object. |
| 72 | + |
| 73 | +```lua |
| 74 | +local result = py:runBytecode(co) |
| 75 | +``` |
| 76 | + |
| 77 | +## Sharing values via globals |
| 78 | + |
| 79 | +Each runtime has a separate global namespace. |
| 80 | + |
| 81 | +```lua |
| 82 | +-- Write from Luau, read from Python |
| 83 | +py:setGlobal("answer", 42) |
| 84 | +py:execute("print(answer)") |
| 85 | + |
| 86 | +-- Or mutate via the table directly |
| 87 | +local g = py:globals() |
| 88 | +g.threshold = 10 |
| 89 | +py:execute("print(threshold)") |
| 90 | + |
| 91 | +-- Read back results written in Python |
| 92 | +py:execute("result = sum([1, 2, 3])") |
| 93 | +print(g.result) -- 6 |
| 94 | +``` |
| 95 | + |
| 96 | +Helpers: |
| 97 | + |
| 98 | +- `py:globals()` returns the globals table |
| 99 | +- `py:setGlobal(name, value)` and `py:getGlobal(name)` access single values |
| 100 | + |
| 101 | +Type notes: |
| 102 | + |
| 103 | +- Numbers, booleans, strings, lists/tuples/dicts/sets created in Python are exposed via PyLua’s object model. |
| 104 | +- On return from the VM, some primitives are unwrapped for convenience (e.g., `int` → Luau number). |
| 105 | + |
| 106 | +## Capturing output / customizing builtins |
| 107 | + |
| 108 | +`print(...)` is implemented in `src/PyLua/builtins/functions.luau` and supports swapping the writer for tests/tools. |
| 109 | + |
| 110 | +```lua |
| 111 | +local Builtins = require("src/PyLua/builtins/functions") |
| 112 | +local lines = {} |
| 113 | +Builtins.setWriter(function(s) table.insert(lines, s) end) |
| 114 | + |
| 115 | +py:execute("print('hello')") |
| 116 | +-- lines now contains { "hello" } |
| 117 | +``` |
| 118 | + |
| 119 | +Providing a custom builtins table via `PyLua.new({ builtins = ... })` is also possible, but most use cases are covered by adjusting the writer. |
| 120 | + |
| 121 | +## Interop notes |
| 122 | + |
| 123 | +- Globals are the simplest way to bridge data in and out. |
| 124 | +- Calling Luau functions from Python is planned but not yet implemented in this build. |
| 125 | + - Workaround: call from Luau by evaluating Python expressions that read/write globals. |
| 126 | + |
| 127 | +Example pattern: |
| 128 | + |
| 129 | +```lua |
| 130 | +-- Expose a Luau value and read back a result |
| 131 | +local g = py:globals() |
| 132 | +g.n = 5 |
| 133 | +py:execute("result = [i*i for i in range(n)]") |
| 134 | +print(g.result) -- e.g., [0, 1, 4, 9, 16] |
| 135 | +``` |
| 136 | + |
| 137 | +## Error handling |
| 138 | + |
| 139 | +Use `pcall` around API calls to catch errors raised from Python execution. |
| 140 | + |
| 141 | +```lua |
| 142 | +local ok, err = pcall(function() |
| 143 | + py:execute("1/0") |
| 144 | +end) |
| 145 | +if not ok then |
| 146 | + warn("Python error:", err) |
| 147 | +end |
| 148 | +``` |
| 149 | + |
| 150 | +## Feature support and limits |
| 151 | + |
| 152 | +Target: Python 3.12 syntax and a practical core subset. |
| 153 | + |
| 154 | +Highlights supported: |
| 155 | + |
| 156 | +- Statements: assignment, if/elif/else, while/for (with break/continue), def |
| 157 | +- Expressions: arithmetic/bitwise, comparisons, calls, attribute/subscript |
| 158 | +- Collections: list/tuple/set/dict literals; list comprehensions |
| 159 | +- Builtins: `print`, `len`, `type`, `range`, `int/float/str/bool`, `sum/min/max`, `bytes`, `repr/ascii/format`, `isinstance` |
| 160 | + |
| 161 | +Important limitations (v0.3.0‑dev): |
| 162 | + |
| 163 | +- Chained comparisons parsed but not yet compiled |
| 164 | +- Dict unpacking in literals not yet compiled |
| 165 | +- For-loop assignment targets beyond simple `Name` unsupported |
| 166 | +- Function defaults/kwargs are stubbed in function creation |
| 167 | +- Exceptions/try-except not implemented |
| 168 | +- f-strings tokenized; full runtime formatting in progress |
| 169 | + |
| 170 | +See [LIMITATIONS.md](LIMITATIONS.md) for the up-to-date list. |
| 171 | + |
| 172 | +## Version |
| 173 | + |
| 174 | +The runtime exposes `__pylua_version__` in globals: |
| 175 | + |
| 176 | +```lua |
| 177 | +print(py:globals().__pylua_version__) |
| 178 | +``` |
0 commit comments