Skip to content

Commit 2769390

Browse files
authored
Cherry pick dont automatically inject PYTHONSTARTUP (#24346) (#24386)
Resolves #24345 and #24290 and #24105 remove automatic injection from before so only thing that allows shell integration to user for Python terminal REPL is the setting itself.
1 parent bc5a49b commit 2769390

File tree

6 files changed

+107
-105
lines changed

6 files changed

+107
-105
lines changed

src/client/common/terminal/service.ts

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
// Licensed under the MIT License.
33

44
import { inject, injectable } from 'inversify';
5-
import * as path from 'path';
6-
import { CancellationToken, Disposable, Event, EventEmitter, Terminal } from 'vscode';
5+
import { CancellationToken, Disposable, Event, EventEmitter, Terminal, TerminalShellExecution } from 'vscode';
76
import '../../common/extensions';
87
import { IInterpreterService } from '../../interpreter/contracts';
98
import { IServiceContainer } from '../../ioc/types';
109
import { captureTelemetry } from '../../telemetry';
1110
import { EventName } from '../../telemetry/constants';
1211
import { ITerminalAutoActivation } from '../../terminals/types';
1312
import { ITerminalManager } from '../application/types';
14-
import { EXTENSION_ROOT_DIR } from '../constants';
1513
import { _SCRIPTS_DIR } from '../process/internal/scripts/constants';
1614
import { IConfigurationService, IDisposableRegistry } from '../types';
1715
import {
@@ -20,7 +18,6 @@ import {
2018
ITerminalService,
2119
TerminalCreationOptions,
2220
TerminalShellType,
23-
ITerminalExecutedCommand,
2421
} from './types';
2522
import { traceVerbose } from '../../logging';
2623

@@ -33,11 +30,12 @@ export class TerminalService implements ITerminalService, Disposable {
3330
private terminalHelper: ITerminalHelper;
3431
private terminalActivator: ITerminalActivator;
3532
private terminalAutoActivator: ITerminalAutoActivation;
36-
private readonly envVarScript = path.join(EXTENSION_ROOT_DIR, 'python_files', 'pythonrc.py');
3733
private readonly executeCommandListeners: Set<Disposable> = new Set();
34+
private _terminalFirstLaunched: boolean = true;
3835
public get onDidCloseTerminal(): Event<void> {
3936
return this.terminalClosed.event.bind(this.terminalClosed);
4037
}
38+
4139
constructor(
4240
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
4341
private readonly options?: TerminalCreationOptions,
@@ -76,24 +74,24 @@ export class TerminalService implements ITerminalService, Disposable {
7674
}
7775
this.terminal!.sendText(text);
7876
}
79-
public async executeCommand(commandLine: string): Promise<ITerminalExecutedCommand | undefined> {
77+
public async executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined> {
8078
const terminal = this.terminal!;
8179
if (!this.options?.hideFromUser) {
8280
terminal.show(true);
8381
}
8482

8583
// If terminal was just launched, wait some time for shell integration to onDidChangeShellIntegration.
86-
if (!terminal.shellIntegration) {
84+
if (!terminal.shellIntegration && this._terminalFirstLaunched) {
85+
this._terminalFirstLaunched = false;
8786
const promise = new Promise<boolean>((resolve) => {
88-
const shellIntegrationChangeEventListener = this.terminalManager.onDidChangeTerminalShellIntegration(
89-
() => {
90-
this.executeCommandListeners.delete(shellIntegrationChangeEventListener);
91-
resolve(true);
92-
},
93-
);
87+
const disposable = this.terminalManager.onDidChangeTerminalShellIntegration(() => {
88+
clearTimeout(timer);
89+
disposable.dispose();
90+
resolve(true);
91+
});
9492
const TIMEOUT_DURATION = 500;
95-
setTimeout(() => {
96-
this.executeCommandListeners.add(shellIntegrationChangeEventListener);
93+
const timer = setTimeout(() => {
94+
disposable.dispose();
9795
resolve(true);
9896
}, TIMEOUT_DURATION);
9997
});
@@ -102,18 +100,8 @@ export class TerminalService implements ITerminalService, Disposable {
102100

103101
if (terminal.shellIntegration) {
104102
const execution = terminal.shellIntegration.executeCommand(commandLine);
105-
return await new Promise((resolve) => {
106-
const listener = this.terminalManager.onDidEndTerminalShellExecution((e) => {
107-
if (e.execution === execution) {
108-
this.executeCommandListeners.delete(listener);
109-
resolve({ execution, exitCode: e.exitCode });
110-
}
111-
});
112-
if (listener) {
113-
this.executeCommandListeners.add(listener);
114-
}
115-
traceVerbose(`Shell Integration is enabled, executeCommand: ${commandLine}`);
116-
});
103+
traceVerbose(`Shell Integration is enabled, executeCommand: ${commandLine}`);
104+
return execution;
117105
} else {
118106
terminal.sendText(commandLine);
119107
traceVerbose(`Shell Integration is disabled, sendText: ${commandLine}`);
@@ -136,7 +124,6 @@ export class TerminalService implements ITerminalService, Disposable {
136124
this.terminalShellType = this.terminalHelper.identifyTerminalShell(this.terminal);
137125
this.terminal = this.terminalManager.createTerminal({
138126
name: this.options?.title || 'Python',
139-
env: { PYTHONSTARTUP: this.envVarScript },
140127
hideFromUser: this.options?.hideFromUser,
141128
});
142129
this.terminalAutoActivator.disableAutoActivation(this.terminal);

src/client/common/terminal/syncTerminalService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
'use strict';
55

66
import { inject } from 'inversify';
7-
import { CancellationToken, Disposable, Event } from 'vscode';
7+
import { CancellationToken, Disposable, Event, TerminalShellExecution } from 'vscode';
88
import { IInterpreterService } from '../../interpreter/contracts';
99
import { traceVerbose } from '../../logging';
1010
import { PythonEnvironment } from '../../pythonEnvironments/info';
@@ -14,7 +14,7 @@ import * as internalScripts from '../process/internal/scripts';
1414
import { createDeferred, Deferred } from '../utils/async';
1515
import { noop } from '../utils/misc';
1616
import { TerminalService } from './service';
17-
import { ITerminalService, ITerminalExecutedCommand } from './types';
17+
import { ITerminalService } from './types';
1818

1919
enum State {
2020
notStarted = 0,
@@ -145,7 +145,7 @@ export class SynchronousTerminalService implements ITerminalService, Disposable
145145
public sendText(text: string): Promise<void> {
146146
return this.terminalService.sendText(text);
147147
}
148-
public executeCommand(commandLine: string): Promise<ITerminalExecutedCommand | undefined> {
148+
public executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined> {
149149
return this.terminalService.executeCommand(commandLine);
150150
}
151151
public show(preserveFocus?: boolean | undefined): Promise<void> {

src/client/common/terminal/types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,10 @@ export interface ITerminalService extends IDisposable {
5454
): Promise<void>;
5555
/** @deprecated */
5656
sendText(text: string): Promise<void>;
57-
executeCommand(commandLine: string): Promise<ITerminalExecutedCommand | undefined>;
57+
executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined>;
5858
show(preserveFocus?: boolean): Promise<void>;
5959
}
6060

61-
export interface ITerminalExecutedCommand {
62-
execution: TerminalShellExecution;
63-
exitCode: number | undefined;
64-
}
65-
6661
export const ITerminalServiceFactory = Symbol('ITerminalServiceFactory');
6762

6863
export type TerminalCreationOptions = {

src/test/common/terminals/helper.unit.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,30 @@ suite('Terminal Service helpers', () => {
9696
teardown(() => shellDetectorIdentifyTerminalShell.restore());
9797
suite('Misc', () => {
9898
setup(doSetup);
99+
test('Creating terminal should not automatically contain PYTHONSTARTUP', () => {
100+
const theTitle = 'Hello';
101+
const terminal = 'Terminal Created';
102+
when(terminalManager.createTerminal(anything())).thenReturn(terminal as any);
103+
const term = helper.createTerminal(theTitle);
104+
const args = capture(terminalManager.createTerminal).first()[0];
105+
expect(term).to.be.deep.equal(terminal);
106+
const terminalOptions = args.env;
107+
const safeTerminalOptions = terminalOptions || {};
108+
expect(safeTerminalOptions).to.not.have.property('PYTHONSTARTUP');
109+
});
110+
111+
test('Env should be undefined if not explicitly passed in ', () => {
112+
const theTitle = 'Hello';
113+
const terminal = 'Terminal Created';
114+
when(terminalManager.createTerminal(anything())).thenReturn(terminal as any);
115+
116+
const term = helper.createTerminal(theTitle);
117+
118+
verify(terminalManager.createTerminal(anything())).once();
119+
const args = capture(terminalManager.createTerminal).first()[0];
120+
expect(term).to.be.deep.equal(terminal);
121+
expect(args.env).to.be.deep.equal(undefined);
122+
});
99123

100124
test('Create terminal without a title', () => {
101125
const terminal = 'Terminal Created';

src/test/smoke/smartSend.smoke.test.ts

Lines changed: 64 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,81 +6,78 @@ import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_SMOKE_TEST } from '../constants';
66
import { closeActiveWindows, initialize, initializeTest } from '../initialize';
77
import { openFile, waitForCondition } from '../common';
88

9-
// TODO: This test is being flaky for windows, need to investigate why only fails on windows
10-
if (process.platform !== 'win32') {
11-
suite('Smoke Test: Run Smart Selection and Advance Cursor', () => {
12-
suiteSetup(async function () {
13-
if (!IS_SMOKE_TEST) {
14-
return this.skip();
15-
}
16-
await initialize();
17-
return undefined;
18-
});
9+
suite('Smoke Test: Run Smart Selection and Advance Cursor', async () => {
10+
suiteSetup(async function () {
11+
if (!IS_SMOKE_TEST) {
12+
return this.skip();
13+
}
14+
await initialize();
15+
return undefined;
16+
});
1917

20-
setup(initializeTest);
21-
suiteTeardown(closeActiveWindows);
22-
teardown(closeActiveWindows);
18+
setup(initializeTest);
19+
suiteTeardown(closeActiveWindows);
20+
teardown(closeActiveWindows);
2321

24-
test('Smart Send', async () => {
25-
const file = path.join(
26-
EXTENSION_ROOT_DIR_FOR_TESTS,
27-
'src',
28-
'testMultiRootWkspc',
29-
'smokeTests',
30-
'create_delete_file.py',
31-
);
32-
const outputFile = path.join(
33-
EXTENSION_ROOT_DIR_FOR_TESTS,
34-
'src',
35-
'testMultiRootWkspc',
36-
'smokeTests',
37-
'smart_send_smoke.txt',
38-
);
22+
test('Smart Send', async () => {
23+
const file = path.join(
24+
EXTENSION_ROOT_DIR_FOR_TESTS,
25+
'src',
26+
'testMultiRootWkspc',
27+
'smokeTests',
28+
'create_delete_file.py',
29+
);
30+
const outputFile = path.join(
31+
EXTENSION_ROOT_DIR_FOR_TESTS,
32+
'src',
33+
'testMultiRootWkspc',
34+
'smokeTests',
35+
'smart_send_smoke.txt',
36+
);
3937

40-
await fs.remove(outputFile);
38+
await fs.remove(outputFile);
4139

42-
const textDocument = await openFile(file);
40+
const textDocument = await openFile(file);
4341

44-
if (vscode.window.activeTextEditor) {
45-
const myPos = new vscode.Position(0, 0);
46-
vscode.window.activeTextEditor!.selections = [new vscode.Selection(myPos, myPos)];
47-
}
48-
await vscode.commands
49-
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
50-
.then(undefined, (err) => {
51-
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
52-
});
42+
if (vscode.window.activeTextEditor) {
43+
const myPos = new vscode.Position(0, 0);
44+
vscode.window.activeTextEditor!.selections = [new vscode.Selection(myPos, myPos)];
45+
}
46+
await vscode.commands
47+
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
48+
.then(undefined, (err) => {
49+
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
50+
});
5351

54-
const checkIfFileHasBeenCreated = () => fs.pathExists(outputFile);
55-
await waitForCondition(checkIfFileHasBeenCreated, 10_000, `"${outputFile}" file not created`);
52+
const checkIfFileHasBeenCreated = () => fs.pathExists(outputFile);
53+
await waitForCondition(checkIfFileHasBeenCreated, 20_000, `"${outputFile}" file not created`);
5654

57-
await vscode.commands
58-
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
59-
.then(undefined, (err) => {
60-
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
61-
});
62-
await vscode.commands
63-
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
64-
.then(undefined, (err) => {
65-
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
66-
});
55+
await vscode.commands
56+
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
57+
.then(undefined, (err) => {
58+
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
59+
});
60+
await vscode.commands
61+
.executeCommand<void>('python.execSelectionInTerminal', textDocument.uri)
62+
.then(undefined, (err) => {
63+
assert.fail(`Something went wrong running the Python file in the terminal: ${err}`);
64+
});
6765

68-
async function wait() {
69-
return new Promise<void>((resolve) => {
70-
setTimeout(() => {
71-
resolve();
72-
}, 10000);
73-
});
74-
}
66+
async function wait() {
67+
return new Promise<void>((resolve) => {
68+
setTimeout(() => {
69+
resolve();
70+
}, 10000);
71+
});
72+
}
7573

76-
await wait();
74+
await wait();
7775

78-
const deletedFile = !(await fs.pathExists(outputFile));
79-
if (deletedFile) {
80-
assert.ok(true, `"${outputFile}" file has been deleted`);
81-
} else {
82-
assert.fail(`"${outputFile}" file still exists`);
83-
}
84-
});
76+
const deletedFile = !(await fs.pathExists(outputFile));
77+
if (deletedFile) {
78+
assert.ok(true, `"${outputFile}" file has been deleted`);
79+
} else {
80+
assert.fail(`"${outputFile}" file still exists`);
81+
}
8582
});
86-
}
83+
});

src/test/smokeTest.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
// Must always be on top to setup expected env.
77
process.env.VSC_PYTHON_SMOKE_TEST = '1';
8-
98
import { spawn } from 'child_process';
109
import * as fs from '../client/common/platform/fs-paths';
1110
import * as glob from 'glob';

0 commit comments

Comments
 (0)