Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,7 @@
},
"python.linting.flake8CategorySeverity.F": {
"type": "string",
"default": "Error",
"default": "Warning",
"description": "Severity of Flake8 message type 'F'.",
"enum": [
"Hint",
Expand Down Expand Up @@ -1859,4 +1859,4 @@
"publisherDisplayName": "Microsoft",
"publisherId": "998b010b-e2af-44a5-a6cd-0b5fd3b9b6f8"
}
}
}
7 changes: 5 additions & 2 deletions src/client/common/configSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,12 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
W: vscode.DiagnosticSeverity.Warning
},
flake8CategorySeverity: {
F: vscode.DiagnosticSeverity.Error,
E: vscode.DiagnosticSeverity.Error,
W: vscode.DiagnosticSeverity.Warning
W: vscode.DiagnosticSeverity.Warning,
// Per http://flake8.pycqa.org/en/latest/glossary.html#term-error-code
// 'F' does not mean 'fatal as in PyLint but rather 'pyflakes' such as
// unused imports, variables, etc.
F: vscode.DiagnosticSeverity.Warning
},
mypyCategorySeverity: {
error: vscode.DiagnosticSeverity.Error,
Expand Down
7 changes: 7 additions & 0 deletions src/client/common/platform/fileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,11 @@ export class FileSystem implements IFileSystem {
return fs.appendFileSync(filename, data, optionsOrEncoding);
}

public getRealPathAsync(filePath: string): Promise<string> {
return new Promise<string>(resolve => {
fs.realpath(filePath, (err, realPath) => {
resolve(err ? filePath : realPath);
});
});
}
}
1 change: 1 addition & 0 deletions src/client/common/platform/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ export interface IFileSystem {
appendFileSync(filename: string, data: {}, options?: { encoding?: string; mode?: number; flag?: string; }): void;
// tslint:disable-next-line:unified-signatures
appendFileSync(filename: string, data: {}, options?: { encoding?: string; mode?: string; flag?: string; }): void;
getRealPathAsync(path: string): Promise<string>;
}
46 changes: 34 additions & 12 deletions src/client/interpreter/configuration/interpreterSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,47 @@ import { ConfigurationTarget, Disposable, QuickPickItem, QuickPickOptions, Uri }
import { IApplicationShell, ICommandManager, IDocumentManager, IWorkspaceService } from '../../common/application/types';
import * as settings from '../../common/configSettings';
import { Commands } from '../../common/constants';
import { IFileSystem } from '../../common/platform/types';
import { IServiceContainer } from '../../ioc/types';
import { IInterpreterService, IShebangCodeLensProvider, PythonInterpreter, WorkspacePythonPath } from '../contracts';
import { IInterpreterSelector, IPythonPathUpdaterServiceManager } from './types';

