From bfa2405ba7fcdb0b1252c99ae8d1723837229bd7 Mon Sep 17 00:00:00 2001 From: Sander Date: Wed, 22 Oct 2025 09:51:25 +0200 Subject: [PATCH 01/20] python: wire up a toggle to disable the python wrapper on newer versions of nixpkgs This can also be toggled manually if needed. --- src/modules/languages/python.nix | 97 +++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix index 840b57d44c..ee27f79e70 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python.nix @@ -1,36 +1,54 @@ -{ pkgs -, config -, lib -, ... +{ + pkgs, + config, + lib, + inputs, + ... }: let cfg = config.languages.python; - libraries = lib.makeLibraryPath ( + + nixpkgsInput = inputs.nixpkgs or null; + + # Determine if wrapper should be enabled by default + # Wrapper should be DISABLED for nixpkgs 25.11+ or unstable after Nov 1, 2025 + defaultWrapperEnabled = + if nixpkgsInput != null then + let + # Check if we have version info (stable release) + version = nixpkgsInput.sourceInfo.version or null; + # Check lastModified timestamp (for unstable) + lastModified = nixpkgsInput.sourceInfo.lastModified or 0; + # November 1, 2025 at 00:00:00 UTC + cutoffTimestamp = 1761955200; + in + if version != null then + # For stable releases, enable if version < 25.11 + lib.versionOlder version "25.11" + else + # For unstable, enable if lastModified <= November 1, 2025 + lastModified <= cutoffTimestamp + else + # If no nixpkgs input, default to enabled + true; + + libraries = cfg.libraries ++ (lib.optional cfg.manylinux.enable pkgs.pythonManylinuxPackages.manylinux2014Package) # see https://matrix.to/#/!kjdutkOsheZdjqYmqp:nixos.org/$XJ5CO4bKMevYzZq_rrNo64YycknVFJIJTy6hVCJjRlA?via=nixos.org&via=matrix.org&via=nixos.dev - ++ [ pkgs.stdenv.cc.cc.lib ] - ); + ++ [ pkgs.stdenv.cc.cc.lib ]; readlink = "${pkgs.coreutils}/bin/readlink -f "; - package = pkgs.callPackage ../../python-wrapper.nix { - python = cfg.package; - requiredPythonModules = cfg.package.pkgs.requiredPythonModules; - makeWrapperArgs = - [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - libraries - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - libraries - ]; - }; + package = + if cfg.wrapper.enable then + pkgs.callPackage ../../python-wrapper.nix { + python = cfg.package; + extraLibs = libraries; + requiredPythonModules = cfg.package.pkgs.requiredPythonModules; + } + else + cfg.package; requirements = pkgs.writeText "requirements.txt" ( toString ( @@ -343,6 +361,19 @@ in default = false; description = "Whether `pip install` should avoid outputting messages during devenv initialisation."; }; + wrapper.enable = lib.mkOption { + type = lib.types.bool; + default = defaultWrapperEnabled; + description = '' + Whether to enable a wrapper that allows Python to import packages from the virtual environment. + + Enabled by default for: + - Stable nixpkgs older than version 25.11 + - Unstable nixpkgs with a last commit date before 01-11-2025. + + Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete. + ''; + }; }; uv = { @@ -482,10 +513,9 @@ in languages.python.poetry.install.enable = lib.mkIf cfg.poetry.enable (lib.mkDefault true); languages.python.poetry.install.arguments = lib.optional cfg.poetry.install.onlyInstallRootPackage "--only-root" - ++ lib.optional - ( - !cfg.poetry.install.installRootPackage && !cfg.poetry.install.onlyInstallRootPackage - ) "--no-root" + ++ lib.optional ( + !cfg.poetry.install.installRootPackage && !cfg.poetry.install.onlyInstallRootPackage + ) "--no-root" ++ lib.optional cfg.poetry.install.compile "--compile" ++ lib.optional cfg.poetry.install.quiet "--quiet" ++ lib.optionals (cfg.poetry.install.groups != [ ]) [ @@ -521,10 +551,11 @@ in cachix.pull = lib.mkIf (cfg.version != null) [ "nixpkgs-python" ]; - packages = - [ package ] - ++ (lib.optional cfg.poetry.enable cfg.poetry.package) - ++ (lib.optional cfg.uv.enable cfg.uv.package); + packages = [ + package + ] + ++ (lib.optional cfg.poetry.enable cfg.poetry.package) + ++ (lib.optional cfg.uv.enable cfg.uv.package); env = (lib.optionalAttrs cfg.uv.enable { @@ -532,7 +563,7 @@ in UV_PROJECT_ENVIRONMENT = "${config.env.DEVENV_STATE}/venv"; # Force uv not to download a Python binary when the version in pyproject.toml does not match the one installed by devenv UV_PYTHON_DOWNLOADS = "never"; - # Make uv choose the first python on PATH that is not uv provided. + # Make uv choose the first python on PATH that is not uv provided. # The one it finds is then consistently the one from nix (which is what we want). UV_PYTHON_PREFERENCE = "only-system"; }) From a476d746f89153f0b2e90b4f7990257ecf64481a Mon Sep 17 00:00:00 2001 From: Sander Date: Wed, 22 Oct 2025 10:02:37 +0200 Subject: [PATCH 02/20] python: fix `withPackages` when used with the legacy python wrapper --- src/modules/languages/python.nix | 79 +++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix index ee27f79e70..9148b98160 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python.nix @@ -9,19 +9,22 @@ let cfg = config.languages.python; - nixpkgsInput = inputs.nixpkgs or null; - - # Determine if wrapper should be enabled by default - # Wrapper should be DISABLED for nixpkgs 25.11+ or unstable after Nov 1, 2025 - defaultWrapperEnabled = + # Determine whether the legacy venv wrapper should be enabled + # Enabled for: + # nixpkgs < 25.11 + # nixpkgs-unstable < 2025-11-01 + legacyWrapperEnabled = + let + nixpkgsInput = inputs.nixpkgs or null; + in if nixpkgsInput != null then let # Check if we have version info (stable release) version = nixpkgsInput.sourceInfo.version or null; # Check lastModified timestamp (for unstable) lastModified = nixpkgsInput.sourceInfo.lastModified or 0; - # November 1, 2025 at 00:00:00 UTC - cutoffTimestamp = 1761955200; + # FIXME: update date to actual nixpkgs-unstable release (-ish) + cutoffTimestamp = 1761091200; in if version != null then # For stable releases, enable if version < 25.11 @@ -40,15 +43,6 @@ let ++ [ pkgs.stdenv.cc.cc.lib ]; readlink = "${pkgs.coreutils}/bin/readlink -f "; - package = - if cfg.wrapper.enable then - pkgs.callPackage ../../python-wrapper.nix { - python = cfg.package; - extraLibs = libraries; - requiredPythonModules = cfg.package.pkgs.requiredPythonModules; - } - else - cfg.package; requirements = pkgs.writeText "requirements.txt" ( toString ( @@ -75,7 +69,7 @@ let VENV_PATH="${config.env.DEVENV_STATE}/venv" - profile_python="$(${readlink} ${package.interpreter})" + profile_python="$(${readlink} ${cfg.package.interpreter})" devenv_interpreter_path="$(${pkgs.coreutils}/bin/cat "$VENV_PATH/.devenv_interpreter" 2> /dev/null || echo false )" venv_python="$(${readlink} "$devenv_interpreter_path")" @@ -92,20 +86,20 @@ let ${ if cfg.uv.enable then '' - echo uv venv -p ${package.interpreter} "$VENV_PATH" - uv venv -p ${package.interpreter} "$VENV_PATH" + echo uv venv -p ${cfg.package.interpreter} "$VENV_PATH" + uv venv -p ${cfg.package.interpreter} "$VENV_PATH" '' else '' - echo ${package.interpreter} -m venv ${ + echo ${cfg.package.interpreter} -m venv ${ if builtins.isNull cfg.version || lib.versionAtLeast cfg.version "3.9" then "--upgrade-deps" else "" } "$VENV_PATH" - ${package.interpreter} -m venv ${ + ${cfg.package.interpreter} -m venv ${ if builtins.isNull cfg.version || lib.versionAtLeast cfg.version "3.9" then "--upgrade-deps" else "" } "$VENV_PATH" '' } - echo "${package.interpreter}" > "$VENV_PATH/.devenv_interpreter" + echo "${cfg.package.interpreter}" > "$VENV_PATH/.devenv_interpreter" fi source "$VENV_PATH"/bin/activate @@ -192,7 +186,7 @@ let # Avoid running "uv sync" for every shell. # Only run it when the "pyproject.toml" file or Python interpreter has changed. - local ACTUAL_UV_CHECKSUM="${package.interpreter}:$(${pkgs.nix}/bin/nix-hash --type sha256 pyproject.toml):''${UV_SYNC_COMMAND[@]}" + local ACTUAL_UV_CHECKSUM="${cfg.package.interpreter}:$(${pkgs.nix}/bin/nix-hash --type sha256 pyproject.toml):''${UV_SYNC_COMMAND[@]}" local UV_CHECKSUM_FILE="$VENV_PATH/uv.sync.checksum" if [ -f "$UV_CHECKSUM_FILE" ] then @@ -242,7 +236,7 @@ let unset VIRTUAL_ENV # Make sure poetry's venv uses the configured Python executable. - ${cfg.poetry.package}/bin/poetry env use --no-interaction --quiet ${package.interpreter} + ${cfg.poetry.package}/bin/poetry env use --no-interaction --quiet ${cfg.package.interpreter} } function _devenv_poetry_install @@ -251,7 +245,7 @@ let # Avoid running "poetry install" for every shell. # Only run it when the "poetry.lock" file or Python interpreter has changed. # We do this by storing the interpreter path and a hash of "poetry.lock" in venv. - local ACTUAL_POETRY_CHECKSUM="${package.interpreter}:$(${pkgs.nix}/bin/nix-hash --type sha256 pyproject.toml):$(${pkgs.nix}/bin/nix-hash --type sha256 poetry.lock):''${POETRY_INSTALL_COMMAND[@]}" + local ACTUAL_POETRY_CHECKSUM="${cfg.package.interpreter}:$(${pkgs.nix}/bin/nix-hash --type sha256 pyproject.toml):$(${pkgs.nix}/bin/nix-hash --type sha256 poetry.lock):''${POETRY_INSTALL_COMMAND[@]}" local POETRY_CHECKSUM_FILE=".venv/poetry.lock.checksum" if [ -f "$POETRY_CHECKSUM_FILE" ] then @@ -298,6 +292,35 @@ in default = pkgs.python3; defaultText = lib.literalExpression "pkgs.python3"; description = "The Python package to use."; + apply = + drv: + let + isBuildEnv = drv: lib.hasAttr "extraLibs" (lib.functionArgs drv.override); + # Add extra libraries to the Python `buildEnv`. + appendLibraries = + drv: + (if isBuildEnv drv then drv else drv.buildEnv).override (args: { + extraLibs = (args.extraLibs or [ ]) ++ libraries; + }); + in + # Apply the venv support wrapper if enabled. + if cfg.venv.wrapper.enable then + lib.pipe drv [ + ( + drv: + drv.overrideAttrs (_: { + buildEnv = pkgs.callPackage ../../python-wrapper.nix { + inherit (drv.pkgs) requiredPythonModules; + python = drv; + extraLibs = libraries; + }; + }) + ) + appendLibraries + ] + else + appendLibraries drv; + }; manylinux.enable = lib.mkOption { @@ -363,7 +386,7 @@ in }; wrapper.enable = lib.mkOption { type = lib.types.bool; - default = defaultWrapperEnabled; + default = legacyWrapperEnabled; description = '' Whether to enable a wrapper that allows Python to import packages from the virtual environment. @@ -552,7 +575,7 @@ in cachix.pull = lib.mkIf (cfg.version != null) [ "nixpkgs-python" ]; packages = [ - package + cfg.package ] ++ (lib.optional cfg.poetry.enable cfg.poetry.package) ++ (lib.optional cfg.uv.enable cfg.uv.package); @@ -613,7 +636,7 @@ in }; enterShell = '' - export PYTHONPATH="$DEVENV_PROFILE/${package.sitePackages}''${PYTHONPATH:+:$PYTHONPATH}" + export PYTHONPATH="$DEVENV_PROFILE/${cfg.package.sitePackages}''${PYTHONPATH:+:$PYTHONPATH}" ''; }; } From 9b96d42435a8c311397bba9f7bc1e243153dc6dc Mon Sep 17 00:00:00 2001 From: Sander Date: Wed, 22 Oct 2025 13:58:32 +0200 Subject: [PATCH 03/20] tests: add a `python3.withPackages` test --- tests/python-with-packages/devenv.nix | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/python-with-packages/devenv.nix diff --git a/tests/python-with-packages/devenv.nix b/tests/python-with-packages/devenv.nix new file mode 100644 index 0000000000..97c499b02f --- /dev/null +++ b/tests/python-with-packages/devenv.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: +{ + languages.python = { + enable = true; + package = pkgs.python3.withPackages (ps: [ + ps.matplotlib + ps.numpy + ps.ipython + ps.tkinter + ]); + }; + + enterTest = '' + python -c 'import matplotlib; print("matplotlib works!")' + python -c 'import numpy; print("numpy works!")' + python -c 'import IPython; print("ipython works!")' + python -c 'import tkinter; print("tkinter works!")' + ''; +} From 645e2705756c73fbc75d06b6755244545faea548 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:04:51 +0000 Subject: [PATCH 04/20] Auto generate missing individual markdowns --- docs/src/reference/options.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/src/reference/options.md b/docs/src/reference/options.md index 9ea2679712..f3bb5b97ac 100644 --- a/docs/src/reference/options.md +++ b/docs/src/reference/options.md @@ -16653,6 +16653,34 @@ null or strings concatenated with “\\n” or absolute path +## languages.python.venv.wrapper.enable + + + +Whether to enable a wrapper that allows Python to import packages from the virtual environment. + +Enabled by default for: + + - Stable nixpkgs older than version 25.11 + - Unstable nixpkgs with a last commit date before 01-11-2025. + +Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete. + + + +*Type:* +boolean + + + +*Default:* +` true ` + +*Declared by:* + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + + + ## languages.python.version From 4d38422a0fd3cc5269c6dce715959e0cd0f6b71d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:05:11 +0000 Subject: [PATCH 05/20] Auto generate docs and examples --- docs/src/supported-languages/python.md | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/src/supported-languages/python.md b/docs/src/supported-languages/python.md index fbfb1b9c3c..10fa9395cc 100644 --- a/docs/src/supported-languages/python.md +++ b/docs/src/supported-languages/python.md @@ -743,6 +743,34 @@ null or strings concatenated with “\\n” or absolute path +### languages\.python\.venv\.wrapper\.enable + + + +Whether to enable a wrapper that allows Python to import packages from the virtual environment\. + +Enabled by default for: + + - Stable nixpkgs older than version 25\.11 + - Unstable nixpkgs with a last commit date before 01-11-2025\. + +Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete\. + + + +*Type:* +boolean + + + +*Default:* +` true ` + +*Declared by:* + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + + + ### languages\.python\.version From 09e9f211370eed76edd423d199f75584d421355d Mon Sep 17 00:00:00 2001 From: Sander Date: Wed, 22 Oct 2025 16:19:40 +0200 Subject: [PATCH 06/20] python: bring back LD_LIBRARY_PATH wrapper --- src/modules/languages/python.nix | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix index 9148b98160..fc46d440db 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python.nix @@ -301,6 +301,18 @@ in drv: (if isBuildEnv drv then drv else drv.buildEnv).override (args: { extraLibs = (args.extraLibs or [ ]) ++ libraries; + makeWrapperArgs = [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ]; }); in # Apply the venv support wrapper if enabled. @@ -308,13 +320,22 @@ in lib.pipe drv [ ( drv: - drv.overrideAttrs (_: { - buildEnv = pkgs.callPackage ../../python-wrapper.nix { - inherit (drv.pkgs) requiredPythonModules; - python = drv; - extraLibs = libraries; - }; - }) + drv.overrideAttrs ( + prevAttrs: + let + buildEnv = pkgs.callPackage ../../python-wrapper.nix { + inherit (drv.pkgs) requiredPythonModules; + python = drv; + extraLibs = libraries; + }; + in + { + inherit buildEnv; + passthru = prevAttrs.passthru // { + inherit buildEnv; + }; + } + ) ) appendLibraries ] From 3f43a184c81e4591942376af0b25065e79840760 Mon Sep 17 00:00:00 2001 From: Sander Date: Thu, 23 Oct 2025 16:56:16 +0200 Subject: [PATCH 07/20] python: post-review pass --- src/modules/languages/python.nix | 76 ++++++++++--------- ...rapper.nix => python-build-env-legacy.nix} | 33 ++++---- tests/python-with-packages/devenv.nix | 16 ++++ 3 files changed, 73 insertions(+), 52 deletions(-) rename src/{python-wrapper.nix => python-build-env-legacy.nix} (81%) diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix index fc46d440db..2422c39afb 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python.nix @@ -9,11 +9,11 @@ let cfg = config.languages.python; - # Determine whether the legacy venv wrapper should be enabled + # Determine whether to patch Python's `buildEnv` # Enabled for: # nixpkgs < 25.11 # nixpkgs-unstable < 2025-11-01 - legacyWrapperEnabled = + legacyBuildEnvPatchEnabled = let nixpkgsInput = inputs.nixpkgs or null; in @@ -296,11 +296,29 @@ in drv: let isBuildEnv = drv: lib.hasAttr "extraLibs" (lib.functionArgs drv.override); + + patchBuildEnv = + drv: + drv.overrideAttrs ( + prevAttrs: + let + buildEnv = pkgs.callPackage ../../python-build-env-legacy.nix { + inherit (drv.pkgs) requiredPythonModules; + python = drv; + }; + in + { + inherit buildEnv; + passthru = prevAttrs.passthru // { + inherit buildEnv; + }; + } + ); + # Add extra libraries to the Python `buildEnv`. appendLibraries = drv: (if isBuildEnv drv then drv else drv.buildEnv).override (args: { - extraLibs = (args.extraLibs or [ ]) ++ libraries; makeWrapperArgs = [ "--prefix" "LD_LIBRARY_PATH" @@ -315,28 +333,10 @@ in ]; }); in - # Apply the venv support wrapper if enabled. - if cfg.venv.wrapper.enable then + # Apply the buildEnv patch if enabled. + if cfg.patches.buildEnv.enable then lib.pipe drv [ - ( - drv: - drv.overrideAttrs ( - prevAttrs: - let - buildEnv = pkgs.callPackage ../../python-wrapper.nix { - inherit (drv.pkgs) requiredPythonModules; - python = drv; - extraLibs = libraries; - }; - in - { - inherit buildEnv; - passthru = prevAttrs.passthru // { - inherit buildEnv; - }; - } - ) - ) + patchBuildEnv appendLibraries ] else @@ -390,6 +390,21 @@ in example = "./directory"; }; + patches.buildEnv.enable = lib.mkOption { + type = lib.types.bool; + default = legacyBuildEnvPatchEnabled; + description = '' + Whether to apply fixes to Python's `buildEnv` for correct runtime initialization: + - Executables use `--inherit-argv0` to ensure Python initializes with correct `sys.prefix` and `sys.base_prefix` + - Python package scripts are unwrapped to invoke the environment's interpreter directly + + Without these fixes, venvs cannot access environment packages via `--system-site-packages`. + + Enabled by default for nixpkgs versions prior to 25.11. + Newer nixpkgs releases include upstream fixes that make this patch obsolete. + ''; + }; + venv = { enable = lib.mkEnableOption "Python virtual environment"; requirements = lib.mkOption { @@ -405,19 +420,6 @@ in default = false; description = "Whether `pip install` should avoid outputting messages during devenv initialisation."; }; - wrapper.enable = lib.mkOption { - type = lib.types.bool; - default = legacyWrapperEnabled; - description = '' - Whether to enable a wrapper that allows Python to import packages from the virtual environment. - - Enabled by default for: - - Stable nixpkgs older than version 25.11 - - Unstable nixpkgs with a last commit date before 01-11-2025. - - Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete. - ''; - }; }; uv = { diff --git a/src/python-wrapper.nix b/src/python-build-env-legacy.nix similarity index 81% rename from src/python-wrapper.nix rename to src/python-build-env-legacy.nix index dc4de5d67b..c37aff7c22 100644 --- a/src/python-wrapper.nix +++ b/src/python-build-env-legacy.nix @@ -1,21 +1,21 @@ -{ lib -, stdenv -, buildEnv -, makeBinaryWrapper +{ + lib, + stdenv, + buildEnv, + makeBinaryWrapper, # manually pased -, python -, requiredPythonModules + python, + requiredPythonModules, # extra opts -, extraLibs ? [ ] -, extraOutputsToInstall ? [ ] -, postBuild ? "" -, ignoreCollisions ? false -, permitUserSite ? false + extraLibs ? [ ], + extraOutputsToInstall ? [ ], + postBuild ? "", + ignoreCollisions ? false, + permitUserSite ? false, # Wrap executables with the given argument. -, makeWrapperArgs ? [ ] -, + makeWrapperArgs ? [ ], }: # Create a python executable that knows about additional packages. @@ -55,14 +55,17 @@ let sed -e '1d' -e '3d' ".$prg-wrapped" >> "$out/bin/$prg" chmod +x "$out/bin/$prg" else - makeWrapper "$path/bin/$prg" "$out/bin/$prg" --inherit-argv0 --resolve-argv0 ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} ${lib.concatStringsSep " " makeWrapperArgs} + makeWrapper "$path/bin/$prg" "$out/bin/$prg" --inherit-argv0 --resolve-argv0 ${ + lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"'' + } ${lib.concatStringsSep " " makeWrapperArgs} fi fi fi done fi done - '' + postBuild; + '' + + postBuild; inherit (python) meta; diff --git a/tests/python-with-packages/devenv.nix b/tests/python-with-packages/devenv.nix index 97c499b02f..06adca95ab 100644 --- a/tests/python-with-packages/devenv.nix +++ b/tests/python-with-packages/devenv.nix @@ -8,12 +8,28 @@ ps.ipython ps.tkinter ]); + venv.enable = true; + venv.requirements = '' + requests + pytest + ''; }; enterTest = '' + echo "Testing imports from Nix's withPackages..." python -c 'import matplotlib; print("matplotlib works!")' python -c 'import numpy; print("numpy works!")' python -c 'import IPython; print("ipython works!")' python -c 'import tkinter; print("tkinter works!")' + + echo "Testing imports from venv..." + python -c 'import requests; print("requests works!")' + python -c 'import pytest; print("pytest works!")' + + echo "Verifying Nix packages still accessible from venv..." + python -c 'import matplotlib; print("matplotlib still works!")' + python -c 'import numpy; print("numpy still works!")' + python -c 'import IPython; print("ipython still works!")' + python -c 'import tkinter; print("tkinter still works!")' ''; } From 26b4d8e2b653f32752cd7e434eafa833cbc4ea4a Mon Sep 17 00:00:00 2001 From: Sander Date: Thu, 23 Oct 2025 17:13:55 +0200 Subject: [PATCH 08/20] tests: add a venv to the python withPackages test --- tests/python-with-packages/devenv.nix | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/python-with-packages/devenv.nix b/tests/python-with-packages/devenv.nix index 06adca95ab..7784a76ce8 100644 --- a/tests/python-with-packages/devenv.nix +++ b/tests/python-with-packages/devenv.nix @@ -25,11 +25,5 @@ echo "Testing imports from venv..." python -c 'import requests; print("requests works!")' python -c 'import pytest; print("pytest works!")' - - echo "Verifying Nix packages still accessible from venv..." - python -c 'import matplotlib; print("matplotlib still works!")' - python -c 'import numpy; print("numpy still works!")' - python -c 'import IPython; print("ipython still works!")' - python -c 'import tkinter; print("tkinter still works!")' ''; } From 12b1322c202ce6a4eb5f6bdf13d228acfb8f56f7 Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 24 Oct 2025 10:06:50 +0200 Subject: [PATCH 09/20] python: properly patch buildEnvs --- docs/devenv.nix | 1 + src/modules/languages/python.nix | 160 ++++++++++++++++++++------ src/python-build-env-legacy.nix | 40 ++++--- tests/python-with-packages/devenv.nix | 38 ++++-- 4 files changed, 177 insertions(+), 62 deletions(-) diff --git a/docs/devenv.nix b/docs/devenv.nix index 1663305e5e..c371d87e31 100644 --- a/docs/devenv.nix +++ b/docs/devenv.nix @@ -22,6 +22,7 @@ # For developing the mkdocs-based documentation python = { enable = true; + package = pkgs.python3.withPackages (ps: [ ps.matplotlib ]); # Use a faster package manager uv.enable = true; venv = { diff --git a/src/modules/languages/python.nix b/src/modules/languages/python.nix index 2422c39afb..837d178023 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python.nix @@ -299,48 +299,138 @@ in patchBuildEnv = drv: - drv.overrideAttrs ( - prevAttrs: + # If we got a buildEnv from withPackages, modify its postBuild to use our wrapper logic + # First add runCommand to create bin directory to extraLibs + let + patchedEnv = (drv.override (args: { + extraLibs = (args.extraLibs or []) ++ [ + (pkgs.runCommand "bin" {} '' + mkdir -p $out/bin + '') + ]; + })).overrideAttrs ( + prevAttrs: let - buildEnv = pkgs.callPackage ../../python-build-env-legacy.nix { - inherit (drv.pkgs) requiredPythonModules; - python = drv; - }; - in - { - inherit buildEnv; - passthru = prevAttrs.passthru // { - inherit buildEnv; - }; - } - ); + pythonExecutable = "$out/bin/${drv.python.executable}"; + pythonPath = "$out/${drv.python.sitePackages}"; + permitUserSite = false; + makeWrapperArgs = lib.concatStringsSep " " ( + [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ); + + # Split postBuild by lines + lines = lib.splitString "\n" prevAttrs.postBuild; + + # Use fold to track state (skip counter) and build new lines + result = + lib.foldl + ( + acc: line: + if acc.skip > 0 then + # Skip this line + { + lines = acc.lines; + skip = acc.skip - 1; + } + else if lib.hasInfix ''if [ -L "$out/bin" ]; then'' line then + # Found the start of the postBuild block - replace entire block with our patched version + { + lines = acc.lines ++ [ + ''for path in $paths; do'' + '' if [ -d "$path/bin" ]; then'' + '' cd "$path/bin"'' + '' for prg in *; do'' + '' if [ -f "$prg" ] && [ -x "$prg" ]; then'' + '' rm -f "$out/bin/$prg"'' + '' if [ "$prg" = "${drv.python.executable}" ]; then'' + '' makeWrapper "${drv.python.interpreter}" "$out/bin/$prg" \'' + '' --inherit-argv0 \'' + '' ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''}'' + '' ${makeWrapperArgs}'' + '' elif [ "$(readlink "$prg")" = "${drv.python.executable}" ]; then'' + '' ln -s "${drv.python.executable}" "$out/bin/$prg"'' + '' else'' + '' makeWrapper "$path/bin/$prg" "$out/bin/$prg" \'' + '' --set NIX_PYTHONPREFIX "$out" \'' + '' --set NIX_PYTHONEXECUTABLE ${pythonExecutable} \'' + '' --set NIX_PYTHONPATH ${pythonPath} \'' + '' ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''}'' + '' ${makeWrapperArgs}'' + '' fi'' + '' fi'' + '' done'' + '' fi'' + ''done'' + ]; + skip = 17; # Skip the next 17 lines of the original block (18 total) + } + else + # Keep this line + { + lines = acc.lines ++ [ line ]; + skip = 0; + } + ) + { + lines = [ ]; + skip = 0; + } + lines; + + filteredLines = result.lines; + in + { + postBuild = lib.concatStringsSep "\n" filteredLines; + passthru = prevAttrs.passthru // { + interpreter = "${patchedEnv}/bin/${drv.python.executable}"; + }; + } + ); + in + patchedEnv; # Add extra libraries to the Python `buildEnv`. appendLibraries = drv: - (if isBuildEnv drv then drv else drv.buildEnv).override (args: { - makeWrapperArgs = [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ]; - }); + if isBuildEnv drv then + # Already a buildEnv, just return it (libraries already added in patchBuildEnv) + drv + else + # Use the patched buildEnv + (drv.buildEnv).override (args: { + makeWrapperArgs = [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ]; + }); in # Apply the buildEnv patch if enabled. - if cfg.patches.buildEnv.enable then - lib.pipe drv [ - patchBuildEnv - appendLibraries - ] - else - appendLibraries drv; + # if cfg.patches.buildEnv.enable then + lib.pipe drv [ + patchBuildEnv + appendLibraries + ]; + # else + # appendLibraries drv; }; diff --git a/src/python-build-env-legacy.nix b/src/python-build-env-legacy.nix index c37aff7c22..55e86a8b9c 100644 --- a/src/python-build-env-legacy.nix +++ b/src/python-build-env-legacy.nix @@ -2,6 +2,7 @@ lib, stdenv, buildEnv, + runCommand, makeBinaryWrapper, # manually pased @@ -22,7 +23,11 @@ let env = let - paths = requiredPythonModules (extraLibs ++ [ python ]); + paths = requiredPythonModules (extraLibs ++ [ python ]) ++ [ + (runCommand "bin" { } '' + mkdir -p $out/bin + '') + ]; pythonPath = "${placeholder "out"}/${python.sitePackages}"; pythonExecutable = "${placeholder "out"}/bin/${python.executable}"; in @@ -36,29 +41,26 @@ let nativeBuildInputs = [ makeBinaryWrapper ]; postBuild = '' - if [ -L "$out/bin" ]; then - unlink "$out/bin" - fi - mkdir -p "$out/bin" - - rm -f $out/bin/.*-wrapped - for path in ${lib.concatStringsSep " " paths}; do if [ -d "$path/bin" ]; then cd "$path/bin" for prg in *; do - if [ -f "$prg" ]; then + if [ -f "$prg" ] && [ -x "$prg" ]; then rm -f "$out/bin/$prg" - if [ -x "$prg" ]; then - if [ -f ".$prg-wrapped" ]; then - echo "#!${pythonExecutable}" > "$out/bin/$prg" - sed -e '1d' -e '3d' ".$prg-wrapped" >> "$out/bin/$prg" - chmod +x "$out/bin/$prg" - else - makeWrapper "$path/bin/$prg" "$out/bin/$prg" --inherit-argv0 --resolve-argv0 ${ - lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"'' - } ${lib.concatStringsSep " " makeWrapperArgs} - fi + if [ "$prg" = "${python.executable}" ]; then + makeWrapper "${python.interpreter}" "$out/bin/$prg" \ + --inherit-argv0 \ + ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} \ + ${lib.concatStringsSep " " makeWrapperArgs} + elif [ "$(readlink "$prg")" = "${python.executable}" ]; then + ln -s "${python.executable}" "$out/bin/$prg" + else + makeWrapper "$path/bin/$prg" "$out/bin/$prg" \ + --set NIX_PYTHONPREFIX "$out" \ + --set NIX_PYTHONEXECUTABLE ${pythonExecutable} \ + --set NIX_PYTHONPATH ${pythonPath} \ + ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} \ + ${lib.concatStringsSep " " makeWrapperArgs} fi fi done diff --git a/tests/python-with-packages/devenv.nix b/tests/python-with-packages/devenv.nix index 7784a76ce8..665123ac35 100644 --- a/tests/python-with-packages/devenv.nix +++ b/tests/python-with-packages/devenv.nix @@ -8,6 +8,7 @@ ps.ipython ps.tkinter ]); + patches.buildEnv.enable = true; venv.enable = true; venv.requirements = '' requests @@ -16,14 +17,35 @@ }; enterTest = '' - echo "Testing imports from Nix's withPackages..." - python -c 'import matplotlib; print("matplotlib works!")' - python -c 'import numpy; print("numpy works!")' - python -c 'import IPython; print("ipython works!")' - python -c 'import tkinter; print("tkinter works!")' + echo "Verifying sys.base_prefix points to wrapped python..." + python <<'EOF' + import sys + import os - echo "Testing imports from venv..." - python -c 'import requests; print("requests works!")' - python -c 'import pytest; print("pytest works!")' + print("sys.base_prefix:", sys.base_prefix) + print("sys.executable:", sys.executable) + + # Check that sys.base_prefix points to the -env buildEnv, not the bare interpreter + assert "-env" in sys.base_prefix, \ + f"sys.base_prefix ({sys.base_prefix}) should point to python-env with packages, not bare interpreter" + + # Verify packages from withPackages are accessible from base_prefix + site_packages = os.path.join(sys.base_prefix, "lib", f"python{sys.version_info.major}.{sys.version_info.minor}", "site-packages") + matplotlib_path = os.path.join(site_packages, "matplotlib") + assert os.path.exists(matplotlib_path), \ + f"matplotlib should exist in base_prefix site-packages at {matplotlib_path}, but it doesn't" + + print("✓ sys.base_prefix correctly points to wrapped python with packages") + EOF + + echo "Testing imports from Nix's withPackages..." + python -c 'import matplotlib; print("matplotlib works!")' + python -c 'import numpy; print("numpy works!")' + python -c 'import IPython; print("ipython works!")' + python -c 'import tkinter; print("tkinter works!")' + + echo "Testing imports from venv..." + python -c 'import requests; print("requests works!")' + python -c 'import pytest; print("pytest works!")' ''; } From bea13ad269ce1d585208e9d26a347d88eafc23fe Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 24 Oct 2025 10:17:37 +0200 Subject: [PATCH 10/20] python: avoid nesting `buildEnv` by patching in-place --- .../{python.nix => python/default.nix} | 239 ++++++++---------- .../languages/python/postbuild-wrapper.nix | 49 ++++ .../languages/python/wrapper-legacy.nix | 72 ++++++ src/python-build-env-legacy.nix | 91 ------- 4 files changed, 232 insertions(+), 219 deletions(-) rename src/modules/languages/{python.nix => python/default.nix} (80%) create mode 100644 src/modules/languages/python/postbuild-wrapper.nix create mode 100644 src/modules/languages/python/wrapper-legacy.nix delete mode 100644 src/python-build-env-legacy.nix diff --git a/src/modules/languages/python.nix b/src/modules/languages/python/default.nix similarity index 80% rename from src/modules/languages/python.nix rename to src/modules/languages/python/default.nix index 837d178023..73123b0b57 100644 --- a/src/modules/languages/python.nix +++ b/src/modules/languages/python/default.nix @@ -1,9 +1,8 @@ -{ - pkgs, - config, - lib, - inputs, - ... +{ pkgs +, config +, lib +, inputs +, ... }: let @@ -27,13 +26,13 @@ let cutoffTimestamp = 1761091200; in if version != null then - # For stable releases, enable if version < 25.11 + # For stable releases, enable if version < 25.11 lib.versionOlder version "25.11" else - # For unstable, enable if lastModified <= November 1, 2025 + # For unstable, enable if lastModified <= November 1, 2025 lastModified <= cutoffTimestamp else - # If no nixpkgs input, default to enabled + # If no nixpkgs input, default to enabled true; libraries = @@ -300,138 +299,121 @@ in patchBuildEnv = drv: # If we got a buildEnv from withPackages, modify its postBuild to use our wrapper logic - # First add runCommand to create bin directory to extraLibs let - patchedEnv = (drv.override (args: { - extraLibs = (args.extraLibs or []) ++ [ - (pkgs.runCommand "bin" {} '' - mkdir -p $out/bin - '') - ]; - })).overrideAttrs ( - prevAttrs: - let - pythonExecutable = "$out/bin/${drv.python.executable}"; - pythonPath = "$out/${drv.python.sitePackages}"; - permitUserSite = false; - makeWrapperArgs = lib.concatStringsSep " " ( - [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ); - - # Split postBuild by lines - lines = lib.splitString "\n" prevAttrs.postBuild; - - # Use fold to track state (skip counter) and build new lines - result = - lib.foldl - ( - acc: line: - if acc.skip > 0 then - # Skip this line - { - lines = acc.lines; - skip = acc.skip - 1; - } - else if lib.hasInfix ''if [ -L "$out/bin" ]; then'' line then - # Found the start of the postBuild block - replace entire block with our patched version - { - lines = acc.lines ++ [ - ''for path in $paths; do'' - '' if [ -d "$path/bin" ]; then'' - '' cd "$path/bin"'' - '' for prg in *; do'' - '' if [ -f "$prg" ] && [ -x "$prg" ]; then'' - '' rm -f "$out/bin/$prg"'' - '' if [ "$prg" = "${drv.python.executable}" ]; then'' - '' makeWrapper "${drv.python.interpreter}" "$out/bin/$prg" \'' - '' --inherit-argv0 \'' - '' ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''}'' - '' ${makeWrapperArgs}'' - '' elif [ "$(readlink "$prg")" = "${drv.python.executable}" ]; then'' - '' ln -s "${drv.python.executable}" "$out/bin/$prg"'' - '' else'' - '' makeWrapper "$path/bin/$prg" "$out/bin/$prg" \'' - '' --set NIX_PYTHONPREFIX "$out" \'' - '' --set NIX_PYTHONEXECUTABLE ${pythonExecutable} \'' - '' --set NIX_PYTHONPATH ${pythonPath} \'' - '' ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''}'' - '' ${makeWrapperArgs}'' - '' fi'' - '' fi'' - '' done'' - '' fi'' - ''done'' - ]; - skip = 17; # Skip the next 17 lines of the original block (18 total) - } - else - # Keep this line - { - lines = acc.lines ++ [ line ]; - skip = 0; - } - ) + makePostBuildWrapper = pkgs.callPackage ./postbuild-wrapper.nix { }; + patchedEnv = + (drv.override (args: { + extraLibs = (args.extraLibs or [ ]) ++ [ + (pkgs.runCommand "bin" { } '' + mkdir -p $out/bin + '') + ]; + })).overrideAttrs + ( + prevAttrs: + let + makeWrapperArgs = [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ]; + + # Generate the patched wrapper script using the shared function + patchedWrapperScript = makePostBuildWrapper { + inherit (drv) python; + inherit makeWrapperArgs; + }; + + # Split postBuild by lines + lines = lib.splitString "\n" prevAttrs.postBuild; + + # Use fold to track state (skip counter) and build new lines + result = + lib.foldl + ( + acc: line: + if acc.skip > 0 then + # Skip this line + { + lines = acc.lines; + skip = acc.skip - 1; + } + else if lib.hasInfix ''if [ -L "$out/bin" ]; then'' line then + # Found the start of the postBuild block - replace entire block with our patched version + { + lines = acc.lines ++ [ patchedWrapperScript ]; + skip = 17; # Skip the next 17 lines of the original block (18 total) + } + else + # Keep this line + { + lines = acc.lines ++ [ line ]; + skip = 0; + } + ) + { + lines = [ ]; + skip = 0; + } + lines; + + filteredLines = result.lines; + in { - lines = [ ]; - skip = 0; + postBuild = lib.concatStringsSep "\n" filteredLines; + passthru = prevAttrs.passthru // { + interpreter = "${patchedEnv}/bin/${drv.python.executable}"; + }; } - lines; - - filteredLines = result.lines; - in - { - postBuild = lib.concatStringsSep "\n" filteredLines; - passthru = prevAttrs.passthru // { - interpreter = "${patchedEnv}/bin/${drv.python.executable}"; - }; - } - ); + ); in - patchedEnv; + if isBuildEnv drv then patchedEnv else drv; + + overrideBuildEnv = + drv: + drv.overrideAttrs (prevAttrs: rec { + buildEnv = pkgs.callPackage ./wrapper-legacy.nix { + python = drv; + requiredPythonModules = drv.pkgs.requiredPythonModules; + }; + passthru = prevAttrs.passthru // { + inherit buildEnv; + }; + }); # Add extra libraries to the Python `buildEnv`. appendLibraries = drv: - if isBuildEnv drv then - # Already a buildEnv, just return it (libraries already added in patchBuildEnv) - drv - else - # Use the patched buildEnv - (drv.buildEnv).override (args: { - makeWrapperArgs = [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ]; - }); + (if isBuildEnv drv then drv else drv.buildEnv).override (args: { + makeWrapperArgs = [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ]; + }); in - # Apply the buildEnv patch if enabled. # if cfg.patches.buildEnv.enable then lib.pipe drv [ patchBuildEnv + overrideBuildEnv appendLibraries ]; # else # appendLibraries drv; - }; manylinux.enable = lib.mkOption { @@ -649,9 +631,10 @@ in languages.python.poetry.install.enable = lib.mkIf cfg.poetry.enable (lib.mkDefault true); languages.python.poetry.install.arguments = lib.optional cfg.poetry.install.onlyInstallRootPackage "--only-root" - ++ lib.optional ( - !cfg.poetry.install.installRootPackage && !cfg.poetry.install.onlyInstallRootPackage - ) "--no-root" + ++ lib.optional + ( + !cfg.poetry.install.installRootPackage && !cfg.poetry.install.onlyInstallRootPackage + ) "--no-root" ++ lib.optional cfg.poetry.install.compile "--compile" ++ lib.optional cfg.poetry.install.quiet "--quiet" ++ lib.optionals (cfg.poetry.install.groups != [ ]) [ diff --git a/src/modules/languages/python/postbuild-wrapper.nix b/src/modules/languages/python/postbuild-wrapper.nix new file mode 100644 index 0000000000..df7bf5e352 --- /dev/null +++ b/src/modules/languages/python/postbuild-wrapper.nix @@ -0,0 +1,49 @@ +{ lib, jq }: + +{ + # Python derivation info + python +, # Wrapper configuration + permitUserSite ? false +, makeWrapperArgs ? [ ] +, +}: + +let + pythonExecutable = "${placeholder "out"}/bin/${python.executable}"; + pythonPath = "${placeholder "out"}/${python.sitePackages}"; +in +'' + # Extract paths from the pkgs JSON (buildEnv provides $pkgs or $pkgsPath) + if [ -n "''${pkgsPath:-}" ]; then + paths=$(${jq}/bin/jq -r '.[].paths[]' "$pkgsPath") + else + paths=$(echo "$pkgs" | ${jq}/bin/jq -r '.[].paths[]') + fi + + for path in $paths; do + if [ -d "$path/bin" ]; then + cd "$path/bin" + for prg in *; do + if [ -f "$prg" ] && [ -x "$prg" ]; then + rm -f "$out/bin/$prg" + if [ "$prg" = "${python.executable}" ]; then + makeWrapper "${python.interpreter}" "$out/bin/$prg" \ + --inherit-argv0 \ + ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''} + ${lib.concatStringsSep " " makeWrapperArgs} + elif [ "$(readlink "$prg")" = "${python.executable}" ]; then + ln -s "${python.executable}" "$out/bin/$prg" + else + makeWrapper "$path/bin/$prg" "$out/bin/$prg" \ + --set NIX_PYTHONPREFIX "$out" \ + --set NIX_PYTHONEXECUTABLE ${pythonExecutable} \ + --set NIX_PYTHONPATH ${pythonPath} \ + ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''} + ${lib.concatStringsSep " " makeWrapperArgs} + fi + fi + done + fi + done +'' diff --git a/src/modules/languages/python/wrapper-legacy.nix b/src/modules/languages/python/wrapper-legacy.nix new file mode 100644 index 0000000000..be356d4f55 --- /dev/null +++ b/src/modules/languages/python/wrapper-legacy.nix @@ -0,0 +1,72 @@ +{ lib +, stdenv +, buildEnv +, runCommand +, makeBinaryWrapper +, callPackage +, # manually passed + python +, requiredPythonModules +, # extra opts + extraLibs ? [ ] +, extraOutputsToInstall ? [ ] +, postBuild ? "" +, ignoreCollisions ? false +, permitUserSite ? false +, # Wrap executables with the given argument. + makeWrapperArgs ? [ ] +, +}: + +# Create a python executable that knows about additional packages. +let + makePostBuildWrapper = callPackage ./postbuild-wrapper.nix { }; + + env = + let + paths = requiredPythonModules (extraLibs ++ [ python ]) ++ [ + (runCommand "bin" { } '' + mkdir -p $out/bin + '') + ]; + in + buildEnv { + name = builtins.trace (toString paths) "${python.name}-env"; + + inherit paths; + inherit ignoreCollisions; + extraOutputsToInstall = [ "out" ] ++ extraOutputsToInstall; + + nativeBuildInputs = [ makeBinaryWrapper ]; + + postBuild = + makePostBuildWrapper + { + inherit + python + permitUserSite + makeWrapperArgs + ; + } + + postBuild; + + inherit (python) meta; + + passthru = python.passthru // { + interpreter = "${env}/bin/${python.executable}"; + inherit python; + env = stdenv.mkDerivation { + name = "interactive-${python.name}-environment"; + nativeBuildInputs = [ env ]; + + buildCommand = '' + echo >&2 "" + echo >&2 "*** Python 'env' attributes are intended for interactive nix-shell sessions, not for building! ***" + echo >&2 "" + exit 1 + ''; + }; + }; + }; +in +env diff --git a/src/python-build-env-legacy.nix b/src/python-build-env-legacy.nix deleted file mode 100644 index 55e86a8b9c..0000000000 --- a/src/python-build-env-legacy.nix +++ /dev/null @@ -1,91 +0,0 @@ -{ - lib, - stdenv, - buildEnv, - runCommand, - makeBinaryWrapper, - - # manually pased - python, - requiredPythonModules, - - # extra opts - extraLibs ? [ ], - extraOutputsToInstall ? [ ], - postBuild ? "", - ignoreCollisions ? false, - permitUserSite ? false, - # Wrap executables with the given argument. - makeWrapperArgs ? [ ], -}: - -# Create a python executable that knows about additional packages. -let - env = - let - paths = requiredPythonModules (extraLibs ++ [ python ]) ++ [ - (runCommand "bin" { } '' - mkdir -p $out/bin - '') - ]; - pythonPath = "${placeholder "out"}/${python.sitePackages}"; - pythonExecutable = "${placeholder "out"}/bin/${python.executable}"; - in - buildEnv { - name = "${python.name}-env"; - - inherit paths; - inherit ignoreCollisions; - extraOutputsToInstall = [ "out" ] ++ extraOutputsToInstall; - - nativeBuildInputs = [ makeBinaryWrapper ]; - - postBuild = '' - for path in ${lib.concatStringsSep " " paths}; do - if [ -d "$path/bin" ]; then - cd "$path/bin" - for prg in *; do - if [ -f "$prg" ] && [ -x "$prg" ]; then - rm -f "$out/bin/$prg" - if [ "$prg" = "${python.executable}" ]; then - makeWrapper "${python.interpreter}" "$out/bin/$prg" \ - --inherit-argv0 \ - ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} \ - ${lib.concatStringsSep " " makeWrapperArgs} - elif [ "$(readlink "$prg")" = "${python.executable}" ]; then - ln -s "${python.executable}" "$out/bin/$prg" - else - makeWrapper "$path/bin/$prg" "$out/bin/$prg" \ - --set NIX_PYTHONPREFIX "$out" \ - --set NIX_PYTHONEXECUTABLE ${pythonExecutable} \ - --set NIX_PYTHONPATH ${pythonPath} \ - ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true"''} \ - ${lib.concatStringsSep " " makeWrapperArgs} - fi - fi - done - fi - done - '' - + postBuild; - - inherit (python) meta; - - passthru = python.passthru // { - interpreter = "${env}/bin/${python.executable}"; - inherit python; - env = stdenv.mkDerivation { - name = "interactive-${python.name}-environment"; - nativeBuildInputs = [ env ]; - - buildCommand = '' - echo >&2 "" - echo >&2 "*** Python 'env' attributes are intended for interactive nix-shell sessions, not for building! ***" - echo >&2 "" - exit 1 - ''; - }; - }; - }; -in -env From bdafca6ccb313ae44f66914752bfb27556ed986b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:36:12 +0000 Subject: [PATCH 11/20] Auto generate missing individual markdowns --- docs/src/reference/options.md | 125 +++++++++++++++++----------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/docs/src/reference/options.md b/docs/src/reference/options.md index f3bb5b97ac..af802bc074 100644 --- a/docs/src/reference/options.md +++ b/docs/src/reference/options.md @@ -15935,7 +15935,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -15956,7 +15956,7 @@ package ` pkgs.python3 ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -15983,7 +15983,7 @@ string ` "./directory" ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16010,7 +16010,7 @@ list of absolute path ``` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16035,7 +16035,36 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) + + + +## languages.python.patches.buildEnv.enable + + + +Whether to apply fixes to Python’s ` buildEnv ` for correct runtime initialization: + + - Executables use ` --inherit-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` + - Python package scripts are unwrapped to invoke the environment’s interpreter directly + +Without these fixes, venvs cannot access environment packages via ` --system-site-packages `. + +Enabled by default for nixpkgs versions prior to 25.11. +Newer nixpkgs releases include upstream fixes that make this patch obsolete. + + + +*Type:* +boolean + + + +*Default:* +` true ` + +*Declared by:* + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16061,7 +16090,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16082,7 +16111,7 @@ package ` pkgs.poetry ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16103,7 +16132,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16129,7 +16158,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16150,7 +16179,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16171,7 +16200,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16192,7 +16221,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16213,7 +16242,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16234,7 +16263,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16255,7 +16284,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16276,7 +16305,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16297,7 +16326,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16318,7 +16347,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16339,7 +16368,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16360,7 +16389,7 @@ one of “no”, “little”, “more”, “debug” ` "no" ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16386,7 +16415,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16407,7 +16436,7 @@ package ` pkgs.uv ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16433,7 +16462,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16454,7 +16483,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16475,7 +16504,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16496,7 +16525,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16517,7 +16546,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16538,7 +16567,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16559,7 +16588,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16580,7 +16609,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16606,7 +16635,7 @@ boolean ` true ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16627,7 +16656,7 @@ boolean ` false ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16649,35 +16678,7 @@ null or strings concatenated with “\\n” or absolute path ` null ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) - - - -## languages.python.venv.wrapper.enable - - - -Whether to enable a wrapper that allows Python to import packages from the virtual environment. - -Enabled by default for: - - - Stable nixpkgs older than version 25.11 - - Unstable nixpkgs with a last commit date before 01-11-2025. - -Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete. - - - -*Type:* -boolean - - - -*Default:* -` true ` - -*Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -16704,7 +16705,7 @@ null or string ` "3.11 or 3.11.2" ` *Declared by:* - - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) From 74f437ebbfd259cd4f49ae0b81a979edb5d9f6eb Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:36:32 +0000 Subject: [PATCH 12/20] Auto generate docs and examples --- docs/src/supported-languages/python.md | 125 +++++++++++++------------ 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/docs/src/supported-languages/python.md b/docs/src/supported-languages/python.md index 10fa9395cc..de16de04e4 100644 --- a/docs/src/supported-languages/python.md +++ b/docs/src/supported-languages/python.md @@ -27,7 +27,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -48,7 +48,7 @@ package ` pkgs.python3 ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -73,7 +73,7 @@ string ` "./directory" ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -100,7 +100,7 @@ list of absolute path ``` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -125,7 +125,36 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) + + + +### languages\.python\.patches\.buildEnv\.enable + + + +Whether to apply fixes to Python’s ` buildEnv ` for correct runtime initialization: + + - Executables use ` --inherit-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` + - Python package scripts are unwrapped to invoke the environment’s interpreter directly + +Without these fixes, venvs cannot access environment packages via ` --system-site-packages `\. + +Enabled by default for nixpkgs versions prior to 25\.11\. +Newer nixpkgs releases include upstream fixes that make this patch obsolete\. + + + +*Type:* +boolean + + + +*Default:* +` true ` + +*Declared by:* + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -151,7 +180,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -172,7 +201,7 @@ package ` pkgs.poetry ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -193,7 +222,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -219,7 +248,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -240,7 +269,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -261,7 +290,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -282,7 +311,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -303,7 +332,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -324,7 +353,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -345,7 +374,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -366,7 +395,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -387,7 +416,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -408,7 +437,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -429,7 +458,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -450,7 +479,7 @@ one of “no”, “little”, “more”, “debug” ` "no" ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -476,7 +505,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -497,7 +526,7 @@ package ` pkgs.uv ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -523,7 +552,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -544,7 +573,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -565,7 +594,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -586,7 +615,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -607,7 +636,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -628,7 +657,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -649,7 +678,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -670,7 +699,7 @@ list of string ` [ ] ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -696,7 +725,7 @@ boolean ` true ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -717,7 +746,7 @@ boolean ` false ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -739,35 +768,7 @@ null or strings concatenated with “\\n” or absolute path ` null ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) - - - -### languages\.python\.venv\.wrapper\.enable - - - -Whether to enable a wrapper that allows Python to import packages from the virtual environment\. - -Enabled by default for: - - - Stable nixpkgs older than version 25\.11 - - Unstable nixpkgs with a last commit date before 01-11-2025\. - -Newer releases of nixpkgs ship with built-in patches that make this wrapper obsolete\. - - - -*Type:* -boolean - - - -*Default:* -` true ` - -*Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) @@ -794,4 +795,4 @@ null or string ` "3.11 or 3.11.2" ` *Declared by:* - - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python\.nix](https://github.com/cachix/devenv/blob/main/src/modules/languages/python.nix) + - [https://github\.com/cachix/devenv/blob/main/src/modules/languages/python](https://github.com/cachix/devenv/blob/main/src/modules/languages/python) From a2f9508a62b3c849ca5749b9e20d142cc81fbab9 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 15:53:35 +0100 Subject: [PATCH 13/20] python: disable the bounds check for the wrapper Let's wait until the upsteam patch is released proper. --- src/modules/languages/python/default.nix | 47 +++++------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/src/modules/languages/python/default.nix b/src/modules/languages/python/default.nix index 73123b0b57..df279fdd2b 100644 --- a/src/modules/languages/python/default.nix +++ b/src/modules/languages/python/default.nix @@ -1,40 +1,12 @@ { pkgs , config , lib -, inputs , ... }: let cfg = config.languages.python; - # Determine whether to patch Python's `buildEnv` - # Enabled for: - # nixpkgs < 25.11 - # nixpkgs-unstable < 2025-11-01 - legacyBuildEnvPatchEnabled = - let - nixpkgsInput = inputs.nixpkgs or null; - in - if nixpkgsInput != null then - let - # Check if we have version info (stable release) - version = nixpkgsInput.sourceInfo.version or null; - # Check lastModified timestamp (for unstable) - lastModified = nixpkgsInput.sourceInfo.lastModified or 0; - # FIXME: update date to actual nixpkgs-unstable release (-ish) - cutoffTimestamp = 1761091200; - in - if version != null then - # For stable releases, enable if version < 25.11 - lib.versionOlder version "25.11" - else - # For unstable, enable if lastModified <= November 1, 2025 - lastModified <= cutoffTimestamp - else - # If no nixpkgs input, default to enabled - true; - libraries = cfg.libraries ++ (lib.optional cfg.manylinux.enable pkgs.pythonManylinuxPackages.manylinux2014Package) @@ -406,14 +378,14 @@ in ]; }); in - # if cfg.patches.buildEnv.enable then - lib.pipe drv [ - patchBuildEnv - overrideBuildEnv - appendLibraries - ]; - # else - # appendLibraries drv; + if cfg.patches.buildEnv.enable then + lib.pipe drv [ + patchBuildEnv + overrideBuildEnv + appendLibraries + ] + else + appendLibraries drv; }; manylinux.enable = lib.mkOption { @@ -464,7 +436,8 @@ in patches.buildEnv.enable = lib.mkOption { type = lib.types.bool; - default = legacyBuildEnvPatchEnabled; + # TODO: Implement bounds check on `lib.version` once the upstream patch reaches a release. + default = true; description = '' Whether to apply fixes to Python's `buildEnv` for correct runtime initialization: - Executables use `--inherit-argv0` to ensure Python initializes with correct `sys.prefix` and `sys.base_prefix` From 7ee4c3ef827d9a42c4608ded05fc021d68bf7749 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 16:31:23 +0100 Subject: [PATCH 14/20] python: remove trace --- src/modules/languages/python/wrapper-legacy.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/languages/python/wrapper-legacy.nix b/src/modules/languages/python/wrapper-legacy.nix index be356d4f55..71dad8975c 100644 --- a/src/modules/languages/python/wrapper-legacy.nix +++ b/src/modules/languages/python/wrapper-legacy.nix @@ -31,7 +31,7 @@ let ]; in buildEnv { - name = builtins.trace (toString paths) "${python.name}-env"; + name = "${python.name}-env"; inherit paths; inherit ignoreCollisions; From 5cb2b37ddec126a4a8bc8b4c420f820038bf7a10 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 16:57:27 +0100 Subject: [PATCH 15/20] python: resolve argv0 to correctly point to venv python when symlinked If the python executable is a symlink, e.g. in a devenv profile, we won't correctly resolve to the buildEnv-wrapped executable without `resolve-argv0`. --- src/modules/languages/python/postbuild-wrapper.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/languages/python/postbuild-wrapper.nix b/src/modules/languages/python/postbuild-wrapper.nix index df7bf5e352..39c553a024 100644 --- a/src/modules/languages/python/postbuild-wrapper.nix +++ b/src/modules/languages/python/postbuild-wrapper.nix @@ -1,5 +1,13 @@ { lib, jq }: +# Modified from the upstream post-build patch. +# https://github.com/NixOS/nixpkgs/blob/4c5533c55af2c3899fa4696e26430ef567601dad/pkgs/development/interpreters/python/wrapper.nix +# +# devenv-specific modifications: +# +# - Use jq to extract buildEnv `paths` and re-process them +# - Use --resolve-argv0 for the main python executable wrapper. +# This correctly points to the wrapped env when the executable is a symlink, e.g. in a devenv profile. { # Python derivation info python @@ -30,6 +38,7 @@ in if [ "$prg" = "${python.executable}" ]; then makeWrapper "${python.interpreter}" "$out/bin/$prg" \ --inherit-argv0 \ + --resolve-argv0 \ ${lib.optionalString (!permitUserSite) ''--set PYTHONNOUSERSITE "true" \''} ${lib.concatStringsSep " " makeWrapperArgs} elif [ "$(readlink "$prg")" = "${python.executable}" ]; then From 5a604b76160b25361abe46e0444b64b25d06deb7 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 16:24:52 +0100 Subject: [PATCH 16/20] python: add a test for uv + withPackages --- src/modules/languages/python/default.nix | 4 +- tests/python-with-packages/.test-config.yml | 1 + tests/python-with-packages/.test.sh | 17 ++++++ .../python-with-packages/check-python-env.py | 48 +++++++++++++++ tests/python-with-packages/devenv.nix | 60 +++++++------------ .../pip-profile/requirements.txt | 2 + .../uv-profile/pyproject.toml | 9 +++ 7 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 tests/python-with-packages/.test-config.yml create mode 100755 tests/python-with-packages/.test.sh create mode 100755 tests/python-with-packages/check-python-env.py create mode 100644 tests/python-with-packages/pip-profile/requirements.txt create mode 100644 tests/python-with-packages/uv-profile/pyproject.toml diff --git a/src/modules/languages/python/default.nix b/src/modules/languages/python/default.nix index df279fdd2b..c286416579 100644 --- a/src/modules/languages/python/default.nix +++ b/src/modules/languages/python/default.nix @@ -445,8 +445,8 @@ in Without these fixes, venvs cannot access environment packages via `--system-site-packages`. - Enabled by default for nixpkgs versions prior to 25.11. - Newer nixpkgs releases include upstream fixes that make this patch obsolete. + Enabled by default. + Newer nixpkgs releases will include upstream fixes that make this patch obsolete. ''; }; diff --git a/tests/python-with-packages/.test-config.yml b/tests/python-with-packages/.test-config.yml new file mode 100644 index 0000000000..13c16cea3a --- /dev/null +++ b/tests/python-with-packages/.test-config.yml @@ -0,0 +1 @@ +use_shell: false diff --git a/tests/python-with-packages/.test.sh b/tests/python-with-packages/.test.sh new file mode 100755 index 0000000000..f7ed71a79c --- /dev/null +++ b/tests/python-with-packages/.test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -exu + +echo "" +echo "================================" +echo "Testing pip profile..." +echo "================================" +devenv shell --profile pip-with-packages ./check-python-env.py + +echo "" +echo "================================" +echo "Testing uv profile..." +echo "================================" +devenv shell --profile uv-with-packages ./check-python-env.py + +echo "" +echo "✓ All tests passed!" diff --git a/tests/python-with-packages/check-python-env.py b/tests/python-with-packages/check-python-env.py new file mode 100755 index 0000000000..6621f34490 --- /dev/null +++ b/tests/python-with-packages/check-python-env.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +import sys +import os + +print("Verifying sys.base_prefix points to wrapped python...") +print("sys.base_prefix:", sys.base_prefix) +print("sys.executable:", sys.executable) + +# Check that sys.base_prefix points to the -env buildEnv, not the bare interpreter +assert "-env" in sys.base_prefix, ( + f"sys.base_prefix ({sys.base_prefix}) should point to python-env with packages, not bare interpreter" +) + +# Verify packages from withPackages are accessible from base_prefix +site_packages = os.path.join( + sys.base_prefix, + "lib", + f"python{sys.version_info.major}.{sys.version_info.minor}", + "site-packages", +) +matplotlib_path = os.path.join(site_packages, "matplotlib") +assert os.path.exists(matplotlib_path), ( + f"matplotlib should exist in base_prefix site-packages at {matplotlib_path}, but it doesn't" +) + +print("✓ sys.base_prefix correctly points to wrapped python with packages") + +print("Testing imports from Nix's withPackages...") +import matplotlib + +print("✓ matplotlib works!") +import numpy + +print("✓ numpy works!") +import IPython + +print("✓ ipython works!") +import tkinter + +print("✓ tkinter works!") + +print("Testing imports from the Python env...") +import requests + +print("✓ requests works!") +import pytest + +print("✓ pytest works!") diff --git a/tests/python-with-packages/devenv.nix b/tests/python-with-packages/devenv.nix index 665123ac35..17131e86e1 100644 --- a/tests/python-with-packages/devenv.nix +++ b/tests/python-with-packages/devenv.nix @@ -1,51 +1,37 @@ -{ pkgs, ... }: +{ pkgs, config, ... }: { languages.python = { enable = true; + + # Load up some python packages via Nix package = pkgs.python3.withPackages (ps: [ ps.matplotlib ps.numpy ps.ipython ps.tkinter ]); - patches.buildEnv.enable = true; - venv.enable = true; - venv.requirements = '' - requests - pytest - ''; - }; - - enterTest = '' - echo "Verifying sys.base_prefix points to wrapped python..." - python <<'EOF' - import sys - import os - - print("sys.base_prefix:", sys.base_prefix) - print("sys.executable:", sys.executable) - # Check that sys.base_prefix points to the -env buildEnv, not the bare interpreter - assert "-env" in sys.base_prefix, \ - f"sys.base_prefix ({sys.base_prefix}) should point to python-env with packages, not bare interpreter" + # Enable the patch (enabled by default) + # patches.buildEnv.enable = true; - # Verify packages from withPackages are accessible from base_prefix - site_packages = os.path.join(sys.base_prefix, "lib", f"python{sys.version_info.major}.{sys.version_info.minor}", "site-packages") - matplotlib_path = os.path.join(site_packages, "matplotlib") - assert os.path.exists(matplotlib_path), \ - f"matplotlib should exist in base_prefix site-packages at {matplotlib_path}, but it doesn't" - - print("✓ sys.base_prefix correctly points to wrapped python with packages") - EOF + # Enable the virtual environment + venv.enable = true; + }; - echo "Testing imports from Nix's withPackages..." - python -c 'import matplotlib; print("matplotlib works!")' - python -c 'import numpy; print("numpy works!")' - python -c 'import IPython; print("ipython works!")' - python -c 'import tkinter; print("tkinter works!")' + profiles.uv-with-packages.module = { + languages.python = { + directory = "./uv-profile"; + uv = { + enable = true; + sync.enable = true; + }; + }; + }; - echo "Testing imports from venv..." - python -c 'import requests; print("requests works!")' - python -c 'import pytest; print("pytest works!")' - ''; + profiles.pip-with-packages.module = { + languages.python = { + directory = "./pip-profile"; + venv.requirements = ./pip-profile/requirements.txt; + }; + }; } diff --git a/tests/python-with-packages/pip-profile/requirements.txt b/tests/python-with-packages/pip-profile/requirements.txt new file mode 100644 index 0000000000..2001f3e256 --- /dev/null +++ b/tests/python-with-packages/pip-profile/requirements.txt @@ -0,0 +1,2 @@ +requests +pytest diff --git a/tests/python-with-packages/uv-profile/pyproject.toml b/tests/python-with-packages/uv-profile/pyproject.toml new file mode 100644 index 0000000000..f8bce08360 --- /dev/null +++ b/tests/python-with-packages/uv-profile/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "mymodule" +description = "test" +version = "0.0.1" + +dependencies = [ + "requests", + "pytest", +] From 13e4ef822ebffc5ee4b5de888412f590f33da054 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 17:19:11 +0100 Subject: [PATCH 17/20] python: simplify how the wrapper is applied --- src/modules/languages/python/default.nix | 82 +++++------------------- 1 file changed, 17 insertions(+), 65 deletions(-) diff --git a/src/modules/languages/python/default.nix b/src/modules/languages/python/default.nix index c286416579..925f62ff5b 100644 --- a/src/modules/languages/python/default.nix +++ b/src/modules/languages/python/default.nix @@ -268,9 +268,21 @@ in let isBuildEnv = drv: lib.hasAttr "extraLibs" (lib.functionArgs drv.override); + makeWrapperArgs = [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + "--prefix" + "DYLD_LIBRARY_PATH" + ":" + (lib.makeLibraryPath libraries) + ]; + patchBuildEnv = drv: - # If we got a buildEnv from withPackages, modify its postBuild to use our wrapper logic let makePostBuildWrapper = pkgs.callPackage ./postbuild-wrapper.nix { }; patchedEnv = @@ -284,68 +296,21 @@ in ( prevAttrs: let - makeWrapperArgs = [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ]; - # Generate the patched wrapper script using the shared function - patchedWrapperScript = makePostBuildWrapper { + patchedPostBuildWrapper = makePostBuildWrapper { inherit (drv) python; inherit makeWrapperArgs; }; - - # Split postBuild by lines - lines = lib.splitString "\n" prevAttrs.postBuild; - - # Use fold to track state (skip counter) and build new lines - result = - lib.foldl - ( - acc: line: - if acc.skip > 0 then - # Skip this line - { - lines = acc.lines; - skip = acc.skip - 1; - } - else if lib.hasInfix ''if [ -L "$out/bin" ]; then'' line then - # Found the start of the postBuild block - replace entire block with our patched version - { - lines = acc.lines ++ [ patchedWrapperScript ]; - skip = 17; # Skip the next 17 lines of the original block (18 total) - } - else - # Keep this line - { - lines = acc.lines ++ [ line ]; - skip = 0; - } - ) - { - lines = [ ]; - skip = 0; - } - lines; - - filteredLines = result.lines; in { - postBuild = lib.concatStringsSep "\n" filteredLines; + postBuild = patchedPostBuildWrapper; passthru = prevAttrs.passthru // { interpreter = "${patchedEnv}/bin/${drv.python.executable}"; }; } ); in + # If we got a buildEnv from withPackages, modify its postBuild to use our wrapper logic if isBuildEnv drv then patchedEnv else drv; overrideBuildEnv = @@ -363,20 +328,7 @@ in # Add extra libraries to the Python `buildEnv`. appendLibraries = drv: - (if isBuildEnv drv then drv else drv.buildEnv).override (args: { - makeWrapperArgs = [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - "--prefix" - "DYLD_LIBRARY_PATH" - ":" - (lib.makeLibraryPath libraries) - ]; - }); + (if isBuildEnv drv then drv else drv.buildEnv).override (args: { inherit makeWrapperArgs; }); in if cfg.patches.buildEnv.enable then lib.pipe drv [ From 95d531cff33ce91cb028e3659f616c3ca3e99073 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 10 Nov 2025 17:20:55 +0100 Subject: [PATCH 18/20] python: don't call the wrapper "legacy" until it is --- src/modules/languages/python/default.nix | 6 +++--- .../languages/python/{wrapper-legacy.nix => wrapper.nix} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename src/modules/languages/python/{wrapper-legacy.nix => wrapper.nix} (100%) diff --git a/src/modules/languages/python/default.nix b/src/modules/languages/python/default.nix index 925f62ff5b..f241f545b8 100644 --- a/src/modules/languages/python/default.nix +++ b/src/modules/languages/python/default.nix @@ -316,7 +316,7 @@ in overrideBuildEnv = drv: drv.overrideAttrs (prevAttrs: rec { - buildEnv = pkgs.callPackage ./wrapper-legacy.nix { + buildEnv = pkgs.callPackage ./wrapper.nix { python = drv; requiredPythonModules = drv.pkgs.requiredPythonModules; }; @@ -392,13 +392,13 @@ in default = true; description = '' Whether to apply fixes to Python's `buildEnv` for correct runtime initialization: - - Executables use `--inherit-argv0` to ensure Python initializes with correct `sys.prefix` and `sys.base_prefix` + - Executables use `--inherit-argv0` and `--resolve-argv0` to ensure Python initializes with correct `sys.prefix` and `sys.base_prefix` - Python package scripts are unwrapped to invoke the environment's interpreter directly Without these fixes, venvs cannot access environment packages via `--system-site-packages`. Enabled by default. - Newer nixpkgs releases will include upstream fixes that make this patch obsolete. + Newer nixpkgs releases may include upstream fixes that make this patch obsolete. ''; }; diff --git a/src/modules/languages/python/wrapper-legacy.nix b/src/modules/languages/python/wrapper.nix similarity index 100% rename from src/modules/languages/python/wrapper-legacy.nix rename to src/modules/languages/python/wrapper.nix From 32a04c71d9f50ecee23ebd255e14a9b02c457da9 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:28:31 +0000 Subject: [PATCH 19/20] Auto generate missing individual markdowns --- docs/src/reference/options.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/reference/options.md b/docs/src/reference/options.md index af802bc074..e732a25d6c 100644 --- a/docs/src/reference/options.md +++ b/docs/src/reference/options.md @@ -16045,13 +16045,13 @@ boolean Whether to apply fixes to Python’s ` buildEnv ` for correct runtime initialization: - - Executables use ` --inherit-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` + - Executables use ` --inherit-argv0 ` and ` --resolve-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` - Python package scripts are unwrapped to invoke the environment’s interpreter directly Without these fixes, venvs cannot access environment packages via ` --system-site-packages `. -Enabled by default for nixpkgs versions prior to 25.11. -Newer nixpkgs releases include upstream fixes that make this patch obsolete. +Enabled by default. +Newer nixpkgs releases may include upstream fixes that make this patch obsolete. From 3f7cac22289ecb8782bcde7de0eb4dd2e858a9a8 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:28:52 +0000 Subject: [PATCH 20/20] Auto generate docs and examples --- docs/src/supported-languages/python.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/supported-languages/python.md b/docs/src/supported-languages/python.md index de16de04e4..dc75773aa4 100644 --- a/docs/src/supported-languages/python.md +++ b/docs/src/supported-languages/python.md @@ -135,13 +135,13 @@ boolean Whether to apply fixes to Python’s ` buildEnv ` for correct runtime initialization: - - Executables use ` --inherit-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` + - Executables use ` --inherit-argv0 ` and ` --resolve-argv0 ` to ensure Python initializes with correct ` sys.prefix ` and ` sys.base_prefix ` - Python package scripts are unwrapped to invoke the environment’s interpreter directly Without these fixes, venvs cannot access environment packages via ` --system-site-packages `\. -Enabled by default for nixpkgs versions prior to 25\.11\. -Newer nixpkgs releases include upstream fixes that make this patch obsolete\. +Enabled by default\. +Newer nixpkgs releases may include upstream fixes that make this patch obsolete\.