From 8e3169d85d418bcc8df101a73b141d54c7ab9abb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Vask=C3=B3?=
 <1771332+vlaci@users.noreply.github.com>
Date: Tue, 29 Oct 2024 17:24:39 +0100
Subject: [PATCH] python: remove `LD_LIBRARY_PATH` hack from running python
 environment

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
---
 src/modules/languages/python.nix | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix
index 9c6aef053..bb6af3905 100644
--- a/src/modules/languages/python.nix
+++ b/src/modules/languages/python.nix
@@ -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"
       ":"
@@ -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;
@@ -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
@@ -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
         fi
@@ -206,6 +229,9 @@ let
         if ''${POETRY_INSTALL_COMMAND[@]}
         then
           echo "$ACTUAL_POETRY_CHECKSUM" > "$POETRY_CHECKSUM_FILE"
+          ${lib.optionalString pkgs.stdenv.isLinux ''
+            ln -snf ${pth_file} ".venv/${package.sitePackages}/devenv.pth"
+          ''}
         else
           echo "Poetry install failed. Run 'poetry install' manually."
         fi