Skip to content

Commit 6346a6f

Browse files
committed
Make shift+enter work in DS and non-DS scenarios (#9445)
* Conda run doesn't work for normalizing lines or starting a repl * Add news entries * Fix the rest of the linter errors * Fix unit tests * Fix helper tests
1 parent c111e98 commit 6346a6f

File tree

9 files changed

+74
-70
lines changed

9 files changed

+74
-70
lines changed

news/2 Fixes/9437.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shift+Enter can no longer send multiple lines to the interactive window.

news/2 Fixes/9439.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shift+Enter can no longer run code in the terminal.

src/client/terminals/codeExecution/djangoShellCodeExecution.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Disposable, Uri } from 'vscode';
99
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../common/application/types';
1010
import '../../common/extensions';
1111
import { IFileSystem, IPlatformService } from '../../common/platform/types';
12-
import { IPythonExecutionFactory, PythonExecutionInfo } from '../../common/process/types';
12+
import { PythonExecutionInfo } from '../../common/process/types';
1313
import { ITerminalServiceFactory } from '../../common/terminal/types';
1414
import { IConfigurationService, IDisposableRegistry } from '../../common/types';
1515
import { DjangoContextInitializer } from './djangoContext';
@@ -25,10 +25,9 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi
2525
@inject(IPlatformService) platformService: IPlatformService,
2626
@inject(ICommandManager) commandManager: ICommandManager,
2727
@inject(IFileSystem) fileSystem: IFileSystem,
28-
@inject(IPythonExecutionFactory) pythonExecFactory: IPythonExecutionFactory,
2928
@inject(IDisposableRegistry) disposableRegistry: Disposable[]
3029
) {
31-
super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService, pythonExecFactory);
30+
super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService);
3231
this.terminalTitle = 'Django Shell';
3332
disposableRegistry.push(new DjangoContextInitializer(documentManager, workspace, fileSystem, commandManager));
3433
}

src/client/terminals/codeExecution/helper.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
3+
import '../../common/extensions';
34

45
import { inject, injectable } from 'inversify';
56
import * as path from 'path';
67
import { Range, TextEditor, Uri } from 'vscode';
8+
79
import { IApplicationShell, IDocumentManager } from '../../common/application/types';
810
import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants';
9-
import '../../common/extensions';
1011
import { traceError } from '../../common/logger';
11-
import { IPythonExecutionFactory } from '../../common/process/types';
12+
import { IProcessServiceFactory } from '../../common/process/types';
13+
import { IInterpreterService } from '../../interpreter/contracts';
1214
import { IServiceContainer } from '../../ioc/types';
1315
import { ICodeExecutionHelper } from '../types';
1416

