Skip to content

table_from(recursive=True): an object referenced more than once converts to an empty table #297

Description

@saquinteros

Version: lupa 2.8 (latest release), Python 3.14.2. Reproduces on every backend: lua51, lua52, lua53, lua54, luajit20, luajit21.

When the same Python object is reachable from more than one place in the structure passed to table_from(..., recursive=True), the first occurrence converts correctly and every later occurrence becomes an empty Lua table. No exception is raised, so the data is lost silently.

from lupa.lua54 import LuaRuntime

rt = LuaRuntime()
shared = {"x": 1}
t = rt.table_from({"a": shared, "b": shared}, recursive=True)

count = rt.eval("function(v) local c = 0 for _ in pairs(v) do c = c + 1 end return c end")
print(count(t.a))                     # 1
print(count(t.b))                     # 0, expected 1
print(rt.eval("rawequal")(t.a, t.b))  # False, expected True

Three references give 1, 0, 0. It also happens when the shared object sits inside lists: {"a": [s], "b": [s]} yields a[1].x == 1 and an empty b[1].

The docstring states that nested structures are converted "handling loops and duplicates via identity de-duplication". Loops do work:

loop = {"x": 1}
loop["self"] = loop
t = rt.table_from(loop, recursive=True)
print(rt.eval("function(t) return rawequal(t, t.self) end")(t))  # True, correct

So the memo is consulted correctly for a self reference, but a sibling duplicate gets a fresh empty table instead of the memoized one.

Expected: t.a and t.b are the same Lua table, which is what identity de-duplication implies.

Impact: silent data loss. A structure built in Python that happens to share a sub-object, which is common when the same row or record is indexed under two keys, arrives in Lua with parts of it empty.

Workaround: recurse in Python and call table_from once per level.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions