Skip to content

Commit

Permalink
python: remove LD_LIBRARY_PATH hack from running python environment
Browse files Browse the repository at this point in the history
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 #1111

[^1]: https://docs.python.org/3/library/site.html
  • Loading branch information
vlaci committed Feb 6, 2025
1 parent 7f756cd commit db96098
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/modules/languages/python.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ let
python = cfg.package;
requiredPythonModules = cfg.package.pkgs.requiredPythonModules;
makeWrapperArgs = [
"--set"
"DEVENV_LD_LIBRARY_PATH_PREFIX"
libraries
"--prefix"
"LD_LIBRARY_PATH"
":"
Expand All @@ -39,6 +42,20 @@ let
follows = [ "nixpkgs" ];
};

pth_file =
let
exec_content = ''
ld_library_path = os.environ.get("LD_LIBRARY_PATH")
ld_library_path_prefix = os.environ.get("DEVENV_LD_LIBRARY_PATH_PREFIX")
if ld_library_path and ld_library_path_prefix:
if ld_library_path == ld_library_path_prefix:
del os.environ["LD_LIBRARY_PATH"]
else:
os.environ["LD_LIBRARY_PATH"] = ld_library_path.removeprefix(ld_library_path_prefix + ":")
'';
in
pkgs.writeText "devenv.pth" ''import os; exec("""${builtins.replaceStrings [ "\n" ] [ "\\n" ] exec_content}""")'';

initVenvScript =
let
USE_UV_SYNC = cfg.uv.sync.enable && builtins.compareVersions cfg.uv.package.version "0.4.4" >= 0;
Expand Down Expand Up @@ -80,6 +97,9 @@ let
''
}
echo "${package.interpreter}" > "$VENV_PATH/.devenv_interpreter"
${lib.optionalString pkgs.stdenv.isLinux ''
ln -snf ${pth_file} "$VENV_PATH/${package.sitePackages}/devenv.pth"
''}
fi
source "$VENV_PATH"/bin/activate
Expand Down Expand Up @@ -157,6 +177,9 @@ let
if "''${UV_SYNC_COMMAND[@]}"
then
echo "$ACTUAL_UV_CHECKSUM" > "$UV_CHECKSUM_FILE"
${lib.optionalString pkgs.stdenv.isLinux ''
ln -snf ${pth_file} "$VENV_PATH/${package.sitePackages}/devenv.pth"
''}
else
echo "uv sync failed. Run 'uv sync' manually." >&2
exit 1
Expand Down Expand Up @@ -186,6 +209,10 @@ let
# Make sure poetry's venv uses the configured Python executable.
${cfg.poetry.package}/bin/poetry env use --no-interaction --quiet ${package.interpreter}
${lib.optionalString pkgs.stdenv.isLinux ''
ln -snf ${pth_file} ".venv/${package.sitePackages}/devenv.pth"
''}
}
function _devenv_poetry_install
Expand Down
11 changes: 11 additions & 0 deletions tests/python-library-path-poetry/.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
python - <<EOF
import os
assert "LD_LIBRARY_PATH" not in os.environ
EOF

LD_LIBRARY_PATH=set-from-shell python - <<EOF
import os
assert os.environ["LD_LIBRARY_PATH"] == "set-from-shell"
EOF
9 changes: 9 additions & 0 deletions tests/python-library-path-poetry/devenv.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{ pkgs, ... }:

{
languages.python = {
enable = true;
poetry.enable = true;
libraries = [ pkgs.file ];
};
}
9 changes: 9 additions & 0 deletions tests/python-library-path-poetry/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[tool.poetry]
name = "python-ld-library-path"
version = "0.1.0"
description = ""
authors = ["Your Name <[email protected]>"]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
13 changes: 13 additions & 0 deletions tests/python-library-path-uv/.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
python - <<EOF
import os
assert "LD_LIBRARY_PATH" not in os.environ
EOF


LD_LIBRARY_PATH=set-from-shell python - <<EOF
import os
import sys
print(sys.prefix)
assert os.environ["LD_LIBRARY_PATH"] == "set-from-shell"
EOF
10 changes: 10 additions & 0 deletions tests/python-library-path-uv/devenv.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ pkgs, ... }:

{
languages.python = {
enable = true;
venv.enable = true;
uv.enable = true;
libraries = [ pkgs.file ];
};
}
11 changes: 11 additions & 0 deletions tests/python-library-path/.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
python - <<EOF
import os
assert "LD_LIBRARY_PATH" not in os.environ
EOF

LD_LIBRARY_PATH=set-from-shell python - <<EOF
import os
assert os.environ["LD_LIBRARY_PATH"] == "set-from-shell"
EOF
9 changes: 9 additions & 0 deletions tests/python-library-path/devenv.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{ pkgs, ... }:

{
languages.python = {
enable = true;
venv.enable = true;
libraries = [ pkgs.file ];
};
}

0 comments on commit db96098

Please sign in to comment.