Skip to content

Commit

Permalink
Guide users to install workaround when deactivate command is run (#22223
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Kartik Raj authored Oct 20, 2023
1 parent a55484d commit 0438813
Show file tree
Hide file tree
Showing 29 changed files with 979 additions and 115 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"quickPickSortByLabel",
"testObserver",
"quickPickItemTooltip",
"saveEditor"
"saveEditor",
"terminalDataWriteEvent"
],
"author": {
"name": "Microsoft Corporation"
Expand Down
33 changes: 33 additions & 0 deletions pythonFiles/deactivate
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 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
}

# Initialize the variables required by deactivate function
_OLD_VIRTUAL_PS1="${PS1:-}"
_OLD_VIRTUAL_PATH="$PATH"
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
fi
6 changes: 6 additions & 0 deletions pythonFiles/deactivate.csh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Same as deactivate in "<venv>/bin/activate.csh"
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'

# Initialize the variables required by deactivate function
set _OLD_VIRTUAL_PROMPT="$prompt"
set _OLD_VIRTUAL_PATH="$PATH"
36 changes: 36 additions & 0 deletions pythonFiles/deactivate.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Same as deactivate in "<venv>/bin/activate.fish"
function deactivate -d "Exit virtual environment and return to normal shell environment"
# reset old environment variables
if test -n "$_OLD_VIRTUAL_PATH"
set -gx PATH $_OLD_VIRTUAL_PATH
set -e _OLD_VIRTUAL_PATH
end
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end

if test -n "$vscode_python_old_fish_prompt_OVERRIDE"
set -e vscode_python_old_fish_prompt_OVERRIDE
if functions -q vscode_python_old_fish_prompt
functions -e fish_prompt
functions -c vscode_python_old_fish_prompt fish_prompt
functions -e vscode_python_old_fish_prompt
end
end

set -e VIRTUAL_ENV
set -e VIRTUAL_ENV_PROMPT
if test "$argv[1]" != "nondestructive"
functions -e deactivate
end
end

# Initialize the variables required by deactivate function
set -gx _OLD_VIRTUAL_PATH $PATH
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
functions -c fish_prompt vscode_python_old_fish_prompt
end
if set -q PYTHONHOME
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
end
31 changes: 31 additions & 0 deletions pythonFiles/deactivate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Same as deactivate in "Activate.ps1"
function global:deactivate ([switch]$NonDestructive) {
if (Test-Path function:_OLD_VIRTUAL_PROMPT) {
copy-item function:_OLD_VIRTUAL_PROMPT function:prompt
remove-item function:_OLD_VIRTUAL_PROMPT
}
if (Test-Path env:_OLD_VIRTUAL_PYTHONHOME) {
copy-item env:_OLD_VIRTUAL_PYTHONHOME env:PYTHONHOME
remove-item env:_OLD_VIRTUAL_PYTHONHOME
}
if (Test-Path env:_OLD_VIRTUAL_PATH) {
copy-item env:_OLD_VIRTUAL_PATH env:PATH
remove-item env:_OLD_VIRTUAL_PATH
}
if (Test-Path env:VIRTUAL_ENV) {
remove-item env:VIRTUAL_ENV
}
if (!$NonDestructive) {
remove-item function:deactivate
}
}

# Initialize the variables required by deactivate function
if (! $env:VIRTUAL_ENV_DISABLE_PROMPT) {
function global:_OLD_VIRTUAL_PROMPT {""}
copy-item function:prompt function:_OLD_VIRTUAL_PROMPT
}
if (Test-Path env:PYTHONHOME) {
copy-item env:PYTHONHOME env:_OLD_VIRTUAL_PYTHONHOME
}
copy-item env:PATH env:_OLD_VIRTUAL_PATH
12 changes: 11 additions & 1 deletion src/client/common/application/applicationShell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
DocumentSelector,
env,
Event,
EventEmitter,
InputBox,
InputBoxOptions,
languages,
Expand Down Expand Up @@ -37,7 +38,8 @@ import {
WorkspaceFolder,
WorkspaceFolderPickOptions,
} from 'vscode';
import { IApplicationShell } from './types';
import { traceError } from '../../logging';
import { IApplicationShell, TerminalDataWriteEvent } from './types';

