bug#64309: Python dlopen()s musl libc

2023-06-26 Thread Athena Martin via Bug reports for GNU Guix
I've had experiences now with multiple Guix packages, including gajim
(bug 60235) and now python-neovim-remote, which have an issue where
Python tries to dlopen() libc, but finds the system libc instead of
Guix's, resulting on Alpine Linux hosts in a crash with this message:

ImportError: libc.musl-x86_64.so.1: cannot open shared object file: No such 
file or directory

There are a variety of tracebacks that lead up to this, depending on
the package in question.


pgpud8qmC_AkJ.pgp
Description: OpenPGP digital signature


bug#64309: Python dlopen()s musl libc

2023-07-07 Thread Ludovic Courtès
Hi,

Athena Martin  skribis:

> I've had experiences now with multiple Guix packages, including gajim
> (bug 60235) and now python-neovim-remote, which have an issue where
> Python tries to dlopen() libc, but finds the system libc instead of
> Guix's, resulting on Alpine Linux hosts in a crash with this message:
>
> ImportError: libc.musl-x86_64.so.1: cannot open shared object file: No such 
> file or directory
>
> There are a variety of tracebacks that lead up to this, depending on
> the package in question.

Could you provide a command to reproduce this?

Thanks in advance,
Ludo’.





bug#64309: Python dlopen()s musl libc

2023-07-08 Thread Athena Martin via Bug reports for GNU Guix
> > ImportError: libc.musl-x86_64.so.1: cannot open shared object file:
> > No such file or directory
> 
> Could you provide a command to reproduce this?

On an Alpine Linux Edge host, I reproduce with:

$ guix install python-neovim-remote
$ nvr

It's possible that there's some quirk of my specific environment.

(I'm currently in the process of switching to Guix System so I may not
be able to reproduce this forever.)


pgpw5r_ldMtq8.pgp
Description: OpenPGP digital signature


bug#64309: Python dlopen()s musl libc

2023-07-09 Thread Josselin Poiret via Bug reports for GNU Guix
Hi Athena,

Athena Martin via Bug reports for GNU Guix  writes:

> On an Alpine Linux Edge host, I reproduce with:
>
> $ guix install python-neovim-remote
> $ nvr
>
> It's possible that there's some quirk of my specific environment.
>
> (I'm currently in the process of switching to Guix System so I may not
> be able to reproduce this forever.)

Can you do `LD_DEBUG=libs nvr` so that we get a log of what ld's trying
to load?

Best,
-- 
Josselin Poiret


signature.asc
Description: PGP signature


bug#64309: Python dlopen()s musl libc

2023-07-09 Thread Athena Martin via Bug reports for GNU Guix
> Can you do `LD_DEBUG=libs nvr` so that we get a log of what ld's
> trying to load?

I've attached the full log. The first mention of musl is on line 200 and
everything seems to happen pretty fast, here's the most relevant
portion:

 16405: find library=libc.musl-x86_64.so.1 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:  search 
path=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib (system 
search path)
 16405:   trying 
file=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libc.musl-x86_64.so.1
 16405: 
Traceback (most recent call last):
  File 
"/gnu/store/dsgxdqs620pp284bfm1drbsjqpb36i4n-python-neovim-remote-2.5.1/bin/.nvr-real",
 line 4, in 
import nvr.nvr as mod
  File "/home/alm/.local/lib/python3.10/site-packages/nvr/__init__.py", line 1, 
in 
from .nvr import main
  File "/home/alm/.local/lib/python3.10/site-packages/nvr/nvr.py", line 34, in 

import psutil
  File "/home/alm/.local/lib/python3.10/site-packages/psutil/__init__.py", line 
102, in 
from . import _pslinux as _psplatform
  File "/home/alm/.local/lib/python3.10/site-packages/psutil/_pslinux.py", line 
26, in 
from . import _psutil_linux as cext
ImportError: libc.musl-x86_64.so.1: cannot open shared object file: No such 
file or directory

After that it starts calling fini()s and terminates.
 16405: find library=libc.so.6 [0]; searching
 16405:  search 
cache=/gnu/store/rib9g2ig1xf3kclyl076w28parmncg4k-bash-minimal-5.1.16/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libc.so.6
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/ld-linux-x86-64.so.2
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libc.so.6
 16405: 
 16405: 
 16405: initialize program: 
