From e26b5418b1fab2a8324f46eef0e266eb187cf96d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 17:44:42 -0700 Subject: [PATCH 01/15] factory to create the process service --- src/client/activation/analysis.ts | 4 +-- src/client/common/configSettings.ts | 6 ++-- .../common/installer/productInstaller.ts | 6 ++-- src/client/common/process/proc.ts | 9 ++++-- src/client/common/process/processFactory.ts | 24 ++++++++++++++++ .../common/process/pythonExecutionFactory.ts | 13 ++++----- src/client/common/process/pythonProcess.ts | 21 ++------------ .../common/process/pythonToolService.ts | 15 ++++------ src/client/common/process/serviceRegistry.ts | 6 ++-- src/client/common/process/types.ts | 6 ++++ src/client/common/types.ts | 6 ++-- .../variables/environmentVariablesProvider.ts | 2 +- .../display/shebangCodeLensProvider.ts | 28 +++++++++---------- .../locators/services/condaService.ts | 26 +++++++++-------- .../locators/services/pipEnvService.ts | 10 +++---- src/client/interpreter/virtualEnvs/index.ts | 9 +++--- src/client/providers/importSortProvider.ts | 14 +++++++--- src/client/refactor/proxy.ts | 25 ++++++++--------- src/client/sortImports.ts | 8 ++---- src/client/workspaceSymbols/generator.ts | 19 +++++++------ src/client/workspaceSymbols/main.ts | 27 ++++++++---------- src/test/format/extension.sort.test.ts | 5 +--- .../interpreters/interpreterVersion.test.ts | 6 ++-- src/test/workspaceSymbols/multiroot.test.ts | 14 +++++----- src/test/workspaceSymbols/standard.test.ts | 18 ++++++------ 25 files changed, 171 insertions(+), 156 deletions(-) create mode 100644 src/client/common/process/processFactory.ts diff --git a/src/client/activation/analysis.ts b/src/client/activation/analysis.ts index d2e853dc7dda..239dbef44edb 100644 --- a/src/client/activation/analysis.ts +++ b/src/client/activation/analysis.ts @@ -9,7 +9,7 @@ import { IApplicationShell } from '../common/application/types'; import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../common/constants'; import { createDeferred, Deferred } from '../common/helpers'; import { IFileSystem, IPlatformService } from '../common/platform/types'; -import { IProcessService } from '../common/process/types'; +import { IProcessServiceFactory } from '../common/process/types'; import { StopWatch } from '../common/stopWatch'; import { IConfigurationService, IOutputChannel, IPythonSettings } from '../common/types'; import { IEnvironmentVariablesProvider } from '../common/variables/types'; @@ -227,7 +227,7 @@ export class AnalysisExtensionActivator implements IExtensionActivator { } private async isDotNetInstalled(): Promise { - const ps = this.services.get(IProcessService); + const ps = await this.services.get(IProcessServiceFactory).create(); const result = await ps.exec('dotnet', ['--version']).catch(() => { return { stdout: '' }; }); return result.stdout.trim().startsWith('2.'); } diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index e040313e90ff..d4db5b867a9a 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -32,12 +32,12 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { public venvPath = ''; public venvFolders: string[] = []; public devOptions: string[] = []; - public linting?: ILintingSettings; + public linting!: ILintingSettings; public formatting!: IFormattingSettings; - public autoComplete?: IAutoCompleteSettings; + public autoComplete!: IAutoCompeteSettings; public unitTest!: IUnitTestSettings; public terminal!: ITerminalSettings; - public sortImports?: ISortImportSettings; + public sortImports!: ISortImportSettings; public workspaceSymbols!: IWorkspaceSymbolSettings; public disableInstallationChecks = false; public globalModuleInstallation = false; diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index a0929e86de66..f74c79136d15 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -9,7 +9,7 @@ import { ITestsHelper } from '../../unittests/common/types'; import { IApplicationShell } from '../application/types'; import { STANDARD_OUTPUT_CHANNEL } from '../constants'; import { IPlatformService } from '../platform/types'; -import { IProcessService, IPythonExecutionFactory } from '../process/types'; +import { IProcessServiceFactory, IPythonExecutionFactory } from '../process/types'; import { ITerminalServiceFactory } from '../terminal/types'; import { IConfigurationService, IInstaller, ILogger, InstallerResponse, IOutputChannel, ModuleNamePurpose, Product } from '../types'; import { ProductNames } from './productNames'; @@ -77,7 +77,7 @@ abstract class BaseInstaller { const pythonProcess = await this.serviceContainer.get(IPythonExecutionFactory).create(resource); return pythonProcess.isModuleInstalled(executableName); } else { - const process = this.serviceContainer.get(IProcessService); + const process = await this.serviceContainer.get(IProcessServiceFactory).create(resource); return process.exec(executableName, ['--version'], { mergeStdOutErr: true }) .then(() => true) .catch(() => false); @@ -225,7 +225,7 @@ export class ProductInstaller implements IInstaller { private ProductTypes = new Map(); constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer, - @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private outputChannel: vscode.OutputChannel) { + @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private outputChannel: OutputChannel) { this.ProductTypes.set(Product.flake8, ProductType.Linter); this.ProductTypes.set(Product.mypy, ProductType.Linter); this.ProductTypes.set(Product.pep8, ProductType.Linter); diff --git a/src/client/common/process/proc.ts b/src/client/common/process/proc.ts index 426519f2ba68..ef7defa1c193 100644 --- a/src/client/common/process/proc.ts +++ b/src/client/common/process/proc.ts @@ -4,22 +4,25 @@ // tslint:disable:no-any import { spawn } from 'child_process'; -import { inject, injectable } from 'inversify'; +import { inject, injectable, optional } from 'inversify'; import { Observable } from 'rxjs/Observable'; import { Disposable } from 'vscode'; import { createDeferred } from '../helpers'; +import { EnvironmentVariables } from '../variables/types'; import { DEFAULT_ENCODING } from './constants'; import { ExecutionResult, IBufferDecoder, IProcessService, ObservableExecutionResult, Output, SpawnOptions, StdErrError } from './types'; @injectable() export class ProcessService implements IProcessService { - constructor(@inject(IBufferDecoder) private decoder: IBufferDecoder) { } + constructor(@inject(IBufferDecoder) private readonly decoder: IBufferDecoder, + @optional() private readonly env?: EnvironmentVariables) { } public execObservable(file: string, args: string[], options: SpawnOptions = {}): ObservableExecutionResult { const encoding = options.encoding = typeof options.encoding === 'string' && options.encoding.length > 0 ? options.encoding : DEFAULT_ENCODING; delete options.encoding; const spawnOptions = { ...options }; if (!spawnOptions.env || Object.keys(spawnOptions).length === 0) { - spawnOptions.env = { ...process.env }; + const env = this.env ? this.env : process.env; + spawnOptions.env = { ...env }; } // Always ensure we have unbuffered output. diff --git a/src/client/common/process/processFactory.ts b/src/client/common/process/processFactory.ts new file mode 100644 index 000000000000..91440cf9bddd --- /dev/null +++ b/src/client/common/process/processFactory.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { Uri } from 'vscode'; +import { IServiceContainer } from '../../ioc/types'; +import { IEnvironmentVariablesProvider } from '../variables/types'; +import { ProcessService } from './proc'; +import { IBufferDecoder, IProcessService, IProcessServiceFactory } from './types'; + +@injectable() +export class ProcessServiceFactory implements IProcessServiceFactory { + private envVarsService: IEnvironmentVariablesProvider; + constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) { + this.envVarsService = serviceContainer.get(IEnvironmentVariablesProvider); + } + public async create(resource?: Uri): Promise { + const customEnvVars = await this.envVarsService.getEnvironmentVariables(resource); + const decoder = this.serviceContainer.get(IBufferDecoder); + return new ProcessService(decoder, customEnvVars); + } +} diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index 4866e6e504f8..ceafb3aa827b 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -4,20 +4,17 @@ import { inject, injectable } from 'inversify'; import { Uri } from 'vscode'; import { IServiceContainer } from '../../ioc/types'; -import { IEnvironmentVariablesProvider } from '../variables/types'; import { PythonExecutionService } from './pythonProcess'; -import { IPythonExecutionFactory, IPythonExecutionService } from './types'; +import { IProcessServiceFactory, IPythonExecutionFactory, IPythonExecutionService } from './types'; @injectable() export class PythonExecutionFactory implements IPythonExecutionFactory { - private envVarsService: IEnvironmentVariablesProvider; + private processServiceFactory: IProcessServiceFactory; constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) { - this.envVarsService = serviceContainer.get(IEnvironmentVariablesProvider); + this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); } public async create(resource?: Uri): Promise { - return this.envVarsService.getEnvironmentVariables(resource) - .then(customEnvVars => { - return new PythonExecutionService(this.serviceContainer, customEnvVars, resource); - }); + const processService = await this.processServiceFactory.create(resource); + return new PythonExecutionService(this.serviceContainer, processService, resource); } } diff --git a/src/client/common/process/pythonProcess.ts b/src/client/common/process/pythonProcess.ts index 0d6622bed472..419b5d88dcba 100644 --- a/src/client/common/process/pythonProcess.ts +++ b/src/client/common/process/pythonProcess.ts @@ -9,17 +9,14 @@ import { ErrorUtils } from '../errors/errorUtils'; import { ModuleNotInstalledError } from '../errors/moduleNotInstalledError'; import { IFileSystem } from '../platform/types'; import { IConfigurationService } from '../types'; -import { EnvironmentVariables } from '../variables/types'; import { ExecutionResult, IProcessService, IPythonExecutionService, ObservableExecutionResult, SpawnOptions } from './types'; @injectable() export class PythonExecutionService implements IPythonExecutionService { - private readonly procService: IProcessService; private readonly configService: IConfigurationService; private readonly fileSystem: IFileSystem; - constructor(private serviceContainer: IServiceContainer, private envVars: EnvironmentVariables | undefined, private resource?: Uri) { - this.procService = serviceContainer.get(IProcessService); + constructor(private serviceContainer: IServiceContainer, private readonly procService: IProcessService, private resource?: Uri) { this.configService = serviceContainer.get(IConfigurationService); this.fileSystem = serviceContainer.get(IFileSystem); } @@ -34,40 +31,28 @@ export class PythonExecutionService implements IPythonExecutionService { if (await this.fileSystem.fileExistsAsync(this.pythonPath)) { return this.pythonPath; } - return this.procService.exec(this.pythonPath, ['-c', 'import sys;print(sys.executable)'], { env: this.envVars, throwOnStdErr: true }) + return this.procService.exec(this.pythonPath, ['-c', 'import sys;print(sys.executable)'], { throwOnStdErr: true }) .then(output => output.stdout.trim()); } public async isModuleInstalled(moduleName: string): Promise { - return this.procService.exec(this.pythonPath, ['-c', `import ${moduleName}`], { env: this.envVars, throwOnStdErr: true }) + return this.procService.exec(this.pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true }) .then(() => true).catch(() => false); } public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { const opts: SpawnOptions = { ...options }; - if (this.envVars) { - opts.env = this.envVars; - } return this.procService.execObservable(this.pythonPath, args, opts); } public execModuleObservable(moduleName: string, args: string[], options: SpawnOptions): ObservableExecutionResult { const opts: SpawnOptions = { ...options }; - if (this.envVars) { - opts.env = this.envVars; - } return this.procService.execObservable(this.pythonPath, ['-m', moduleName, ...args], opts); } public async exec(args: string[], options: SpawnOptions): Promise> { const opts: SpawnOptions = { ...options }; - if (this.envVars) { - opts.env = this.envVars; - } return this.procService.exec(this.pythonPath, args, opts); } public async execModule(moduleName: string, args: string[], options: SpawnOptions): Promise> { const opts: SpawnOptions = { ...options }; - if (this.envVars) { - opts.env = this.envVars; - } const result = await this.procService.exec(this.pythonPath, ['-m', moduleName, ...args], opts); // If a module is not installed we'll have something in stderr. diff --git a/src/client/common/process/pythonToolService.ts b/src/client/common/process/pythonToolService.ts index 9419b005250b..3369c35ac6b6 100644 --- a/src/client/common/process/pythonToolService.ts +++ b/src/client/common/process/pythonToolService.ts @@ -5,12 +5,11 @@ import { inject, injectable } from 'inversify'; import { Uri } from 'vscode'; import { IServiceContainer } from '../../ioc/types'; import { ExecutionInfo } from '../types'; -import { IEnvironmentVariablesProvider } from '../variables/types'; -import { ExecutionResult, IProcessService, IPythonExecutionFactory, IPythonToolExecutionService, ObservableExecutionResult, SpawnOptions } from './types'; +import { ExecutionResult, IProcessServiceFactory, IPythonExecutionFactory, IPythonToolExecutionService, ObservableExecutionResult, SpawnOptions } from './types'; @injectable() export class PythonToolExecutionService implements IPythonToolExecutionService { - constructor( @inject(IServiceContainer) private serviceContainer: IServiceContainer) { } + constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) { } public async execObservable(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise> { if (options.env) { throw new Error('Environment variables are not supported'); @@ -19,9 +18,8 @@ export class PythonToolExecutionService implements IPythonToolExecutionService { const pythonExecutionService = await this.serviceContainer.get(IPythonExecutionFactory).create(resource); return pythonExecutionService.execModuleObservable(executionInfo.moduleName, executionInfo.args, options); } else { - const env = await this.serviceContainer.get(IEnvironmentVariablesProvider).getEnvironmentVariables(resource); - const processService = this.serviceContainer.get(IProcessService); - return processService.execObservable(executionInfo.execPath!, executionInfo.args, { ...options, env }); + const processService = await this.serviceContainer.get(IProcessServiceFactory).create(resource); + return processService.execObservable(executionInfo.execPath!, executionInfo.args, { ...options }); } } public async exec(executionInfo: ExecutionInfo, options: SpawnOptions, resource: Uri): Promise> { @@ -32,9 +30,8 @@ export class PythonToolExecutionService implements IPythonToolExecutionService { const pythonExecutionService = await this.serviceContainer.get(IPythonExecutionFactory).create(resource); return pythonExecutionService.execModule(executionInfo.moduleName!, executionInfo.args, options); } else { - const env = await this.serviceContainer.get(IEnvironmentVariablesProvider).getEnvironmentVariables(resource); - const processService = this.serviceContainer.get(IProcessService); - return processService.exec(executionInfo.execPath!, executionInfo.args, { ...options, env }); + const processService = await this.serviceContainer.get(IProcessServiceFactory).create(resource); + return processService.exec(executionInfo.execPath!, executionInfo.args, { ...options }); } } } diff --git a/src/client/common/process/serviceRegistry.ts b/src/client/common/process/serviceRegistry.ts index dd4242fec55e..27684a20cc32 100644 --- a/src/client/common/process/serviceRegistry.ts +++ b/src/client/common/process/serviceRegistry.ts @@ -3,14 +3,14 @@ import { IServiceManager } from '../../ioc/types'; import { BufferDecoder } from './decoder'; -import { ProcessService } from './proc'; +import { ProcessServiceFactory } from './processFactory'; import { PythonExecutionFactory } from './pythonExecutionFactory'; import { PythonToolExecutionService } from './pythonToolService'; -import { IBufferDecoder, IProcessService, IPythonExecutionFactory, IPythonToolExecutionService } from './types'; +import { IBufferDecoder, IProcessServiceFactory, IPythonExecutionFactory, IPythonToolExecutionService } from './types'; export function registerTypes(serviceManager: IServiceManager) { serviceManager.addSingleton(IBufferDecoder, BufferDecoder); - serviceManager.addSingleton(IProcessService, ProcessService); + serviceManager.addSingleton(IProcessServiceFactory, ProcessServiceFactory); serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); serviceManager.addSingleton(IPythonToolExecutionService, PythonToolExecutionService); } diff --git a/src/client/common/process/types.ts b/src/client/common/process/types.ts index c4a79d6435a6..dad674ef79a4 100644 --- a/src/client/common/process/types.ts +++ b/src/client/common/process/types.ts @@ -41,6 +41,12 @@ export interface IProcessService { exec(file: string, args: string[], options?: SpawnOptions): Promise>; } +export const IProcessServiceFactory = Symbol('IProcessServiceFactory'); + +export interface IProcessServiceFactory { + create(resource?: Uri): Promise; +} + export const IPythonExecutionFactory = Symbol('IPythonExecutionFactory'); export interface IPythonExecutionFactory { diff --git a/src/client/common/types.ts b/src/client/common/types.ts index fabac04aa35d..f55c58f2abfc 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -103,12 +103,12 @@ export interface IPythonSettings { readonly jediPath: string; readonly jediMemoryLimit: number; readonly devOptions: string[]; - readonly linting?: ILintingSettings; + readonly linting: ILintingSettings; readonly formatting: IFormattingSettings; readonly unitTest: IUnitTestSettings; - readonly autoComplete?: IAutoCompleteSettings; + readonly autoComplete: IAutoCompeteSettings; readonly terminal: ITerminalSettings; - readonly sortImports?: ISortImportSettings; + readonly sortImports: ISortImportSettings; readonly workspaceSymbols: IWorkspaceSymbolSettings; readonly envFile: string; readonly disableInstallationChecks: boolean; diff --git a/src/client/common/variables/environmentVariablesProvider.ts b/src/client/common/variables/environmentVariablesProvider.ts index f1e2ca6541bf..a10bb94c0a65 100644 --- a/src/client/common/variables/environmentVariablesProvider.ts +++ b/src/client/common/variables/environmentVariablesProvider.ts @@ -14,7 +14,7 @@ export class EnvironmentVariablesProvider implements IEnvironmentVariablesProvid private fileWatchers = new Map(); private disposables: Disposable[] = []; private changeEventEmitter: EventEmitter; - constructor( @inject(IEnvironmentVariablesService) private envVarsService: IEnvironmentVariablesService, + constructor(@inject(IEnvironmentVariablesService) private envVarsService: IEnvironmentVariablesService, @inject(IDisposableRegistry) disposableRegistry: Disposable[], @inject(IsWindows) private isWidows: boolean, @inject(ICurrentProcess) private process: ICurrentProcess) { disposableRegistry.push(this); diff --git a/src/client/interpreter/display/shebangCodeLensProvider.ts b/src/client/interpreter/display/shebangCodeLensProvider.ts index 8bb2595e752b..6a12f243c9e4 100644 --- a/src/client/interpreter/display/shebangCodeLensProvider.ts +++ b/src/client/interpreter/display/shebangCodeLensProvider.ts @@ -1,8 +1,7 @@ import { inject, injectable } from 'inversify'; -import * as vscode from 'vscode'; -import { CancellationToken, CodeLens, TextDocument } from 'vscode'; +import { CancellationToken, CodeLens, Command, Event, Position, Range, TextDocument, Uri, workspace } from 'vscode'; import * as settings from '../../common/configSettings'; -import { IProcessService } from '../../common/process/types'; +import { IProcessServiceFactory } from '../../common/process/types'; import { IS_WINDOWS } from '../../common/utils'; import { IServiceContainer } from '../../ioc/types'; import { IShebangCodeLensProvider } from '../contracts'; @@ -10,10 +9,10 @@ import { IShebangCodeLensProvider } from '../contracts'; @injectable() export class ShebangCodeLensProvider implements IShebangCodeLensProvider { // tslint:disable-next-line:no-any - public onDidChangeCodeLenses: vscode.Event = vscode.workspace.onDidChangeConfiguration as any as vscode.Event; - private readonly processService: IProcessService; + public onDidChangeCodeLenses: Event = workspace.onDidChangeConfiguration as any as Event; + private readonly processServiceFactory: IProcessServiceFactory; constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { - this.processService = serviceContainer.get(IProcessService); + this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); } public async detectShebang(document: TextDocument): Promise { const firstLine = document.lineAt(0); @@ -26,14 +25,14 @@ export class ShebangCodeLensProvider implements IShebangCodeLensProvider { } const shebang = firstLine.text.substr(2).trim(); - const pythonPath = await this.getFullyQualifiedPathToInterpreter(shebang); + const pythonPath = await this.getFullyQualifiedPathToInterpreter(shebang, document.uri); return typeof pythonPath === 'string' && pythonPath.length > 0 ? pythonPath : undefined; } public async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise { const codeLenses = await this.createShebangCodeLens(document); return Promise.resolve(codeLenses); } - private async getFullyQualifiedPathToInterpreter(pythonPath: string) { + private async getFullyQualifiedPathToInterpreter(pythonPath: string, resource: Uri) { let cmdFile = pythonPath; let args = ['-c', 'import sys;print(sys.executable)']; if (pythonPath.indexOf('bin/env ') >= 0 && !IS_WINDOWS) { @@ -42,24 +41,25 @@ export class ShebangCodeLensProvider implements IShebangCodeLensProvider { cmdFile = parts.shift()!; args = parts.concat(args); } - return this.processService.exec(cmdFile, args) + const processService = await this.processServiceFactory.create(); + return processService.exec(cmdFile, args) .then(output => output.stdout.trim()) .catch(() => ''); } private async createShebangCodeLens(document: TextDocument) { const shebang = await this.detectShebang(document); const pythonPath = settings.PythonSettings.getInstance(document.uri).pythonPath; - const resolvedPythonPath = await this.getFullyQualifiedPathToInterpreter(pythonPath); + const resolvedPythonPath = await this.getFullyQualifiedPathToInterpreter(pythonPath, document.uri); if (!shebang || shebang === resolvedPythonPath) { return []; } const firstLine = document.lineAt(0); - const startOfShebang = new vscode.Position(0, 0); - const endOfShebang = new vscode.Position(0, firstLine.text.length - 1); - const shebangRange = new vscode.Range(startOfShebang, endOfShebang); + const startOfShebang = new Position(0, 0); + const endOfShebang = new Position(0, firstLine.text.length - 1); + const shebangRange = new Range(startOfShebang, endOfShebang); - const cmd: vscode.Command = { + const cmd: Command = { command: 'python.setShebangInterpreter', title: 'Set as interpreter' }; diff --git a/src/client/interpreter/locators/services/condaService.ts b/src/client/interpreter/locators/services/condaService.ts index f73cb277fd35..2e990c30fcbe 100644 --- a/src/client/interpreter/locators/services/condaService.ts +++ b/src/client/interpreter/locators/services/condaService.ts @@ -1,7 +1,7 @@ import { inject, injectable, named, optional } from 'inversify'; import * as path from 'path'; import { IFileSystem, IPlatformService } from '../../../common/platform/types'; -import { IProcessService } from '../../../common/process/types'; +import { IProcessServiceFactory } from '../../../common/process/types'; import { ILogger, IPersistentStateFactory } from '../../../common/types'; import { VersionUtils } from '../../../common/versionUtils'; import { IServiceContainer } from '../../../ioc/types'; @@ -17,9 +17,9 @@ export const KNOWN_CONDA_LOCATIONS = ['~/anaconda/bin/conda', '~/miniconda/bin/c @injectable() export class CondaService implements ICondaService { - private condaFile: Promise; + private condaFile!: Promise; private isAvailable: boolean | undefined; - private readonly processService: IProcessService; + private readonly processServiceFactory: IProcessServiceFactory; private readonly platform: IPlatformService; private readonly logger: ILogger; private readonly fileSystem: IFileSystem; @@ -30,7 +30,7 @@ export class CondaService implements ICondaService { } constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer, @inject(IInterpreterLocatorService) @named(WINDOWS_REGISTRY_SERVICE) @optional() private registryLookupForConda?: IInterpreterLocatorService) { - this.processService = this.serviceContainer.get(IProcessService); + this.processServiceFactory = this.serviceContainer.get(IProcessServiceFactory); this.platform = this.serviceContainer.get(IPlatformService); this.logger = this.serviceContainer.get(ILogger); this.fileSystem = this.serviceContainer.get(IFileSystem); @@ -54,20 +54,23 @@ export class CondaService implements ICondaService { .catch(() => this.isAvailable = false); } public async getCondaVersion(): Promise { + const processService = await this.processServiceFactory.create(); return this.getCondaFile() - .then(condaFile => this.processService.exec(condaFile, ['--version'], {})) + .then(condaFile => processService.exec(condaFile, ['--version'], {})) .then(result => result.stdout.trim()) .catch(() => undefined); } public async isCondaInCurrentPath() { - return this.processService.exec('conda', ['--version']) + const processService = await this.processServiceFactory.create(); + return processService.exec('conda', ['--version']) .then(output => output.stdout.length > 0) .catch(() => false); } public async getCondaInfo(): Promise { try { const condaFile = await this.getCondaFile(); - const condaInfo = await this.processService.exec(condaFile, ['info', '--json']).then(output => output.stdout); + const processService = await this.processServiceFactory.create(); + const condaInfo = await processService.exec(condaFile, ['info', '--json']).then(output => output.stdout); return JSON.parse(condaInfo) as CondaInfo; } catch (ex) { @@ -90,7 +93,7 @@ export class CondaService implements ICondaService { const condaMetaDirectory = isWindows ? path.join(dir, 'conda-meta') : path.join(dir, '..', 'conda-meta'); return fs.directoryExistsAsync(condaMetaDirectory); } - public async getCondaEnvironment(interpreterPath: string): Promise<{ name: string, path: string } | undefined> { + public async getCondaEnvironment(interpreterPath: string): Promise<{ name: string; path: string } | undefined> { const isCondaEnv = await this.isCondaEnvironment(interpreterPath); if (!isCondaEnv) { return; @@ -118,18 +121,19 @@ export class CondaService implements ICondaService { // If still not available, then the user created the env after starting vs code. // The only solution is to get the user to re-start vscode. } - public async getCondaEnvironments(ignoreCache: boolean): Promise<({ name: string, path: string }[]) | undefined> { + public async getCondaEnvironments(ignoreCache: boolean): Promise<({ name: string; path: string }[]) | undefined> { // Global cache. const persistentFactory = this.serviceContainer.get(IPersistentStateFactory); // tslint:disable-next-line:no-any - const globalPersistence = persistentFactory.createGlobalPersistentState<{ data: { name: string, path: string }[] | undefined }>('CONDA_ENVIRONMENTS', undefined as any); + const globalPersistence = persistentFactory.createGlobalPersistentState<{ data: { name: string; path: string }[] | undefined }>('CONDA_ENVIRONMENTS', undefined as any); if (!ignoreCache && globalPersistence.value) { return globalPersistence.value.data; } try { const condaFile = await this.getCondaFile(); - const envInfo = await this.processService.exec(condaFile, ['env', 'list']).then(output => output.stdout); + const processService = await this.processServiceFactory.create(); + const envInfo = await processService.exec(condaFile, ['env', 'list']).then(output => output.stdout); const environments = this.condaHelper.parseCondaEnvironmentNames(envInfo); await globalPersistence.updateValue({ data: environments }); return environments; diff --git a/src/client/interpreter/locators/services/pipEnvService.ts b/src/client/interpreter/locators/services/pipEnvService.ts index c2db5ff46020..10d4d451a44a 100644 --- a/src/client/interpreter/locators/services/pipEnvService.ts +++ b/src/client/interpreter/locators/services/pipEnvService.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import { Uri } from 'vscode'; import { IApplicationShell, IWorkspaceService } from '../../../common/application/types'; import { IFileSystem } from '../../../common/platform/types'; -import { IProcessService } from '../../../common/process/types'; +import { IProcessServiceFactory } from '../../../common/process/types'; import { ICurrentProcess } from '../../../common/types'; import { IEnvironmentVariablesProvider } from '../../../common/variables/types'; import { getPythonExecutable } from '../../../debugger/Common/Utils'; @@ -20,7 +20,7 @@ const pipEnvFileNameVariable = 'PIPENV_PIPFILE'; @injectable() export class PipEnvService extends CacheableLocatorService { private readonly versionService: IInterpreterVersionService; - private readonly process: IProcessService; + private readonly processServiceFactory: IProcessServiceFactory; private readonly workspace: IWorkspaceService; private readonly fs: IFileSystem; private readonly envVarsProvider: IEnvironmentVariablesProvider; @@ -28,7 +28,7 @@ export class PipEnvService extends CacheableLocatorService { constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { super('PipEnvService', serviceContainer); this.versionService = this.serviceContainer.get(IInterpreterVersionService); - this.process = this.serviceContainer.get(IProcessService); + this.processServiceFactory = this.serviceContainer.get(IProcessServiceFactory); this.workspace = this.serviceContainer.get(IWorkspaceService); this.fs = this.serviceContainer.get(IFileSystem); this.envVarsProvider = this.serviceContainer.get(IEnvironmentVariablesProvider); @@ -94,8 +94,8 @@ export class PipEnvService extends CacheableLocatorService { private async invokePipenv(arg: string, rootPath: string): Promise { try { - const env = await this.envVarsProvider.getEnvironmentVariables(Uri.file(rootPath)); - const result = await this.process.exec(execName, [arg], { cwd: rootPath, env }); + const processService = await this.processServiceFactory.create(Uri.file(rootPath)); + const result = await processService.exec(execName, [arg], { cwd: rootPath }); if (result) { const stdout = result.stdout ? result.stdout.trim() : ''; const stderr = result.stderr ? result.stderr.trim() : ''; diff --git a/src/client/interpreter/virtualEnvs/index.ts b/src/client/interpreter/virtualEnvs/index.ts index 4f535cb99151..bf05b6a3b917 100644 --- a/src/client/interpreter/virtualEnvs/index.ts +++ b/src/client/interpreter/virtualEnvs/index.ts @@ -2,22 +2,23 @@ // Licensed under the MIT License. import { inject, injectable } from 'inversify'; -import { IProcessService } from '../../common/process/types'; +import { IProcessServiceFactory } from '../../common/process/types'; import { IServiceContainer } from '../../ioc/types'; import { IVirtualEnvironmentManager } from './types'; @injectable() export class VirtualEnvironmentManager implements IVirtualEnvironmentManager { - private processService: IProcessService; + private processServiceFactory: IProcessServiceFactory; constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { - this.processService = serviceContainer.get(IProcessService); + this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); } public async getEnvironmentName(pythonPath: string): Promise { // https://stackoverflow.com/questions/1871549/determine-if-python-is-running-inside-virtualenv // hasattr(sys, 'real_prefix') works for virtualenv while // '(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))' works for venv const code = 'import sys\nif hasattr(sys, "real_prefix"):\n print("virtualenv")\nelif hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix:\n print("venv")'; - const output = await this.processService.exec(pythonPath, ['-c', code]); + const processService = await this.processServiceFactory.create(); + const output = await processService.exec(pythonPath, ['-c', code]); if (output.stdout.length > 0) { return output.stdout.trim(); } diff --git a/src/client/providers/importSortProvider.ts b/src/client/providers/importSortProvider.ts index e7e6d3347133..74effd123947 100644 --- a/src/client/providers/importSortProvider.ts +++ b/src/client/providers/importSortProvider.ts @@ -3,14 +3,19 @@ import * as path from 'path'; import { TextDocument, TextEdit } from 'vscode'; import { PythonSettings } from '../common/configSettings'; import { getTempFileWithDocumentContents, getTextEditsFromPatch } from '../common/editor'; -import { ExecutionResult, IProcessService, IPythonExecutionFactory } from '../common/process/types'; +import { ExecutionResult, IProcessServiceFactory, IPythonExecutionFactory } from '../common/process/types'; +import { IServiceContainer } from '../ioc/types'; import { captureTelemetry } from '../telemetry'; import { FORMAT_SORT_IMPORTS } from '../telemetry/constants'; // tslint:disable-next-line:completed-docs export class PythonImportSortProvider { - constructor(private pythonExecutionFactory: IPythonExecutionFactory, - private processService: IProcessService) { } + private readonly processServiceFactory: IProcessServiceFactory; + private readonly pythonExecutionFactory: IPythonExecutionFactory; + constructor(serviceContainer: IServiceContainer) { + this.pythonExecutionFactory = serviceContainer.get(IPythonExecutionFactory); + this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); + } @captureTelemetry(FORMAT_SORT_IMPORTS) public async sortImports(extensionDir: string, document: TextDocument): Promise { if (document.lineCount === 1) { @@ -30,7 +35,8 @@ export class PythonImportSortProvider { if (typeof isort === 'string' && isort.length > 0) { // Lets just treat this as a standard tool. - promise = this.processService.exec(isort, args, { throwOnStdErr: true }); + const processService = await this.processServiceFactory.create(document.uri); + promise = processService.exec(isort, args, { throwOnStdErr: true }); } else { promise = this.pythonExecutionFactory.create(document.uri) .then(executionService => executionService.exec([importScript].concat(args), { throwOnStdErr: true })); diff --git a/src/client/refactor/proxy.ts b/src/client/refactor/proxy.ts index 523b4e726084..725f5eb65005 100644 --- a/src/client/refactor/proxy.ts +++ b/src/client/refactor/proxy.ts @@ -2,8 +2,7 @@ import { ChildProcess } from 'child_process'; import * as path from 'path'; -import * as vscode from 'vscode'; -import { Uri } from 'vscode'; +import { Disposable, Position, Range, TextDocument, TextEditorOptions, Uri, window } from 'vscode'; import '../common/extensions'; import { createDeferred, Deferred } from '../common/helpers'; import { IPythonExecutionFactory } from '../common/process/types'; @@ -11,15 +10,15 @@ import { IPythonSettings } from '../common/types'; import { getWindowsLineEndingCount, IS_WINDOWS } from '../common/utils'; import { IServiceContainer } from '../ioc/types'; -export class RefactorProxy extends vscode.Disposable { +export class RefactorProxy extends Disposable { private _process?: ChildProcess; private _extensionDir: string; private _previousOutData: string = ''; private _previousStdErrData: string = ''; private _startedSuccessfully: boolean = false; private _commandResolve?: (value?: any | PromiseLike) => void; - private _commandReject: (reason?: any) => void; - private initialized: Deferred; + private _commandReject!: (reason?: any) => void; + private initialized!: Deferred; constructor(extensionDir: string, private pythonSettings: IPythonSettings, private workspaceRoot: string, private serviceContainer: IServiceContainer) { super(() => { }); @@ -33,7 +32,7 @@ export class RefactorProxy extends vscode.Disposable { } this._process = undefined; } - private getOffsetAt(document: vscode.TextDocument, position: vscode.Position): number { + private getOffsetAt(document: TextDocument, position: Position): number { if (!IS_WINDOWS) { return document.offsetAt(position); } @@ -47,9 +46,9 @@ export class RefactorProxy extends vscode.Disposable { return offset - winEols; } - public rename(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public rename(document: TextDocument, name: string, filePath: string, range: Range, options?: TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor!.options; + options = window.activeTextEditor!.options; } const command = { lookup: 'rename', @@ -62,9 +61,9 @@ export class RefactorProxy extends vscode.Disposable { return this.sendCommand(JSON.stringify(command)); } - public extractVariable(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public extractVariable(document: TextDocument, name: string, filePath: string, range: Range, options?: TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor!.options; + options = window.activeTextEditor!.options; } const command = { lookup: 'extract_variable', @@ -77,9 +76,9 @@ export class RefactorProxy extends vscode.Disposable { }; return this.sendCommand(JSON.stringify(command)); } - public extractMethod(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range, options?: vscode.TextEditorOptions): Promise { + public extractMethod(document: TextDocument, name: string, filePath: string, range: Range, options?: TextEditorOptions): Promise { if (!options) { - options = vscode.window.activeTextEditor!.options; + options = window.activeTextEditor!.options; } // Ensure last line is an empty line if (!document.lineAt(document.lineCount - 1).isEmptyOrWhitespace && range.start.line === document.lineCount - 1) { @@ -131,7 +130,7 @@ export class RefactorProxy extends vscode.Disposable { // Possible there was an exception in parsing the data returned // So append the data then parse it let dataStr = this._previousStdErrData = this._previousStdErrData + data + ''; - let errorResponse: { message: string, traceback: string, type: string }[]; + let errorResponse: { message: string; traceback: string; type: string }[]; try { errorResponse = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); this._previousStdErrData = ''; diff --git a/src/client/sortImports.ts b/src/client/sortImports.ts index 53fadc8769a9..b32488ec1ed3 100644 --- a/src/client/sortImports.ts +++ b/src/client/sortImports.ts @@ -1,6 +1,5 @@ import * as os from 'os'; import * as vscode from 'vscode'; -import { IProcessService, IPythonExecutionFactory } from './common/process/types'; import { IServiceContainer } from './ioc/types'; import * as sortProvider from './providers/importSortProvider'; @@ -29,15 +28,14 @@ export function activate(context: vscode.ExtensionContext, outChannel: vscode.Ou }); } return emptyLineAdded.then(() => { - const processService = serviceContainer.get(IProcessService); - const pythonExecutionFactory = serviceContainer.get(IPythonExecutionFactory); - return new sortProvider.PythonImportSortProvider(pythonExecutionFactory, processService).sortImports(rootDir, activeEditor.document); + return new sortProvider.PythonImportSortProvider(serviceContainer).sortImports(rootDir, activeEditor.document); }).then(changes => { if (!changes || changes!.length === 0) { return; } - return new Promise((resolve, reject) => activeEditor.edit(builder => changes.forEach(change => builder.replace(change.range, change.newText))).then(resolve, reject)); + // tslint:disable-next-line:no-any + return new Promise((resolve, reject) => activeEditor.edit(builder => changes.forEach(change => builder.replace(change.range, change.newText))).then(resolve, reject)); }).catch(error => { const message = typeof error === 'string' ? error : (error.message ? error.message : error); outChannel.appendLine(error); diff --git a/src/client/workspaceSymbols/generator.ts b/src/client/workspaceSymbols/generator.ts index 9d4a6458374b..86ea33be0eef 100644 --- a/src/client/workspaceSymbols/generator.ts +++ b/src/client/workspaceSymbols/generator.ts @@ -1,15 +1,15 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as vscode from 'vscode'; +import { Disposable, OutputChannel, Uri, window } from 'vscode'; import { PythonSettings } from '../common/configSettings'; -import { IProcessService } from '../common/process/types'; +import { IProcessServiceFactory } from '../common/process/types'; import { IPythonSettings } from '../common/types'; import { captureTelemetry } from '../telemetry'; import { WORKSPACE_SYMBOLS_BUILD } from '../telemetry/constants'; -export class Generator implements vscode.Disposable { +export class Generator implements Disposable { private optionsFile: string; - private disposables: vscode.Disposable[]; + private disposables: Disposable[]; private pythonSettings: IPythonSettings; public get tagFilePath(): string { return this.pythonSettings.workspaceSymbols.tagFilePath; @@ -17,8 +17,8 @@ export class Generator implements vscode.Disposable { public get enabled(): boolean { return this.pythonSettings.workspaceSymbols.enabled; } - constructor(public readonly workspaceFolder: vscode.Uri, private output: vscode.OutputChannel, - private processService: IProcessService) { + constructor(public readonly workspaceFolder: Uri, private readonly output: OutputChannel, + private readonly processServiceFactory: IProcessServiceFactory) { this.disposables = []; this.optionsFile = path.join(__dirname, '..', '..', '..', 'resources', 'ctagOptions'); this.pythonSettings = PythonSettings.getInstance(workspaceFolder); @@ -60,8 +60,9 @@ export class Generator implements vscode.Disposable { args.push('-o', outputFile, '.'); this.output.appendLine(`${'-'.repeat(10)}Generating Tags${'-'.repeat(10)}`); this.output.appendLine(`${cmd} ${args.join(' ')}`); - const promise = new Promise((resolve, reject) => { - const result = this.processService.execObservable(cmd, args, { cwd: source.directory }); + const promise = new Promise(async (resolve, reject) => { + const processService = await this.processServiceFactory.create(); + const result = processService.execObservable(cmd, args, { cwd: source.directory }); let errorMsg = ''; result.out.subscribe(output => { if (output.source === 'stderr') { @@ -79,7 +80,7 @@ export class Generator implements vscode.Disposable { }); }); - vscode.window.setStatusBarMessage('Generating Tags', promise); + window.setStatusBarMessage('Generating Tags', promise); return promise; } diff --git a/src/client/workspaceSymbols/main.ts b/src/client/workspaceSymbols/main.ts index 9077e33a09d1..59919555ba11 100644 --- a/src/client/workspaceSymbols/main.ts +++ b/src/client/workspaceSymbols/main.ts @@ -1,8 +1,7 @@ -import * as vscode from 'vscode'; -import { OutputChannel, workspace } from 'vscode'; +import { CancellationToken, commands, Disposable, languages, OutputChannel, workspace } from 'vscode'; import { Commands, STANDARD_OUTPUT_CHANNEL } from '../common/constants'; import { isNotInstalledError } from '../common/helpers'; -import { IProcessService } from '../common/process/types'; +import { IProcessServiceFactory } from '../common/process/types'; import { IInstaller, InstallerResponse, IOutputChannel, Product } from '../common/types'; import { fsExistsAsync } from '../common/utils'; import { IServiceContainer } from '../ioc/types'; @@ -11,20 +10,18 @@ import { WorkspaceSymbolProvider } from './provider'; const MAX_NUMBER_OF_ATTEMPTS_TO_INSTALL_AND_BUILD = 2; -export class WorkspaceSymbols implements vscode.Disposable { - private disposables: vscode.Disposable[]; +export class WorkspaceSymbols implements Disposable { + private disposables: Disposable[]; private generators: Generator[] = []; private readonly outputChannel: OutputChannel; - // tslint:disable-next-line:no-any - private timeout: any; constructor(private serviceContainer: IServiceContainer) { this.outputChannel = this.serviceContainer.get(IOutputChannel, STANDARD_OUTPUT_CHANNEL); this.disposables = []; this.disposables.push(this.outputChannel); this.registerCommands(); this.initializeGenerators(); - vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.generators, this.outputChannel)); - this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(() => this.initializeGenerators())); + languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.generators, this.outputChannel)); + this.disposables.push(workspace.onDidChangeWorkspaceFolders(() => this.initializeGenerators())); } public dispose() { this.disposables.forEach(d => d.dispose()); @@ -35,21 +32,21 @@ export class WorkspaceSymbols implements vscode.Disposable { generator.dispose(); } - if (Array.isArray(vscode.workspace.workspaceFolders)) { - vscode.workspace.workspaceFolders.forEach(wkSpc => { - const processService = this.serviceContainer.get(IProcessService); - this.generators.push(new Generator(wkSpc.uri, this.outputChannel, processService)); + if (Array.isArray(workspace.workspaceFolders)) { + workspace.workspaceFolders.forEach(wkSpc => { + const processServiceFactory = this.serviceContainer.get(IProcessServiceFactory); + this.generators.push(new Generator(wkSpc.uri, this.outputChannel, processServiceFactory)); }); } } private registerCommands() { - this.disposables.push(vscode.commands.registerCommand(Commands.Build_Workspace_Symbols, async (rebuild: boolean = true, token?: vscode.CancellationToken) => { + this.disposables.push(commands.registerCommand(Commands.Build_Workspace_Symbols, async (rebuild: boolean = true, token?: CancellationToken) => { const promises = this.buildWorkspaceSymbols(rebuild, token); return Promise.all(promises); })); } // tslint:disable-next-line:no-any - private buildWorkspaceSymbols(rebuild: boolean = true, token?: vscode.CancellationToken): Promise[] { + private buildWorkspaceSymbols(rebuild: boolean = true, token?: CancellationToken): Promise[] { if (token && token.isCancellationRequested) { return []; } diff --git a/src/test/format/extension.sort.test.ts b/src/test/format/extension.sort.test.ts index af88891e6e91..884113bdb77b 100644 --- a/src/test/format/extension.sort.test.ts +++ b/src/test/format/extension.sort.test.ts @@ -3,7 +3,6 @@ import * as fs from 'fs'; import { EOL } from 'os'; import * as path from 'path'; import { commands, ConfigurationTarget, Position, Range, Uri, window, workspace } from 'vscode'; -import { IProcessService, IPythonExecutionFactory } from '../../client/common/process/types'; import { PythonImportSortProvider } from '../../client/providers/importSortProvider'; import { updateSetting } from '../common'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; @@ -39,9 +38,7 @@ suite('Sorting', () => { fs.writeFileSync(fileToFormatWithConfig1, fs.readFileSync(originalFileToFormatWithConfig1)); await updateSetting('sortImports.args', [], Uri.file(sortingPath), configTarget); await closeActiveWindows(); - const pythonExecutionFactory = ioc.serviceContainer.get(IPythonExecutionFactory); - const processService = ioc.serviceContainer.get(IProcessService); - sorter = new PythonImportSortProvider(pythonExecutionFactory, processService); + sorter = new PythonImportSortProvider(ioc.serviceContainer); }); teardown(async () => { ioc.dispose(); diff --git a/src/test/interpreters/interpreterVersion.test.ts b/src/test/interpreters/interpreterVersion.test.ts index 9e78d8ee5e66..6b9c9da81ad7 100644 --- a/src/test/interpreters/interpreterVersion.test.ts +++ b/src/test/interpreters/interpreterVersion.test.ts @@ -4,7 +4,7 @@ import { assert, expect, use } from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import '../../client/common/extensions'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { IInterpreterVersionService } from '../../client/interpreter/contracts'; import { PIP_VERSION_REGEX } from '../../client/interpreter/interpreterVersion'; import { PYTHON_PATH } from '../common'; @@ -31,7 +31,7 @@ suite('Interpreters display version', () => { } test('Must return the Python Version', async () => { - const pythonProcess = ioc.serviceContainer.get(IProcessService); + const pythonProcess = await ioc.serviceContainer.get(IProcessServiceFactory).create(); const output = await pythonProcess.exec(PYTHON_PATH, ['--version'], { cwd: __dirname, mergeStdOutErr: true }); const version = output.stdout.splitLines()[0]; const interpreterVersion = ioc.serviceContainer.get(IInterpreterVersionService); @@ -44,7 +44,7 @@ suite('Interpreters display version', () => { assert.equal(pyVersion, 'DEFAULT_TEST_VALUE', 'Incorrect version'); }); test('Must return the pip Version', async () => { - const pythonProcess = ioc.serviceContainer.get(IProcessService); + const pythonProcess = await ioc.serviceContainer.get(IProcessServiceFactory).create(); const result = await pythonProcess.exec(PYTHON_PATH, ['-m', 'pip', '--version'], { cwd: __dirname, mergeStdOutErr: true }); const output = result.stdout.splitLines()[0]; // Take the second part, see below example. diff --git a/src/test/workspaceSymbols/multiroot.test.ts b/src/test/workspaceSymbols/multiroot.test.ts index ccbf64b3adcb..e6e0f45febe3 100644 --- a/src/test/workspaceSymbols/multiroot.test.ts +++ b/src/test/workspaceSymbols/multiroot.test.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import * as path from 'path'; import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { Generator } from '../../client/workspaceSymbols/generator'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; @@ -13,7 +13,7 @@ const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRo suite('Multiroot Workspace Symbols', () => { let ioc: UnitTestIocContainer; - let processService: IProcessService; + let processServiceFactory: IProcessServiceFactory; suiteSetup(function () { if (!IS_MULTI_ROOT_TEST) { // tslint:disable-next-line:no-invalid-this @@ -37,7 +37,7 @@ suite('Multiroot Workspace Symbols', () => { ioc.registerCommonTypes(); ioc.registerVariableTypes(); ioc.registerProcessTypes(); - processService = ioc.serviceContainer.get(IProcessService); + processServiceFactory = ioc.serviceContainer.get(IProcessServiceFactory); } test('symbols should be returned when enabeld and vice versa', async () => { @@ -46,7 +46,7 @@ suite('Multiroot Workspace Symbols', () => { await updateSetting('workspaceSymbols.enabled', false, childWorkspaceUri, ConfigurationTarget.WorkspaceFolder); - let generator = new Generator(childWorkspaceUri, outputChannel, processService); + let generator = new Generator(childWorkspaceUri, outputChannel, processServiceFactory); let provider = new WorkspaceSymbolProvider([generator], outputChannel); let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); @@ -54,7 +54,7 @@ suite('Multiroot Workspace Symbols', () => { await updateSetting('workspaceSymbols.enabled', true, childWorkspaceUri, ConfigurationTarget.WorkspaceFolder); - generator = new Generator(childWorkspaceUri, outputChannel, processService); + generator = new Generator(childWorkspaceUri, outputChannel, processServiceFactory); provider = new WorkspaceSymbolProvider([generator], outputChannel); symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); assert.notEqual(symbols.length, 0, 'Symbols should be returned when workspace symbols are turned on'); @@ -68,8 +68,8 @@ suite('Multiroot Workspace Symbols', () => { await updateSetting('workspaceSymbols.enabled', true, workspace2Uri, ConfigurationTarget.WorkspaceFolder); const generators = [ - new Generator(childWorkspaceUri, outputChannel, processService), - new Generator(workspace2Uri, outputChannel, processService)]; + new Generator(childWorkspaceUri, outputChannel, processServiceFactory), + new Generator(workspace2Uri, outputChannel, processServiceFactory)]; const provider = new WorkspaceSymbolProvider(generators, outputChannel); const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index 5d264e7fd35a..46fdd9ba6102 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import * as path from 'path'; import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; import { PythonSettings } from '../../client/common/configSettings'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { Generator } from '../../client/workspaceSymbols/generator'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; @@ -15,7 +15,7 @@ const configUpdateTarget = IS_MULTI_ROOT_TEST ? ConfigurationTarget.WorkspaceFol suite('Workspace Symbols', () => { let ioc: UnitTestIocContainer; - let processService: IProcessService; + let processServiceFactory: IProcessServiceFactory; suiteSetup(initialize); suiteTeardown(closeActiveWindows); setup(async () => { @@ -32,7 +32,7 @@ suite('Workspace Symbols', () => { ioc.registerCommonTypes(); ioc.registerVariableTypes(); ioc.registerProcessTypes(); - processService = ioc.serviceContainer.get(IProcessService); + processServiceFactory = ioc.serviceContainer.get(IProcessServiceFactory); } test('symbols should be returned when enabeld and vice versa', async () => { @@ -42,9 +42,9 @@ suite('Workspace Symbols', () => { // The workspace will be in the output test folder // So lets modify the settings so it sees the source test folder let settings = PythonSettings.getInstance(workspaceUri); - settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); + settings.workspaceSymbols!.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); - let generator = new Generator(workspaceUri, outputChannel, processService); + let generator = new Generator(workspaceUri, outputChannel, processServiceFactory); let provider = new WorkspaceSymbolProvider([generator], outputChannel); let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); @@ -55,9 +55,9 @@ suite('Workspace Symbols', () => { // The workspace will be in the output test folder // So lets modify the settings so it sees the source test folder settings = PythonSettings.getInstance(workspaceUri); - settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); + settings.workspaceSymbols!.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); - generator = new Generator(workspaceUri, outputChannel, processService); + generator = new Generator(workspaceUri, outputChannel, processServiceFactory); provider = new WorkspaceSymbolProvider([generator], outputChannel); symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); assert.notEqual(symbols.length, 0, 'Symbols should be returned when workspace symbols are turned on'); @@ -70,9 +70,9 @@ suite('Workspace Symbols', () => { // The workspace will be in the output test folder // So lets modify the settings so it sees the source test folder const settings = PythonSettings.getInstance(workspaceUri); - settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); + settings.workspaceSymbols!.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags'); - const generators = [new Generator(workspaceUri, outputChannel, processService)]; + const generators = [new Generator(workspaceUri, outputChannel, processServiceFactory)]; const provider = new WorkspaceSymbolProvider(generators, outputChannel); const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); From d1cefe43f78f5c5af3f7ccfd7e4297bf9c2a9f8e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 17:49:53 -0700 Subject: [PATCH 02/15] :hammer: pass resource --- src/client/interpreter/display/shebangCodeLensProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/interpreter/display/shebangCodeLensProvider.ts b/src/client/interpreter/display/shebangCodeLensProvider.ts index 6a12f243c9e4..457e914200a8 100644 --- a/src/client/interpreter/display/shebangCodeLensProvider.ts +++ b/src/client/interpreter/display/shebangCodeLensProvider.ts @@ -41,7 +41,7 @@ export class ShebangCodeLensProvider implements IShebangCodeLensProvider { cmdFile = parts.shift()!; args = parts.concat(args); } - const processService = await this.processServiceFactory.create(); + const processService = await this.processServiceFactory.create(resource); return processService.exec(cmdFile, args) .then(output => output.stdout.trim()) .catch(() => ''); From 068e91462f829ffcf7aed23a6aedf6dc1d357590 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 17:52:06 -0700 Subject: [PATCH 03/15] :memo: news entry --- news/3 Code Health/1339.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/3 Code Health/1339.md diff --git a/news/3 Code Health/1339.md b/news/3 Code Health/1339.md new file mode 100644 index 000000000000..e06d19240f67 --- /dev/null +++ b/news/3 Code Health/1339.md @@ -0,0 +1 @@ +Ensure custom environment variables are always used when spawning any process from within the extension. From 2f37a9113f10a6d80590101cb2d4b22996a3c8c4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 18:54:34 -0700 Subject: [PATCH 04/15] remove process service --- src/client/common/process/proc.ts | 5 +---- src/client/common/process/types.ts | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/client/common/process/proc.ts b/src/client/common/process/proc.ts index ef7defa1c193..63a868135571 100644 --- a/src/client/common/process/proc.ts +++ b/src/client/common/process/proc.ts @@ -4,7 +4,6 @@ // tslint:disable:no-any import { spawn } from 'child_process'; -import { inject, injectable, optional } from 'inversify'; import { Observable } from 'rxjs/Observable'; import { Disposable } from 'vscode'; import { createDeferred } from '../helpers'; @@ -12,10 +11,8 @@ import { EnvironmentVariables } from '../variables/types'; import { DEFAULT_ENCODING } from './constants'; import { ExecutionResult, IBufferDecoder, IProcessService, ObservableExecutionResult, Output, SpawnOptions, StdErrError } from './types'; -@injectable() export class ProcessService implements IProcessService { - constructor(@inject(IBufferDecoder) private readonly decoder: IBufferDecoder, - @optional() private readonly env?: EnvironmentVariables) { } + constructor(private readonly decoder: IBufferDecoder, private readonly env?: EnvironmentVariables) { } public execObservable(file: string, args: string[], options: SpawnOptions = {}): ObservableExecutionResult { const encoding = options.encoding = typeof options.encoding === 'string' && options.encoding.length > 0 ? options.encoding : DEFAULT_ENCODING; delete options.encoding; diff --git a/src/client/common/process/types.ts b/src/client/common/process/types.ts index dad674ef79a4..22fb6965be55 100644 --- a/src/client/common/process/types.ts +++ b/src/client/common/process/types.ts @@ -34,8 +34,6 @@ export type ExecutionResult = { stderr?: T; }; -export const IProcessService = Symbol('IProcessService'); - export interface IProcessService { execObservable(file: string, args: string[], options?: SpawnOptions): ObservableExecutionResult; exec(file: string, args: string[], options?: SpawnOptions): Promise>; From 07c18c90fc25c080c5390e878701f182e5c3aa8e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 19:31:16 -0700 Subject: [PATCH 05/15] :hammer: use factory to create the service --- src/client/interpreter/interpreterVersion.ts | 10 +++--- .../locators/services/currentPathService.ts | 14 ++++---- src/test/common/installer.test.ts | 7 ++-- src/test/common/moduleInstaller.test.ts | 6 ++-- src/test/common/process/execFactory.test.ts | 11 ++++--- .../common/terminals/activation.conda.test.ts | 7 ++-- src/test/format/extension.format.test.ts | 16 ++++++---- src/test/interpreters/condaService.test.ts | 8 +++-- src/test/interpreters/pipEnvService.test.ts | 7 ++-- .../interpreters/virtualEnvManager.test.ts | 16 ++++++---- src/test/mocks/proc.ts | 4 +-- src/test/serviceRegistry.ts | 13 +++++--- src/test/unittests/nosetest.disovery.test.ts | 16 +++++----- src/test/unittests/pytest.discovery.test.ts | 16 +++++----- src/test/unittests/pytest.run.test.ts | 32 +++++++++---------- src/test/unittests/serviceRegistry.ts | 1 + src/test/unittests/unittest.discovery.test.ts | 14 ++++---- src/test/unittests/unittest.run.test.ts | 22 ++++++------- 18 files changed, 122 insertions(+), 98 deletions(-) diff --git a/src/client/interpreter/interpreterVersion.ts b/src/client/interpreter/interpreterVersion.ts index 5235d60f52ce..4b00ddadead2 100644 --- a/src/client/interpreter/interpreterVersion.ts +++ b/src/client/interpreter/interpreterVersion.ts @@ -1,21 +1,23 @@ import { inject, injectable } from 'inversify'; import '../common/extensions'; -import { IProcessService } from '../common/process/types'; +import { IProcessServiceFactory } from '../common/process/types'; import { IInterpreterVersionService } from './contracts'; export const PIP_VERSION_REGEX = '\\d+\\.\\d+(\\.\\d+)'; @injectable() export class InterpreterVersionService implements IInterpreterVersionService { - constructor(@inject(IProcessService) private processService: IProcessService) { } + constructor(@inject(IProcessServiceFactory) private readonly processServiceFactory: IProcessServiceFactory) { } public async getVersion(pythonPath: string, defaultValue: string): Promise { - return this.processService.exec(pythonPath, ['--version'], { mergeStdOutErr: true }) + const processService = await this.processServiceFactory.create(); + return processService.exec(pythonPath, ['--version'], { mergeStdOutErr: true }) .then(output => output.stdout.splitLines()[0]) .then(version => version.length === 0 ? defaultValue : version) .catch(() => defaultValue); } public async getPipVersion(pythonPath: string): Promise { - const output = await this.processService.exec(pythonPath, ['-m', 'pip', '--version'], { mergeStdOutErr: true }); + const processService = await this.processServiceFactory.create(); + const output = await processService.exec(pythonPath, ['-m', 'pip', '--version'], { mergeStdOutErr: true }); if (output.stdout.length > 0) { // Here's a sample output: // pip 9.0.1 from /Users/donjayamanne/anaconda3/lib/python3.6/site-packages (python 3.6). diff --git a/src/client/interpreter/locators/services/currentPathService.ts b/src/client/interpreter/locators/services/currentPathService.ts index 48444e0dfba9..a1630c3d47a0 100644 --- a/src/client/interpreter/locators/services/currentPathService.ts +++ b/src/client/interpreter/locators/services/currentPathService.ts @@ -5,6 +5,7 @@ import { Uri } from 'vscode'; import { IFileSystem } from '../../../common/platform/types'; import { IProcessService } from '../../../common/process/types'; import { IConfigurationService } from '../../../common/types'; +import { IProcessServiceFactory } from '../../../common/process/types'; import { IServiceContainer } from '../../../ioc/types'; import { IInterpreterVersionService, InterpreterType, PythonInterpreter } from '../../contracts'; import { IVirtualEnvironmentManager } from '../../virtualEnvs/types'; @@ -15,7 +16,7 @@ export class CurrentPathService extends CacheableLocatorService { private readonly fs: IFileSystem; public constructor(@inject(IVirtualEnvironmentManager) private virtualEnvMgr: IVirtualEnvironmentManager, @inject(IInterpreterVersionService) private versionProvider: IInterpreterVersionService, - @inject(IProcessService) private processService: IProcessService, + @inject(IProcessServiceFactory) private readonly processServiceFactory: IProcessServiceFactory, @inject(IServiceContainer) serviceContainer: IServiceContainer) { super('CurrentPathService', serviceContainer); this.fs = serviceContainer.get(IFileSystem); @@ -54,12 +55,11 @@ export class CurrentPathService extends CacheableLocatorService { } private async getInterpreter(pythonPath: string, defaultValue: string) { try { - const output = await this.processService.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], {}); - const executablePath = output.stdout.trim(); - if (executablePath.length > 0 && await this.fs.fileExistsAsync(executablePath)) { - return executablePath; - } - return defaultValue; + const processService = await this.processServiceFactory.create(); + return processService.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], {}) + .then(output => output.stdout.trim()) + .then(value => value.length === 0 ? defaultValue : value) + .catch(() => defaultValue); // Ignore exceptions in getting the executable. } catch { return defaultValue; // Ignore exceptions in getting the executable. } diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts index c491d93cd680..09b0385eaca2 100644 --- a/src/test/common/installer.test.ts +++ b/src/test/common/installer.test.ts @@ -12,10 +12,9 @@ import { Logger } from '../../client/common/logger'; import { PersistentStateFactory } from '../../client/common/persistentState'; import { PathUtils } from '../../client/common/platform/pathUtils'; import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { IConfigurationService, ICurrentProcess, IInstaller, ILogger, IPathUtils, IPersistentStateFactory, IsWindows, ModuleNamePurpose, Product } from '../../client/common/types'; -import { rootWorkspaceUri } from '../common'; -import { updateSetting } from '../common'; +import { rootWorkspaceUri, updateSetting } from '../common'; import { MockModuleInstaller } from '../mocks/moduleInstaller'; import { MockProcessService } from '../mocks/proc'; import { UnitTestIocContainer } from '../unittests/serviceRegistry'; @@ -68,7 +67,7 @@ suite('Installer', () => { async function testCheckingIfProductIsInstalled(product: Product) { const installer = ioc.serviceContainer.get(IInstaller); - const processService = ioc.serviceContainer.get(IProcessService); + const processService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; const checkInstalledDef = createDeferred(); processService.onExec((file, args, options, callback) => { const moduleName = installer.translateProductToModuleName(product, ModuleNamePurpose.run); diff --git a/src/test/common/moduleInstaller.test.ts b/src/test/common/moduleInstaller.test.ts index 31ea81de250a..84835f18f7b2 100644 --- a/src/test/common/moduleInstaller.test.ts +++ b/src/test/common/moduleInstaller.test.ts @@ -16,7 +16,7 @@ import { PathUtils } from '../../client/common/platform/pathUtils'; import { PlatformService } from '../../client/common/platform/platformService'; import { Architecture, IFileSystem, IPlatformService } from '../../client/common/platform/types'; import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { IProcessService, IPythonExecutionFactory } from '../../client/common/process/types'; +import { IProcessServiceFactory, IPythonExecutionFactory } from '../../client/common/process/types'; import { ITerminalService, ITerminalServiceFactory } from '../../client/common/terminal/types'; import { IConfigurationService, ICurrentProcess, IInstaller, ILogger, IPathUtils, IPersistentStateFactory, IPythonSettings, IsWindows } from '../../client/common/types'; import { ICondaService, IInterpreterLocatorService, IInterpreterService, INTERPRETER_LOCATOR_SERVICE, InterpreterType, PIPENV_SERVICE, PythonInterpreter } from '../../client/interpreter/contracts'; @@ -104,7 +104,7 @@ suite('Module Installer', () => { ioc.serviceManager.addSingletonInstance(IInterpreterLocatorService, mockInterpreterLocator.object, INTERPRETER_LOCATOR_SERVICE); ioc.serviceManager.addSingletonInstance(IInterpreterLocatorService, TypeMoq.Mock.ofType().object, PIPENV_SERVICE); - const processService = ioc.serviceContainer.get(IProcessService); + const processService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; processService.onExec((file, args, options, callback) => { if (args.length > 1 && args[0] === '-c' && args[1] === 'import pip') { callback({ stdout: '' }); @@ -137,7 +137,7 @@ suite('Module Installer', () => { ioc.serviceManager.addSingletonInstance(IInterpreterLocatorService, mockInterpreterLocator.object, INTERPRETER_LOCATOR_SERVICE); ioc.serviceManager.addSingletonInstance(IInterpreterLocatorService, TypeMoq.Mock.ofType().object, PIPENV_SERVICE); - const processService = ioc.serviceContainer.get(IProcessService); + const processService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; processService.onExec((file, args, options, callback) => { if (args.length > 1 && args[0] === '-c' && args[1] === 'import pip') { callback({ stdout: '' }); diff --git a/src/test/common/process/execFactory.test.ts b/src/test/common/process/execFactory.test.ts index 9a8aa3ce4322..b8d878b3fe3a 100644 --- a/src/test/common/process/execFactory.test.ts +++ b/src/test/common/process/execFactory.test.ts @@ -5,7 +5,7 @@ import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; import { Uri } from 'vscode'; import { IFileSystem } from '../../../client/common/platform/types'; -import { IProcessService } from '../../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../../client/common/process/types'; import { IConfigurationService, IPythonSettings } from '../../../client/common/types'; import { IEnvironmentVariablesProvider } from '../../../client/common/variables/types'; import { InterpreterVersionService } from '../../../client/interpreter/interpreterVersion'; @@ -16,17 +16,20 @@ suite('PythonExecutableService', () => { let serviceContainer: TypeMoq.IMock; let configService: TypeMoq.IMock; let procService: TypeMoq.IMock; + let procServiceFactory: TypeMoq.IMock; setup(() => { serviceContainer = TypeMoq.Mock.ofType(); const envVarsProvider = TypeMoq.Mock.ofType(); + procServiceFactory = TypeMoq.Mock.ofType(); procService = TypeMoq.Mock.ofType(); configService = TypeMoq.Mock.ofType(); const fileSystem = TypeMoq.Mock.ofType(); fileSystem.setup(f => f.fileExistsAsync(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))).returns(() => envVarsProvider.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService))).returns(() => procService.object); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory))).returns(() => procServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))).returns(() => configService.object); + procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(procService.object)); envVarsProvider.setup(v => v.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); }); @@ -38,7 +41,7 @@ suite('PythonExecutableService', () => { configService.setup(c => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => pythonSettings.object); procService.setup(p => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ stdout: pythonVersion })); - const versionService = new InterpreterVersionService(procService.object); + const versionService = new InterpreterVersionService(procServiceFactory.object); const version = await versionService.getVersion(pythonPath, ''); expect(version).to.be.equal(pythonVersion); @@ -52,7 +55,7 @@ suite('PythonExecutableService', () => { configService.setup(c => c.getSettings(TypeMoq.It.isValue(resource))).returns(() => pythonSettings.object); procService.setup(p => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ stdout: pythonVersion })); - const versionService = new InterpreterVersionService(procService.object); + const versionService = new InterpreterVersionService(procServiceFactory.object); const version = await versionService.getVersion(pythonPath, ''); expect(version).to.be.equal(pythonVersion); diff --git a/src/test/common/terminals/activation.conda.test.ts b/src/test/common/terminals/activation.conda.test.ts index d1d868dcc44f..3af7c42e3e22 100644 --- a/src/test/common/terminals/activation.conda.test.ts +++ b/src/test/common/terminals/activation.conda.test.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vscode'; import { EnumEx } from '../../../client/common/enumUtils'; import '../../../client/common/extensions'; import { IFileSystem, IPlatformService } from '../../../client/common/platform/types'; -import { IProcessService } from '../../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../../client/common/process/types'; import { CondaActivationCommandProvider } from '../../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; import { TerminalHelper } from '../../../client/common/terminal/helper'; import { ITerminalActivationCommandProvider, TerminalShellType } from '../../../client/common/terminal/types'; @@ -26,6 +26,7 @@ suite('Terminal Environment Activation conda', () => { let pythonSettings: TypeMoq.IMock; let serviceContainer: TypeMoq.IMock; let processService: TypeMoq.IMock; + let procServiceFactory: TypeMoq.IMock; let condaService: TypeMoq.IMock; setup(() => { @@ -37,10 +38,12 @@ suite('Terminal Environment Activation conda', () => { platformService = TypeMoq.Mock.ofType(); processService = TypeMoq.Mock.ofType(); condaService = TypeMoq.Mock.ofType(); + procServiceFactory = TypeMoq.Mock.ofType(); + procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())).returns(() => platformService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService), TypeMoq.It.isAny())).returns(() => processService.object); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())).returns(() => procServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())).returns(() => condaService.object); const configService = TypeMoq.Mock.ofType(); diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index f50e678b3886..1bd8cdf37ca6 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -2,6 +2,8 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import * as vscode from 'vscode'; import { IProcessService, IPythonExecutionFactory } from '../../client/common/process/types'; +import { CancellationTokenSource, Position, Uri, window, workspace } from 'vscode'; +import { IProcessServiceFactory, IPythonExecutionFactory } from '../../client/common/process/types'; import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter'; import { BlackFormatter } from '../../client/formatters/blackFormatter'; import { YapfFormatter } from '../../client/formatters/yapfFormatter'; @@ -10,7 +12,7 @@ import { MockProcessService } from '../mocks/proc'; import { compareFiles } from '../textUtils'; import { UnitTestIocContainer } from '../unittests/serviceRegistry'; -const ch = vscode.window.createOutputChannel('Tests'); +const ch = window.createOutputChannel('Tests'); const formatFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'formatting'); const workspaceRootPath = path.join(__dirname, '..', '..', '..', 'src', 'test'); const originalUnformattedFile = path.join(formatFilesPath, 'fileToFormat.py'); @@ -85,8 +87,8 @@ suite('Formatting', () => { ioc.registerMockProcessTypes(); } - function injectFormatOutput(outputFileName: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectFormatOutput(outputFileName: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.indexOf('--diff') >= 0) { callback({ @@ -102,7 +104,7 @@ suite('Formatting', () => { const textEditor = await vscode.window.showTextDocument(textDocument); const options = { insertSpaces: textEditor.options.insertSpaces! as boolean, tabSize: textEditor.options.tabSize! as number }; - injectFormatOutput(outputFileName); + await injectFormatOutput(outputFileName); const edits = await formatter.formatDocument(textDocument, options, new vscode.CancellationTokenSource().token); await textEditor.edit(editBuilder => { @@ -137,11 +139,11 @@ suite('Formatting', () => { fs.copySync(path.join(sourceDir, originalName), fileToFormat, { overwrite: true }); fs.copySync(path.join(sourceDir, resultsName), formattedFile, { overwrite: true }); - const textDocument = await vscode.workspace.openTextDocument(fileToFormat); - const textEditor = await vscode.window.showTextDocument(textDocument); + const textDocument = await workspace.openTextDocument(fileToFormat); + const textEditor = await window.showTextDocument(textDocument); await textEditor.edit(builder => { // Make file dirty. Trailing blanks will be removed. - builder.insert(new vscode.Position(0, 0), '\n \n'); + builder.insert(new Position(0, 0), '\n \n'); }); const dir = path.dirname(fileToFormat); diff --git a/src/test/interpreters/condaService.test.ts b/src/test/interpreters/condaService.test.ts index f6a16891e71a..829a42f388ea 100644 --- a/src/test/interpreters/condaService.test.ts +++ b/src/test/interpreters/condaService.test.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as TypeMoq from 'typemoq'; import { FileSystem } from '../../client/common/platform/fileSystem'; import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../client/common/process/types'; import { ILogger, IPersistentStateFactory } from '../../client/common/types'; import { IInterpreterLocatorService, InterpreterType, PythonInterpreter } from '../../client/interpreter/contracts'; import { CondaService, KNOWN_CONDA_LOCATIONS } from '../../client/interpreter/locators/services/condaService'; @@ -25,14 +25,18 @@ suite('Interpreters Conda Service', () => { let fileSystem: TypeMoq.IMock; let registryInterpreterLocatorService: TypeMoq.IMock; let serviceContainer: TypeMoq.IMock; + let procServiceFactory: TypeMoq.IMock; setup(async () => { const logger = TypeMoq.Mock.ofType(); processService = TypeMoq.Mock.ofType(); platformService = TypeMoq.Mock.ofType(); registryInterpreterLocatorService = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); + procServiceFactory = TypeMoq.Mock.ofType(); + procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); + serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService), TypeMoq.It.isAny())).returns(() => processService.object); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())).returns(() => procServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())).returns(() => platformService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ILogger), TypeMoq.It.isAny())).returns(() => logger.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())).returns(() => fileSystem.object); diff --git a/src/test/interpreters/pipEnvService.test.ts b/src/test/interpreters/pipEnvService.test.ts index 65545cfc7b53..241226339e01 100644 --- a/src/test/interpreters/pipEnvService.test.ts +++ b/src/test/interpreters/pipEnvService.test.ts @@ -12,7 +12,7 @@ import { Uri, WorkspaceFolder } from 'vscode'; import { IApplicationShell, IWorkspaceService } from '../../client/common/application/types'; import { EnumEx } from '../../client/common/enumUtils'; import { IFileSystem } from '../../client/common/platform/types'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../client/common/process/types'; import { ICurrentProcess, IPersistentState, IPersistentStateFactory } from '../../client/common/types'; import { IEnvironmentVariablesProvider } from '../../client/common/variables/types'; import { IInterpreterLocatorService, IInterpreterVersionService } from '../../client/interpreter/contracts'; @@ -38,6 +38,7 @@ suite('Interpreters - PipEnv', () => { let appShell: TypeMoq.IMock; let persistentStateFactory: TypeMoq.IMock; let envVarsProvider: TypeMoq.IMock; + let procServiceFactory: TypeMoq.IMock; setup(() => { serviceContainer = TypeMoq.Mock.ofType(); const workspaceService = TypeMoq.Mock.ofType(); @@ -48,6 +49,8 @@ suite('Interpreters - PipEnv', () => { currentProcess = TypeMoq.Mock.ofType(); persistentStateFactory = TypeMoq.Mock.ofType(); envVarsProvider = TypeMoq.Mock.ofType(); + procServiceFactory = TypeMoq.Mock.ofType(); + procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); // tslint:disable-next-line:no-any const persistentState = TypeMoq.Mock.ofType>(); @@ -61,9 +64,9 @@ suite('Interpreters - PipEnv', () => { workspaceService.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); workspaceService.setup(w => w.rootPath).returns(() => rootWorkspace); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())).returns(() => procServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))).returns(() => workspaceService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IInterpreterVersionService))).returns(() => interpreterVersionService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService))).returns(() => processService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => currentProcess.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); diff --git a/src/test/interpreters/virtualEnvManager.test.ts b/src/test/interpreters/virtualEnvManager.test.ts index 3d00204b9269..855bae72c057 100644 --- a/src/test/interpreters/virtualEnvManager.test.ts +++ b/src/test/interpreters/virtualEnvManager.test.ts @@ -6,7 +6,7 @@ import { Container } from 'inversify'; import * as TypeMoq from 'typemoq'; import { BufferDecoder } from '../../client/common/process/decoder'; import { ProcessService } from '../../client/common/process/proc'; -import { IBufferDecoder, IProcessService } from '../../client/common/process/types'; +import { IBufferDecoder, IProcessService, IProcessServiceFactory } from '../../client/common/process/types'; import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs'; import { ServiceContainer } from '../../client/ioc/container'; import { ServiceManager } from '../../client/ioc/serviceManager'; @@ -15,7 +15,6 @@ import { PYTHON_PATH } from '../common'; suite('Virtual environment manager', () => { let serviceManager: ServiceManager; let serviceContainer: ServiceContainer; - let process: TypeMoq.IMock; setup(async () => { const cont = new Container(); @@ -28,7 +27,10 @@ suite('Virtual environment manager', () => { test('Virtualenv Python environment suffix', async () => testSuffix('virtualenv')); test('Run actual virtual env detection code', async () => { - serviceManager.addSingleton(IProcessService, ProcessService); + const processServiceFactory = TypeMoq.Mock.ofType(); + // tslint:disable-next-line:no-any + processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(new ProcessService(new BufferDecoder(), process.env as any))); + serviceManager.addSingletonInstance(IProcessServiceFactory, processServiceFactory.object); serviceManager.addSingleton(IBufferDecoder, BufferDecoder); const venvManager = new VirtualEnvironmentManager(serviceContainer); const name = await venvManager.getEnvironmentName(PYTHON_PATH); @@ -37,11 +39,13 @@ suite('Virtual environment manager', () => { }); async function testSuffix(expectedName: string) { - process = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IProcessService, process.object); + const processService = TypeMoq.Mock.ofType(); + const processServiceFactory = TypeMoq.Mock.ofType(); + processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); + serviceManager.addSingletonInstance(IProcessServiceFactory, processServiceFactory.object); const venvManager = new VirtualEnvironmentManager(serviceContainer); - process + processService .setup(x => x.exec(PYTHON_PATH, TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: expectedName, diff --git a/src/test/mocks/proc.ts b/src/test/mocks/proc.ts index e910d0da4720..dcf1d5388859 100644 --- a/src/test/mocks/proc.ts +++ b/src/test/mocks/proc.ts @@ -1,5 +1,4 @@ import { EventEmitter } from 'events'; -import { inject, injectable } from 'inversify'; import 'rxjs/add/observable/of'; import { Observable } from 'rxjs/Observable'; import { ExecutionResult, IProcessService, ObservableExecutionResult, Output, SpawnOptions } from '../../client/common/process/types'; @@ -9,9 +8,8 @@ type ExecCallback = (result: ExecutionResult) => void; export const IOriginalProcessService = Symbol('IProcessService'); -@injectable() export class MockProcessService extends EventEmitter implements IProcessService { - constructor( @inject(IOriginalProcessService) private procService: IProcessService) { + constructor(private procService: IProcessService) { super(); } public onExecObservable(handler: (file: string, args: string[], options: SpawnOptions, callback: ExecObservableCallback) => void) { diff --git a/src/test/serviceRegistry.ts b/src/test/serviceRegistry.ts index f38344a474bd..3159e4695a2d 100644 --- a/src/test/serviceRegistry.ts +++ b/src/test/serviceRegistry.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { Container } from 'inversify'; +import * as TypeMoq from 'typemoq'; import { Disposable, Memento, OutputChannel } from 'vscode'; import { STANDARD_OUTPUT_CHANNEL } from '../client/common/constants'; import { Logger } from '../client/common/logger'; @@ -16,7 +17,7 @@ import { ProcessService } from '../client/common/process/proc'; import { PythonExecutionFactory } from '../client/common/process/pythonExecutionFactory'; import { PythonToolExecutionService } from '../client/common/process/pythonToolService'; import { registerTypes as processRegisterTypes } from '../client/common/process/serviceRegistry'; -import { IBufferDecoder, IProcessService, IPythonExecutionFactory, IPythonToolExecutionService } from '../client/common/process/types'; +import { IBufferDecoder, IProcessServiceFactory, IPythonExecutionFactory, IPythonToolExecutionService } from '../client/common/process/types'; import { registerTypes as commonRegisterTypes } from '../client/common/serviceRegistry'; import { GLOBAL_MEMENTO, ICurrentProcess, IDisposableRegistry, ILogger, IMemento, IOutputChannel, IPathUtils, Is64Bit, IsWindows, WORKSPACE_MEMENTO } from '../client/common/types'; import { registerTypes as variableRegisterTypes } from '../client/common/variables/serviceRegistry'; @@ -30,7 +31,7 @@ import { TEST_OUTPUT_CHANNEL } from '../client/unittests/common/constants'; import { registerTypes as unittestsRegisterTypes } from '../client/unittests/serviceRegistry'; import { MockOutputChannel } from './mockClasses'; import { MockMemento } from './mocks/mementos'; -import { IOriginalProcessService, MockProcessService } from './mocks/proc'; +import { MockProcessService } from './mocks/proc'; import { MockProcess } from './mocks/process'; export class IocContainer { @@ -93,8 +94,12 @@ export class IocContainer { } public registerMockProcessTypes() { this.serviceManager.addSingleton(IBufferDecoder, BufferDecoder); - this.serviceManager.addSingleton(IOriginalProcessService, ProcessService); - this.serviceManager.addSingleton(IProcessService, MockProcessService); + const processServiceFactory = TypeMoq.Mock.ofType(); + processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => { + // tslint:disable-next-line:no-any + const processService = new MockProcessService(new ProcessService(new BufferDecoder(), process.env as any)); + return Promise.resolve(processService); + }); this.serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); this.serviceManager.addSingleton(IPythonToolExecutionService, PythonToolExecutionService); } diff --git a/src/test/unittests/nosetest.disovery.test.ts b/src/test/unittests/nosetest.disovery.test.ts index d1acbecf149b..51001b1ab568 100644 --- a/src/test/unittests/nosetest.disovery.test.ts +++ b/src/test/unittests/nosetest.disovery.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -62,8 +62,8 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { ioc.registerMockProcessTypes(); } - function injectTestDiscoveryOutput(outputFileName: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(outputFileName: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.indexOf('--collect-only') >= 0) { let out = fs.readFileSync(path.join(UNITTEST_TEST_FILES_PATH, outputFileName), 'utf8'); @@ -78,7 +78,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { } test('Discover Tests (single test file)', async () => { - injectTestDiscoveryOutput('one.output'); + await injectTestDiscoveryOutput('one.output'); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_SINGLE_TEST_FILE_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); @@ -89,7 +89,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { }); test('Check that nameToRun in testSuites has class name after : (single test file)', async () => { - injectTestDiscoveryOutput('two.output'); + await injectTestDiscoveryOutput('two.output'); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_SINGLE_TEST_FILE_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); @@ -99,7 +99,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { assert.equal(tests.testSuites.every(t => t.testSuite.name === t.testSuite.nameToRun.split(':')[1]), true, 'Suite name does not match class name'); }); test('Discover Tests (-m=test)', async () => { - injectTestDiscoveryOutput('three.output'); + await injectTestDiscoveryOutput('three.output'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -115,7 +115,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { }); test('Discover Tests (-w=specific -m=tst)', async () => { - injectTestDiscoveryOutput('four.output'); + await injectTestDiscoveryOutput('four.output'); await updateSetting('unitTest.nosetestArgs', ['-w', 'specific', '-m', 'tst'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -128,7 +128,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { }); test('Discover Tests (-m=test_)', async () => { - injectTestDiscoveryOutput('five.output'); + await injectTestDiscoveryOutput('five.output'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); diff --git a/src/test/unittests/pytest.discovery.test.ts b/src/test/unittests/pytest.discovery.test.ts index 9f073f8440bc..bdd6002ab81c 100644 --- a/src/test/unittests/pytest.discovery.test.ts +++ b/src/test/unittests/pytest.discovery.test.ts @@ -4,7 +4,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as vscode from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -44,8 +44,8 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { ioc.registerMockProcessTypes(); } - function injectTestDiscoveryOutput(output: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(output: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.indexOf('--collect-only') >= 0) { callback({ @@ -58,7 +58,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { test('Discover Tests (single test file)', async () => { // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(` + await injectTestDiscoveryOutput(` ============================= test session starts ============================== platform darwin -- Python 3.6.2, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /Users/donjayamanne/.vscode/extensions/pythonVSCode/src/test/pythonFiles/testFiles/single, inifile: @@ -89,7 +89,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { test('Discover Tests (pattern = test_)', async () => { // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(` + await injectTestDiscoveryOutput(` ============================= test session starts ============================== platform darwin -- Python 3.6.2, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /Users/donjayamanne/.vscode/extensions/pythonVSCode/src/test/pythonFiles/testFiles/standard, inifile: @@ -172,7 +172,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { test('Discover Tests (pattern = _test)', async () => { // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(` + await injectTestDiscoveryOutput(` ============================= test session starts ============================== platform darwin -- Python 3.6.2, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /Users/donjayamanne/.vscode/extensions/pythonVSCode/src/test/pythonFiles/testFiles/standard, inifile: @@ -198,7 +198,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { test('Discover Tests (with config)', async () => { // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(` + await injectTestDiscoveryOutput(` ============================= test session starts ============================== platform darwin -- Python 3.6.2, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /Users/donjayamanne/.vscode/extensions/pythonVSCode/src/test/pythonFiles/testFiles/unitestsWithConfigs, inifile: pytest.ini @@ -243,7 +243,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { test('Setting cwd should return tests', async () => { // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(` + await injectTestDiscoveryOutput(` ============================= test session starts ============================== platform darwin -- Python 3.6.2, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /Users/donjayamanne/.vscode/extensions/pythonVSCode/src/test/pythonFiles/testFiles/cwd/src, inifile: diff --git a/src/test/unittests/pytest.run.test.ts b/src/test/unittests/pytest.run.test.ts index b6695db63311..57cc69f7bf97 100644 --- a/src/test/unittests/pytest.run.test.ts +++ b/src/test/unittests/pytest.run.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory, TestFile, TestsToRun } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -43,8 +43,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { ioc.registerMockProcessTypes(); } - function injectTestDiscoveryOutput(outputFileName: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(outputFileName: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.indexOf('--collect-only') >= 0) { callback({ @@ -55,8 +55,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { }); } - function injectTestRunOutput(outputFileName: string, failedOutput: boolean = false) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestRunOutput(outputFileName: string, failedOutput: boolean = false) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (failedOutput && args.indexOf('--last-failed') === -1) { return; @@ -73,8 +73,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { } test('Run Tests', async () => { - injectTestDiscoveryOutput('one.output'); - injectTestRunOutput('one.xml'); + await injectTestDiscoveryOutput('one.output'); + await injectTestRunOutput('one.xml'); await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('pytest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -86,9 +86,9 @@ suite('Unit Tests - pytest - run with mocked process output', () => { }); test('Run Failed Tests', async () => { - injectTestDiscoveryOutput('two.output'); - injectTestRunOutput('two.xml'); - injectTestRunOutput('two.again.xml', true); + await injectTestDiscoveryOutput('two.output'); + await injectTestRunOutput('two.xml'); + await injectTestRunOutput('two.again.xml', true); await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('pytest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -106,8 +106,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { }); test('Run Specific Test File', async () => { - injectTestDiscoveryOutput('three.output'); - injectTestRunOutput('three.xml'); + await injectTestDiscoveryOutput('three.output'); + await injectTestRunOutput('three.xml'); await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('pytest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -130,8 +130,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { }); test('Run Specific Test Suite', async () => { - injectTestDiscoveryOutput('four.output'); - injectTestRunOutput('four.xml'); + await injectTestDiscoveryOutput('four.output'); + await injectTestRunOutput('four.xml'); await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('pytest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -145,8 +145,8 @@ suite('Unit Tests - pytest - run with mocked process output', () => { }); test('Run Specific Test Function', async () => { - injectTestDiscoveryOutput('five.output'); - injectTestRunOutput('five.xml'); + await injectTestDiscoveryOutput('five.output'); + await injectTestRunOutput('five.xml'); await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('pytest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); diff --git a/src/test/unittests/serviceRegistry.ts b/src/test/unittests/serviceRegistry.ts index 725dba422e81..f3a6d21d2315 100644 --- a/src/test/unittests/serviceRegistry.ts +++ b/src/test/unittests/serviceRegistry.ts @@ -13,6 +13,7 @@ import { TestFlatteningVisitor } from '../../client/unittests/common/testVisitor import { TestFolderGenerationVisitor } from '../../client/unittests/common/testVisitors/folderGenerationVisitor'; import { TestResultResetVisitor } from '../../client/unittests/common/testVisitors/resultResetVisitor'; import { ITestResultsService, ITestsHelper, ITestsParser, ITestVisitor, IUnitTestSocketServer, TestProvider } from '../../client/unittests/common/types'; +// tslint:disable-next-line:no-duplicate-imports import { ITestCollectionStorageService, ITestDiscoveryService, ITestManager, ITestManagerFactory, ITestManagerService, ITestManagerServiceFactory } from '../../client/unittests/common/types'; import { TestManager as NoseTestManager } from '../../client/unittests/nosetest/main'; import { TestDiscoveryService as NoseTestDiscoveryService } from '../../client/unittests/nosetest/services/discoveryService'; diff --git a/src/test/unittests/unittest.discovery.test.ts b/src/test/unittests/unittest.discovery.test.ts index b1f95c6ed978..e573726c2b4e 100644 --- a/src/test/unittests/unittest.discovery.test.ts +++ b/src/test/unittests/unittest.discovery.test.ts @@ -6,7 +6,7 @@ import * as fs from 'fs-extra'; import { EOL } from 'os'; import * as path from 'path'; import { ConfigurationTarget } from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -59,8 +59,8 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { ioc.registerMockProcessTypes(); } - function injectTestDiscoveryOutput(output: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(output: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.length > 1 && args[0] === '-c' && args[1].includes('import unittest') && args[1].includes('loader = unittest.TestLoader()')) { callback({ @@ -75,7 +75,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { test('Discover Tests (single test file)', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_one.Test_test1.test_A test_one.Test_test1.test_B test_one.Test_test1.test_c @@ -92,7 +92,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { test('Discover Tests', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test1.test_A test_unittest_one.Test_test1.test_B test_unittest_one.Test_test1.test_c @@ -116,7 +116,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { test('Discover Tests (pattern = *_test_*.py)', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=*_test*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start unittest_three_test.Test_test3.test_A unittest_three_test.Test_test3.test_B `); @@ -132,7 +132,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { test('Setting cwd should return tests', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_cwd.Test_Current_Working_Directory.test_cwd `); const factory = ioc.serviceContainer.get(ITestManagerFactory); diff --git a/src/test/unittests/unittest.run.test.ts b/src/test/unittests/unittest.run.test.ts index f74ff2522acb..757ecf836199 100644 --- a/src/test/unittests/unittest.run.test.ts +++ b/src/test/unittests/unittest.run.test.ts @@ -6,7 +6,7 @@ import * as fs from 'fs-extra'; import { EOL } from 'os'; import * as path from 'path'; import { ConfigurationTarget } from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory, IUnitTestSocketServer, TestsToRun } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -43,7 +43,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { } await initializeTest(); initializeDI(); - ignoreTestLauncher(); + await ignoreTestLauncher(); }); teardown(async () => { ioc.dispose(); @@ -70,8 +70,8 @@ suite('Unit Tests - unittest - run with mocked process output', () => { ioc.registerTestVisitors(); } - function ignoreTestLauncher() { - const procService = ioc.serviceContainer.get(IProcessService); + async function ignoreTestLauncher() { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; // When running the python test launcher, just return. procService.onExecObservable((file, args, options, callback) => { if (args.length > 1 && args[0].endsWith('visualstudio_py_testlauncher.py')) { @@ -79,8 +79,8 @@ suite('Unit Tests - unittest - run with mocked process output', () => { } }); } - function injectTestDiscoveryOutput(output: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(output: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.length > 1 && args[0] === '-c' && args[1].includes('import unittest') && args[1].includes('loader = unittest.TestLoader()')) { callback({ @@ -101,7 +101,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { test('Run Tests', async () => { await updateSetting('unitTest.unittestArgs', ['-v', '-s', './tests', '-p', 'test_unittest*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test1.test_A test_unittest_one.Test_test1.test_B test_unittest_one.Test_test1.test_c @@ -138,7 +138,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { test('Run Failed Tests', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test1.test_A test_unittest_one.Test_test1.test_B test_unittest_one.Test_test1.test_c @@ -191,7 +191,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test_one_1.test_1_1_1 test_unittest_one.Test_test_one_1.test_1_1_2 test_unittest_one.Test_test_one_1.test_1_1_3 @@ -228,7 +228,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { test('Run Specific Test Suite', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test_one_1.test_1_1_1 test_unittest_one.Test_test_one_1.test_1_1_2 test_unittest_one.Test_test_one_1.test_1_1_3 @@ -265,7 +265,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { test('Run Specific Test Function', async () => { await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); // tslint:disable-next-line:no-multiline-string - injectTestDiscoveryOutput(`start + await injectTestDiscoveryOutput(`start test_unittest_one.Test_test1.test_A test_unittest_one.Test_test1.test_B test_unittest_one.Test_test1.test_c From 3abc648f03f19e61394410cffa774c86cb03744f Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 19:43:36 -0700 Subject: [PATCH 06/15] :bug: singleton --- src/test/serviceRegistry.ts | 8 +++---- src/test/unittests/nosetest.run.test.ts | 32 ++++++++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/test/serviceRegistry.ts b/src/test/serviceRegistry.ts index 3159e4695a2d..202589fc7425 100644 --- a/src/test/serviceRegistry.ts +++ b/src/test/serviceRegistry.ts @@ -95,11 +95,9 @@ export class IocContainer { public registerMockProcessTypes() { this.serviceManager.addSingleton(IBufferDecoder, BufferDecoder); const processServiceFactory = TypeMoq.Mock.ofType(); - processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => { - // tslint:disable-next-line:no-any - const processService = new MockProcessService(new ProcessService(new BufferDecoder(), process.env as any)); - return Promise.resolve(processService); - }); + // tslint:disable-next-line:no-any + const processService = new MockProcessService(new ProcessService(new BufferDecoder(), process.env as any)); + processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService)); this.serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); this.serviceManager.addSingleton(IPythonToolExecutionService, PythonToolExecutionService); } diff --git a/src/test/unittests/nosetest.run.test.ts b/src/test/unittests/nosetest.run.test.ts index 1fc34a684c34..37d26716a31e 100644 --- a/src/test/unittests/nosetest.run.test.ts +++ b/src/test/unittests/nosetest.run.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessServiceFactory } from '../../client/common/process/types'; import { CommandSource } from '../../client/unittests/common/constants'; import { ITestManagerFactory, TestsToRun } from '../../client/unittests/common/types'; import { rootWorkspaceUri, updateSetting } from '../common'; @@ -60,8 +60,8 @@ suite('Unit Tests - nose - run against actual python process', () => { ioc.registerMockProcessTypes(); } - function injectTestDiscoveryOutput(outputFileName: string) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestDiscoveryOutput(outputFileName: string) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (args.indexOf('--collect-only') >= 0) { callback({ @@ -72,8 +72,8 @@ suite('Unit Tests - nose - run against actual python process', () => { }); } - function injectTestRunOutput(outputFileName: string, failedOutput: boolean = false) { - const procService = ioc.serviceContainer.get(IProcessService); + async function injectTestRunOutput(outputFileName: string, failedOutput: boolean = false) { + const procService = await ioc.serviceContainer.get(IProcessServiceFactory).create() as MockProcessService; procService.onExecObservable((file, args, options, callback) => { if (failedOutput && args.indexOf('--failed') === -1) { return; @@ -90,8 +90,8 @@ suite('Unit Tests - nose - run against actual python process', () => { } test('Run Tests', async () => { - injectTestDiscoveryOutput('run.one.output'); - injectTestRunOutput('run.one.result'); + await injectTestDiscoveryOutput('run.one.output'); + await injectTestRunOutput('run.one.result'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -103,9 +103,9 @@ suite('Unit Tests - nose - run against actual python process', () => { }); test('Run Failed Tests', async () => { - injectTestDiscoveryOutput('run.two.output'); - injectTestRunOutput('run.two.result'); - injectTestRunOutput('run.two.again.result', true); + await injectTestDiscoveryOutput('run.two.output'); + await injectTestRunOutput('run.two.result'); + await injectTestRunOutput('run.two.again.result', true); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -123,8 +123,8 @@ suite('Unit Tests - nose - run against actual python process', () => { }); test('Run Specific Test File', async () => { - injectTestDiscoveryOutput('run.three.output'); - injectTestRunOutput('run.three.result'); + await injectTestDiscoveryOutput('run.three.output'); + await injectTestRunOutput('run.three.result'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -141,8 +141,8 @@ suite('Unit Tests - nose - run against actual python process', () => { }); test('Run Specific Test Suite', async () => { - injectTestDiscoveryOutput('run.four.output'); - injectTestRunOutput('run.four.result'); + await injectTestDiscoveryOutput('run.four.output'); + await injectTestRunOutput('run.four.result'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); @@ -159,8 +159,8 @@ suite('Unit Tests - nose - run against actual python process', () => { }); test('Run Specific Test Function', async () => { - injectTestDiscoveryOutput('run.five.output'); - injectTestRunOutput('run.five.result'); + await injectTestDiscoveryOutput('run.five.output'); + await injectTestRunOutput('run.five.result'); await updateSetting('unitTest.nosetestArgs', ['-m', 'test'], rootWorkspaceUri, configTarget); const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri, UNITTEST_TEST_FILES_PATH); From 1d91faca745a637641896702389f7554ee24645e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 7 Apr 2018 19:51:23 -0700 Subject: [PATCH 07/15] :bug: fix registraion (oops) --- src/test/serviceRegistry.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/serviceRegistry.ts b/src/test/serviceRegistry.ts index 202589fc7425..5ce82911dea3 100644 --- a/src/test/serviceRegistry.ts +++ b/src/test/serviceRegistry.ts @@ -98,6 +98,7 @@ export class IocContainer { // tslint:disable-next-line:no-any const processService = new MockProcessService(new ProcessService(new BufferDecoder(), process.env as any)); processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService)); + this.serviceManager.addSingletonInstance(IProcessServiceFactory, processServiceFactory.object); this.serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); this.serviceManager.addSingleton(IPythonToolExecutionService, PythonToolExecutionService); } From d03c075d9f6a13f3fcdc0c2a8973b31ef20aa6ef Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 17:56:44 -0700 Subject: [PATCH 08/15] Remove dependency on IProcessService --- src/client/terminals/codeExecution/helper.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/client/terminals/codeExecution/helper.ts b/src/client/terminals/codeExecution/helper.ts index de31a8b94049..e5cc2df6e44f 100644 --- a/src/client/terminals/codeExecution/helper.ts +++ b/src/client/terminals/codeExecution/helper.ts @@ -7,7 +7,7 @@ import { Range, TextEditor, Uri } from 'vscode'; import { IApplicationShell, IDocumentManager } from '../../common/application/types'; import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants'; import '../../common/extensions'; -import { IProcessService } from '../../common/process/types'; +import { IProcessServiceFactory } from '../../common/process/types'; import { IConfigurationService } from '../../common/types'; import { IEnvironmentVariablesProvider } from '../../common/variables/types'; import { IServiceContainer } from '../../ioc/types'; @@ -18,13 +18,13 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { private readonly documentManager: IDocumentManager; private readonly applicationShell: IApplicationShell; private readonly envVariablesProvider: IEnvironmentVariablesProvider; - private readonly processService: IProcessService; + private readonly processServiceFactory: IProcessServiceFactory; private readonly configurationService: IConfigurationService; constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { this.documentManager = serviceContainer.get(IDocumentManager); this.applicationShell = serviceContainer.get(IApplicationShell); this.envVariablesProvider = serviceContainer.get(IEnvironmentVariablesProvider); - this.processService = serviceContainer.get(IProcessService); + this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); this.configurationService = serviceContainer.get(IConfigurationService); } public async normalizeLines(code: string, resource?: Uri): Promise { @@ -35,7 +35,8 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { const env = await this.envVariablesProvider.getEnvironmentVariables(resource); const pythonPath = this.configurationService.getSettings(resource).pythonPath; const args = [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'normalizeForInterpreter.py'), code]; - const proc = await this.processService.exec(pythonPath, args, { env, throwOnStdErr: true }); + const processService = await this.processServiceFactory.create(resource); + const proc = await processService.exec(pythonPath, args, { env, throwOnStdErr: true }); return proc.stdout; } catch (ex) { console.error(ex, 'Python: Failed to normalize code for execution in terminal'); From e29a084a31108bf0ee677a6849a14e085be33961 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 17:58:17 -0700 Subject: [PATCH 09/15] Fix linter errors --- src/test/format/extension.format.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 1bd8cdf37ca6..503de9c117c2 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -1,7 +1,6 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import * as vscode from 'vscode'; -import { IProcessService, IPythonExecutionFactory } from '../../client/common/process/types'; import { CancellationTokenSource, Position, Uri, window, workspace } from 'vscode'; import { IProcessServiceFactory, IPythonExecutionFactory } from '../../client/common/process/types'; import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter'; From 80280a5e9e73dbcf759b6e43d70b2f02cdef34f0 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 20:57:51 -0700 Subject: [PATCH 10/15] Fix linter errors --- src/client/common/process/proc.ts | 3 ++- src/test/common/process/execFactory.test.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client/common/process/proc.ts b/src/client/common/process/proc.ts index 63a868135571..ed511447db1f 100644 --- a/src/client/common/process/proc.ts +++ b/src/client/common/process/proc.ts @@ -79,7 +79,8 @@ export class ProcessService implements IProcessService { delete options.encoding; const spawnOptions = { ...options }; if (!spawnOptions.env || Object.keys(spawnOptions).length === 0) { - spawnOptions.env = { ...process.env }; + const env = this.env ? this.env : process.env; + spawnOptions.env = { ...env }; } // Always ensure we have unbuffered output. diff --git a/src/test/common/process/execFactory.test.ts b/src/test/common/process/execFactory.test.ts index b8d878b3fe3a..2802a38cb749 100644 --- a/src/test/common/process/execFactory.test.ts +++ b/src/test/common/process/execFactory.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// tslint:disable:max-func-body-length no-any + import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; import { Uri } from 'vscode'; @@ -11,7 +13,6 @@ import { IEnvironmentVariablesProvider } from '../../../client/common/variables/ import { InterpreterVersionService } from '../../../client/interpreter/interpreterVersion'; import { IServiceContainer } from '../../../client/ioc/types'; -// tslint:disable-next-line:max-func-body-length suite('PythonExecutableService', () => { let serviceContainer: TypeMoq.IMock; let configService: TypeMoq.IMock; @@ -29,6 +30,7 @@ suite('PythonExecutableService', () => { serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))).returns(() => envVarsProvider.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory))).returns(() => procServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))).returns(() => configService.object); + procService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(procService.object)); envVarsProvider.setup(v => v.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); From 8374dc77bfb8668c45ec2fcff3667af15b94f381 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 21:24:43 -0700 Subject: [PATCH 11/15] Fix errors in tests --- src/test/common/terminals/activation.conda.test.ts | 4 +++- src/test/interpreters/condaService.test.ts | 4 ++-- src/test/interpreters/currentPathService.test.ts | 12 +++++++----- src/test/interpreters/pipEnvService.test.ts | 1 + src/test/interpreters/virtualEnvManager.test.ts | 4 +++- src/test/terminals/codeExecution/helper.test.ts | 5 ++--- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/test/common/terminals/activation.conda.test.ts b/src/test/common/terminals/activation.conda.test.ts index 3af7c42e3e22..b4498ce8edeb 100644 --- a/src/test/common/terminals/activation.conda.test.ts +++ b/src/test/common/terminals/activation.conda.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// tslint:disable:max-func-body-length no-any + import { expect } from 'chai'; import * as path from 'path'; import * as TypeMoq from 'typemoq'; @@ -16,7 +18,6 @@ import { IConfigurationService, IDisposableRegistry, IPythonSettings, ITerminalS import { ICondaService } from '../../../client/interpreter/contracts'; import { IServiceContainer } from '../../../client/ioc/types'; -// tslint:disable-next-line:max-func-body-length suite('Terminal Environment Activation conda', () => { let terminalHelper: TerminalHelper; let disposables: Disposable[] = []; @@ -38,6 +39,7 @@ suite('Terminal Environment Activation conda', () => { platformService = TypeMoq.Mock.ofType(); processService = TypeMoq.Mock.ofType(); condaService = TypeMoq.Mock.ofType(); + processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory = TypeMoq.Mock.ofType(); procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); diff --git a/src/test/interpreters/condaService.test.ts b/src/test/interpreters/condaService.test.ts index 829a42f388ea..21059a94e537 100644 --- a/src/test/interpreters/condaService.test.ts +++ b/src/test/interpreters/condaService.test.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-require-imports no-var-requires no-any max-func-body-length import * as assert from 'assert'; import { expect } from 'chai'; import { EOL } from 'os'; @@ -12,12 +13,10 @@ import { CondaService, KNOWN_CONDA_LOCATIONS } from '../../client/interpreter/lo import { IServiceContainer } from '../../client/ioc/types'; import { MockState } from './mocks'; -// tslint:disable-next-line:no-require-imports no-var-requires const untildify: (value: string) => string = require('untildify'); const environmentsPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'environments'); -// tslint:disable-next-line:max-func-body-length suite('Interpreters Conda Service', () => { let processService: TypeMoq.IMock; let platformService: TypeMoq.IMock; @@ -33,6 +32,7 @@ suite('Interpreters Conda Service', () => { registryInterpreterLocatorService = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); procServiceFactory = TypeMoq.Mock.ofType(); + processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); serviceContainer = TypeMoq.Mock.ofType(); diff --git a/src/test/interpreters/currentPathService.test.ts b/src/test/interpreters/currentPathService.test.ts index 4d136735edf9..19e44c6eca76 100644 --- a/src/test/interpreters/currentPathService.test.ts +++ b/src/test/interpreters/currentPathService.test.ts @@ -3,17 +3,18 @@ 'use strict'; +// tslint:disable:max-func-body-length no-any + import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; import { IFileSystem } from '../../client/common/platform/types'; -import { IProcessService } from '../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../client/common/process/types'; import { IConfigurationService, IPersistentState, IPersistentStateFactory, IPythonSettings } from '../../client/common/types'; import { IInterpreterVersionService, InterpreterType, PythonInterpreter } from '../../client/interpreter/contracts'; import { CurrentPathService } from '../../client/interpreter/locators/services/currentPathService'; import { IVirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs/types'; import { IServiceContainer } from '../../client/ioc/types'; -// tslint:disable-next-line:max-func-body-length suite('Interpreters CurrentPath Service', () => { let processService: TypeMoq.IMock; let fileSystem: TypeMoq.IMock; @@ -32,21 +33,22 @@ suite('Interpreters CurrentPath Service', () => { configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); const persistentStateFactory = TypeMoq.Mock.ofType(); persistentState = TypeMoq.Mock.ofType>(); - // tslint:disable-next-line:no-any + processService.setup((x: any) => x.then).returns(() => undefined); persistentState.setup(p => p.value).returns(() => undefined as any); persistentState.setup(p => p.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); fileSystem = TypeMoq.Mock.ofType(); persistentStateFactory.setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => persistentState.object); + const procServiceFactory = TypeMoq.Mock.ofType(); + procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService), TypeMoq.It.isAny())).returns(() => processService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IVirtualEnvironmentManager), TypeMoq.It.isAny())).returns(() => virtualEnvironmentManager.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IInterpreterVersionService), TypeMoq.It.isAny())).returns(() => interpreterVersionService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())).returns(() => fileSystem.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())).returns(() => persistentStateFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())).returns(() => configurationService.object); - currentPathService = new CurrentPathService(virtualEnvironmentManager.object, interpreterVersionService.object, processService.object, serviceContainer.object); + currentPathService = new CurrentPathService(virtualEnvironmentManager.object, interpreterVersionService.object, procServiceFactory.object, serviceContainer.object); }); test('Interpreters that do not exist on the file system are not excluded from the list', async () => { diff --git a/src/test/interpreters/pipEnvService.test.ts b/src/test/interpreters/pipEnvService.test.ts index 241226339e01..61d9722849bc 100644 --- a/src/test/interpreters/pipEnvService.test.ts +++ b/src/test/interpreters/pipEnvService.test.ts @@ -50,6 +50,7 @@ suite('Interpreters - PipEnv', () => { persistentStateFactory = TypeMoq.Mock.ofType(); envVarsProvider = TypeMoq.Mock.ofType(); procServiceFactory = TypeMoq.Mock.ofType(); + processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); // tslint:disable-next-line:no-any diff --git a/src/test/interpreters/virtualEnvManager.test.ts b/src/test/interpreters/virtualEnvManager.test.ts index 855bae72c057..4db50c4ac344 100644 --- a/src/test/interpreters/virtualEnvManager.test.ts +++ b/src/test/interpreters/virtualEnvManager.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// tslint:disable:no-any + import { expect } from 'chai'; import { Container } from 'inversify'; import * as TypeMoq from 'typemoq'; @@ -28,7 +30,6 @@ suite('Virtual environment manager', () => { test('Run actual virtual env detection code', async () => { const processServiceFactory = TypeMoq.Mock.ofType(); - // tslint:disable-next-line:no-any processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(new ProcessService(new BufferDecoder(), process.env as any))); serviceManager.addSingletonInstance(IProcessServiceFactory, processServiceFactory.object); serviceManager.addSingleton(IBufferDecoder, BufferDecoder); @@ -41,6 +42,7 @@ suite('Virtual environment manager', () => { async function testSuffix(expectedName: string) { const processService = TypeMoq.Mock.ofType(); const processServiceFactory = TypeMoq.Mock.ofType(); + processService.setup((x: any) => x.then).returns(() => undefined); processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); serviceManager.addSingletonInstance(IProcessServiceFactory, processServiceFactory.object); diff --git a/src/test/terminals/codeExecution/helper.test.ts b/src/test/terminals/codeExecution/helper.test.ts index 56db9e5657cd..69f635dc0784 100644 --- a/src/test/terminals/codeExecution/helper.test.ts +++ b/src/test/terminals/codeExecution/helper.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// tslint:disable:no-multiline-string no-trailing-whitespace +// tslint:disable:no-multiline-string no-trailing-whitespace max-func-body-length no-any import { expect } from 'chai'; import * as fs from 'fs-extra'; @@ -24,7 +24,6 @@ import { PYTHON_PATH } from '../../common'; const TEST_FILES_PATH = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'pythonFiles', 'terminalExec'); -// tslint:disable-next-line:max-func-body-length suite('Terminal - Code Execution Helper', () => { let documentManager: TypeMoq.IMock; let applicationShell: TypeMoq.IMock; @@ -42,12 +41,12 @@ suite('Terminal - Code Execution Helper', () => { configService = TypeMoq.Mock.ofType(); const pythonSettings = TypeMoq.Mock.ofType(); pythonSettings.setup(p => p.pythonPath).returns(() => PYTHON_PATH); + processService.setup((x: any) => x.then).returns(() => undefined); configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); envVariablesProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())).returns(() => documentManager.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())).returns(() => applicationShell.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())).returns(() => envVariablesProvider.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessService), TypeMoq.It.isAny())).returns(() => processService.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())).returns(() => configService.object); helper = new CodeExecutionHelper(serviceContainer.object); From 2cf88615bbd3a55a0cf8d553a3872e6b007a1b14 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 21:36:31 -0700 Subject: [PATCH 12/15] Remove use of env provider service --- src/test/interpreters/pipEnvService.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/test/interpreters/pipEnvService.test.ts b/src/test/interpreters/pipEnvService.test.ts index 61d9722849bc..4571dd14b939 100644 --- a/src/test/interpreters/pipEnvService.test.ts +++ b/src/test/interpreters/pipEnvService.test.ts @@ -105,7 +105,6 @@ suite('Interpreters - PipEnv', () => { }); test(`Should display warning message if there is a \'PipFile\' but \'pipenv --venv\' failes with stderr ${testSuffix}`, async () => { const env = {}; - envVarsProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})).verifiable(TypeMoq.Times.once()); currentProcess.setup(c => c.env).returns(() => env); processService.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ stderr: 'PipEnv Failed', stdout: '' })); fileSystem.setup(fs => fs.fileExistsAsync(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))).returns(() => Promise.resolve(true)); @@ -113,13 +112,11 @@ suite('Interpreters - PipEnv', () => { const environments = await pipEnvService.getInterpreters(resource); expect(environments).to.be.deep.equal([]); - envVarsProvider.verifyAll(); appShell.verifyAll(); }); test(`Should return interpreter information${testSuffix}`, async () => { const env = {}; const venvDir = 'one'; - envVarsProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})).verifiable(TypeMoq.Times.once()); currentProcess.setup(c => c.env).returns(() => env); processService.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ stdout: venvDir })); interpreterVersionService.setup(v => v.getVersion(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('xyz')); @@ -129,7 +126,6 @@ suite('Interpreters - PipEnv', () => { expect(environments).to.be.lengthOf(1); fileSystem.verifyAll(); - envVarsProvider.verifyAll(); }); test(`Should return interpreter information using PipFile defined in Env variable${testSuffix}`, async () => { const envPipFile = 'XYZ'; @@ -137,7 +133,6 @@ suite('Interpreters - PipEnv', () => { PIPENV_PIPFILE: envPipFile }; const venvDir = 'one'; - envVarsProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})).verifiable(TypeMoq.Times.once()); currentProcess.setup(c => c.env).returns(() => env); processService.setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ stdout: venvDir })); interpreterVersionService.setup(v => v.getVersion(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('xyz')); @@ -148,7 +143,6 @@ suite('Interpreters - PipEnv', () => { expect(environments).to.be.lengthOf(1); fileSystem.verifyAll(); - envVarsProvider.verifyAll(); }); }); }); From 708edc2684591ed532a1e63cb2e791b1198cbf7b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 21:56:35 -0700 Subject: [PATCH 13/15] Revert changes --- .../locators/services/currentPathService.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/client/interpreter/locators/services/currentPathService.ts b/src/client/interpreter/locators/services/currentPathService.ts index a1630c3d47a0..cf2f5dc8d321 100644 --- a/src/client/interpreter/locators/services/currentPathService.ts +++ b/src/client/interpreter/locators/services/currentPathService.ts @@ -3,9 +3,8 @@ import * as _ from 'lodash'; import * as path from 'path'; import { Uri } from 'vscode'; import { IFileSystem } from '../../../common/platform/types'; -import { IProcessService } from '../../../common/process/types'; -import { IConfigurationService } from '../../../common/types'; import { IProcessServiceFactory } from '../../../common/process/types'; +import { IConfigurationService } from '../../../common/types'; import { IServiceContainer } from '../../../ioc/types'; import { IInterpreterVersionService, InterpreterType, PythonInterpreter } from '../../contracts'; import { IVirtualEnvironmentManager } from '../../virtualEnvs/types'; @@ -58,7 +57,12 @@ export class CurrentPathService extends CacheableLocatorService { const processService = await this.processServiceFactory.create(); return processService.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], {}) .then(output => output.stdout.trim()) - .then(value => value.length === 0 ? defaultValue : value) + .then(async value => { + if (value.length > 0 && await this.fs.fileExistsAsync(value)) { + return value; + } + return defaultValue; + }) .catch(() => defaultValue); // Ignore exceptions in getting the executable. } catch { return defaultValue; // Ignore exceptions in getting the executable. From 4b18ad59aff3da04c2724d0801f52d17d48be1fe Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 22:11:38 -0700 Subject: [PATCH 14/15] Fix tests --- src/client/terminals/codeExecution/helper.ts | 6 +----- src/test/terminals/codeExecution/helper.test.ts | 5 ++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/client/terminals/codeExecution/helper.ts b/src/client/terminals/codeExecution/helper.ts index e5cc2df6e44f..94380e8f247c 100644 --- a/src/client/terminals/codeExecution/helper.ts +++ b/src/client/terminals/codeExecution/helper.ts @@ -9,7 +9,6 @@ import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants'; import '../../common/extensions'; import { IProcessServiceFactory } from '../../common/process/types'; import { IConfigurationService } from '../../common/types'; -import { IEnvironmentVariablesProvider } from '../../common/variables/types'; import { IServiceContainer } from '../../ioc/types'; import { ICodeExecutionHelper } from '../types'; @@ -17,13 +16,11 @@ import { ICodeExecutionHelper } from '../types'; export class CodeExecutionHelper implements ICodeExecutionHelper { private readonly documentManager: IDocumentManager; private readonly applicationShell: IApplicationShell; - private readonly envVariablesProvider: IEnvironmentVariablesProvider; private readonly processServiceFactory: IProcessServiceFactory; private readonly configurationService: IConfigurationService; constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { this.documentManager = serviceContainer.get(IDocumentManager); this.applicationShell = serviceContainer.get(IApplicationShell); - this.envVariablesProvider = serviceContainer.get(IEnvironmentVariablesProvider); this.processServiceFactory = serviceContainer.get(IProcessServiceFactory); this.configurationService = serviceContainer.get(IConfigurationService); } @@ -32,11 +29,10 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { if (code.trim().length === 0) { return ''; } - const env = await this.envVariablesProvider.getEnvironmentVariables(resource); const pythonPath = this.configurationService.getSettings(resource).pythonPath; const args = [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'normalizeForInterpreter.py'), code]; const processService = await this.processServiceFactory.create(resource); - const proc = await processService.exec(pythonPath, args, { env, throwOnStdErr: true }); + const proc = await processService.exec(pythonPath, args, { throwOnStdErr: true }); return proc.stdout; } catch (ex) { console.error(ex, 'Python: Failed to normalize code for execution in terminal'); diff --git a/src/test/terminals/codeExecution/helper.test.ts b/src/test/terminals/codeExecution/helper.test.ts index 69f635dc0784..62c00bbd0115 100644 --- a/src/test/terminals/codeExecution/helper.test.ts +++ b/src/test/terminals/codeExecution/helper.test.ts @@ -14,7 +14,7 @@ import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../../client/common/cons import '../../../client/common/extensions'; import { BufferDecoder } from '../../../client/common/process/decoder'; import { ProcessService } from '../../../client/common/process/proc'; -import { IProcessService } from '../../../client/common/process/types'; +import { IProcessService, IProcessServiceFactory } from '../../../client/common/process/types'; import { IConfigurationService, IPythonSettings } from '../../../client/common/types'; import { IEnvironmentVariablesProvider } from '../../../client/common/variables/types'; import { IServiceContainer } from '../../../client/ioc/types'; @@ -44,6 +44,9 @@ suite('Terminal - Code Execution Helper', () => { processService.setup((x: any) => x.then).returns(() => undefined); configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); envVariablesProvider.setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); + const processServiceFactory = TypeMoq.Mock.ofType(); + processServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())).returns(() => processServiceFactory.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())).returns(() => documentManager.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())).returns(() => applicationShell.object); serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())).returns(() => envVariablesProvider.object); From f3832816eb20fcc554681b69cb3b057d3ffaccda Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 7 May 2018 23:33:02 -0700 Subject: [PATCH 15/15] Fix merge issue --- src/client/common/configSettings.ts | 2 +- src/client/common/installer/productInstaller.ts | 2 +- src/client/common/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index d4db5b867a9a..85a6ac61bc14 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -34,7 +34,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { public devOptions: string[] = []; public linting!: ILintingSettings; public formatting!: IFormattingSettings; - public autoComplete!: IAutoCompeteSettings; + public autoComplete!: IAutoCompleteSettings; public unitTest!: IUnitTestSettings; public terminal!: ITerminalSettings; public sortImports!: ISortImportSettings; diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index f74c79136d15..d214653c6f43 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -225,7 +225,7 @@ export class ProductInstaller implements IInstaller { private ProductTypes = new Map(); constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer, - @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private outputChannel: OutputChannel) { + @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private outputChannel: vscode.OutputChannel) { this.ProductTypes.set(Product.flake8, ProductType.Linter); this.ProductTypes.set(Product.mypy, ProductType.Linter); this.ProductTypes.set(Product.pep8, ProductType.Linter); diff --git a/src/client/common/types.ts b/src/client/common/types.ts index f55c58f2abfc..027437f169cc 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -106,7 +106,7 @@ export interface IPythonSettings { readonly linting: ILintingSettings; readonly formatting: IFormattingSettings; readonly unitTest: IUnitTestSettings; - readonly autoComplete: IAutoCompeteSettings; + readonly autoComplete: IAutoCompleteSettings; readonly terminal: ITerminalSettings; readonly sortImports: ISortImportSettings; readonly workspaceSymbols: IWorkspaceSymbolSettings;