Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Lua load arguments (mode and chunkname) #248

Closed
astoff opened this issue Oct 28, 2023 · 9 comments · Fixed by #252
Closed

Expose Lua load arguments (mode and chunkname) #248

astoff opened this issue Oct 28, 2023 · 9 comments · Fixed by #252
Milestone

Comments

@astoff
Copy link

astoff commented Oct 28, 2023

It would be nice to expose the mode and chunkname arguments of Lua's load function in LuaRunner.{eval,execute,compile}.

Setting mode to "t" is important for sanboxing purposes. Specifying the chunk name is less important by I guess it's nice to have.

@gentooise
Copy link

This is a patch that I wrote to expose the chunk name (it works for my needs). I don't have time right now to open a PR, but I'll leave the changes here in case anyone needs them:

diff --git a/lupa/_lupa.pyx b/lupa/_lupa.pyx
index 9310e35..34a4d3b 100644
--- a/lupa/_lupa.pyx
+++ b/lupa/_lupa.pyx
@@ -393,36 +393,45 @@ cdef class LuaRuntime:
             raise
         return 0

-    def eval(self, lua_code, *args):
+    def eval(self, lua_code, name = '<python>', *args):
         """Evaluate a Lua expression passed in a string.
         """
         assert self._state is not NULL
         if isinstance(lua_code, unicode):
             lua_code = (<unicode>lua_code).encode(self._source_encoding)
-        return run_lua(self, b'return ' + lua_code, args)
+        if not isinstance(name, (bytes, unicode)):
+            raise TypeError("Lua program name must be a string")
+        name = (<unicode>name).encode('UTF-8')
+        return run_lua(self, b'return ' + lua_code, name, args)

-    def execute(self, lua_code, *args):
+    def execute(self, lua_code, name = '<python>', *args):
         """Execute a Lua program passed in a string.
         """
         assert self._state is not NULL
         if isinstance(lua_code, unicode):
             lua_code = (<unicode>lua_code).encode(self._source_encoding)
-        return run_lua(self, lua_code, args)
+        if not isinstance(name, (bytes, unicode)):
+            raise TypeError("Lua program name must be a string")
+        name = (<unicode>name).encode('UTF-8')
+        return run_lua(self, lua_code, name, args)

-    def compile(self, lua_code):
+    def compile(self, lua_code, name = '<python>'):
         """Compile a Lua program into a callable Lua function.
         """
         assert self._state is not NULL
         cdef const char *err
         if isinstance(lua_code, unicode):
             lua_code = (<unicode>lua_code).encode(self._source_encoding)
+        if not isinstance(name, (bytes, unicode)):
+            raise TypeError("Lua program name must be a string")
+        name = (<unicode>name).encode('UTF-8')
         L = self._state
         lock_runtime(self)
         old_top = lua.lua_gettop(L)
         cdef size_t size
         try:
             check_lua_stack(L, 1)
-            status = lua.luaL_loadbuffer(L, lua_code, len(lua_code), b'<python>')
+            status = lua.luaL_loadbuffer(L, lua_code, len(lua_code), name)
             if status == 0:
                 return py_from_lua(self, L, -1)
             else:
@@ -1719,14 +1728,14 @@ cdef build_lua_error_message(LuaRuntime runtime, lua_State* L, int stack_index=-

 # calling into Lua

-cdef run_lua(LuaRuntime runtime, bytes lua_code, tuple args):
+cdef run_lua(LuaRuntime runtime, bytes lua_code, bytes name, tuple args):
     """Run Lua code with arguments"""
     cdef lua_State* L = runtime._state
     lock_runtime(runtime)
     old_top = lua.lua_gettop(L)
     try:
         check_lua_stack(L, 1)
-        if lua.luaL_loadbuffer(L, lua_code, len(lua_code), '<python>'):
+        if lua.luaL_loadbuffer(L, lua_code, len(lua_code), name):
             error = build_lua_error_message(runtime, L)
             if error.startswith("not enough memory"):
                 raise LuaMemoryError(error)

@scoder
Copy link
Owner

scoder commented Nov 20, 2023

This is a patch that I wrote to expose the chunk name (it works for my needs).

Note that you cannot cast a bytes object to a unicode object. They are two different things. This probably just works for you because you never tried passing a bytes object. You should only do the encoding if you have a unicode string.

@gentooise
Copy link

gentooise commented Nov 20, 2023

Note that you cannot cast a bytes object to a unicode object. They are two different things. This probably just works for you because you never tried passing a bytes object. You should only do the encoding if you have a unicode string.

Which line are you referring to? Because the name parameter is actually a str initially (for easier usability), and then internally I convert it into bytes.

Please help me fix the code if needed, thank you.

@scoder
Copy link
Owner

scoder commented Nov 22, 2023

A pattern that you could use is:

if isinstance(name, unicode):
    name = (<unicode>name).encode('UTF-8')
elif not isinstance(name, bytes):
    raise TypeError(…)

That way, you end up with a bytes object in the end either way.

EDIT: Although, this would still fail for subtypes of bytes, given that the function call that follows expects exactly a bytes object. You may get away with a cast in that case, though, like:

return run_lua(self, b'return ' + lua_code, <bytes>name, args)

@astoff
Copy link
Author

astoff commented Nov 22, 2023

I would suggest changing the default chunk name to either nil or =<python>.

To compare the results, let say you call lua.eval("{}+1")

  • Currently, the error reads
     [string "<python>"]:1: attempt to perform arithmetic on a table value
    
  • With a nil chunk name, the error would read
    [string "return {}+1"]:1: attempt to perform arithmetic on a table value
    
  • With chunk name =<python>, the error would read
    <python>:1: attempt to perform arithmetic on a table value
    

@scoder
Copy link
Owner

scoder commented Dec 10, 2023

BTW, according to the Lua documentation:
"""
The string mode works as in function load, with the addition that a NULL value is equivalent to the string "bt".
"""
So, IIUC, we use 'bt' as current mode by default.

@scoder
Copy link
Owner

scoder commented Dec 10, 2023

@astoff, could you explain why you'd need to change the mode argument? You can only pass strings into Lupa's execution methods, so b doesn't really apply, does it?

@astoff
Copy link
Author

astoff commented Dec 10, 2023

So, IIUC, we use 'bt' as current mode by default.

This is correct. I guess the only caveat is that feeding in some malformed bytecode can crash the interpreter.

You can only pass strings into Lupa's execution methods, so b doesn't really apply, does it?

Well, Lua doesn't care about string vs. bytes. The way it works internally is this: normally (mode=NULL or "bt"), Lua will first byte-compile the code, then load it. However, if the code starts with ESC, then it will assume it's valid bytecode and skip the byte-compilation step.

With mode = "t" (respectively mode = "b"), Lua will raise an error if the code string starts with (respectively doesn't start with) ESC.

However, the fact that bytecode is marked by an initial ESC is an implementation detail. The only official way to distinguish them is by the mode argument.

scoder added a commit that referenced this issue Dec 10, 2023
…to allow finer control of input and debug output.

Closes #248
@scoder
Copy link
Owner

scoder commented Dec 10, 2023

Ok, got it. #252 adds these options.

scoder added a commit that referenced this issue Dec 11, 2023
…to allow finer control of allowed input and debug output. (#252)

These match the same arguments of the Lua `load()` function.

Closes #248
@scoder scoder added this to the 2.1 milestone Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants