Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
615 changes: 562 additions & 53 deletions Lib/test/test_venv.py

Large diffs are not rendered by default.

607 changes: 392 additions & 215 deletions Lib/venv/__init__.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Lib/venv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
main()
rc = 0
except Exception as e:
print('Error: %s' % e, file=sys.stderr)
print('Error:', e, file=sys.stderr)
sys.exit(rc)
3 changes: 2 additions & 1 deletion Lib/venv/scripts/common/Activate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ deactivate -nondestructive
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir

$env:VIRTUAL_ENV_PROMPT = $Prompt

if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {

Write-Verbose "Setting prompt to '$Prompt'"
Expand All @@ -233,7 +235,6 @@ if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
_OLD_VIRTUAL_PROMPT
}
$env:VIRTUAL_ENV_PROMPT = $Prompt
}

# Clear PYTHONHOME
Expand Down
43 changes: 25 additions & 18 deletions Lib/venv/scripts/common/activate
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
# You cannot run it directly

deactivate () {
# reset old environment variables
Expand All @@ -14,12 +14,10 @@ deactivate () {
unset _OLD_VIRTUAL_PYTHONHOME
fi

# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
# Call hash to forget past locations. Without forgetting
# past locations the $PATH changes we made may not be respected.
# See "man bash" for more details. hash is usually a builtin of your shell
hash -r 2> /dev/null

if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
Expand All @@ -38,13 +36,27 @@ deactivate () {
# unset irrelevant variables
deactivate nondestructive

VIRTUAL_ENV="__VENV_DIR__"
export VIRTUAL_ENV
# on Windows, a path can contain colons and backslashes and has to be converted:
case "$(uname)" in
CYGWIN*|MSYS*|MINGW*)
# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
# and to /cygdrive/d/path/to/venv on Cygwin
VIRTUAL_ENV=$(cygpath __VENV_DIR__)
export VIRTUAL_ENV
;;
*)
# use the path as-is
export VIRTUAL_ENV=__VENV_DIR__
;;
esac

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
export PATH

VIRTUAL_ENV_PROMPT=__VENV_PROMPT__
export VIRTUAL_ENV_PROMPT

# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
Expand All @@ -55,15 +67,10 @@ fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1="__VENV_PROMPT__${PS1:-}"
PS1="("__VENV_PROMPT__") ${PS1:-}"
export PS1
VIRTUAL_ENV_PROMPT="__VENV_PROMPT__"
export VIRTUAL_ENV_PROMPT
fi

# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# Call hash to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
hash -r 2> /dev/null
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
# (https://fishshell.com/); you cannot run it directly.
# (https://fishshell.com/). You cannot run it directly.

function deactivate -d "Exit virtual environment and return to normal shell environment"
# reset old environment variables
Expand All @@ -13,10 +13,13 @@ function deactivate -d "Exit virtual environment and return to normal shell env
end

if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
functions -e fish_prompt
set -e _OLD_FISH_PROMPT_OVERRIDE
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
# prevents error when using nested fish instances (Issue #93858)
if functions -q _old_fish_prompt
functions -e fish_prompt
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
end
end

set -e VIRTUAL_ENV
Expand All @@ -30,10 +33,11 @@ end
# Unset irrelevant variables.
deactivate nondestructive

set -gx VIRTUAL_ENV "__VENV_DIR__"
set -gx VIRTUAL_ENV __VENV_DIR__

set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH
set -gx PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__ $PATH
set -gx VIRTUAL_ENV_PROMPT __VENV_PROMPT__

# Unset PYTHONHOME if set.
if set -q PYTHONHOME
Expand All @@ -53,7 +57,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
set -l old_status $status

# Output the venv prompt; color taken from the blue of the Python logo.
printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal)
printf "%s(%s)%s " (set_color 4B8BBE) __VENV_PROMPT__ (set_color normal)

# Restore the return status of the previous command.
echo "exit $old_status" | .
Expand All @@ -62,5 +66,4 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
end

