Using `_ENV` to overwrite a scope
Is there any way to use _ENV with Lupa? From my testing I've gathered that each call to either .execute or .eval resets/uses its own _ENV, so if you set it in one call it resets by the next.
Reproduction
from lupa import LuaRuntime
lua = LuaRuntime()
code = """
hidden = 1
print(hidden)
_ENV = { print = print }
print(hidden)
"""
for line in code.splitlines():
lua.exec(line)
The Lua code outputs 1 then nil (like on this compiler), but using Lupa results in 1 and 1.
Context
I'm using Lupa to embed Lua into a UI library I'm making. Each widget has its own script, and their _ENV-s create a sort of cascade where each widget within a container has access to things defined above it, but not to its siblings. The end goal is something like Alpine.js.
I suspect the same cascade would be possible using functions instead of inline do ... end scopes, but that massively complicates everything.
Cheers, and enjoy the holidays!
I ran a couple of tests on WSL, these are the results:
Lua 5.1
1
1
Lua 5.2
1
nil
Lua 5.3
1
nil
Lua 5.4
1
nil
LuaJIT 2.1
1
1
Try importing like this:
from lupa.lua54 import LuaRuntime
_ENV was added in Lua 5.2, replacing setfenv.
I would suggest looking into load(chunk, type, env), instead of modifying _ENV directly.
@Kirstukas Did you run this in Lupa or just Lua? In your output 5.1 and JIT 2.1 are incorrect in their output, the rest are correct.
load is a good suggestion, but isn't what I needed (I got around the problem by building up the snippet and then executing it all at once). Overwriting _ENV is an intentional feature, and is the most commonly recommended way to sandbox Lua past 5.2. If there is a technical reason not to support it I'm also fine with that (again, I did get around it), but it would be nice to have it documented so the next guy doesn't have to spend a day trying to understand why something works in seemingly every Lua compiler but not in Lupa :)
I think that the issue is that you are executing it line by line. I think that execute compiles code into a function (just like load in Lua) and then runs it. Because _ENV variable is scoped locally to a function, the effect is not carried over.
Instead of:
for line in code.splitlines():
lua.execute(line)
Try:
lua.execute(code)
Yes, that's what I assumed was happening in the initial report:
From my testing I've gathered that each call to either .execute or .eval resets/uses its own _ENV, so if you set it in one call it resets by the next.
I would just like a way to either:
- Share _ENV between calls (could be enabled by a toggle)
- Documentation of this behaviour ("Each call to
.executeand.evalcreate their own environments, so overwriting_ENVin one call will not affect subsequent calls")
This is Lua behaviour, not specific to Lupa.
You can store _ENV to an external variable, and assign it when needed.
sandboxed_env = {...}
function wrapper()
local _ENV = sandboxed_env
-- Will use sandboxed_env
some_callback()
end
But be carefull with load as it loads chunk into "default environment", thus escaping the sandbox.
Yes, again I knew all of this before I made the issue. I laid out my request in my previous comment, but here it is again:
- Share _ENV between calls (could be enabled by a toggle)
- Documentation of this behaviour ("Each call to .execute and .eval create their own environments, so overwriting _ENV in one call will not affect subsequent calls")
I'll just wait for a maintainer's response now. You don't seem to be one (correct me if I'm wrong), so us going back and forth isn't doing anyone any good.