/gnu/store/rib9g2ig1xf3kclyl076w28parmncg4k-bash-minimal-5.1.16/bin/bash
 16405: 
 16405: 
 16405: transferring control: 
/gnu/store/rib9g2ig1xf3kclyl076w28parmncg4k-bash-minimal-5.1.16/bin/bash
 16405: 
 16405: find library=libpython3.10.so.1.0 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/lib/libpython3.10.so.1.0
 16405: 
 16405: find library=libcrypt.so.1 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libcrypt.so.1
 16405: 
 16405: find library=libm.so.6 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libm.so.6
 16405: 
 16405: find library=libexpat.so.1 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/fw1wywd34vh33l4dq182ds5d7jdz45j5-expat-2.5.0/lib/libexpat.so.1
 16405: 
 16405: find library=libgcc_s.so.1 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/930nwsiysdvy2x5zv1sf6v7ym75z8ayk-gcc-11.3.0-lib/lib/libgcc_s.so.1
 16405: 
 16405: find library=libc.so.6 [0]; searching
 16405:  search 
cache=/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/etc/ld.so.cache
 16405:   trying 
file=/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libc.so.6
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/ld-linux-x86-64.so.2
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libc.so.6
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/930nwsiysdvy2x5zv1sf6v7ym75z8ayk-gcc-11.3.0-lib/lib/libgcc_s.so.1
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libm.so.6
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/fw1wywd34vh33l4dq182ds5d7jdz45j5-expat-2.5.0/lib/libexpat.so.1
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/gsjczqir1wbz8p770zndrpw4rnppmxi3-glibc-2.35/lib/libcrypt.so.1
 16405: 
 16405: 
 16405: calling init: 
/gnu/store/kj6wzba6p192baizq99b489rs8bynpn7-python-3.10.7/lib/libpython3.10.so.1.0
 16405: 
 16405:

bug#64309: Python dlopen()s musl libc

2023-07-10 Thread Josselin Poiret via Bug reports for GNU Guix
Hi Athena,

So it's not the LD_DEBUG output that hold a clue, but rather the Python
traceback.

Athena Martin  writes:

>   File 
> "/gnu/store/dsgxdqs620pp284bfm1drbsjqpb36i4n-python-neovim-remote-2.5.1/bin/.nvr-real",
>  line 4, in 
> import nvr.nvr as mod
>   File "/home/alm/.local/lib/python3.10/site-packages/nvr/__init__.py", line 
> 1, in 
> from .nvr import main
>   File "/home/alm/.local/lib/python3.10/site-packages/nvr/nvr.py", line 34, 
> in 
> import psutil
>   File "/home/alm/.local/lib/python3.10/site-packages/psutil/__init__.py", 
> line 102, in 
> from . import _pslinux as _psplatform
>   File "/home/alm/.local/lib/python3.10/site-packages/psutil/_pslinux.py", 
> line 26, in 
> from . import _psutil_linux as cext
> ImportError: libc.musl-x86_64.so.1: cannot open shared object file: No such 
> file or directory

The nvr package in ~/.local seems to be used instead of a Guix package.
That locally installed nvr package expects to use the host's libc, but
since the python interpreter being used has a fixed RPATH and system
search path it won't find it.

.nvr-real should definitely be using the Python code inside the store, I
wonder why that isn't being done.  Maybe our sitecustomize.py is
misbehaving?  Can you do `guix shell python-neovim-remote python --
python3` then type `import sys.path; sys.path`?

Best,
-- 
Josselin Poiret


signature.asc
Description: PGP signature


bug#64309: Python dlopen()s musl libc

2023-07-10 Thread Athena Martin via Bug reports for GNU Guix
> So it's not the LD_DEBUG output that hold a clue, but rather the
> Python traceback.

> The nvr package in ~/.local seems to be used instead of a Guix
> package. That locally installed nvr package expects to use the host's
> libc, but since the python interpreter being used has a fixed RPATH
> and system search path it won't find it.

Ah, I see.

> .nvr-real should definitely be using the Python code inside the
> store, I wonder why that isn't being done.  Maybe our
> sitecustomize.py is misbehaving?  Can you do `guix shell
> python-neovim-remote python -- python3` then type `import sys.path;
> sys.path`?

