scoder / lupa

Lua in Python
http://pypi.python.org/pypi/lupa
Other
1.02k stars 136 forks source link

Error loading C modules in 2.x #246

Closed riconnon closed 11 months ago

riconnon commented 1 year ago

I'm using the system Lua installation with lupa such that I can load C modules built against Lua.

Upgrading from 1.14.1 to 2.x I start to get undefined symbol errors when loading such a module.

See the following output for minimal steps to reproduce in the official Python docker image:

$ docker run -it --rm python /bin/bash
root@f7608e99322a:/# apt update
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]
Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8906 kB]
Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [6408 B]
Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [78.6 kB]
Fetched 9242 kB in 1s (8169 kB/s)                     
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
7 packages can be upgraded. Run 'apt list --upgradable' to see them.
root@f7608e99322a:/# apt install liblua5.4-dev lua-cjson
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  liblua5.4-0 libtool-bin
The following NEW packages will be installed:
  liblua5.4-0 liblua5.4-dev libtool-bin lua-cjson
0 upgraded, 4 newly installed, 0 to remove and 7 not upgraded.
Need to get 758 kB of archives.
After this operation, 2609 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://deb.debian.org/debian bookworm/main amd64 liblua5.4-0 amd64 5.4.4-3 [137 kB]
Get:2 http://deb.debian.org/debian bookworm/main amd64 liblua5.4-dev amd64 5.4.4-3 [166 kB]
Get:3 http://deb.debian.org/debian bookworm/main amd64 libtool-bin amd64 2.4.7-5 [436 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 lua-cjson amd64 2.1.0+dfsg-2.2 [19.4 kB]
Fetched 758 kB in 0s (12.1 MB/s)      
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package liblua5.4-0:amd64.
(Reading database ... 23974 files and directories currently installed.)
Preparing to unpack .../liblua5.4-0_5.4.4-3_amd64.deb ...
Unpacking liblua5.4-0:amd64 (5.4.4-3) ...
Selecting previously unselected package liblua5.4-dev:amd64.
Preparing to unpack .../liblua5.4-dev_5.4.4-3_amd64.deb ...
Unpacking liblua5.4-dev:amd64 (5.4.4-3) ...
Selecting previously unselected package libtool-bin.
Preparing to unpack .../libtool-bin_2.4.7-5_amd64.deb ...
Unpacking libtool-bin (2.4.7-5) ...
Selecting previously unselected package lua-cjson:amd64.
Preparing to unpack .../lua-cjson_2.1.0+dfsg-2.2_amd64.deb ...
Unpacking lua-cjson:amd64 (2.1.0+dfsg-2.2) ...
Setting up libtool-bin (2.4.7-5) ...
Setting up lua-cjson:amd64 (2.1.0+dfsg-2.2) ...
Setting up liblua5.4-0:amd64 (5.4.4-3) ...
Setting up liblua5.4-dev:amd64 (5.4.4-3) ...
update-alternatives: using /usr/lib/x86_64-linux-gnu/pkgconfig/lua5.4.pc to provide /usr/lib/x86_64-linux-gnu/pkgconfig/lua.pc (lua-pkgconfig-x86_64-linux-gnu) in auto mode
Processing triggers for libc-bin (2.36-9+deb12u1) ...
root@f7608e99322a:/# LUPA_NO_BUNDLE=true LUPA_NO_LUAJIT=true pip install git+https://github.com/scoder/lupa.git
Collecting git+https://github.com/scoder/lupa.git
  Cloning https://github.com/scoder/lupa.git to /tmp/pip-req-build-9vbdt11_
  Running command git clone --filter=blob:none --quiet https://github.com/scoder/lupa.git /tmp/pip-req-build-9vbdt11_
  Resolved https://github.com/scoder/lupa.git to commit 96c1e3ed2695b015fe6a8686352fed0e42660092
  Running command git submodule update --init --recursive -q
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: lupa
  Building wheel for lupa (setup.py) ... done
  Created wheel for lupa: filename=lupa-2.1-cp312-cp312-linux_x86_64.whl size=799301 sha256=d2e1138d1eb1214144bb0a7d710b44f535b10d0598e2b8e848169c0e7851dcb4
  Stored in directory: /tmp/pip-ephem-wheel-cache-k9ldr9xp/wheels/c9/4e/8a/84f829ca492c88f5af2efe2f631a93395e3966403ce187d844
Successfully built lupa
Installing collected packages: lupa
Successfully installed lupa-2.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
root@f7608e99322a:/# python
Python 3.12.0 (main, Oct  3 2023, 01:48:15) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import lupa
>>> lupa.LuaRuntime().execute("require('cjson')")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "lupa/lua.pyx", line 412, in lupa.lua.LuaRuntime.execute
  File "lupa/lua.pyx", line 1736, in lupa.lua.run_lua
  File "lupa/lua.pyx", line 1750, in lupa.lua.call_lua
  File "lupa/lua.pyx", line 1776, in lupa.lua.execute_lua_call
  File "lupa/lua.pyx", line 1665, in lupa.lua.raise_lua_error
lupa.lua.LuaError: error loading module 'cjson' from file '/usr/lib/x86_64-linux-gnu/lua/5.4/cjson.so':
        /usr/lib/x86_64-linux-gnu/lua/5.4/cjson.so: undefined symbol: lua_checkstack
stack traceback:
        [string "<python>"]:1: in main chunk
        [C]: in function 'require'
        [C]: in ?
>>> 

By comparison, using 1.14.1, this works fine:

docker run -it --rm python /bin/bash                                                                                              
root@3086dcc31d10:/# apt update                                                                                                      
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]                                                                       
Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]                                                              
Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]                                                    
Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8906 kB]                                                            
Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [6408 B]                                                     
Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [78.6 kB]                                          
Fetched 9242 kB in 1s (8119 kB/s)                                                                                                    
Reading package lists... Done                                                                                                        
Building dependency tree... Done                                                                                                     
Reading state information... Done                                                                                                    
7 packages can be upgraded. Run 'apt list --upgradable' to see them.                                                                 
root@3086dcc31d10:/# apt install liblua5.4-dev lua-cjson                                                                             
Reading package lists... Done                                                                                                        
Building dependency tree... Done                                                                                                     
Reading state information... Done                                                                                                    
The following additional packages will be installed:                                                                                 
  liblua5.4-0 libtool-bin                                                                                                            
The following NEW packages will be installed:                                                                                        
  liblua5.4-0 liblua5.4-dev libtool-bin lua-cjson                                                                                    
0 upgraded, 4 newly installed, 0 to remove and 7 not upgraded.
Need to get 758 kB of archives.
After this operation, 2609 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://deb.debian.org/debian bookworm/main amd64 liblua5.4-0 amd64 5.4.4-3 [137 kB]
Get:2 http://deb.debian.org/debian bookworm/main amd64 liblua5.4-dev amd64 5.4.4-3 [166 kB]
Get:3 http://deb.debian.org/debian bookworm/main amd64 libtool-bin amd64 2.4.7-5 [436 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 lua-cjson amd64 2.1.0+dfsg-2.2 [19.4 kB]
Fetched 758 kB in 0s (11.7 MB/s)      
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package liblua5.4-0:amd64.
(Reading database ... 23974 files and directories currently installed.)
Preparing to unpack .../liblua5.4-0_5.4.4-3_amd64.deb ...
Unpacking liblua5.4-0:amd64 (5.4.4-3) ...
Selecting previously unselected package liblua5.4-dev:amd64.
Preparing to unpack .../liblua5.4-dev_5.4.4-3_amd64.deb ...
Unpacking liblua5.4-dev:amd64 (5.4.4-3) ...
Selecting previously unselected package libtool-bin.
Preparing to unpack .../libtool-bin_2.4.7-5_amd64.deb ...
Unpacking libtool-bin (2.4.7-5) ...
Selecting previously unselected package lua-cjson:amd64.
Preparing to unpack .../lua-cjson_2.1.0+dfsg-2.2_amd64.deb ...
Unpacking lua-cjson:amd64 (2.1.0+dfsg-2.2) ...
Setting up libtool-bin (2.4.7-5) ...
Setting up lua-cjson:amd64 (2.1.0+dfsg-2.2) ...
Setting up liblua5.4-0:amd64 (5.4.4-3) ...
Setting up liblua5.4-dev:amd64 (5.4.4-3) ...
update-alternatives: using /usr/lib/x86_64-linux-gnu/pkgconfig/lua5.4.pc to provide /usr/lib/x86_64-linux-gnu/pkgconfig/lua.pc (lua-p
kgconfig-x86_64-linux-gnu) in auto mode
Processing triggers for libc-bin (2.36-9+deb12u1) ...
root@3086dcc31d10:/# pip install cython
Collecting cython
  Obtaining dependency information for cython from https://files.pythonhosted.org/packages/7f/a1/05d0b15cbbcb1c9fee9da1624ee64baec31aaf1277aee0ae5a664e0f86bc/Cython-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
  Downloading Cython-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.2 kB)
Downloading Cython-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.5 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.5/3.5 MB 47.7 MB/s eta 0:00:00
Installing collected packages: cython
Successfully installed cython-3.0.3
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
root@3086dcc31d10:/# LUPA_NO_BUNDLE=true LUPA_NO_LUAJIT=true pip install git+https://github.com/scoder/lupa.git@lupa-1.14.1
Collecting git+https://github.com/scoder/lupa.git@lupa-1.14.1
  Cloning https://github.com/scoder/lupa.git (to revision lupa-1.14.1) to /tmp/pip-req-build-416qz4d2
  Running command git clone --filter=blob:none --quiet https://github.com/scoder/lupa.git /tmp/pip-req-build-416qz4d2
  Running command git checkout -q 0e36d838f2052754dcdf87ab02ac4df5f07fabba
  Resolved https://github.com/scoder/lupa.git to commit 0e36d838f2052754dcdf87ab02ac4df5f07fabba
  Running command git submodule update --init --recursive -q
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: lupa
  Building wheel for lupa (setup.py) ... done
  Created wheel for lupa: filename=lupa-1.14.1-cp312-cp312-linux_x86_64.whl size=678262 sha256=9494a178eaf2267579337ad4f4aab313552c59105d14358953816190f3a0fc95
  Stored in directory: /tmp/pip-ephem-wheel-cache-ang9kt48/wheels/27/83/9f/c6285f68b57d504ad9cbe455ac504e5693b192d8b4375c8d98
Successfully built lupa
Installing collected packages: lupa
Successfully installed lupa-1.14.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
root@3086dcc31d10:/# python
Python 3.12.0 (main, Oct  3 2023, 01:48:15) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import lupa
>>> lupa.LuaRuntime().execute("require('cjson')")
>>> 
riconnon commented 1 year ago

I think https://github.com/scoder/lupa/pull/247 might be the fix for this.

scoder commented 1 year ago

Bringing the general discussion back here from #247.

It was found that only one luaXY module can be loaded with global symbols enabled. Otherwise, symbols can get mixed up and link incorrectly when loading Lua extensions.

To allow users an easy selection of the one module to enable it for, we could use lazy loading for the extension modules, but that would probably just make it more obscure when they are actually being loaded. And you'd get random failures late on first usage if anything is wrong with them or the way they are loaded.

One proposal that would actually be possible is an import wrapper, e.g. lua = lupa.load_lua("5.4", dlopen=True) Then users would only ever import lupa and leave the rest to that package. The advantage is that users wouldn't have to care about the platform specific details.

I'm uncertain whether this is worth the complexity, though. I doubt that Lua extensions are a sufficiently common thing to use in Lupa's context to make all users go through a non-standard import mechanism.

riconnon commented 1 year ago

Great summary @scoder and I really appreciate your engagement on this issue!

I think, to perhaps aid discussion, I want summarise the state as of the end of the lupa 1.x series:

What changed in 2.x is the position of the loading of the module and now the system lua dynamic linking no longer works out of the box with system lua.

I, personally, have no qualms adopting explicit use of a context manager to set the flags as required, but I believe it's worth noting this is a regression, specifically for system lua users, compared to 1.x

I do wonder if, for system lua users, where we can guarantee there is only one lua runtime due to the way that setup.py works we could still automatically enable the same dlopenflags.

riconnon commented 1 year ago

I'm uncertain whether this is worth the complexity, though. I doubt that Lua extensions are a sufficiently common thing to use in Lupa's context to make all users go through a non-standard import mechanism.

A further thought on this topic... On the one hand I'm the only person to report this and 2.x has been out for a while, but on the other hand I've been tracking this for a while but was happy to stick on 1.x until the Python 3.12 release prompted me to need a lupa built with a newer Cython.

scoder commented 1 year ago

I'm still not sure regarding the default behaviour. I mean, there were cases where it used to work before, as you wrote in the PR, notably the "I built my own lupa" case with a single extension module. It doesn't seem wrong to enable module loading when you just say "import lupa; lua = lupa.LuaRuntime()`. It would then even work in a few more cases than before.

However, larger applications contexts are an issue where more than one party wants to use Lupa, e.g. two independent dependencies. Then one might say from lupa import LuaRuntime and the other with modload(): from lupa import lua53, and that kind of innocent code would lead to a collision now. Any such setup would run into problems, really, because only one Lua module can support Lua extensions and the first one necessarily wins.

So, definitely pros and cons for both, which speaks for leaving it to the users entirely.

riconnon commented 10 months ago

@scoder I think you said you were keen to release 2.1 with this fix (among others) Do you have any plans for the timescale of that?