Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python: remove LD_LIBRARY_PATH hack from running python environment #1562

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

vlaci
Copy link
Contributor

@vlaci vlaci commented Oct 30, 2024

Injecting LD_LIBRARY_PATH to the Python runtime environment is great
to bypass the need of having to patch non-nix binaries loaded into
that environment, however it breaks down, when Python executes any
other program not compiled for the given Nix system, e.g. a shell
script via subprocess.

To work this around, devenv will inject a pth1 file to the virtual
environment it creates, which mangles the LD_LIBRARY_PATH variable,
undoing any changes to it made by devenv but preserving changes from
other sources.

I am unsure what the best way would be to integrate this, bit I think the approach in general is sane. I am tested only the poetry case currently.

Fixes #1111

Footnotes

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

vlaci added a commit to onekey-sec/unblob that referenced this pull request Oct 30, 2024
Flake lock file updates:

• Updated input 'devenv':
    'github:vlaci/devenv/5186fecbe2835ac45018ea4940388f8523bf1624' (2024-10-04)
  → 'github:vlaci/devenv/953a526754c5ff5e64ab6925c4388b26ae2c45c6' (2024-10-30)

From the related PR[^1] in `devenv`:

Injecting `LD_LIBRARY_PATH` to the Python runtime environment is great
to bypass the need of having to patch non-nix binaries loaded into
that environment, however it breaks down, when Python executes any
other program not compiled for the given Nix system, e.g. a shell
script via `subprocess`.

To work this around, `devenv` will inject a `pth`[^2] file to the virtual
environment it creates, which mangles the `LD_LIBRARY_PATH` variable,
undoing any changes to it made by `devenv` but preserving changes from
other sources.

[^1]: cachix/devenv#1562
[^2]: https://docs.python.org/3/library/site.html
vlaci added a commit to onekey-sec/unblob that referenced this pull request Oct 30, 2024
Flake lock file updates:

• Updated input 'devenv':
    'github:vlaci/devenv/5186fecbe2835ac45018ea4940388f8523bf1624' (2024-10-04)
  → 'github:vlaci/devenv/385d38cfb6872e7f95cae30d94f7fc4afd23fd76' (2024-10-30)

From the related PR[^1] in `devenv`:

Injecting `LD_LIBRARY_PATH` to the Python runtime environment is great
to bypass the need of having to patch non-nix binaries loaded into
that environment, however it breaks down, when Python executes any
other program not compiled for the given Nix system, e.g. a shell
script via `subprocess`.

To work this around, `devenv` will inject a `pth`[^2] file to the virtual
environment it creates, which mangles the `LD_LIBRARY_PATH` variable,
undoing any changes to it made by `devenv` but preserving changes from
other sources.

[^1]: cachix/devenv#1562
[^2]: https://docs.python.org/3/library/site.html
Copy link
Contributor

@bobvanderlinden bobvanderlinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening this PR! The idea of using a pth file makes a lot of sense 👍

pth_file =
let
exec_content = ''
ld_library_path_var = "DYLD_LIBRARY_PATH" if sys.platform == "darwin" else "LD_LIBRARY_PATH"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fairly sure DYLD_LIBRARY_PATH doesn't propagate to subprocesses. So it is probably not needed to support it here.

Maybe the pth file only needs to be added on Linux machines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea about it. I am okay to remove it

os.environ[ld_library_path_var] = ld_library_path.removeprefix(ld_library_path_prefix + ":")
'';
in
pkgs.writeText "devenv.pth" ''import os; exec("""${builtins.replaceStrings [ "\n" ] [ "\\n" ] exec_content}""")'';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need exec? I thought it might be possible to be executed in pth as-is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pth is executed line by line, so this is the only way to use multi-line constructs like if conditions

@bobvanderlinden
Copy link
Contributor

I was thinking, since this will probably be needed just for Linux, that it might be nice to eventually have an LD_PRELOAD that removed the path from LD_LIBRARY_PATH. That way this exact same trick can be applied to ruby, nodejs and more.

Still useful to have this pth to see how it fares in the python world.

@domenkozar
Copy link
Member

Can we get some tests for this?

Injecting `LD_LIBRARY_PATH` to the Python runtime environment is great
to bypass the need of having to patch non-nix binaries loaded into
that environment, however it breaks down, when Python executes any
other program not compiled for the given Nix system, e.g. a shell
script via `subprocess`.

To work this around, `devenv` will inject a `pth`[^1] file to the virtual
environment it creates, which mangles the `LD_LIBRARY_PATH` variable,
undoing any changes to it made by `devenv` but preserving changes from
other sources.

Fixes cachix#1111

[^1]: https://docs.python.org/3/library/site.html
@vlaci
Copy link
Contributor Author

vlaci commented Feb 6, 2025

I've been testing this change in a few projects, and I have mixed results with it:

  • in general everything is fine, when running from the venv
  • there are some edge cases, if some tool creates a new virtualenv, it will be completely broken (like pre-commit when no nix integration is used)

I am also testing a completely different approach, by using patchelf instead:
main...vlaci:devenv:python-venv-patchelf

It has its issues, like pip install in the venv won't recheck for binaries to patch, otherwise it is also a viable approach.

It may also be implemented as an add-on, so python packages providing binaries, like ruff also work.

@vlaci vlaci marked this pull request as ready for review February 6, 2025 17:13
@dsalaza4
Copy link

dsalaza4 commented Feb 6, 2025

Hi,

I think this PR relates to #1699.

I was not aware that languages.python was injecting LD_LIBRARY_PATH to the runtime.

What I noticed is that when using languages.python.poetry.enable = true and providing a pyproject.toml and poetry.lock with ruff = "0.8.0", the fetched ruff binary from pypi does not work properly on the devenv container as it looks for linking libraries in /lib and /lib64.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mixing system packages with python results in shared library errors
4 participants