interface IInterpreterQuickPickItem extends QuickPickItem {
export interface IInterpreterQuickPickItem extends QuickPickItem {
path: string;
}

@injectable()
export class InterpreterSelector implements IInterpreterSelector {
private disposables: Disposable[] = [];
private pythonPathUpdaterService: IPythonPathUpdaterServiceManager;
private readonly interpreterManager: IInterpreterService;
private readonly workspaceService: IWorkspaceService;
private readonly applicationShell: IApplicationShell;
private readonly documentManager: IDocumentManager;
private readonly fileSystem: IFileSystem;

constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
this.interpreterManager = serviceContainer.get<IInterpreterService>(IInterpreterService);
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
this.applicationShell = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
this.documentManager = this.serviceContainer.get<IDocumentManager>(IDocumentManager);
this.fileSystem = this.serviceContainer.get<IFileSystem>(IFileSystem);

const commandManager = serviceContainer.get<ICommandManager>(ICommandManager);
this.disposables.push(commandManager.registerCommand(Commands.Set_Interpreter, this.setInterpreter.bind(this)));
this.disposables.push(commandManager.registerCommand(Commands.Set_ShebangInterpreter, this.setShebangInterpreter.bind(this)));
this.pythonPathUpdaterService = serviceContainer.get<IPythonPathUpdaterServiceManager>(IPythonPathUpdaterServiceManager);
}
public dispose() {
this.disposables.forEach(disposable => disposable.dispose());
}

public async getSuggestions(resourceUri?: Uri) {
let interpreters = await this.interpreterManager.getInterpreters(resourceUri);
interpreters = await this.removeDuplicates(interpreters);
// tslint:disable-next-line:no-non-null-assertion
interpreters.sort((a, b) => a.displayName! > b.displayName! ? 1 : -1);
return Promise.all(interpreters.map(item => this.suggestionToQuickPickItem(item, resourceUri)));
}

private async getWorkspaceToSetPythonPath(): Promise<WorkspacePythonPath | undefined> {
if (!Array.isArray(this.workspaceService.workspaceFolders) || this.workspaceService.workspaceFolders.length === 0) {
return undefined;
Expand All @@ -47,6 +58,7 @@ export class InterpreterSelector implements IInterpreterSelector {
const workspaceFolder = await applicationShell.showWorkspaceFolderPick({ placeHolder: 'Select a workspace' });
return workspaceFolder ? { folderUri: workspaceFolder.uri, configTarget: ConfigurationTarget.WorkspaceFolder } : undefined;
}

private async suggestionToQuickPickItem(suggestion: PythonInterpreter, workspaceUri?: Uri): Promise<IInterpreterQuickPickItem> {
let detail = suggestion.path;
if (workspaceUri && suggestion.path.startsWith(workspaceUri.fsPath)) {
Expand All @@ -62,11 +74,19 @@ export class InterpreterSelector implements IInterpreterSelector {
};
}

private async getSuggestions(resourceUri?: Uri) {
const interpreters = await this.interpreterManager.getInterpreters(resourceUri);
// tslint:disable-next-line:no-non-null-assertion
interpreters.sort((a, b) => a.displayName! > b.displayName! ? 1 : -1);
return Promise.all(interpreters.map(item => this.suggestionToQuickPickItem(item, resourceUri)));
private async removeDuplicates(interpreters: PythonInterpreter[]): Promise<PythonInterpreter[]> {
const result: PythonInterpreter[] = [];
await Promise.all(interpreters.filter(async x => {
x.realPath = await this.fileSystem.getRealPathAsync(x.path);
return true;
}));
interpreters.forEach(x => {
if (result.findIndex(a => a.displayName === x.displayName
&& a.type === x.type && this.fileSystem.arePathsSame(a.realPath!, x.realPath!)) < 0) {
result.push(x);
}
});
return result;
}

private async setInterpreter() {
Expand Down Expand Up @@ -95,7 +115,8 @@ export class InterpreterSelector implements IInterpreterSelector {

const selection = await this.applicationShell.showQuickPick(suggestions, quickPickOptions);
if (selection !== undefined) {
await this.pythonPathUpdaterService.updatePythonPath(selection.path, configTarget, 'ui', wkspace);
const pythonPathUpdaterService = this.serviceContainer.get<IPythonPathUpdaterServiceManager>(IPythonPathUpdaterServiceManager);
await pythonPathUpdaterService.updatePythonPath(selection.path, configTarget, 'ui', wkspace);
}
}

Expand All @@ -110,16 +131,17 @@ export class InterpreterSelector implements IInterpreterSelector {
const workspaceFolder = this.workspaceService.getWorkspaceFolder(this.documentManager.activeTextEditor!.document.uri);
const isWorkspaceChange = Array.isArray(this.workspaceService.workspaceFolders) && this.workspaceService.workspaceFolders.length === 1;

const pythonPathUpdaterService = this.serviceContainer.get<IPythonPathUpdaterServiceManager>(IPythonPathUpdaterServiceManager);
if (isGlobalChange) {
await this.pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.Global, 'shebang');
await pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.Global, 'shebang');
return;
}

if (isWorkspaceChange || !workspaceFolder) {
await this.pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.Workspace, 'shebang', this.workspaceService.workspaceFolders![0].uri);
await pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.Workspace, 'shebang', this.workspaceService.workspaceFolders![0].uri);
return;
}

await this.pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.WorkspaceFolder, 'shebang', workspaceFolder.uri);
await pythonPathUpdaterService.updatePythonPath(shebang, ConfigurationTarget.WorkspaceFolder, 'shebang', workspaceFolder.uri);
}
}
4 changes: 2 additions & 2 deletions src/client/interpreter/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ export interface ICondaService {
export enum InterpreterType {
Unknown = 1,
Conda = 2,
VirtualEnv = 4,
VEnv = 8
VirtualEnv = 4
}

export type PythonInterpreter = {
Expand All @@ -67,6 +66,7 @@ export type PythonInterpreter = {
envName?: string;
envPath?: string;
cachedEntry?: boolean;
realPath?: string;
};

export type WorkspacePythonPath = {
Expand Down
6 changes: 2 additions & 4 deletions src/client/interpreter/display/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ export class InterpreterDisplay implements IInterpreterDisplay {
}
this.statusBar.show();
}
private async getVirtualEnvironmentName(pythonPath: string) {
return this.virtualEnvMgr
.detect(pythonPath)
.then(env => env ? env.name : '');
private async getVirtualEnvironmentName(pythonPath: string): Promise<string> {
return this.virtualEnvMgr.getEnvironmentName(pythonPath);
}
}
5 changes: 2 additions & 3 deletions src/client/interpreter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,13 @@ export class InterpreterManager implements Disposable, IInterpreterService {
const pythonExecutableName = path.basename(fullyQualifiedPath);
const versionInfo = await this.serviceContainer.get<IInterpreterVersionService>(IInterpreterVersionService).getVersion(fullyQualifiedPath, pythonExecutableName);
const virtualEnvManager = this.serviceContainer.get<IVirtualEnvironmentManager>(IVirtualEnvironmentManager);
const virtualEnv = await virtualEnvManager.detect(fullyQualifiedPath);
const virtualEnvName = virtualEnv ? virtualEnv.name : '';
const virtualEnvName = await virtualEnvManager.getEnvironmentName(fullyQualifiedPath);
const dislayNameSuffix = virtualEnvName.length > 0 ? ` (${virtualEnvName})` : '';
const displayName = `${versionInfo}${dislayNameSuffix}`;
return {
displayName,
path: fullyQualifiedPath,
type: virtualEnv ? virtualEnv.type : InterpreterType.Unknown,
type: virtualEnvName.length > 0 ? InterpreterType.VirtualEnv : InterpreterType.Unknown,
version: versionInfo
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ export class BaseVirtualEnvService extends CacheableLocatorService {
private async getVirtualEnvDetails(interpreter: string): Promise<PythonInterpreter> {
return Promise.all([
this.versionProvider.getVersion(interpreter, path.basename(interpreter)),
this.virtualEnvMgr.detect(interpreter)
this.virtualEnvMgr.getEnvironmentName(interpreter)
])
.then(([displayName, virtualEnv]) => {
const virtualEnvSuffix = virtualEnv ? virtualEnv.name : this.getVirtualEnvironmentRootDirectory(interpreter);
.then(([displayName, virtualEnvName]) => {
const virtualEnvSuffix = virtualEnvName.length ? virtualEnvName : this.getVirtualEnvironmentRootDirectory(interpreter);
return {
displayName: `${displayName} (${virtualEnvSuffix})`.trim(),
path: interpreter,
type: virtualEnv ? virtualEnv.type : InterpreterType.Unknown
type: virtualEnvName.length > 0 ? InterpreterType.VirtualEnv : InterpreterType.Unknown
};
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ export class CurrentPathService extends CacheableLocatorService {
private async getInterpreterDetails(interpreter: string): Promise<PythonInterpreter> {
return Promise.all([
this.versionProvider.getVersion(interpreter, path.basename(interpreter)),
this.virtualEnvMgr.detect(interpreter)
this.virtualEnvMgr.getEnvironmentName(interpreter)
]).
then(([displayName, virtualEnv]) => {
displayName += virtualEnv ? ` (${virtualEnv.name})` : '';
then(([displayName, virtualEnvName]) => {
displayName += virtualEnvName.length > 0 ? ` (${virtualEnvName})` : '';
return {
displayName,
path: interpreter,
type: virtualEnv ? virtualEnv.type : InterpreterType.Unknown
type: virtualEnvName ? InterpreterType.VirtualEnv : InterpreterType.Unknown
};
});
}
Expand Down
7 changes: 1 addition & 6 deletions src/client/interpreter/serviceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,14 @@ import { getKnownSearchPathsForInterpreters, KnownPathsService } from './locator
import { WindowsRegistryService } from './locators/services/windowsRegistryService';
import { WorkspaceVirtualEnvironmentsSearchPathProvider, WorkspaceVirtualEnvService } from './locators/services/workspaceVirtualEnvService';
import { VirtualEnvironmentManager } from './virtualEnvs/index';
import { IVirtualEnvironmentIdentifier, IVirtualEnvironmentManager } from './virtualEnvs/types';
import { VEnv } from './virtualEnvs/venv';
import { VirtualEnv } from './virtualEnvs/virtualEnv';
import { IVirtualEnvironmentManager } from './virtualEnvs/types';

export function registerTypes(serviceManager: IServiceManager) {
serviceManager.addSingletonInstance<string[]>(IKnownSearchPathsForInterpreters, getKnownSearchPathsForInterpreters());
serviceManager.addSingleton<IVirtualEnvironmentsSearchPathProvider>(IVirtualEnvironmentsSearchPathProvider, GlobalVirtualEnvironmentsSearchPathProvider, 'global');
serviceManager.addSingleton<IVirtualEnvironmentsSearchPathProvider>(IVirtualEnvironmentsSearchPathProvider, WorkspaceVirtualEnvironmentsSearchPathProvider, 'workspace');

serviceManager.addSingleton<ICondaService>(ICondaService, CondaService);
serviceManager.addSingleton<IVirtualEnvironmentIdentifier>(IVirtualEnvironmentIdentifier, VirtualEnv);
serviceManager.addSingleton<IVirtualEnvironmentIdentifier>(IVirtualEnvironmentIdentifier, VEnv);

serviceManager.addSingleton<IVirtualEnvironmentManager>(IVirtualEnvironmentManager, VirtualEnvironmentManager);

serviceManager.addSingleton<IInterpreterVersionService>(IInterpreterVersionService, InterpreterVersionService);
Expand Down
35 changes: 20 additions & 15 deletions src/client/interpreter/virtualEnvs/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { injectable, multiInject } from 'inversify';
import { IVirtualEnvironmentIdentifier, IVirtualEnvironmentManager } from './types';
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { inject, injectable } from 'inversify';
import { IProcessService } from '../../common/process/types';
import { IServiceContainer } from '../../ioc/types';
import { IVirtualEnvironmentManager } from './types';

@injectable()
export class VirtualEnvironmentManager implements IVirtualEnvironmentManager {
constructor(@multiInject(IVirtualEnvironmentIdentifier) private envs: IVirtualEnvironmentIdentifier[]) {
private processService: IProcessService;
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
this.processService = serviceContainer.get<IProcessService>(IProcessService);
}
public detect(pythonPath: string): Promise<IVirtualEnvironmentIdentifier | undefined> {
const promises = this.envs
.map(item => item.detect(pythonPath)
.then(result => {
return { env: item, result };
}));

return Promise.all(promises)
.then(results => {
const env = results.find(items => items.result === true);
return env ? env.env : undefined;
});
public async getEnvironmentName(pythonPath: string): Promise<string> {
// https://stackoverflow.com/questions/1871549/determine-if-python-is-running-inside-virtualenv
const output = await this.processService.exec(pythonPath, ['-c', 'import sys;print(hasattr(sys, "real_prefix"))']);
if (output.stdout.length > 0) {
const result = output.stdout.trim();
if (result === 'True') {
return 'virtualenv';
}
}
return '';
}
}
10 changes: 1 addition & 9 deletions src/client/interpreter/virtualEnvs/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { InterpreterType } from '../contracts';
export const IVirtualEnvironmentIdentifier = Symbol('IVirtualEnvironment');

export interface IVirtualEnvironmentIdentifier {
readonly name: string;
readonly type: InterpreterType.VEnv | InterpreterType.VirtualEnv;
detect(pythonPath: string): Promise<boolean>;
}
export const IVirtualEnvironmentManager = Symbol('VirtualEnvironmentManager');
export interface IVirtualEnvironmentManager {
detect(pythonPath: string): Promise<IVirtualEnvironmentIdentifier | undefined>;
getEnvironmentName(pythonPath: string): Promise<string>;
}
27 changes: 0 additions & 27 deletions src/client/interpreter/virtualEnvs/venv.ts

This file was deleted.

27 changes: 0 additions & 27 deletions src/client/interpreter/virtualEnvs/virtualEnv.ts

This file was deleted.

Loading