@injectable()
export class ApplicationShell implements IApplicationShell {
Expand Down Expand Up @@ -172,4 +174,12 @@ export class ApplicationShell implements IApplicationShell {
public createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem {
return languages.createLanguageStatusItem(id, selector);
}
public get onDidWriteTerminalData(): Event<TerminalDataWriteEvent> {
try {
return window.onDidWriteTerminalData;
} catch (ex) {
traceError('Failed to get proposed API onDidWriteTerminalData', ex);
return new EventEmitter<TerminalDataWriteEvent>().event;
}
}
}
32 changes: 32 additions & 0 deletions src/client/common/application/progressService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { ProgressOptions } from 'vscode';
import { Deferred, createDeferred } from '../utils/async';
import { IApplicationShell } from './types';

export class ProgressService {
private deferred: Deferred<void> | undefined;

constructor(private readonly shell: IApplicationShell) {}

public showProgress(options: ProgressOptions): void {
if (!this.deferred) {
this.createProgress(options);
}
}

public hideProgress(): void {
if (this.deferred) {
this.deferred.resolve();
this.deferred = undefined;
}
}

private createProgress(options: ProgressOptions) {
this.shell.withProgress(options, () => {
this.deferred = createDeferred();
return this.deferred.promise;
});
}
}
18 changes: 18 additions & 0 deletions src/client/common/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ import { Resource } from '../types';
import { ICommandNameArgumentTypeMapping } from './commands';
import { ExtensionContextKey } from './contextKeys';

export interface TerminalDataWriteEvent {
/**
* The {@link Terminal} for which the data was written.
*/
readonly terminal: Terminal;
/**
* The data being written.
*/
readonly data: string;
}

export const IApplicationShell = Symbol('IApplicationShell');
export interface IApplicationShell {
/**
Expand All @@ -75,6 +86,13 @@ export interface IApplicationShell {
*/
readonly onDidChangeWindowState: Event<WindowState>;

/**
* An event which fires when the terminal's child pseudo-device is written to (the shell).
* In other words, this provides access to the raw data stream from the process running
* within the terminal, including VT sequences.
*/
readonly onDidWriteTerminalData: Event<TerminalDataWriteEvent>;

showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;

/**
Expand Down
4 changes: 3 additions & 1 deletion src/client/common/experiments/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { env, workspace } from 'vscode';
import { IExperimentService } from '../types';
import { TerminalEnvVarActivation } from './groups';
import { isTestExecution } from '../constants';
import { traceInfo } from '../../logging';

export function inTerminalEnvVarExperiment(experimentService: IExperimentService): boolean {
if (!isTestExecution() && workspace.workspaceFile && env.remoteName) {
if (!isTestExecution() && env.remoteName && workspace.workspaceFolders && workspace.workspaceFolders.length > 1) {
// TODO: Remove this if statement once https://github.com/microsoft/vscode/issues/180486 is fixed.
traceInfo('Not enabling terminal env var experiment in multiroot remote workspaces');
return false;
}
if (!experimentService.inExperimentSync(TerminalEnvVarActivation.experiment)) {
Expand Down
20 changes: 20 additions & 0 deletions src/client/common/platform/fs-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import * as nodepath from 'path';
import { getSearchPathEnvVarNames } from '../utils/exec';
import * as fs from 'fs-extra';
import { getOSType, OSType } from '../utils/platform';
import { IExecutables, IFileSystemPaths, IFileSystemPathUtils } from './types';

Expand Down Expand Up @@ -170,3 +171,22 @@ export function isParentPath(filePath: string, parentPath: string): boolean {
export function arePathsSame(path1: string, path2: string): boolean {
return normCasePath(path1) === normCasePath(path2);
}

export async function copyFile(src: string, dest: string): Promise<void> {
const destDir = nodepath.dirname(dest);
if (!(await fs.pathExists(destDir))) {
await fs.mkdirp(destDir);
}

await fs.copy(src, dest, {
overwrite: true,
});
}

export function pathExists(absPath: string): Promise<boolean> {
return fs.pathExists(absPath);
}

export function createFile(filename: string): Promise<void> {
return fs.createFile(filename);
}
6 changes: 6 additions & 0 deletions src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export namespace Common {
export const noIWillDoItLater = l10n.t('No, I will do it later');
export const notNow = l10n.t('Not now');
export const doNotShowAgain = l10n.t("Don't show again");
export const editSomething = l10n.t('Edit {0}');
export const reload = l10n.t('Reload');
export const moreInfo = l10n.t('More Info');
export const learnMore = l10n.t('Learn more');
Expand Down Expand Up @@ -198,6 +199,11 @@ export namespace Interpreters {
export const terminalEnvVarCollectionPrompt = l10n.t(
'The Python extension automatically activates all terminals using the selected environment, even when the name of the environment{0} is not 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...');
export const terminalDeactivatePrompt = l10n.t(
'Deactivating virtual environments may not work by default due to a technical limitation in our activation approach, but it can be resolved by appending a line to "{0}". Be sure to restart the shell afterward. [Learn more](https://aka.ms/AAmx2ft).',
);
export const activatedCondaEnvLaunch = l10n.t(
'We noticed VS Code was launched from an activated conda environment, would you like to select it?',
);
Expand Down
8 changes: 0 additions & 8 deletions src/client/interpreter/activation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,3 @@ export interface IEnvironmentActivationService {
interpreter?: PythonEnvironment,
): Promise<string[] | undefined>;
}

export const ITerminalEnvVarCollectionService = Symbol('ITerminalEnvVarCollectionService');
export interface ITerminalEnvVarCollectionService {
/**
* Returns true if we know with high certainity the terminal prompt is set correctly for a particular resource.
*/
isTerminalPromptSetCorrectly(resource?: Resource): boolean;
}
13 changes: 1 addition & 12 deletions src/client/interpreter/serviceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import { IExtensionActivationService, IExtensionSingleActivationService } from '../activation/types';
import { IServiceManager } from '../ioc/types';
import { EnvironmentActivationService } from './activation/service';
import { TerminalEnvVarCollectionPrompt } from './activation/terminalEnvVarCollectionPrompt';
import { TerminalEnvVarCollectionService } from './activation/terminalEnvVarCollectionService';
import { IEnvironmentActivationService, ITerminalEnvVarCollectionService } from './activation/types';
import { IEnvironmentActivationService } from './activation/types';
import { InterpreterAutoSelectionService } from './autoSelection/index';
import { InterpreterAutoSelectionProxyService } from './autoSelection/proxy';
import { IInterpreterAutoSelectionService, IInterpreterAutoSelectionProxyService } from './autoSelection/types';
Expand Down Expand Up @@ -110,13 +108,4 @@ export function registerTypes(serviceManager: IServiceManager): void {
IEnvironmentActivationService,
EnvironmentActivationService,
);
serviceManager.addSingleton<ITerminalEnvVarCollectionService>(
ITerminalEnvVarCollectionService,
TerminalEnvVarCollectionService,
);
serviceManager.addBinding(ITerminalEnvVarCollectionService, IExtensionActivationService);
serviceManager.addSingleton<IExtensionSingleActivationService>(
IExtensionSingleActivationService,
TerminalEnvVarCollectionPrompt,
);
}
5 changes: 4 additions & 1 deletion src/client/interpreter/virtualEnvs/activatedEnvLaunch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ export class ActivatedEnvironmentLaunch implements IActivatedEnvironmentLaunch {
}
if (process.env.VSCODE_CLI !== '1') {
// We only want to select the interpreter if VS Code was launched from the command line.
traceVerbose('VS Code was not launched from the command line, not selecting activated interpreter');
traceVerbose(
'VS Code was not launched from the command line, not selecting activated interpreter',
JSON.stringify(process.env, undefined, 4),
);
return undefined;
}
const prefix = await this.getPrefixOfSelectedActivatedEnv();
Expand Down
Loading

0 comments on commit 0438813

Please sign in to comment.