set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
set -gx VIRTUAL_ENV_PROMPT "__VENV_PROMPT__"
end
10 changes: 5 additions & 5 deletions Lib/venv/scripts/nt/activate.bat
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ if defined _OLD_CODEPAGE (
"%SystemRoot%\System32\chcp.com" 65001 > nul
)

set VIRTUAL_ENV=__VENV_DIR__
set "VIRTUAL_ENV=__VENV_DIR__"

if not defined PROMPT set PROMPT=$P$G

if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%

set _OLD_VIRTUAL_PROMPT=%PROMPT%
set PROMPT=__VENV_PROMPT__%PROMPT%
set "_OLD_VIRTUAL_PROMPT=%PROMPT%"
set "PROMPT=(__VENV_PROMPT__) %PROMPT%"

if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
set PYTHONHOME=

if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%

set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%
set VIRTUAL_ENV_PROMPT=__VENV_PROMPT__
set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%"
set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__"

:END
if defined _OLD_CODEPAGE (
Expand Down
9 changes: 5 additions & 4 deletions Lib/venv/scripts/posix/activate.csh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.

# Created by Davide Di Blasi <[email protected]>.
# Ported to Python 3.3 venv by Andrew Svetlov <[email protected]>

Expand All @@ -8,17 +9,17 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
# Unset irrelevant variables.
deactivate nondestructive

setenv VIRTUAL_ENV "__VENV_DIR__"
setenv VIRTUAL_ENV __VENV_DIR__

set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
setenv PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
setenv VIRTUAL_ENV_PROMPT __VENV_PROMPT__


set _OLD_VIRTUAL_PROMPT="$prompt"

if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
set prompt = "__VENV_PROMPT__$prompt"
setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__"
set prompt = "("__VENV_PROMPT__") $prompt:q"
endif

alias pydoc python -m pydoc
Expand Down
65 changes: 62 additions & 3 deletions crates/vm/src/stdlib/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,70 @@ mod sys {
#[pyattr]
fn _base_executable(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
// First check __PYVENV_LAUNCHER__ environment variable
if let Ok(var) = env::var("__PYVENV_LAUNCHER__") {
ctx.new_str(var).into()
} else {
executable(vm)
return ctx.new_str(var).into();
}

// Try to detect if we're running from a venv by looking for pyvenv.cfg
if let Some(base_exe) = get_venv_base_executable() {
return ctx.new_str(base_exe).into();
}

executable(vm)
}

/// Try to find base executable from pyvenv.cfg (see getpath.py)
fn get_venv_base_executable() -> Option<String> {
// TODO: This is a minimal implementation of getpath.py
// To fully support all cases, `getpath.py` should be placed in @crates/vm/Lib/python_builtins/

// Get current executable path
#[cfg(not(target_arch = "wasm32"))]
let exe_path = {
let exec_arg = env::args_os().next()?;
which::which(exec_arg).ok()?
};
#[cfg(target_arch = "wasm32")]
let exe_path = {
let exec_arg = env::args().next()?;
path::PathBuf::from(exec_arg)
};

let exe_dir = exe_path.parent()?;
let exe_name = exe_path.file_name()?;

// Look for pyvenv.cfg in parent directory (typical venv layout: venv/bin/python)
let venv_dir = exe_dir.parent()?;
let pyvenv_cfg = venv_dir.join("pyvenv.cfg");

if !pyvenv_cfg.exists() {
return None;
}

// Parse pyvenv.cfg and extract home directory
let content = std::fs::read_to_string(&pyvenv_cfg).ok()?;

for line in content.lines() {
if let Some((key, value)) = line.split_once('=') {
let key = key.trim().to_lowercase();
let value = value.trim();

if key == "home" {
// First try to resolve symlinks (getpath.py line 373-377)
if let Ok(resolved) = std::fs::canonicalize(&exe_path)
&& resolved != exe_path
{
return Some(resolved.to_string_lossy().into_owned());
}
// Fallback: home_dir + executable_name (getpath.py line 381)
let base_exe = path::Path::new(value).join(exe_name);
return Some(base_exe.to_string_lossy().into_owned());
}
}
}

None
}

#[pyattr]
Expand Down
Loading