1517
@injectable()
1618
export class CodeExecutionHelper implements ICodeExecutionHelper {
1719
private readonly documentManager: IDocumentManager;
1820
private readonly applicationShell: IApplicationShell;
19-
private readonly pythonServiceFactory: IPythonExecutionFactory;
21+
private readonly processServiceFactory: IProcessServiceFactory;
22+
private readonly interpreterService: IInterpreterService;
2023
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
2124
this.documentManager = serviceContainer.get<IDocumentManager>(IDocumentManager);
2225
this.applicationShell = serviceContainer.get<IApplicationShell>(IApplicationShell);
23-
this.pythonServiceFactory = serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
26+
this.processServiceFactory = serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
27+
this.interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
2428
}
2529
public async normalizeLines(code: string, resource?: Uri): Promise<string> {
2630
try {
@@ -30,9 +34,10 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
3034
// On windows cr is not handled well by python when passing in/out via stdin/stdout.
3135
// So just remove cr from the input.
3236
code = code.replace(new RegExp('\\r', 'g'), '');
37+
const interpreter = await this.interpreterService.getActiveInterpreter(resource);
38+
const processService = await this.processServiceFactory.create(resource);
3339
const args = [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'normalizeForInterpreter.py'), code];
34-
const processService = await this.pythonServiceFactory.create({ resource });
35-
const proc = await processService.exec(args, { throwOnStdErr: true });
40+
const proc = await processService.exec(interpreter?.path || 'python', args, { throwOnStdErr: true });
3641

3742
return proc.stdout;
3843
} catch (ex) {

src/client/terminals/codeExecution/repl.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { inject, injectable } from 'inversify';
77
import { Disposable } from 'vscode';
88
import { IWorkspaceService } from '../../common/application/types';
99
import { IPlatformService } from '../../common/platform/types';
10-
import { IPythonExecutionFactory } from '../../common/process/types';
1110
import { ITerminalServiceFactory } from '../../common/terminal/types';
1211
import { IConfigurationService, IDisposableRegistry } from '../../common/types';
1312
import { TerminalCodeExecutionProvider } from './terminalCodeExecution';
@@ -18,11 +17,10 @@ export class ReplProvider extends TerminalCodeExecutionProvider {
1817
@inject(ITerminalServiceFactory) terminalServiceFactory: ITerminalServiceFactory,
1918
@inject(IConfigurationService) configurationService: IConfigurationService,
2019
@inject(IWorkspaceService) workspace: IWorkspaceService,
21-
@inject(IPythonExecutionFactory) pythonExecFactory: IPythonExecutionFactory,
2220
@inject(IDisposableRegistry) disposableRegistry: Disposable[],
2321
@inject(IPlatformService) platformService: IPlatformService
2422
) {
25-
super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService, pythonExecFactory);
23+
super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService);
2624
this.terminalTitle = 'REPL';
2725
}
2826
}

src/client/terminals/codeExecution/terminalCodeExecution.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Disposable, Uri } from 'vscode';
99
import { IWorkspaceService } from '../../common/application/types';
1010
import '../../common/extensions';
1111
import { IPlatformService } from '../../common/platform/types';
12-
import { IPythonExecutionFactory, PythonExecutionInfo } from '../../common/process/types';
12+
import { PythonExecutionInfo } from '../../common/process/types';
1313
import { ITerminalService, ITerminalServiceFactory } from '../../common/terminal/types';
1414
import { IConfigurationService, IDisposableRegistry } from '../../common/types';
1515
import { ICodeExecutionService } from '../../terminals/types';
@@ -24,8 +24,7 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
2424
@inject(IConfigurationService) protected readonly configurationService: IConfigurationService,
2525
@inject(IWorkspaceService) protected readonly workspace: IWorkspaceService,
2626
@inject(IDisposableRegistry) protected readonly disposables: Disposable[],
27-
@inject(IPlatformService) protected readonly platformService: IPlatformService,
28-
@inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory
27+
@inject(IPlatformService) protected readonly platformService: IPlatformService
2928
) {}
3029

