Skip to content
2 changes: 1 addition & 1 deletion pkgs/modules/python/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ in
POETRY_USE_USER_SITE = "1";
PIP_CONFIG_FILE = pip.config.outPath;
PYTHONUSERBASE = userbase;
PYTHONPATH = "${sitecustomize}:${pip.pip}/${python.sitePackages}";
PYTHONPATH = "${sitecustomize}:${pip.sitePackages}";
REPLIT_PYTHONPATH = "${userbase}/${python.sitePackages}:${pypkgs.setuptools}/${python.sitePackages}";
UV_PROJECT_ENVIRONMENT = "$REPL_HOME/.pythonlibs";
UV_PYTHON_DOWNLOADS = "never";
Expand Down
41 changes: 39 additions & 2 deletions pkgs/modules/python/pip.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pkgs @ { pypkgs, ... }:
pkgs @ { pypkgs, python, ... }:

let
pip = pypkgs.pip.overridePythonAttrs (old: rec {
Expand All @@ -18,6 +18,41 @@ let
];
});

# This pip wrapper will detect if python is called from within
# the virtualenv: `.pythonlibs/bin`. If so, it will install a shell script
# called `pip` into the virtualenv so that if pip is called in the future
# python will think it's in the virtualenv.
pip-wrapper = pkgs.writeShellApplication {
name = "pip";
text = ''
python_location=$(which python)
venv_pip_location="''${REPL_HOME}/.pythonlibs/bin/pip"
venv_pip3_location="''${REPL_HOME}/.pythonlibs/bin/pip3"

function init_venv_pip {
cat <<EOF > "''${venv_pip_location}"
#! /bin/sh
export PIP_CONFIG_FILE=
python -m pip "\$@"
EOF
chmod u+x "''${venv_pip_location}"
if [ ! -f "''${venv_pip3_location}" ]; then
ln -s "''${venv_pip_location}" "''${venv_pip3_location}"
fi
}

if [ "$python_location" = "''${REPL_HOME}/.pythonlibs/bin/python" ]; then
if [ ! -f "''${venv_pip_location}" ]; then
init_venv_pip
fi
exec "''${venv_pip_location}" "$@"
else
unset PYTHONNOUSERSITE
exec "${pip}/bin/pip" "$@"
fi
'';
};

config = pkgs.writeTextFile {
name = "pip.conf";
text = ''
Expand All @@ -29,5 +64,7 @@ let
};
in
{
inherit pip config;
pip = pip-wrapper;
sitePackages = "${pip}/${python.sitePackages}";
inherit config;
}
19 changes: 19 additions & 0 deletions pkgs/python-wrapped/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"os"
"strings"
"syscall"
"fmt"
"path/filepath"
)

var PythonExePath string
Expand Down Expand Up @@ -39,6 +41,22 @@ func legacy() {
}
}

// Unsets the PIP_CONFIG_FILE config if we are running in virtualenv mode,
// because that config file - see pip.nix - works only for `--user` mode pip installs
// but pip cannot do `--user` inside virtualenv.
func unsetPipConfigFileForVirtualEnvPython() {
var exePath, err = filepath.Abs(os.Args[0])
if err == nil {
repl_home := os.Getenv("REPL_HOME")
if repl_home != "" {
dot_python_dir := fmt.Sprintf("%s/%s", repl_home, ".pythonlibs/bin/")
if strings.HasPrefix(exePath, dot_python_dir) {
os.Unsetenv("PIP_CONFIG_FILE")
}
}
}
}

// Set up environment for non-legacy nixpkgs
func modern() {
if ldAudit := os.Getenv("REPLIT_LD_AUDIT"); ldAudit != "" {
Expand All @@ -65,6 +83,7 @@ func channelWorksWithRtldLoader(channel string) bool {
}

func main() {
unsetPipConfigFileForVirtualEnvPython()
os.Unsetenv("PYTHONNOUSERSITE")

if val, ok := os.LookupEnv("REPLIT_NIX_CHANNEL"); ok && channelWorksWithRtldLoader(val) {
Expand Down