Skip to content

Commit

Permalink
merge upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
synodriver committed Mar 16, 2024
2 parents 2e93427 + b204445 commit f665f19
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 131 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ jobs:
LUPA_WITH_LUA_DLOPEN: ${{ startsWith(matrix.os, 'windows') && 'false' || 'true' }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Check out recursively
run: git submodule update --init --recursive

- name: Set up Python ${{ matrix.python-version }}
if: ${{ !startsWith(matrix.os, 'ubuntu') || startsWith(matrix.python-version, '3.') || startsWith(matrix.python-version, 'pypy') }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ jobs:
pyversion: "cp312*"

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Check out recursively
run: git submodule update --init --recursive

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9

Expand Down Expand Up @@ -163,13 +163,13 @@ jobs:
LUPA_WITH_LUA_DLOPEN: ${{ startsWith(matrix.os, 'windows') && 'false' || 'true' }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Check out recursively
run: git submodule update --init --recursive

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.pyversion }}

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ build
dist
MANIFEST
*.pyc
*.pyo
*.pyd
*.dll
*.egg-info
*.so
*.c
*.patch
wheel*/
*.orig
*~
*wheel*/
venv*
.eggs/
lupa/version.py
lupa/lua*.pyx
TEST/

# Vim swapfiles
*.swp
Expand Down
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Lupa change log
* The bundled Lua 5.1 was updated to 5.1.5 and Lua 5.2 to 5.2.4.
(patch by xxyzz)

* Built with Cython 3.0.6 for improved support of Python 3.12.
* Built with Cython 3.0.8 for improved support of Python 3.12.


2.0 (2023-04-03)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sdist dist/lupa-$(VERSION).tar.gz:
${PYTHON} setup.py sdist

test: local
PYTHONPATH=. $(PYTHON) -m unittest lupa.tests.test
PYTHONPATH=. $(PYTHON) -m unittest -v lupa.tests.test

clean:
rm -fr build lupa/_lupa*.so lupa/lua*.pyx lupa/*.c
Expand Down
134 changes: 71 additions & 63 deletions lupa/_lupa.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -533,48 +533,16 @@ cdef class LuaRuntime:
are placed in the table in order.
Nested mappings / iterables are passed to Lua as userdata
(wrapped Python objects) if `recursive` is False, they are not converted to Lua tables.
(wrapped Python objects) by default. If `recursive` is True,
they are converted to Lua tables recursively, handling loops
and duplicates via identity de-duplication.
"""
assert self._state is not NULL
cdef lua_State *L = self._state
cdef int i = 1
lock_runtime(self)
old_top = lua.lua_gettop(L)
try:
check_lua_stack(L, 5)
lua.lua_newtable(L)
# FIXME: how to check for failure? and nested dict
for obj in args:
if isinstance(obj, dict):
for key, value in obj.iteritems():
py_to_lua(self, L, key, True, recursive)
py_to_lua(self, L, value, False, recursive)
lua.lua_rawset(L, -3)

elif isinstance(obj, _LuaTable):
# Stack: # tbl
(<_LuaObject>obj).push_lua_object(L) # tbl, obj
lua.lua_pushnil(L) # tbl, obj, nil // iterate over obj (-2)
while lua.lua_next(L, -2): # tbl, obj, k, v
lua.lua_pushvalue(L, -2) # tbl, obj, k, v, k // copy key (because
lua.lua_insert(L, -2) # tbl, obj, k, k, v // lua_next needs a key for iteration)
lua.lua_settable(L, -5) # tbl, obj, k // tbl[k] = v
lua.lua_pop(L, 1) # tbl // remove obj from stack

elif isinstance(obj, Mapping):
for key in obj:
value = obj[key]
py_to_lua(self, L, key, True, recursive)
py_to_lua(self, L, value, False, recursive)
lua.lua_rawset(L, -3)
else:
for arg in obj:
py_to_lua(self, L, arg, False, recursive)
lua.lua_rawseti(L, -2, i)
i += 1
return py_from_lua(self, L, -1)
return py_to_lua_table(self, L, args, recursive=recursive)
finally:
lua.lua_settop(L, old_top)
unlock_runtime(self)

def set_max_memory(self, size_t max_memory, total=False):
Expand Down Expand Up @@ -1628,7 +1596,7 @@ cdef int py_to_lua_handle_overflow(LuaRuntime runtime, lua_State *L, object o) e
lua.lua_settop(L, old_top)
raise

cdef int py_to_lua(LuaRuntime runtime, lua_State *L, object o, bint wrap_none=False, bint recursive=False, dict mapped_objs = None) except -1:
cdef int py_to_lua(LuaRuntime runtime, lua_State *L, object o, bint wrap_none=False, bint recursive=False, dict mapped_tables=None) except -1:
"""Converts Python object to Lua
Preconditions:
1 extra slot in the Lua stack
Expand Down Expand Up @@ -1684,39 +1652,19 @@ cdef int py_to_lua(LuaRuntime runtime, lua_State *L, object o, bint wrap_none=Fa
type_flags = (<_PyProtocolWrapper> o)._type_flags
o = (<_PyProtocolWrapper> o)._obj
pushed_values_count = py_to_lua_custom(runtime, L, o, type_flags)
elif recursive and isinstance(o, (list, Sequence)):
if mapped_objs is None:
mapped_objs = {}
if id(o) not in mapped_objs:
lua.lua_createtable(L, _len_as_int(len(o)), 0) # create a table at the top of stack, with narr already known
mapped_objs[id(o)] = lua.lua_gettop(L)
for i, v in enumerate(o, 1):
py_to_lua(runtime, L, v, wrap_none, recursive, mapped_objs)
lua.lua_rawseti(L, -2, i)
else: # self-reference detected
idx = mapped_objs[id(o)]
lua.lua_pushvalue(L, idx)
pushed_values_count = 1
elif recursive and isinstance(o, (dict, Mapping)):
if mapped_objs is None:
mapped_objs = {}
if id(o) not in mapped_objs:
lua.lua_createtable(L, 0, _len_as_int(len(o))) # create a table at the top of stack, with nrec already known
mapped_objs[id(o)] = lua.lua_gettop(L)
for key, value in o.items():
py_to_lua(runtime, L, key, wrap_none, recursive, mapped_objs)
py_to_lua(runtime, L, value, wrap_none, recursive, mapped_objs)
lua.lua_rawset(L, -3)
else: # self-reference detected
idx = mapped_objs[id(o)]
lua.lua_pushvalue(L, idx)
elif recursive and isinstance(o, (list, dict, Sequence, Mapping)):
if mapped_tables is None:
mapped_tables = {}
table = py_to_lua_table(runtime, L, (o,), recursive=recursive, mapped_tables=mapped_tables)
(<_LuaObject> table).push_lua_object(L)
pushed_values_count = 1
else:
# prefer __getitem__ over __getattr__ by default
type_flags = OBJ_AS_INDEX if hasattr(o, '__getitem__') else 0
pushed_values_count = py_to_lua_custom(runtime, L, o, type_flags)
return pushed_values_count


cdef int push_encoded_unicode_string(LuaRuntime runtime, lua_State *L, unicode ustring) except -1:
cdef bytes bytes_string = ustring.encode(runtime._encoding)
lua.lua_pushlstring(L, <char*>bytes_string, len(bytes_string))
Expand Down Expand Up @@ -1775,6 +1723,7 @@ cdef bint py_to_lua_custom(LuaRuntime runtime, lua_State *L, object o, int type_

return 1 # values pushed


cdef inline int _isascii(unsigned char* s) noexcept:
cdef unsigned char c = 0
while s[0]:
Expand All @@ -1797,6 +1746,65 @@ cdef bytes _asciiOrNone(s):
return <bytes>s


cdef _LuaTable py_to_lua_table(LuaRuntime runtime, lua_State* L, tuple items, bint recursive=False, dict mapped_tables=None):
"""
Create a new Lua table and add different kinds of values from the sequence 'items' to it.
Dicts, Mappings and Lua tables are unpacked into key-value pairs.
Everything else is considered a sequence of plain values that get appended to the table.
"""
cdef int i = 1
check_lua_stack(L, 5)
old_top = lua.lua_gettop(L)
lua.lua_newtable(L)
# FIXME: handle allocation errors
cdef int lua_table_ref = lua.lua_gettop(L) # the index of the lua table which we are filling
if recursive and mapped_tables is None:
mapped_tables = {}
try:
for obj in items:
if recursive:
if id(obj) not in mapped_tables:
# this object is never seen before, we should cache it
mapped_tables[id(obj)] = lua_table_ref
else:
# this object has been cached, just get the corresponding lua table's index
idx = mapped_tables[id(obj)]
return new_lua_table(runtime, L, <int>idx)
if isinstance(obj, dict):
for key, value in (<dict>obj).items():
py_to_lua(runtime, L, key, wrap_none=True, recursive=recursive, mapped_tables=mapped_tables)
py_to_lua(runtime, L, value, wrap_none=False, recursive=recursive, mapped_tables=mapped_tables)
lua.lua_rawset(L, -3)

elif isinstance(obj, _LuaTable):
# Stack: # tbl
(<_LuaObject> obj).push_lua_object(L) # tbl, obj
lua.lua_pushnil(L) # tbl, obj, nil // iterate over obj (-2)
while lua.lua_next(L, -2): # tbl, obj, k, v
lua.lua_pushvalue(L, -2) # tbl, obj, k, v, k // copy key (because
lua.lua_insert(L, -2) # tbl, obj, k, k, v // lua_next needs a key for iteration)
lua.lua_settable(L, -5) # tbl, obj, k // tbl[k] = v
lua.lua_pop(L, 1) # tbl // remove obj from stack

elif isinstance(obj, Mapping):
for key in obj:
value = obj[key]
py_to_lua(runtime, L, key, wrap_none=True, recursive=recursive, mapped_tables=mapped_tables)
py_to_lua(runtime, L, value, wrap_none=False, recursive=recursive, mapped_tables=mapped_tables)
lua.lua_rawset(L, -3)

else:
for arg in obj:
py_to_lua(runtime, L, arg, wrap_none=False, recursive=recursive, mapped_tables=mapped_tables)
lua.lua_rawseti(L, -2, i)
i += 1

return new_lua_table(runtime, L, -1)
finally:
lua.lua_settop(L, old_top)


# error handling

cdef int raise_lua_error(LuaRuntime runtime, lua_State* L, int result) except -1:
Expand Down
8 changes: 8 additions & 0 deletions lupa/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ class LupaTestCase(unittest.TestCase):
"""
lupa = lupa

if sys.version_info < (3, 4):
from contextlib import contextmanager

@contextmanager
def subTest(self, message=None, **parameters):
"""Dummy implementation"""
yield


def find_lua_modules():
modules = [lupa]
Expand Down
Loading

0 comments on commit f665f19

Please sign in to comment.