From 145f2e12e8dfa2f65773eeb81320ac79da65f3ab Mon Sep 17 00:00:00 2001 From: guidanoli Date: Wed, 28 Apr 2021 23:37:29 -0300 Subject: [PATCH 1/2] Fix python.args usage and optimize call_python --- lupa/_lupa.pyx | 42 +++++++++++++++++++++++++++++++----------- lupa/tests/test.py | 24 ++++++++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/lupa/_lupa.pyx b/lupa/_lupa.pyx index 83b23051..819c0846 100644 --- a/lupa/_lupa.pyx +++ b/lupa/_lupa.pyx @@ -1648,6 +1648,7 @@ cdef int py_object_gc(lua_State* L) nogil: cdef bint call_python(LuaRuntime runtime, lua_State *L, py_object* py_obj) except -1: # Callers must assure that py_obj.obj is not NULL, i.e. it points to a valid Python object. cdef int i, nargs = lua.lua_gettop(L) - 1 + cdef Py_ssize_t j cdef tuple args cdef dict kwargs @@ -1657,16 +1658,31 @@ cdef bint call_python(LuaRuntime runtime, lua_State *L, py_object* py_obj) excep lua.lua_settop(L, 0) # FIXME result = f() else: - args = () - kwargs = {} - - for i in range(nargs): - arg = py_from_lua(runtime, L, i+2) - if isinstance(arg, _PyArguments): - args += (<_PyArguments>arg).args - kwargs = dict(**kwargs, **(<_PyArguments>arg).kwargs) - else: - args += (arg, ) + # Special treatment for the last argument + last_arg = py_from_lua(runtime, L, nargs + 1) + + if isinstance(last_arg, _PyArguments): + # Calling a function and _PyArguments is the last argument + # Lua f(..., python.args{a, b, c=1, d=2}) => Python as f(..., a, b, c=1, d=2) + kwargs = (<_PyArguments>last_arg).kwargs + moreargs = (<_PyArguments>last_arg).args + args = cpython.tuple.PyTuple_New(nargs - 1 + cpython.tuple.PyTuple_Size(moreargs)) + for j, arg in enumerate(moreargs): + cpython.ref.Py_INCREF(arg) + cpython.tuple.PyTuple_SET_ITEM(args, nargs - 1 + j, arg) + else: + # Calling a function normally + # Lua f(...) => Python as f(...) + kwargs = None + args = cpython.tuple.PyTuple_New(nargs) + cpython.ref.Py_INCREF(last_arg) + cpython.tuple.PyTuple_SET_ITEM(args, nargs - 1, last_arg) + + # Process the rest of the arguments + for i in range(nargs - 1): + arg = py_from_lua(runtime, L, i + 2) + cpython.ref.Py_INCREF(arg) + cpython.tuple.PyTuple_SET_ITEM(args, i, arg) if args and PyMethod_Check(f) and (args[0]) is PyMethod_GET_SELF(f): # Calling a bound method and self is already the first argument. @@ -1681,7 +1697,11 @@ cdef bint call_python(LuaRuntime runtime, lua_State *L, py_object* py_obj) excep f = PyMethod_GET_FUNCTION(f) lua.lua_settop(L, 0) # FIXME - result = f(*args, **kwargs) + + if kwargs: + result = f(*args, **kwargs) + else: + result = f(*args) return py_function_result_to_lua(runtime, L, result) diff --git a/lupa/tests/test.py b/lupa/tests/test.py index 17cf02ed..cf2b2e72 100644 --- a/lupa/tests/test.py +++ b/lupa/tests/test.py @@ -2712,11 +2712,27 @@ def test_all_types(self): self.assertIncorrect('python.args{[kwargs["%s"]] = true}' % objtype, regex='table key is neither an integer nor a string') - def test_kwargs_merge(self): - self.assertResult('python.args{1, a=1}, python.args{2}, python.args{}, python.args{b=2}', (1, 2), dict(a=1, b=2)) + def test_args_merge(self): + self.assertResult('1, 2, python.args{a=5, b=6}', (1, 2), dict(a=5, b=6)) + self.assertResult('1, 2, python.args{3, 4, a=5, b=6}', (1, 2, 3, 4), dict(a=5, b=6)) - def test_kwargs_merge_conflict(self): - self.assertIncorrect('python.args{a=1}, python.args{a=2}', regex='multiple values') + + def test_multiple_args(self): + _G = self.lua.globals() + + x = self.lua.eval('python.args{1, 2, a=5, b=6}') + _G.x = x + self.assertEqual(_G.x, x) + + y = self.lua.eval('python.args{11, c=13}') + _G.y = y + self.assertEqual(_G.y, y) + + callfxy = self.lua.eval('function(f) return f(x, y) end') + + # contrary to y, x will not be unpacked + self.assertEqual(callfxy(self.get_args), (x, 11)) + self.assertEqual(callfxy(self.get_kwargs), dict(c=13)) class PythonArgumentsInLuaMethodsTest(PythonArgumentsInLuaTest): From d05aeb9dba4a970b05dd72d45993f44c16c4ebc0 Mon Sep 17 00:00:00 2001 From: Guilherme Dantas Date: Fri, 16 Jul 2021 12:03:35 -0300 Subject: [PATCH 2/2] Update lupa/_lupa.pyx Co-authored-by: scoder --- lupa/_lupa.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lupa/_lupa.pyx b/lupa/_lupa.pyx index 819c0846..079e8a10 100644 --- a/lupa/_lupa.pyx +++ b/lupa/_lupa.pyx @@ -1666,7 +1666,7 @@ cdef bint call_python(LuaRuntime runtime, lua_State *L, py_object* py_obj) excep # Lua f(..., python.args{a, b, c=1, d=2}) => Python as f(..., a, b, c=1, d=2) kwargs = (<_PyArguments>last_arg).kwargs moreargs = (<_PyArguments>last_arg).args - args = cpython.tuple.PyTuple_New(nargs - 1 + cpython.tuple.PyTuple_Size(moreargs)) + args = cpython.tuple.PyTuple_New(nargs - 1 + len(moreargs)) for j, arg in enumerate(moreargs): cpython.ref.Py_INCREF(arg) cpython.tuple.PyTuple_SET_ITEM(args, nargs - 1 + j, arg)