Skip to content

Commit

Permalink
Support deactivating virtual environments without user intervention (#…
Browse files Browse the repository at this point in the history
…22405)

Closes #22448

Adds deactivate script to `PATH`
  • Loading branch information
Kartik Raj committed Nov 10, 2023
1 parent 8d174a8 commit b68ddee
Show file tree
Hide file tree
Showing 21 changed files with 359 additions and 680 deletions.
6 changes: 0 additions & 6 deletions pythonFiles/deactivate.csh

This file was deleted.

36 changes: 0 additions & 36 deletions pythonFiles/deactivate.fish

This file was deleted.

31 changes: 0 additions & 31 deletions pythonFiles/deactivate.ps1

This file was deleted.

44 changes: 44 additions & 0 deletions pythonFiles/deactivate/bash/deactivate
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Same as deactivate in "<venv>/bin/activate"
deactivate () {
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
unset -f deactivate
fi
}

# Get the directory of the current script
SCRIPT_DIR=$(dirname "$0")
# Construct the path to envVars.txt relative to the script directory
ENV_FILE="$SCRIPT_DIR/envVars.txt"

# Read the JSON file and set the variables
TEMP_PS1=$(grep '^PS1=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PATH=$(grep '^PATH=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PYTHONHOME=$(grep '^PYTHONHOME=' $ENV_FILE | cut -d '=' -f 2)
# Initialize the variables required by deactivate function
_OLD_VIRTUAL_PS1="${TEMP_PS1:-}"
_OLD_VIRTUAL_PATH="$TEMP_PATH"
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${TEMP_PYTHONHOME:-}"
fi
deactivate
bash
44 changes: 44 additions & 0 deletions pythonFiles/deactivate/fish/deactivate
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Same as deactivate in "<venv>/bin/activate"
deactivate () {
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
unset -f deactivate
fi
}

# Get the directory of the current script
SCRIPT_DIR=$(dirname "$0")
# Construct the path to envVars.txt relative to the script directory
ENV_FILE="$SCRIPT_DIR/envVars.txt"

# Read the JSON file and set the variables
TEMP_PS1=$(grep '^PS1=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PATH=$(grep '^PATH=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PYTHONHOME=$(grep '^PYTHONHOME=' $ENV_FILE | cut -d '=' -f 2)
# Initialize the variables required by deactivate function
_OLD_VIRTUAL_PS1="${TEMP_PS1:-}"
_OLD_VIRTUAL_PATH="$TEMP_PATH"
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${TEMP_PYTHONHOME:-}"
fi
deactivate
fish
11 changes: 11 additions & 0 deletions pythonFiles/deactivate/powershell/deactivate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Load dotenv-style file and restore environment variables
Get-Content -Path "$PSScriptRoot\envVars.txt" | ForEach-Object {
# Split each line into key and value at the first '='
$parts = $_ -split '=', 2
if ($parts.Count -eq 2) {
$key = $parts[0].Trim()
$value = $parts[1].Trim()
# Set the environment variable
Set-Item -Path "env:$key" -Value $value
}
}
17 changes: 14 additions & 3 deletions pythonFiles/deactivate → pythonFiles/deactivate/zsh/deactivate
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,20 @@ deactivate () {
fi
}

# Get the directory of the current script
SCRIPT_DIR=$(dirname "$0")
# Construct the path to envVars.txt relative to the script directory
ENV_FILE="$SCRIPT_DIR/envVars.txt"

# Read the JSON file and set the variables
TEMP_PS1=$(grep '^PS1=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PATH=$(grep '^PATH=' $ENV_FILE | cut -d '=' -f 2)
TEMP_PYTHONHOME=$(grep '^PYTHONHOME=' $ENV_FILE | cut -d '=' -f 2)
# Initialize the variables required by deactivate function
_OLD_VIRTUAL_PS1="${PS1:-}"
_OLD_VIRTUAL_PATH="$PATH"
_OLD_VIRTUAL_PS1="${TEMP_PS1:-}"
_OLD_VIRTUAL_PATH="$TEMP_PATH"
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
_OLD_VIRTUAL_PYTHONHOME="${TEMP_PYTHONHOME:-}"
fi
deactivate
zsh
11 changes: 5 additions & 6 deletions pythonFiles/printEnvVariablesToFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
# Licensed under the MIT License.

import os
import json
import sys

# Last argument is the target file into which we'll write the env variables line by line.
output_file = sys.argv[-1]

# Last argument is the target file into which we'll write the env variables as json.
json_file = sys.argv[-1]

with open(json_file, "w") as outfile:
json.dump(dict(os.environ), outfile)
with open(output_file, "w") as outfile:
for key, val in os.environ.items():
outfile.write(f"{key}={val}\n")
34 changes: 34 additions & 0 deletions src/client/common/utils/async.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable no-async-promise-executor */
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

Expand Down Expand Up @@ -228,3 +230,35 @@ export async function flattenIterator<T>(iterator: IAsyncIterator<T>): Promise<T
}
return results;
}

/**
* Wait for a condition to be fulfilled within a timeout.
*
* @export
* @param {() => Promise<boolean>} condition
* @param {number} timeoutMs
* @param {string} errorMessage
* @returns {Promise<void>}
*/
export async function waitForCondition(
condition: () => Promise<boolean>,
timeoutMs: number,
errorMessage: string,
): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
const timeout = setTimeout(() => {
clearTimeout(timeout);

clearTimeout(timer);
reject(new Error(errorMessage));
}, timeoutMs);
const timer = setInterval(async () => {
if (!(await condition().catch(() => false))) {
return;
}
clearTimeout(timeout);
clearTimeout(timer);
resolve();
}, 10);
});
}
2 changes: 1 addition & 1 deletion src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export namespace Interpreters {
export const activatingTerminals = l10n.t('Reactivating terminals...');
export const activateTerminalDescription = l10n.t('Activated environment for');
export const terminalEnvVarCollectionPrompt = l10n.t(
'{0} environment was successfully activated, even though {1} may not be present in the terminal prompt. [Learn more](https://aka.ms/vscodePythonTerminalActivation).',
'{0} environment was successfully activated, even though {1} indicator may not be present in the terminal prompt. [Learn more](https://aka.ms/vscodePythonTerminalActivation).',
);
export const terminalDeactivateProgress = l10n.t('Editing {0}...');
export const restartingTerminal = l10n.t('Restarting terminal and deactivating...');
Expand Down
8 changes: 4 additions & 4 deletions src/client/interpreter/activation/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,15 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi
}
return undefined;
}
// Run the activate command collect the environment from it.
const activationCommand = fixActivationCommands(activationCommands).join(' && ');
// In order to make sure we know where the environment output is,
// put in a dummy echo we can look for
const commandSeparator = [TerminalShellType.powershell, TerminalShellType.powershellCore].includes(
shellInfo.shellType,
)
? ';'
: '&&';
// Run the activate command collect the environment from it.
const activationCommand = fixActivationCommands(activationCommands).join(` ${commandSeparator} `);
// In order to make sure we know where the environment output is,
// put in a dummy echo we can look for
command = `${activationCommand} ${commandSeparator} echo '${ENVIRONMENT_PREFIX}' ${commandSeparator} python ${args.join(
' ',
)}`;
Expand Down
Loading

0 comments on commit b68ddee

Please sign in to comment.