Skip to content

Commit bf2d5d5

Browse files
committed
fix #456
1 parent e6c9472 commit bf2d5d5

File tree

8 files changed

+84
-43
lines changed

8 files changed

+84
-43
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { inject, injectable } from 'inversify';
5+
import { Uri } from 'vscode';
6+
import { IServiceContainer } from '../../ioc/types';
7+
import { ExecutionInfo } from '../types';
8+
import { IEnvironmentVariablesProvider } from '../variables/types';
9+
import { ExecutionResult, IProcessService, IPythonExecutionFactory, IPythonToolExecutionService, ObservableExecutionResult, SpawnOptions } from './types';
10+
11+
@injectable()
12+
export class PythonToolExecutionService implements IPythonToolExecutionService {
13+
constructor( @inject(IServiceContainer) private serviceContainer: IServiceContainer) { }
14+
public async execObservable(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise<ObservableExecutionResult<string>> {
15+
if (options.env) {
16+
throw new Error('Environment variables are not supported');
17+
}
18+
if (executionInfo.moduleName && executionInfo.moduleName.length > 0) {
19+
const pythonExecutionService = await this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create(resource);
20+
return pythonExecutionService.execModuleObservable(executionInfo.moduleName, executionInfo.args, options);
21+
} else {
22+
const env = await this.serviceContainer.get<IEnvironmentVariablesProvider>(IEnvironmentVariablesProvider).getEnvironmentVariables(resource);
23+
const processService = this.serviceContainer.get<IProcessService>(IProcessService);
24+
return processService.execObservable(executionInfo.execPath!, executionInfo.args, { ...options, env });
25+
}
26+
}
27+
public async exec(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise<ExecutionResult<string>> {
28+
if (options.env) {
29+
throw new Error('Environment variables are not supported');
30+
}
31+
if (executionInfo.moduleName && executionInfo.moduleName.length > 0) {
32+
const pythonExecutionService = await this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create(resource);
33+
return pythonExecutionService.execModule(executionInfo.moduleName!, executionInfo.args, options);
34+
} else {
35+
const env = await this.serviceContainer.get<IEnvironmentVariablesProvider>(IEnvironmentVariablesProvider).getEnvironmentVariables(resource);
36+
const processService = this.serviceContainer.get<IProcessService>(IProcessService);
37+
return processService.exec(executionInfo.execPath!, executionInfo.args, { ...options, env });
38+
}
39+
}
40+
}

src/client/common/process/serviceRegistry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { IServiceManager } from '../../ioc/types';
55
import { BufferDecoder } from './decoder';
66
import { ProcessService } from './proc';
77
import { PythonExecutionFactory } from './pythonExecutionFactory';
8-
import { IBufferDecoder, IProcessService, IPythonExecutionFactory } from './types';
8+
import { PythonToolExecutionService } from './pythonToolService';
9+
import { IBufferDecoder, IProcessService, IPythonExecutionFactory, IPythonToolExecutionService } from './types';
910

1011
export function registerTypes(serviceManager: IServiceManager) {
1112
serviceManager.addSingleton<IBufferDecoder>(IBufferDecoder, BufferDecoder);
1213
serviceManager.addSingleton<IProcessService>(IProcessService, ProcessService);
1314
serviceManager.addSingleton<IPythonExecutionFactory>(IPythonExecutionFactory, PythonExecutionFactory);
15+
serviceManager.addSingleton<IPythonToolExecutionService>(IPythonToolExecutionService, PythonToolExecutionService);
1416
}

src/client/common/process/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { ChildProcess, SpawnOptions as ChildProcessSpawnOptions } from 'child_process';
55
import * as Rx from 'rxjs';
66
import { CancellationToken, Uri } from 'vscode';
7+
import { ExecutionInfo } from '../types';
78
import { EnvironmentVariables } from '../variables/types';
89

910
export const IBufferDecoder = Symbol('IBufferDecoder');
@@ -69,3 +70,10 @@ export class StdErrError extends Error {
6970
export interface IExecutionEnvironmentVariablesService {
7071
getEnvironmentVariables(resource?: Uri): Promise<EnvironmentVariables | undefined>;
7172
}
73+
74+
export const IPythonToolExecutionService = Symbol('IPythonToolRunnerService');
75+
76+
export interface IPythonToolExecutionService {
77+
execObservable(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise<ObservableExecutionResult<string>>;
78+
exec(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise<ExecutionResult<string>>;
79+
}

src/client/formatters/baseFormatter.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as vscode from 'vscode';
44
import { OutputChannel, TextEdit, Uri } from 'vscode';
55
import { STANDARD_OUTPUT_CHANNEL } from '../common/constants';
66
import { isNotInstalledError } from '../common/helpers';
7+
import { IPythonToolExecutionService } from '../common/process/types';
78
import { IProcessService, IPythonExecutionFactory } from '../common/process/types';
89
import { IInstaller, IOutputChannel, Product } from '../common/types';
910
import { IEnvironmentVariablesProvider } from '../common/variables/types';
@@ -53,21 +54,11 @@ export abstract class BaseFormatter {
5354
return [];
5455
}
5556

56-
let executionPromise: Promise<string>;
5757
const executionInfo = this.helper.getExecutionInfo(this.product, args, document.uri);
58-
// Check if required to run as a module or executable.
59-
if (executionInfo.moduleName) {
60-
executionPromise = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create(document.uri)
61-
.then(pythonExecutionService => pythonExecutionService.execModule(executionInfo.moduleName!, executionInfo.args.concat([filePath]), { cwd, throwOnStdErr: true, token }))
62-
.then(output => output.stdout);
63-
} else {
64-
const executionService = this.serviceContainer.get<IProcessService>(IProcessService);
65-
executionPromise = this.serviceContainer.get<IEnvironmentVariablesProvider>(IEnvironmentVariablesProvider).getEnvironmentVariables(document.uri)
66-
.then(env => executionService.exec(executionInfo.execPath!, executionInfo.args.concat([filePath]), { cwd, env, throwOnStdErr: true, token }))
67-
.then(output => output.stdout);
68-
}
69-
70-
const promise = executionPromise
58+
executionInfo.args.push(filePath);
59+
const pythonToolsExecutionService = this.serviceContainer.get<IPythonToolExecutionService>(IPythonToolExecutionService);
60+
const promise = pythonToolsExecutionService.exec(executionInfo, { cwd, throwOnStdErr: true, token }, document.uri)
61+
.then(output => output.stdout)
7162
.then(data => {
7263
if (token && token.isCancellationRequested) {
7364
return [] as TextEdit[];

src/client/linters/baseLinter.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as path from 'path';
2-
import { CancellationToken, OutputChannel, TextDocument, Uri } from 'vscode';
32
import * as vscode from 'vscode';
3+
import { CancellationToken, OutputChannel, TextDocument, Uri } from 'vscode';
44
import { IPythonSettings, PythonSettings } from '../common/configSettings';
55
import '../common/extensions';
6+
import { IPythonToolExecutionService } from '../common/process/types';
67
import { ExecutionResult, IProcessService, IPythonExecutionFactory } from '../common/process/types';
78
import { ExecutionInfo, IInstaller, ILogger, Product } from '../common/types';
89
import { IEnvironmentVariablesProvider } from '../common/variables/types';
@@ -117,19 +118,9 @@ export abstract class BaseLinter {
117118
protected async run(args: string[], document: vscode.TextDocument, cancellation: vscode.CancellationToken, regEx: string = REGEX): Promise<ILintMessage[]> {
118119
const executionInfo = this.helper.getExecutionInfo(this.product, args, document.uri);
119120
const cwd = this.getWorkspaceRootPath(document);
120-
let executionPromise: Promise<ExecutionResult<string>>;
121-
122-
// Check if required to run as a module or executable.
123-
if (executionInfo.moduleName) {
124-
const pythonExecutionService = await this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create(document.uri);
125-
executionPromise = pythonExecutionService.execModule(executionInfo.moduleName!, executionInfo.args, { cwd, mergeStdOutErr: true, token: cancellation });
126-
} else {
127-
const env = await this.serviceContainer.get<IEnvironmentVariablesProvider>(IEnvironmentVariablesProvider).getEnvironmentVariables(document.uri);
128-
const executionService = this.serviceContainer.get<IProcessService>(IProcessService);
129-
executionPromise = executionService.exec(executionInfo.execPath!, executionInfo.args, { cwd, env, token: cancellation, mergeStdOutErr: true });
130-
}
121+
const pythonToolsExecutionService = this.serviceContainer.get<IPythonToolExecutionService>(IPythonToolExecutionService);
131122
try {
132-
const result = await executionPromise;
123+
const result = await pythonToolsExecutionService.exec(executionInfo, {cwd, token: cancellation, mergeStdOutErr: true}, document.uri);
133124
this.displayLinterResultHeader(result.stdout);
134125
return await this.parseMessages(result.stdout, document, cancellation, regEx);
135126
} catch (error) {

src/client/unittests/common/runner.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ import { CancellationToken, OutputChannel, Uri } from 'vscode';
33
import { IPythonSettings, PythonSettings } from '../../common/configSettings';
44
import { ErrorUtils } from '../../common/errors/errorUtils';
55
import { ModuleNotInstalledError } from '../../common/errors/moduleNotInstalledError';
6+
import { IPythonToolExecutionService } from '../../common/process/types';
67
import {
78
IProcessService,
89
IPythonExecutionFactory,
910
IPythonExecutionService,
1011
ObservableExecutionResult,
1112
SpawnOptions
1213
} from '../../common/process/types';
14+
import { ExecutionInfo } from '../../common/types';
1315
import { IEnvironmentVariablesProvider } from '../../common/variables/types';
1416
import { IServiceContainer } from '../../ioc/types';
1517
import { NOSETEST_PROVIDER, PYTEST_PROVIDER, UNITTEST_PROVIDER } from './constants';
16-
import { TestProvider } from './types';
18+
import { ITestsHelper, TestProvider } from './types';
1719

1820
export type Options = {
1921
workspaceFolder: Uri;
@@ -36,21 +38,17 @@ export async function run(serviceContainer: IServiceContainer, testProvider: Tes
3638
// Unit tests have a special way of being executed
3739
const pythonServiceFactory = serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
3840
pythonExecutionServicePromise = pythonServiceFactory.create(options.workspaceFolder);
39-
promise = pythonExecutionServicePromise.then(executionService => {
40-
return executionService.execObservable(options.args, { ...spawnOptions });
41-
});
42-
} else if (testExecutablePath) {
43-
const processService = serviceContainer.get<IProcessService>(IProcessService);
44-
const envVarsService = serviceContainer.get<IEnvironmentVariablesProvider>(IEnvironmentVariablesProvider);
45-
promise = envVarsService.getEnvironmentVariables(options.workspaceFolder).then(executionService => {
46-
return processService.execObservable(testExecutablePath, options.args, { ...spawnOptions });
47-
});
41+
promise = pythonExecutionServicePromise.then(executionService => executionService.execObservable(options.args, { ...spawnOptions }));
4842
} else {
49-
const pythonServiceFactory = serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
50-
pythonExecutionServicePromise = pythonServiceFactory.create(options.workspaceFolder);
51-
promise = pythonExecutionServicePromise.then(executionService => {
52-
return executionService.execModuleObservable(moduleName, options.args, { ...spawnOptions });
53-
});
43+
const pythonToolsExecutionService = serviceContainer.get<IPythonToolExecutionService>(IPythonToolExecutionService);
44+
const testHelper = serviceContainer.get<ITestsHelper>(ITestsHelper);
45+
const executionInfo: ExecutionInfo = {
46+
execPath: testExecutablePath,
47+
args: options.args,
48+
moduleName: testExecutablePath && testExecutablePath.length > 0 ? undefined : moduleName,
49+
product: testHelper.parseProduct(testProvider)
50+
};
51+
promise = pythonToolsExecutionService.execObservable(executionInfo, spawnOptions, options.workspaceFolder);
5452
}
5553

5654
return promise.then(result => {

src/client/unittests/common/testUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ export class TestsHelper implements ITestsHelper {
5555
}
5656
}
5757
}
58+
public parseProduct(provider: TestProvider): UnitTestProduct {
59+
switch (provider) {
60+
case 'nosetest': return Product.nosetest;
61+
case 'pytest': return Product.pytest;
62+
case 'unittest': return Product.unittest;
63+
default: {
64+
throw new Error(`Unknown Test Provider ${provider}`);
65+
}
66+
}
67+
}
5868
public getSettingsPropertyNames(product: UnitTestProduct): TestSettingsPropertyNames {
5969
const id = this.parseProviderName(product);
6070
switch (id) {

src/client/unittests/common/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export const ITestsHelper = Symbol('ITestsHelper');
156156

157157
export interface ITestsHelper {
158158
parseProviderName(product: UnitTestProduct): TestProvider;
159+
parseProduct(provider: TestProvider): UnitTestProduct;
159160
getSettingsPropertyNames(product: Product): TestSettingsPropertyNames;
160161
flattenTestFiles(testFiles: TestFile[]): Tests;
161162
placeTestFilesIntoFolders(tests: Tests): void;

0 commit comments

Comments
 (0)