$ guix shell python-neovim-remote python -- python3
>>> sys.path
['', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python310.zip', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/lib-dynload',
 '/home/alm/.local/lib/python3.10/site-packages', 
'/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/site-packages']

(It's not from the environment:)

$ guix shell python-neovim-remote python --pure -- python3
>>> sys.path
['', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python310.zip', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10', 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/lib-dynload',
 '/home/alm/.local/lib/python3.10/site-packages', 
'/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
 
'/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/site-packages']
>>> os.environ
environ({'HOME': '/home/alm', 'LOGNAME': 'alm', 'PAGER': 'less', 'DISPLAY': 
':0', 'USER': 'alm', 'TERM': 'xterm-256color', 'PATH': 
'/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/bin', 'GUIX_PYTHONPATH': 
'/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
 'GUIX_ENVIRONMENT': '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile'})


pgpM2JLV3bskc.pgp
Description: OpenPGP digital signature


bug#64309: Python dlopen()s musl libc

2023-07-10 Thread Athena Martin via Bug reports for GNU Guix
> The nvr package in ~/.local seems to be used instead of a Guix
> package. That locally installed nvr package expects to use the
> host's libc, but since the python interpreter being used has a
> fixed RPATH and system search path it won't find it.  

I've just checked and temporarily removing .local/lib/python3.10 makes
Guix's nvr work, and takes the .local site-packages off sys.path.


pgpz6JI82iCYQ.pgp
Description: OpenPGP digital signature


bug#64309: Python dlopen()s musl libc

2023-07-11 Thread Josselin Poiret via Bug reports for GNU Guix
Hi Athena,

Athena Martin  writes:

> $ guix shell python-neovim-remote python -- python3
 sys.path
> ['', 
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python310.zip',
>  '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10', 
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/lib-dynload',
>  '/home/alm/.local/lib/python3.10/site-packages', 
> '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
>  
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/site-packages']
>
> (It's not from the environment:)
>
> $ guix shell python-neovim-remote python --pure -- python3
 sys.path
> ['', 
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python310.zip',
>  '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10', 
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/lib-dynload',
>  '/home/alm/.local/lib/python3.10/site-packages', 
> '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
>  
> '/gnu/store/dy3xh053ahkhrp2jamggq8cpsyvp8mg0-python-3.10.7/lib/python3.10/site-packages']
 os.environ
> environ({'HOME': '/home/alm', 'LOGNAME': 'alm', 'PAGER': 'less', 'DISPLAY': 
> ':0', 'USER': 'alm', 'TERM': 'xterm-256color', 'PATH': 
> '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/bin', 'GUIX_PYTHONPATH': 
> '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile/lib/python3.10/site-packages',
>  'GUIX_ENVIRONMENT': '/gnu/store/ll75wx2cvm1dbbxjr095lcs1653q2zz1-profile'})

So, looks like the default Python behavior is to load the usercustomize
after the sitecustomize [1], which leads to exactly the behavior you're
experiencing.  I don't know what we should do here, maybe pass `-s` to
the shebang line of Python to disable loading the usercustomize?  That
would probably be a world-rebuild though.  CC'ing the Python team to see
what they think.

[1] https://docs.python.org/3/library/site.html

Best,
-- 
Josselin Poiret


signature.asc
Description: PGP signature


bug#64309: Python dlopen()s musl libc

2023-07-11 Thread Lars-Dominik Braun
Hi,

there’s a similar issue #63912 and a thread on the guix-devel
mailinglist at
https://lists.gnu.org/archive/html/guix-devel/2023-06/msg00066.html

> So, looks like the default Python behavior is to load the usercustomize
> after the sitecustomize [1], which leads to exactly the behavior you're
> experiencing.  I don't know what we should do here, maybe pass `-s` to
> the shebang line of Python to disable loading the usercustomize?  That
> would probably be a world-rebuild though.  CC'ing the Python team to see
> what they think.

I think the problem is bigger than usercustomize. Any custom PYTHONPATH
also slips through and causes this issue, as well as any custom
GUIX_PYTHONPATH, because the executable wrapper appends it (think nested
`guix shell` invokations with different versions of a library for
an example where this could go wrong).

Guix-managed Python packages (libraries nor applications) should
generally not pick up dependencies from random paths – only those
from their package description, so we can keep Guix’ promise of being
self-contained.

I have experimented with customizing Python’s importing mechanism
through a custom MetaPathFinder. It works by adding a __guix_pythonpath__
variable to every Python package’s __init__.py file and modifying the
module loader’s search path accordingly if such a variable exists. It
would provide exactly that guarantee, but it’s just a PoC at this
point – see attached file.

Apart from that I don’t see a good short-term solution right now. It’s
just how Python works.

Cheers,
Lars

# Credits to
# 
https://tenthousandmeters.com/blog/python-behind-the-scenes-11-how-the-python-import-system-works/
# for the very good explanation of how Python’s import statement works.

import sys, os
from importlib.abc import MetaPathFinder
from importlib.machinery import SourceFileLoader, ModuleSpec, PathFinder

class GuixPythonFinder (MetaPathFinder):
def find_spec (self, fullname, path, target=None):
# Short-circuit for non-top-level imports, which already have a path.
if path:
return None

attrname = '__guix_pythonpath__'
searchPath = None
# Search for our caller.
frame = sys._getframe ()
while frame:
# If he has a search path, use it. This is mainly for executable
# scripts with `__name__ == '__main__'`.
searchPath = frame.f_globals.get (attrname, None)
if not searchPath:
# Otherwise check the top-level package for search paths
# declared in __init__.py
package = frame.f_globals.get ('__package__')
if package:
module = sys.modules.get (package)
if module:
searchPath = getattr (module, attrname, None)
if searchPath is not None:
break
frame = frame.f_back

# If we have a caller…
if searchPath is not None:
return PathFinder.find_spec (fullname, searchPath, target=target)
else:
# Otherwise we’re not responsible for this module.
return None

sys.meta_path.insert (0, GuixPythonFinder ())



bug#64309: Python dlopen()s musl libc

2023-07-12 Thread Josselin Poiret via Bug reports for GNU Guix
Hi Lars,

Lars-Dominik Braun  writes:

> I think the problem is bigger than usercustomize. Any custom PYTHONPATH
> also slips through and causes this issue, as well as any custom
> GUIX_PYTHONPATH, because the executable wrapper appends it (think nested
> `guix shell` invokations with different versions of a library for
> an example where this could go wrong).
>
> Guix-managed Python packages (libraries nor applications) should
> generally not pick up dependencies from random paths – only those
> from their package description, so we can keep Guix’ promise of being
> self-contained.
>
> I have experimented with customizing Python’s importing mechanism
> through a custom MetaPathFinder. It works by adding a __guix_pythonpath__
> variable to every Python package’s __init__.py file and modifying the
> module loader’s search path accordingly if such a variable exists. It
> would provide exactly that guarantee, but it’s just a PoC at this
> point – see attached file.

Woah, looks like a neat solution.  Do you think it would scale for all
our Python packages without manual intervention?  If so, this would
definitely be the way forward.

> Apart from that I don’t see a good short-term solution right now. It’s
> just how Python works.

I mostly agree with you, but for this rather common case of having also
a usercustomize it would be nice to circumvent it.  In general, I don't
think we ever want a Guix-produced Python script to load the
usercustomize, hence my suggestion.  The other case of PYTHONPATH is
also annoying but can be tamed by modifying the env variable
temporarily.

Best,
-- 
Josselin Poiret


signature.asc
Description: PGP signature


bug#64309: Python dlopen()s musl libc

2023-07-16 Thread Lars-Dominik Braun
Hi Josselin,

> Woah, looks like a neat solution.  Do you think it would scale for all
> our Python packages without manual intervention?  If so, this would
> definitely be the way forward.

I hope so, but haven’t tried it yet.

> I mostly agree with you, but for this rather common case of having also
> a usercustomize it would be nice to circumvent it.  In general, I don't
> think we ever want a Guix-produced Python script to load the
> usercustomize, hence my suggestion.  The other case of PYTHONPATH is
> also annoying but can be tamed by modifying the env variable
> temporarily.

True, PYTHONPATH can be unset more easily than moving the user site
dir. I’ll have a look at #64573, which should work around this issue.

Cheers,
Lars