diff --git a/src/client/extension.ts b/src/client/extension.ts index d4fdafc7e995..6b32a7518df1 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -1,6 +1,7 @@ 'use strict'; import * as vscode from 'vscode'; +import { JediFactory } from './languageServices/jediProxyFactory'; import { PythonCompletionItemProvider } from './providers/completionProvider'; import { PythonHoverProvider } from './providers/hoverProvider'; import { PythonDefinitionProvider } from './providers/definitionProvider'; @@ -68,7 +69,8 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(activateUpdateSparkLibraryProvider()); activateSimplePythonRefactorProvider(context, formatOutChannel); context.subscriptions.push(activateFormatOnSaveProvider(PYTHON, formatOutChannel)); - context.subscriptions.push(activateGoToObjectDefinitionProvider(context)); + const jediFactory = new JediFactory(context.asAbsolutePath('.')); + context.subscriptions.push(...activateGoToObjectDefinitionProvider(jediFactory)); context.subscriptions.push(vscode.commands.registerCommand(Commands.Start_REPL, () => { getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]).catch(() => { @@ -99,19 +101,19 @@ export async function activate(context: vscode.ExtensionContext) { ] }); + context.subscriptions.push(jediFactory); context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, new PythonRenameProvider(formatOutChannel))); - const definitionProvider = new PythonDefinitionProvider(context); - const jediProx = definitionProvider.JediProxy; + const definitionProvider = new PythonDefinitionProvider(jediFactory); context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, definitionProvider)); - context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(context, jediProx))); - context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context, jediProx))); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(context, jediProx), '.')); + context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(jediFactory))); + context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(jediFactory))); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(jediFactory), '.')); context.subscriptions.push(vscode.languages.registerCodeLensProvider(PYTHON, new ShebangCodeLensProvider())) - const symbolProvider = new PythonSymbolProvider(context, jediProx); + const symbolProvider = new PythonSymbolProvider(jediFactory); context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, symbolProvider)); if (pythonSettings.devOptions.indexOf('DISABLE_SIGNATURE') === -1) { - context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, jediProx), '(', ',')); + context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(jediFactory), '(', ',')); } if (pythonSettings.formatting.provider !== 'none') { const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel); diff --git a/src/client/jedi/main.ts b/src/client/jedi/main.ts index 9d2055f8961d..6586a1e27eb4 100644 --- a/src/client/jedi/main.ts +++ b/src/client/jedi/main.ts @@ -1,10 +1,10 @@ "use strict"; -import { SocketClient } from './socketClient'; -import { SocketServer } from '../common/comms/socketServer'; import * as child_process from 'child_process'; import * as path from 'path'; import * as vscode from 'vscode'; +import { SocketClient } from './socketClient'; +import { SocketServer } from '../common/comms/socketServer'; import { createDeferred, Deferred } from '../common/helpers'; import { PythonSettings } from '../common/configSettings'; import { EventEmitter } from 'events'; @@ -80,7 +80,8 @@ export class ClientAdapter extends EventEmitter { this.startSocketServer().then(port => { const def = createDeferred(); const options = { env: newEnv, cwd: this.rootDir }; - this.process = child_process.spawn(PythonSettings.getInstance().pythonPath, [pyFile, port.toString()], options); + const rootDirUri = this.rootDir ? vscode.Uri.file(this.rootDir) : undefined; + this.process = child_process.spawn(PythonSettings.getInstance(rootDirUri).pythonPath, [pyFile, port.toString()], options); this.process.stdout.setEncoding('utf8'); this.process.stderr.setEncoding('utf8'); diff --git a/src/client/jedi/parsers/CompletionParser.ts b/src/client/jedi/parsers/CompletionParser.ts index 8f7368e5e4d9..eedc47842730 100644 --- a/src/client/jedi/parsers/CompletionParser.ts +++ b/src/client/jedi/parsers/CompletionParser.ts @@ -1,12 +1,10 @@ -import { CompletionItem, SymbolKind, SnippetString } from 'vscode'; import * as proxy from '../../providers/jediProxy'; import { extractSignatureAndDocumentation } from '../../providers/jediHelpers'; import { PythonSettings } from '../../common/configSettings'; - -const pythonSettings = PythonSettings.getInstance(); +import { CompletionItem, SymbolKind, SnippetString, Uri } from 'vscode'; export class CompletionParser { - public static parse(data: proxy.ICompletionResult): CompletionItem[] { + public static parse(data: proxy.ICompletionResult, resource: Uri): CompletionItem[] { if (!data || data.items.length === 0) { return []; } @@ -16,7 +14,7 @@ export class CompletionParser { completionItem.kind = item.type; completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1]; completionItem.detail = sigAndDocs[0].split(/\r?\n/).join(''); - if (pythonSettings.autoComplete.addBrackets === true && + if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true && (item.kind === SymbolKind.Function || item.kind === SymbolKind.Method)) { completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")"); } diff --git a/src/client/languageServices/jediProxyFactory.ts b/src/client/languageServices/jediProxyFactory.ts new file mode 100644 index 000000000000..9af0b012d648 --- /dev/null +++ b/src/client/languageServices/jediProxyFactory.ts @@ -0,0 +1,38 @@ +import { Disposable, Uri, workspace } from 'vscode'; +import { JediProxy, JediProxyHandler, ICommandResult } from '../providers/jediProxy'; + +export class JediFactory implements Disposable { + private disposables: Disposable[]; + private jediProxyHandlers: Map>; + + constructor(private extensionRootPath: string) { + this.disposables = []; + this.jediProxyHandlers = new Map>(); + } + + public dispose() { + this.disposables.forEach(disposable => disposable.dispose()); + this.disposables = []; + } + public getJediProxyHandler(resource: Uri): JediProxyHandler { + const workspaceFolder = workspace.getWorkspaceFolder(resource); + let workspacePath = workspaceFolder ? workspaceFolder.uri.fsPath : undefined; + if (!workspacePath) { + if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { + workspacePath = workspace.workspaceFolders[0].uri.fsPath; + } + else { + workspacePath = __dirname; + } + } + + if (!this.jediProxyHandlers.has(workspacePath)) { + const jediProxy = new JediProxy(this.extensionRootPath, workspacePath); + const jediProxyHandler = new JediProxyHandler(jediProxy); + this.disposables.push(jediProxy, jediProxyHandler); + this.jediProxyHandlers.set(workspacePath, jediProxyHandler); + } + // tslint:disable-next-line:no-non-null-assertion + return this.jediProxyHandlers.get(workspacePath)! as JediProxyHandler; + } +} diff --git a/src/client/providers/completionProvider.ts b/src/client/providers/completionProvider.ts index 7bbafce3f561..a72e45a679bf 100644 --- a/src/client/providers/completionProvider.ts +++ b/src/client/providers/completionProvider.ts @@ -7,17 +7,13 @@ import * as telemetryContracts from '../common/telemetryContracts'; import { extractSignatureAndDocumentation } from './jediHelpers'; import { EOL } from 'os'; import { PythonSettings } from '../common/configSettings'; -import { SnippetString } from 'vscode'; - -const pythonSettings = PythonSettings.getInstance(); +import { SnippetString, Uri } from 'vscode'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonCompletionItemProvider implements vscode.CompletionItemProvider { - private jediProxyHandler: proxy.JediProxyHandler; - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } - private static parseData(data: proxy.ICompletionResult): vscode.CompletionItem[] { + public constructor(private jediFactory: JediFactory) { } + private static parseData(data: proxy.ICompletionResult, resource: Uri): vscode.CompletionItem[] { if (data && data.items.length > 0) { return data.items.map(item => { const sigAndDocs = extractSignatureAndDocumentation(item); @@ -25,7 +21,7 @@ export class PythonCompletionItemProvider implements vscode.CompletionItemProvid completionItem.kind = item.type; completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1]; completionItem.detail = sigAndDocs[0].split(/\r?\n/).join(''); - if (pythonSettings.autoComplete.addBrackets === true && + if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true && (item.kind === vscode.SymbolKind.Function || item.kind === vscode.SymbolKind.Method)) { completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")"); } @@ -67,10 +63,10 @@ export class PythonCompletionItemProvider implements vscode.CompletionItemProvid }; const timer = new telemetryHelper.Delays(); - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { timer.stop(); telemetryHelper.sendTelemetryEvent(telemetryContracts.IDE.Completion, {}, timer.toMeasures()); - const completions = PythonCompletionItemProvider.parseData(data); + const completions = PythonCompletionItemProvider.parseData(data, document.uri); return completions; }); } diff --git a/src/client/providers/definitionProvider.ts b/src/client/providers/definitionProvider.ts index f678baaf5351..9fd32b58c222 100644 --- a/src/client/providers/definitionProvider.ts +++ b/src/client/providers/definitionProvider.ts @@ -2,17 +2,11 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; -import * as telemetryContracts from "../common/telemetryContracts"; +import * as telemetryContracts from '../common/telemetryContracts'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonDefinitionProvider implements vscode.DefinitionProvider { - private jediProxyHandler: proxy.JediProxyHandler; - public get JediProxy(): proxy.JediProxy { - return this.jediProxyHandler.JediProxy; - } - - public constructor(context: vscode.ExtensionContext) { - this.jediProxyHandler = new proxy.JediProxyHandler(context); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IDefinitionResult, possibleWord: string): vscode.Definition { if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { const definitions = data.definitions.filter(d => d.text === possibleWord); @@ -46,7 +40,7 @@ export class PythonDefinitionProvider implements vscode.DefinitionProvider { cmd.source = document.getText(); } let possibleWord = document.getText(range); - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonDefinitionProvider.parseData(data, possibleWord); }); } diff --git a/src/client/providers/hoverProvider.ts b/src/client/providers/hoverProvider.ts index b6a1abf70faf..376150f298a8 100644 --- a/src/client/providers/hoverProvider.ts +++ b/src/client/providers/hoverProvider.ts @@ -4,13 +4,10 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; import { highlightCode } from './jediHelpers'; import { EOL } from 'os'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonHoverProvider implements vscode.HoverProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IHoverResult, currentWord: string): vscode.Hover { let results = []; let capturedInfo: string[] = []; @@ -96,7 +93,7 @@ export class PythonHoverProvider implements vscode.HoverProvider { cmd.source = document.getText(); } - const data = await this.jediProxyHandler.sendCommand(cmd, token); + const data = await this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token); if (!data || !data.items.length) { return; } diff --git a/src/client/providers/jediProxy.ts b/src/client/providers/jediProxy.ts index 5b9220dfb223..dfbb5ca0c6cf 100644 --- a/src/client/providers/jediProxy.ts +++ b/src/client/providers/jediProxy.ts @@ -5,15 +5,14 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from './../common/configSettings'; import * as logger from './../common/logger'; -import * as telemetryHelper from "../common/telemetry"; +import * as telemetryHelper from '../common/telemetry'; import { execPythonFile, validatePath } from "../common/utils"; import { createDeferred, Deferred } from '../common/helpers'; import { getCustomEnvVars } from '../common/utils'; import { mergeEnvVariables } from '../common/envFileParser'; +import { IPythonSettings, PythonSettings } from '../common/configSettings'; const IS_WINDOWS = /^win/.test(process.platform); -var proc: child_process.ChildProcess; -var pythonSettings = settings.PythonSettings.getInstance(); const pythonVSCodeTypeMappings = new Map(); pythonVSCodeTypeMappings.set('none', vscode.CompletionItemKind.Value); @@ -122,202 +121,250 @@ commandNames.set(CommandType.Hover, "tooltip"); commandNames.set(CommandType.Usages, "usages"); commandNames.set(CommandType.Symbols, "names"); -export class JediProxy extends vscode.Disposable { - public constructor(context: vscode.ExtensionContext) { - super(killProcess); - - context.subscriptions.push(this); - initialize(context.asAbsolutePath(".")); +export class JediProxy implements vscode.Disposable { + private proc: child_process.ChildProcess; + private pythonSettings: PythonSettings; + + public constructor(private extensionRootDir: string, private workspacePath: string) { + this.pythonSettings = PythonSettings.getInstance(vscode.Uri.file(workspacePath)); + this.lastKnownPythonInterpreter = this.pythonSettings.pythonPath + this.pythonSettings.on('change', this.onPythonSettingsChanged.bind(this)); + vscode.workspace.onDidChangeConfiguration(this.onConfigChanged.bind(this)); + this.onConfigChanged(); + this.initialize(extensionRootDir); + } + public dispose() { + this.killProcess(); } - private cmdId: number = 0; public getNextCommandId(): number { return this.cmdId++; } - public sendCommand(cmd: ICommand): Promise { - return sendCommand(cmd); - } -} -// keep track of the directory so we can re-spawn the process -let pythonProcessCWD = ""; -function initialize(dir: string) { - pythonProcessCWD = dir; - spawnProcess(path.join(dir, "pythonFiles")); -} + // keep track of the directory so we can re-spawn the process + private pythonProcessCWD = ""; + private initialize(dir: string) { + this.pythonProcessCWD = dir; + this.spawnProcess(path.join(dir, "pythonFiles")); + } -// Check if settings changes -let lastKnownPythonInterpreter = pythonSettings.pythonPath; -pythonSettings.on('change', onPythonSettingsChanged); + // Check if settings changes + private lastKnownPythonInterpreter: string; + private onPythonSettingsChanged() { + if (this.lastKnownPythonInterpreter === this.pythonSettings.pythonPath) { + return; + } + this.killProcess(); + this.clearPendingRequests(); + this.initialize(this.pythonProcessCWD); + } -function onPythonSettingsChanged() { - if (lastKnownPythonInterpreter === pythonSettings.pythonPath) { - return; + private clearPendingRequests() { + this.commandQueue = []; + this.commands.forEach(item => { + item.deferred.resolve(); + }); + this.commands.clear(); } - killProcess(); - clearPendingRequests(); - initialize(pythonProcessCWD); -} + private previousData = ""; + private commands = new Map>(); + private commandQueue: number[] = []; -function clearPendingRequests() { - commandQueue = []; - commands.forEach(item => { - item.deferred.resolve(); - }); - commands.clear(); -} -var previousData = ""; -var commands = new Map>(); -var commandQueue: number[] = []; - -function killProcess() { - try { - if (proc) { - proc.kill(); + private killProcess() { + try { + if (this.proc) { + this.proc.kill(); + } } + catch (ex) { } + this.proc = null; } - catch (ex) { } - proc = null; -} -function handleError(source: string, errorMessage: string) { - logger.error(source + ' jediProxy', `Error (${source}) ${errorMessage}`); -} + private handleError(source: string, errorMessage: string) { + logger.error(source + ' jediProxy', `Error (${source}) ${errorMessage}`); + } -let spawnRetryAttempts = 0; -function spawnProcess(dir: string) { - try { - let environmentVariables = { 'PYTHONUNBUFFERED': '1' }; - let customEnvironmentVars = getCustomEnvVars(); - if (customEnvironmentVars) { - environmentVariables = mergeEnvVariables(environmentVariables, customEnvironmentVars); - } - environmentVariables = mergeEnvVariables(environmentVariables); - - logger.log('child_process.spawn in jediProxy', 'Value of pythonSettings.pythonPath is :' + pythonSettings.pythonPath); - const args = ["completion.py"]; - if (typeof pythonSettings.jediPath !== 'string' || pythonSettings.jediPath.length === 0) { - if (Array.isArray(pythonSettings.devOptions) && - pythonSettings.devOptions.some(item => item.toUpperCase().trim() === 'USERELEASEAUTOCOMP')) { - // Use standard version of jedi library - args.push('std'); + private spawnRetryAttempts = 0; + private spawnProcess(dir: string) { + try { + let environmentVariables = { 'PYTHONUNBUFFERED': '1' }; + let customEnvironmentVars = getCustomEnvVars(); + if (customEnvironmentVars) { + environmentVariables = mergeEnvVariables(environmentVariables, customEnvironmentVars); + } + environmentVariables = mergeEnvVariables(environmentVariables); + + logger.log('child_process.spawn in jediProxy', 'Value of pythonSettings.pythonPath is :' + this.pythonSettings.pythonPath); + const args = ["completion.py"]; + if (typeof this.pythonSettings.jediPath !== 'string' || this.pythonSettings.jediPath.length === 0) { + if (Array.isArray(this.pythonSettings.devOptions) && + this.pythonSettings.devOptions.some(item => item.toUpperCase().trim() === 'USERELEASEAUTOCOMP')) { + // Use standard version of jedi library + args.push('std'); + } + else { + // Use preview version of jedi library + args.push('preview'); + } } else { - // Use preview version of jedi library - args.push('preview'); + args.push('custom'); + args.push(this.pythonSettings.jediPath); } + if (Array.isArray(this.pythonSettings.autoComplete.preloadModules) && + this.pythonSettings.autoComplete.preloadModules.length > 0) { + var modules = this.pythonSettings.autoComplete.preloadModules.filter(m => m.trim().length > 0).join(','); + args.push(modules); + } + this.proc = child_process.spawn(this.pythonSettings.pythonPath, args, { + cwd: dir, + env: environmentVariables + }); } - else { - args.push('custom'); - args.push(pythonSettings.jediPath); - } - if (Array.isArray(pythonSettings.autoComplete.preloadModules) && - pythonSettings.autoComplete.preloadModules.length > 0) { - var modules = pythonSettings.autoComplete.preloadModules.filter(m => m.trim().length > 0).join(','); - args.push(modules); + catch (ex) { + return this.handleError("spawnProcess", ex.message); } - proc = child_process.spawn(pythonSettings.pythonPath, args, { - cwd: dir, - env: environmentVariables + this.proc.stderr.setEncoding('utf8'); + this.proc.stderr.on("data", (data: string) => { + this.handleError("stderr", data); }); - } - catch (ex) { - return handleError("spawnProcess", ex.message); - } - proc.stderr.setEncoding('utf8'); - proc.stderr.on("data", (data: string) => { - handleError("stderr", data); - }); - proc.on("end", (end) => { - logger.error('spawnProcess.end', "End - " + end); - }); - proc.on("error", error => { - handleError("error", error + ''); - spawnRetryAttempts++; - if (spawnRetryAttempts < 10 && error && error.message && - error.message.indexOf('This socket has been ended by the other party') >= 0) { - spawnProcess(dir); - } - }); - proc.stdout.setEncoding('utf8'); - proc.stdout.on("data", (data: string) => { - //Possible there was an exception in parsing the data returned - //So append the data then parse it - var dataStr = previousData = previousData + data + ""; - var responses: any[]; - try { - responses = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); - previousData = ""; - } - catch (ex) { - // Possible we've only received part of the data, hence don't clear previousData - // Don't log errors when we haven't received the entire response - if (ex.message.indexOf('Unexpected end of input') === -1 && - ex.message.indexOf('Unexpected end of JSON input') === -1 && - ex.message.indexOf('Unexpected token') === -1) { - handleError("stdout", ex.message); + this.proc.on("end", (end) => { + logger.error('spawnProcess.end', "End - " + end); + }); + this.proc.on("error", error => { + this.handleError("error", error + ''); + this.spawnRetryAttempts++; + if (this.spawnRetryAttempts < 10 && error && error.message && + error.message.indexOf('This socket has been ended by the other party') >= 0) { + this.spawnProcess(dir); } - return; - } - - responses.forEach((response) => { - // What's this, can't remember, - // Great example of poorly written code (this whole file is a mess) - // I think this needs to be removed, because this is misspelt, it is argments, 'U' is missing - // And that case is handled further down - // case CommandType.Arguments: { - // Rewrite this mess to use stratergy.. - if (response["argments"]) { - var index = commandQueue.indexOf(cmd.id); - commandQueue.splice(index, 1); - return; + }); + this.proc.stdout.setEncoding('utf8'); + this.proc.stdout.on("data", (data: string) => { + //Possible there was an exception in parsing the data returned + //So append the data then parse it + var dataStr = this.previousData = this.previousData + data + ""; + var responses: any[]; + try { + responses = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); + this.previousData = ""; } - var responseId = response["id"]; - - var cmd = >commands.get(responseId); - if (typeof cmd === "object" && cmd !== null) { - commands.delete(responseId); - var index = commandQueue.indexOf(cmd.id); - commandQueue.splice(index, 1); - - if (cmd.delays && typeof cmd.telemetryEvent === 'string') { - // cmd.delays.stop(); - // telemetryHelper.sendTelemetryEvent(cmd.telemetryEvent, null, cmd.delays.toMeasures()); + catch (ex) { + // Possible we've only received part of the data, hence don't clear previousData + // Don't log errors when we haven't received the entire response + if (ex.message.indexOf('Unexpected end of input') === -1 && + ex.message.indexOf('Unexpected end of JSON input') === -1 && + ex.message.indexOf('Unexpected token') === -1) { + this.handleError("stdout", ex.message); } + return; + } - // Check if this command has expired - if (cmd.token.isCancellationRequested) { - cmd.deferred.resolve(); + responses.forEach((response) => { + // What's this, can't remember, + // Great example of poorly written code (this whole file is a mess) + // I think this needs to be removed, because this is misspelt, it is argments, 'U' is missing + // And that case is handled further down + // case CommandType.Arguments: { + // Rewrite this mess to use stratergy.. + if (response["argments"]) { + var index = this.commandQueue.indexOf(cmd.id); + this.commandQueue.splice(index, 1); return; } + var responseId = response["id"]; + + var cmd = >this.commands.get(responseId); + if (typeof cmd === "object" && cmd !== null) { + this.commands.delete(responseId); + var index = this.commandQueue.indexOf(cmd.id); + this.commandQueue.splice(index, 1); + + if (cmd.delays && typeof cmd.telemetryEvent === 'string') { + // cmd.delays.stop(); + // telemetryHelper.sendTelemetryEvent(cmd.telemetryEvent, null, cmd.delays.toMeasures()); + } - switch (cmd.command) { - case CommandType.Completions: { - let results = response['results']; - results = Array.isArray(results) ? results : []; - results.forEach(item => { - const originalType = item.type; - item.type = getMappedVSCodeType(originalType); - item.kind = getMappedVSCodeSymbol(originalType); - item.rawType = getMappedVSCodeType(originalType); - }); - - let completionResult: ICompletionResult = { - items: results, - requestId: cmd.id - }; - cmd.deferred.resolve(completionResult); - break; + // Check if this command has expired + if (cmd.token.isCancellationRequested) { + cmd.deferred.resolve(); + return; } - case CommandType.Definitions: { - let defs = response['results']; - let defResult: IDefinitionResult = { - requestId: cmd.id, - definitions: [] - }; - if (defs.length > 0) { - defResult.definitions = defs.map(def => { + + switch (cmd.command) { + case CommandType.Completions: { + let results = response['results']; + results = Array.isArray(results) ? results : []; + results.forEach(item => { + const originalType = item.type; + item.type = getMappedVSCodeType(originalType); + item.kind = getMappedVSCodeSymbol(originalType); + item.rawType = getMappedVSCodeType(originalType); + }); + + let completionResult: ICompletionResult = { + items: results, + requestId: cmd.id + }; + cmd.deferred.resolve(completionResult); + break; + } + case CommandType.Definitions: { + let defs = response['results']; + let defResult: IDefinitionResult = { + requestId: cmd.id, + definitions: [] + }; + if (defs.length > 0) { + defResult.definitions = defs.map(def => { + const originalType = def.type as string; + return { + fileName: def.fileName, + text: def.text, + rawType: originalType, + type: getMappedVSCodeType(originalType), + kind: getMappedVSCodeSymbol(originalType), + container: def.container, + range: { + startLine: def.range.start_line, + startColumn: def.range.start_column, + endLine: def.range.end_line, + endColumn: def.range.end_column + } + }; + }); + } + + cmd.deferred.resolve(defResult); + break; + } + case CommandType.Hover: { + let defs = response['results']; + var defResult: IHoverResult = { + requestId: cmd.id, + items: defs.map(def => { + return { + kind: getMappedVSCodeSymbol(def.type), + description: def.description, + signature: def.signature, + docstring: def.docstring, + text: def.text + }; + }) + }; + + cmd.deferred.resolve(defResult); + break; + } + case CommandType.Symbols: { + let defs = response['results']; + defs = Array.isArray(defs) ? defs : []; + var defResults: ISymbolResult = { + requestId: cmd.id, + definitions: [] + }; + defResults.definitions = defs.map(def => { const originalType = def.type as string; return { fileName: def.fileName, @@ -334,250 +381,201 @@ function spawnProcess(dir: string) { } }; }); - } - cmd.deferred.resolve(defResult); - break; - } - case CommandType.Hover: { - let defs = response['results']; - var defResult: IHoverResult = { - requestId: cmd.id, - items: defs.map(def => { - return { - kind: getMappedVSCodeSymbol(def.type), - description: def.description, - signature: def.signature, - docstring: def.docstring, - text: def.text - }; - }) - }; - - cmd.deferred.resolve(defResult); - break; - } - case CommandType.Symbols: { - let defs = response['results']; - defs = Array.isArray(defs) ? defs : []; - var defResults: ISymbolResult = { - requestId: cmd.id, - definitions: [] - }; - defResults.definitions = defs.map(def => { - const originalType = def.type as string; - return { - fileName: def.fileName, - text: def.text, - rawType: originalType, - type: getMappedVSCodeType(originalType), - kind: getMappedVSCodeSymbol(originalType), - container: def.container, - range: { - startLine: def.range.start_line, - startColumn: def.range.start_column, - endLine: def.range.end_line, - endColumn: def.range.end_column + cmd.deferred.resolve(defResults); + break; + } + case CommandType.Usages: { + let defs = response['results']; + defs = Array.isArray(defs) ? defs : []; + var refResult: IReferenceResult = { + requestId: cmd.id, + references: defs.map(item => { + return { + columnIndex: item.column, + fileName: item.fileName, + lineIndex: item.line - 1, + moduleName: item.moduleName, + name: item.name + }; } + ) }; - }); - - cmd.deferred.resolve(defResults); - break; - } - case CommandType.Usages: { - let defs = response['results']; - defs = Array.isArray(defs) ? defs : []; - var refResult: IReferenceResult = { - requestId: cmd.id, - references: defs.map(item => { - return { - columnIndex: item.column, - fileName: item.fileName, - lineIndex: item.line - 1, - moduleName: item.moduleName, - name: item.name - }; - } - ) - }; - cmd.deferred.resolve(refResult); - break; - } - case CommandType.Arguments: { - let defs = response["results"]; - cmd.deferred.resolve({ - requestId: cmd.id, - definitions: defs - }); - break; + cmd.deferred.resolve(refResult); + break; + } + case CommandType.Arguments: { + let defs = response["results"]; + cmd.deferred.resolve({ + requestId: cmd.id, + definitions: defs + }); + break; + } } } - } - //Ok, check if too many pending requets - if (commandQueue.length > 10) { - var items = commandQueue.splice(0, commandQueue.length - 10); - items.forEach(id => { - if (commands.has(id)) { - const cmd = commands.get(id); - try { - cmd.deferred.resolve(null); - } - catch (ex) { + //Ok, check if too many pending requets + if (this.commandQueue.length > 10) { + var items = this.commandQueue.splice(0, this.commandQueue.length - 10); + items.forEach(id => { + if (this.commands.has(id)) { + const cmd = this.commands.get(id); + try { + cmd.deferred.resolve(null); + } + catch (ex) { + } + this.commands.delete(id); } - commands.delete(id); - } - }); - } + }); + } + }); }); - }); -} - -function sendCommand(cmd: ICommand): Promise { - if (!proc) { - return Promise.reject(new Error("Python proc not initialized")); } - var executionCmd = >cmd; - var payload = createPayload(executionCmd); - executionCmd.deferred = createDeferred(); - // if (typeof executionCmd.telemetryEvent === 'string') { - // executionCmd.delays = new telemetryHelper.Delays(); - // } - try { - proc.stdin.write(JSON.stringify(payload) + "\n"); - commands.set(executionCmd.id, executionCmd); - commandQueue.push(executionCmd.id); - } - catch (ex) { - console.error(ex); - //If 'This socket is closed.' that means process didn't start at all (at least not properly) - if (ex.message === "This socket is closed.") { - killProcess(); + public sendCommand(cmd: ICommand): Promise { + if (!this.proc) { + return Promise.reject(new Error("Python proc not initialized")); } - else { - handleError("sendCommand", ex.message); + var executionCmd = >cmd; + var payload = this.createPayload(executionCmd); + executionCmd.deferred = createDeferred(); + // if (typeof executionCmd.telemetryEvent === 'string') { + // executionCmd.delays = new telemetryHelper.Delays(); + // } + try { + this.proc.stdin.write(JSON.stringify(payload) + "\n"); + this.commands.set(executionCmd.id, executionCmd); + this.commandQueue.push(executionCmd.id); } - return Promise.reject(ex); - } - return executionCmd.deferred.promise; -} - -function createPayload(cmd: IExecutionCommand): any { - var payload = { - id: cmd.id, - prefix: "", - lookup: commandNames.get(cmd.command), - path: cmd.fileName, - source: cmd.source, - line: cmd.lineIndex, - column: cmd.columnIndex, - config: getConfig() - }; - - if (cmd.command === CommandType.Symbols) { - delete payload.column; - delete payload.line; - } - - return payload; -} + catch (ex) { + console.error(ex); + //If 'This socket is closed.' that means process didn't start at all (at least not properly) + if (ex.message === "This socket is closed.") { -let lastKnownPythonPath: string = null; -let additionalAutoCopletePaths: string[] = []; -function getPathFromPythonCommand(args: string[]): Promise { - return execPythonFile(pythonSettings.pythonPath, args, vscode.workspace.rootPath).then(stdout => { - if (stdout.length === 0) { - return ""; + this.killProcess(); + } + else { + this.handleError("sendCommand", ex.message); + } + return Promise.reject(ex); } - let lines = stdout.split(/\r?\n/g).filter(line => line.length > 0); - return validatePath(lines[0]); - }).catch(() => { - return ""; - }); -} -vscode.workspace.onDidChangeConfiguration(onConfigChanged); -onConfigChanged(); -function onConfigChanged() { - // We're only interested in changes to the python path - if (lastKnownPythonPath === pythonSettings.pythonPath) { - return; + return executionCmd.deferred.promise; } - lastKnownPythonPath = pythonSettings.pythonPath; - let filePaths = [ - // Sysprefix - getPathFromPythonCommand(["-c", "import sys;print(sys.prefix)"]), - // exeucutable path - getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]), - // Python specific site packages - getPathFromPythonCommand(["-c", "from distutils.sysconfig import get_python_lib; print(get_python_lib())"]), - // Python global site packages, as a fallback in case user hasn't installed them in custom environment - getPathFromPythonCommand(["-m", "site", "--user-site"]), - ]; - - let PYTHONPATH: string = process.env['PYTHONPATH']; - if (typeof PYTHONPATH !== 'string') { - PYTHONPATH = ''; - } - let customEnvironmentVars = getCustomEnvVars(); - if (customEnvironmentVars && customEnvironmentVars['PYTHONPATH']) { - let PYTHONPATHFromEnvFile = customEnvironmentVars['PYTHONPATH'] as string; - if (!path.isAbsolute(PYTHONPATHFromEnvFile) && typeof vscode.workspace.rootPath === 'string') { - PYTHONPATHFromEnvFile = path.resolve(vscode.workspace.rootPath, PYTHONPATHFromEnvFile); + private createPayload(cmd: IExecutionCommand): any { + var payload = { + id: cmd.id, + prefix: "", + lookup: commandNames.get(cmd.command), + path: cmd.fileName, + source: cmd.source, + line: cmd.lineIndex, + column: cmd.columnIndex, + config: this.getConfig() + }; + + if (cmd.command === CommandType.Symbols) { + delete payload.column; + delete payload.line; } - PYTHONPATH += (PYTHONPATH.length > 0 ? + path.delimiter : '') + PYTHONPATHFromEnvFile; + + return payload; } - if (typeof PYTHONPATH === 'string' && PYTHONPATH.length > 0) { - filePaths.push(Promise.resolve(PYTHONPATH.trim())); + + private lastKnownPythonPath: string = null; + private additionalAutoCopletePaths: string[] = []; + private getPathFromPythonCommand(args: string[]): Promise { + return execPythonFile(this.pythonSettings.pythonPath, args, this.workspacePath).then(stdout => { + if (stdout.length === 0) { + return ""; + } + let lines = stdout.split(/\r?\n/g).filter(line => line.length > 0); + return validatePath(lines[0]); + }).catch(() => { + return ""; + }); } - Promise.all(filePaths).then(paths => { - // Last item return a path, we need only the folder - if (paths[1].length > 0) { - paths[1] = path.dirname(paths[1]); + private onConfigChanged() { + // We're only interested in changes to the python path + if (this.lastKnownPythonPath === this.pythonSettings.pythonPath) { + return; } - // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages) - // This is returned by "from distutils.sysconfig import get_python_lib; print(get_python_lib())" - if (IS_WINDOWS && paths[2].length > 0) { - paths.splice(3, 0, path.join(paths[2], "..")); + this.lastKnownPythonPath = this.pythonSettings.pythonPath; + let filePaths = [ + // Sysprefix + this.getPathFromPythonCommand(["-c", "import sys;print(sys.prefix)"]), + // exeucutable path + this.getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]), + // Python specific site packages + this.getPathFromPythonCommand(["-c", "from distutils.sysconfig import get_python_lib; print(get_python_lib())"]), + // Python global site packages, as a fallback in case user hasn't installed them in custom environment + this.getPathFromPythonCommand(["-m", "site", "--user-site"]), + ]; + + let PYTHONPATH: string = process.env['PYTHONPATH']; + if (typeof PYTHONPATH !== 'string') { + PYTHONPATH = ''; } - additionalAutoCopletePaths = paths.filter(p => p.length > 0); - }); -} - -function getConfig() { - // Add support for paths relative to workspace - let extraPaths = pythonSettings.autoComplete.extraPaths.map(extraPath => { - if (path.isAbsolute(extraPath)) { - return extraPath; + let customEnvironmentVars = getCustomEnvVars(); + if (customEnvironmentVars && customEnvironmentVars['PYTHONPATH']) { + let PYTHONPATHFromEnvFile = customEnvironmentVars['PYTHONPATH'] as string; + if (!path.isAbsolute(PYTHONPATHFromEnvFile) && this.workspacePath === 'string') { + PYTHONPATHFromEnvFile = path.resolve(this.workspacePath, PYTHONPATHFromEnvFile); + } + PYTHONPATH += (PYTHONPATH.length > 0 ? + path.delimiter : '') + PYTHONPATHFromEnvFile; } - if (typeof vscode.workspace.rootPath !== 'string') { - return ''; + if (typeof PYTHONPATH === 'string' && PYTHONPATH.length > 0) { + filePaths.push(Promise.resolve(PYTHONPATH.trim())); } - return path.join(vscode.workspace.rootPath, extraPath); - }); + Promise.all(filePaths).then(paths => { + // Last item return a path, we need only the folder + if (paths[1].length > 0) { + paths[1] = path.dirname(paths[1]); + } - // Always add workspace path into extra paths - if (typeof vscode.workspace.rootPath === 'string') { - extraPaths.unshift(vscode.workspace.rootPath); + // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages) + // This is returned by "from distutils.sysconfig import get_python_lib; print(get_python_lib())" + if (IS_WINDOWS && paths[2].length > 0) { + paths.splice(3, 0, path.join(paths[2], "..")); + } + this.additionalAutoCopletePaths = paths.filter(p => p.length > 0); + }); } - let distinctExtraPaths = extraPaths.concat(additionalAutoCopletePaths) - .filter(value => value.length > 0) - .filter((value, index, self) => self.indexOf(value) === index); - - return { - extraPaths: distinctExtraPaths, - useSnippets: false, - caseInsensitiveCompletion: true, - showDescriptions: true, - fuzzyMatcher: true - }; -} + private getConfig() { + // Add support for paths relative to workspace + let extraPaths = this.pythonSettings.autoComplete.extraPaths.map(extraPath => { + if (path.isAbsolute(extraPath)) { + return extraPath; + } + if (typeof this.workspacePath !== 'string') { + return ''; + } + return path.join(this.workspacePath, extraPath); + }); + + // Always add workspace path into extra paths + if (typeof this.workspacePath === 'string') { + extraPaths.unshift(this.workspacePath); + } + let distinctExtraPaths = extraPaths.concat(this.additionalAutoCopletePaths) + .filter(value => value.length > 0) + .filter((value, index, self) => self.indexOf(value) === index); + + return { + extraPaths: distinctExtraPaths, + useSnippets: false, + caseInsensitiveCompletion: true, + showDescriptions: true, + fuzzyMatcher: true + }; + } +} export interface ICommand { telemetryEvent?: string; command: CommandType; @@ -675,18 +673,18 @@ export interface IHoverItem { signature: string; } -export class JediProxyHandler { - private jediProxy: JediProxy; +export class JediProxyHandler implements vscode.Disposable { private cancellationTokenSource: vscode.CancellationTokenSource; public get JediProxy(): JediProxy { return this.jediProxy; } - public constructor(context: vscode.ExtensionContext, jediProxy: JediProxy = null) { - this.jediProxy = jediProxy ? jediProxy : new JediProxy(context); + public constructor(private jediProxy: JediProxy = null) { + } + public dispose() { + this.jediProxy.dispose(); } - public sendCommand(cmd: ICommand, token?: vscode.CancellationToken): Promise { var executionCmd = >cmd; executionCmd.id = executionCmd.id || this.jediProxy.getNextCommandId(); diff --git a/src/client/providers/objectDefinitionProvider.ts b/src/client/providers/objectDefinitionProvider.ts index f7ffea73dfb4..b455c4ba1933 100644 --- a/src/client/providers/objectDefinitionProvider.ts +++ b/src/client/providers/objectDefinitionProvider.ts @@ -2,16 +2,18 @@ import * as vscode from 'vscode'; import * as defProvider from './definitionProvider'; +import { JediFactory } from '../languageServices/jediProxyFactory'; -export function activateGoToObjectDefinitionProvider(context: vscode.ExtensionContext): vscode.Disposable { - let def = new PythonObjectDefinitionProvider(context); - return vscode.commands.registerCommand("python.goToPythonObject", () => def.goToObjectDefinition()); +export function activateGoToObjectDefinitionProvider(jediFactory: JediFactory): vscode.Disposable[] { + const def = new PythonObjectDefinitionProvider(jediFactory); + const commandRegistration = vscode.commands.registerCommand("python.goToPythonObject", () => def.goToObjectDefinition()); + return [def, commandRegistration] as vscode.Disposable[]; } export class PythonObjectDefinitionProvider { private readonly _defProvider: defProvider.PythonDefinitionProvider; - public constructor(context: vscode.ExtensionContext) { - this._defProvider = new defProvider.PythonDefinitionProvider(context); + public constructor(jediFactory: JediFactory) { + this._defProvider = new defProvider.PythonDefinitionProvider(jediFactory); } public async goToObjectDefinition() { diff --git a/src/client/providers/referenceProvider.ts b/src/client/providers/referenceProvider.ts index d25eefb8823a..c8f2031cf5ed 100644 --- a/src/client/providers/referenceProvider.ts +++ b/src/client/providers/referenceProvider.ts @@ -2,14 +2,11 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonReferenceProvider implements vscode.ReferenceProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IReferenceResult): vscode.Location[] { if (data && data.references.length > 0) { var references = data.references.filter(ref => { @@ -52,7 +49,7 @@ export class PythonReferenceProvider implements vscode.ReferenceProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonReferenceProvider.parseData(data); }); } diff --git a/src/client/providers/signatureProvider.ts b/src/client/providers/signatureProvider.ts index 7d0794444578..5ed411ed47b6 100644 --- a/src/client/providers/signatureProvider.ts +++ b/src/client/providers/signatureProvider.ts @@ -1,8 +1,9 @@ "use strict"; -import * as vscode from "vscode"; +import * as vscode from 'vscode'; +import * as proxy from './jediProxy'; import { TextDocument, Position, CancellationToken, SignatureHelp } from "vscode"; -import * as proxy from "./jediProxy"; +import { JediFactory } from '../languageServices/jediProxyFactory'; const DOCSTRING_PARAM_PATTERNS = [ "\\s*:type\\s*PARAMNAME:\\s*([^\\n, ]+)", // Sphinx @@ -43,11 +44,7 @@ function extractParamDocString(paramName: string, docString: string): string { return paramDocString.trim(); } export class PythonSignatureProvider implements vscode.SignatureHelpProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IArgumentsResult): vscode.SignatureHelp { if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { let signature = new SignatureHelp(); @@ -86,7 +83,7 @@ export class PythonSignatureProvider implements vscode.SignatureHelpProvider { lineIndex: position.line, source: document.getText() }; - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonSignatureProvider.parseData(data); }); } diff --git a/src/client/providers/symbolProvider.ts b/src/client/providers/symbolProvider.ts index 4e89b27f8426..78181b2a62e8 100644 --- a/src/client/providers/symbolProvider.ts +++ b/src/client/providers/symbolProvider.ts @@ -2,13 +2,10 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(document: vscode.TextDocument, data: proxy.ISymbolResult): vscode.SymbolInformation[] { if (data) { let symbols = data.definitions.filter(sym => sym.fileName === document.fileName); @@ -38,7 +35,7 @@ export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonSymbolProvider.parseData(document, data); }); } @@ -56,7 +53,7 @@ export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommandNonCancellableCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommandNonCancellableCommand(cmd, token).then(data => { return PythonSymbolProvider.parseData(document, data); }); } diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index a1e3feeb9491..ee203a46835f 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { execPythonFile } from '../../client/common/utils'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const fileOne = path.join(autoCompPath, 'one.py'); const fileImport = path.join(autoCompPath, 'imp.py'); @@ -27,7 +28,7 @@ suite('Autocomplete', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - let version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + let version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/autocomplete/pep484.test.ts b/src/test/autocomplete/pep484.test.ts index 546c26d1253d..1ad220ea929c 100644 --- a/src/test/autocomplete/pep484.test.ts +++ b/src/test/autocomplete/pep484.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const filePep484 = path.join(autoCompPath, 'pep484.py'); @@ -21,7 +22,7 @@ suite('Autocomplete PEP 484', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + const version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/autocomplete/pep526.test.ts b/src/test/autocomplete/pep526.test.ts index b7c4b37de08a..6d330195ccdf 100644 --- a/src/test/autocomplete/pep526.test.ts +++ b/src/test/autocomplete/pep526.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const filePep526 = path.join(autoCompPath, 'pep526.py'); @@ -21,7 +22,7 @@ suite('Autocomplete PEP 526', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + const version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 826ff1b9633d..3b2e7a1c85eb 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -13,6 +13,7 @@ import * as path from 'path'; import { initialize, IS_TRAVIS } from './../initialize'; import { PythonSettings } from '../../client/common/configSettings'; import { SystemVariables } from '../../client/common/systemVariables'; +import { rootWorkspaceUri } from '../common'; const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test');