3130
public async executeFile(file: Uri) {
@@ -64,11 +63,6 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
6463
const command = pythonSettings.pythonPath;
6564
const launchArgs = pythonSettings.terminal.launchArgs;
6665

67-
const condaExecutionService = await this.pythonExecFactory.createCondaExecutionService(command, undefined, resource);
68-
if (condaExecutionService) {
69-
return condaExecutionService.getExecutionInfo([...launchArgs, ...args]);
70-
}
71-
7266
const isWindows = this.platformService.isWindows;
7367

7468
return {

src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ suite('Terminal - Django Shell Code Execution', () => {
3737
workspace
3838
.setup(c => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
3939
.returns(() => {
40-
return {
41-
dispose: () => void 0
42-
};
43-
});
40+
return {
41+
dispose: () => void 0
42+
};
43+
});
4444
platform = TypeMoq.Mock.ofType<IPlatformService>();
4545
const documentManager = TypeMoq.Mock.ofType<IDocumentManager>();
4646
const commandManager = TypeMoq.Mock.ofType<ICommandManager>();
@@ -54,7 +54,6 @@ suite('Terminal - Django Shell Code Execution', () => {
5454
platform.object,
5555
commandManager.object,
5656
fileSystem.object,
57-
pythonExecutionFactory.object,
5857
disposables
5958
);
6059

@@ -74,7 +73,14 @@ suite('Terminal - Django Shell Code Execution', () => {
7473
disposables = [];
7574
});
7675

77-
async function testReplCommandArguments(isWindows: boolean, pythonPath: string, expectedPythonPath: string, terminalArgs: string[], expectedTerminalArgs: string[], resource?: Uri) {
76+
async function testReplCommandArguments(
77+
isWindows: boolean,
78+
pythonPath: string,
79+
expectedPythonPath: string,
80+
terminalArgs: string[],
81+
expectedTerminalArgs: string[],
82+
resource?: Uri
83+
) {
7884
platform.setup(p => p.isWindows).returns(() => isWindows);
7985
settings.setup(s => s.pythonPath).returns(() => pythonPath);
8086
terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs);
@@ -179,16 +185,16 @@ suite('Terminal - Django Shell Code Execution', () => {
179185
const serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
180186
const processService = TypeMoq.Mock.ofType<IProcessService>();
181187
const condaExecutionService = new CondaExecutionService(serviceContainer.object, processService.object, pythonPath, condaFile, condaEnv);
182-
const hasEnvName = condaEnv.name !== '';
183-
const condaArgs = ['run', ...(hasEnvName ? ['-n', condaEnv.name] : ['-p', condaEnv.path]), 'python'];
184-
const expectedTerminalArgs = [...condaArgs, ...terminalArgs, 'manage.py', 'shell'];
185-
pythonExecutionFactory.setup(p => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(condaExecutionService));
188+
const expectedTerminalArgs = [...terminalArgs, 'manage.py', 'shell'];
189+
pythonExecutionFactory
190+
.setup(p => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
191+
.returns(() => Promise.resolve(condaExecutionService));
186192

187193
const replCommandArgs = await (executor as DjangoShellCodeExecutionProvider).getExecutableInfo(resource);
188194

189195
expect(replCommandArgs).not.to.be.an('undefined', 'Conda command args are undefined');
190-
expect(replCommandArgs.command).to.be.equal(condaFile, 'Incorrect conda path');
191-
expect(replCommandArgs.args).to.be.deep.equal(expectedTerminalArgs, 'Incorrect conda arguments');
196+
expect(replCommandArgs.command).to.be.equal(pythonPath, 'Repl should use python not conda');
197+
expect(replCommandArgs.args).to.be.deep.equal(expectedTerminalArgs, 'Incorrect terminal arguments');
192198
}
193199

194200
test('Ensure conda args including env name are passed when using a conda environment with a name', async () => {

src/test/terminals/codeExecution/helper.test.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import { expect } from 'chai';
77
import * as fs from 'fs-extra';
88
import { EOL } from 'os';
99
import * as path from 'path';
10+
import { SemVer } from 'semver';
1011
import * as TypeMoq from 'typemoq';
1112
import { Range, Selection, TextDocument, TextEditor, TextLine, Uri } from 'vscode';
1213
import { IApplicationShell, IDocumentManager } from '../../../client/common/application/types';
1314
import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../../client/common/constants';
1415
import '../../../client/common/extensions';
1516
import { BufferDecoder } from '../../../client/common/process/decoder';
1617
import { ProcessService } from '../../../client/common/process/proc';
17-
import { IPythonExecutionFactory, IPythonExecutionService } from '../../../client/common/process/types';
18-
import { OSType } from '../../../client/common/utils/platform';
18+
import { IProcessService, IProcessServiceFactory } from '../../../client/common/process/types';
19+
import { Architecture, OSType } from '../../../client/common/utils/platform';
1920
import { IEnvironmentVariablesProvider } from '../../../client/common/variables/types';
21+
import { IInterpreterService, InterpreterType, PythonInterpreter } from '../../../client/interpreter/contracts';
2022
import { IServiceContainer } from '../../../client/ioc/types';
2123
import { CodeExecutionHelper } from '../../../client/terminals/codeExecution/helper';
2224
import { ICodeExecutionHelper } from '../../../client/terminals/types';
@@ -31,19 +33,33 @@ suite('Terminal - Code Execution Helper', () => {
3133
let helper: ICodeExecutionHelper;
3234
let document: TypeMoq.IMock<TextDocument>;
3335
let editor: TypeMoq.IMock<TextEditor>;
34-
let pythonService: TypeMoq.IMock<IPythonExecutionService>;
36+
let processService: TypeMoq.IMock<IProcessService>;
37+
let interpreterService: TypeMoq.IMock<IInterpreterService>;
38+
const workingPython: PythonInterpreter = {
39+
path: PYTHON_PATH,
40+
version: new SemVer('3.6.6-final'),
41+
sysVersion: '1.0.0.0',
42+
sysPrefix: 'Python',
43+
displayName: 'Python',
44+
type: InterpreterType.Unknown,
45+
architecture: Architecture.x64
46+
};
47+
3548
setup(() => {
3649
const serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
3750
documentManager = TypeMoq.Mock.ofType<IDocumentManager>();
3851
applicationShell = TypeMoq.Mock.ofType<IApplicationShell>();
3952
const envVariablesProvider = TypeMoq.Mock.ofType<IEnvironmentVariablesProvider>();
40-
pythonService = TypeMoq.Mock.ofType<IPythonExecutionService>();
53+
processService = TypeMoq.Mock.ofType<IProcessService>();
54+
interpreterService = TypeMoq.Mock.ofType<IInterpreterService>();
4155
// tslint:disable-next-line:no-any
42-
pythonService.setup((x: any) => x.then).returns(() => undefined);
56+
processService.setup((x: any) => x.then).returns(() => undefined);
57+
interpreterService.setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())).returns(() => Promise.resolve(workingPython));
58+
const processServiceFactory = TypeMoq.Mock.ofType<IProcessServiceFactory>();
59+
processServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object));
4360
envVariablesProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({}));
44-
const pythonExecFactory = TypeMoq.Mock.ofType<IPythonExecutionFactory>();
45-
pythonExecFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(pythonService.object));
46-
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPythonExecutionFactory), TypeMoq.It.isAny())).returns(() => pythonExecFactory.object);
61+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())).returns(() => processServiceFactory.object);
62+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())).returns(() => interpreterService.object);
4763
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())).returns(() => documentManager.object);
4864
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())).returns(() => applicationShell.object);
4965
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())).returns(() => envVariablesProvider.object);
@@ -56,10 +72,10 @@ suite('Terminal - Code Execution Helper', () => {
5672

5773
async function ensureBlankLinesAreRemoved(source: string, expectedSource: string) {
5874
const actualProcessService = new ProcessService(new BufferDecoder());
59-
pythonService
60-
.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
61-
.returns((args, options) => {
62-
return actualProcessService.exec.apply(actualProcessService, [PYTHON_PATH, args, options]);
75+
processService
76+
.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
77+
.returns((file, args, options) => {
78+
return actualProcessService.exec.apply(actualProcessService, [file, args, options]);
6379
});
6480
const normalizedZCode = await helper.normalizeLines(source);
6581
// In case file has been saved with different line endings.
@@ -81,9 +97,9 @@ suite('Terminal - Code Execution Helper', () => {
8197
test('Ensure there are no multiple-CR elements in the normalized code.', async () => {
8298
const code = ['import sys', '', '', '', 'print(sys.executable)', '', 'print("1234")', '', '', 'print(1)', 'print(2)'];
8399
const actualProcessService = new ProcessService(new BufferDecoder());
84-
pythonService
85-
.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
86-
.returns((args, options) => {
100+
processService
101+
.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
102+
.returns((_file, args, options) => {
87103
return actualProcessService.exec.apply(actualProcessService, [PYTHON_PATH, args, options]);
88104
});
89105
const normalizedCode = await helper.normalizeLines(code.join(EOL));

0 commit comments

Comments
 (0)