Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pyenv and direnv #854

Merged
merged 46 commits into from
Feb 22, 2018
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
6546892
Fix pylint search
Feb 16, 2018
76af122
Handle quote escapes in strings
Feb 16, 2018
5d4d022
Escapes in strings
Feb 16, 2018
29edac2
CR feedback
Feb 16, 2018
0492aab
Missing pip
Feb 17, 2018
d0a449f
Test
Feb 17, 2018
55197c3
Tests
Feb 17, 2018
f6a0123
Tests
Feb 17, 2018
d07d3ef
Mac python path
Feb 17, 2018
2b0cc92
Tests
Feb 17, 2018
3867ec2
Tests
Feb 17, 2018
85fc4ef
Test
Feb 17, 2018
00887a4
"Go To Python object" doesn't work
Feb 17, 2018
f89dd96
Proper detection and type population in virtual env
Feb 17, 2018
32394e2
Test fixes
Feb 17, 2018
2e9c039
Simplify venv check
Feb 17, 2018
ec563c7
Remove duplicates
Feb 17, 2018
2ad4475
Test
Feb 18, 2018
1ee0be2
Discover pylintrc better + tests
Feb 19, 2018
44665b9
Merge branch 'master' into 804
Feb 19, 2018
29d3925
Merge branch 'master' of https://github.com/Microsoft/vscode-python i…
Feb 19, 2018
04c5733
Undo change
Feb 19, 2018
37328a5
CR feedback
Feb 20, 2018
55ff4e5
Set interprereter before checking install
Feb 20, 2018
9a0cfa8
Merge master
Feb 20, 2018
023af49
Merge master
Feb 20, 2018
436e5a9
Fix typo and path compare on Windows
Feb 20, 2018
a53d815
Rename method
Feb 20, 2018
7f3e4fa
#815 - 'F' in flake8 means warning
Feb 20, 2018
da034f4
730 - same folder temp
Feb 20, 2018
71a508d
Properly resolve ~
Feb 20, 2018
4422811
Merge branch 'master' of https://github.com/Microsoft/vscode-python i…
Feb 20, 2018
6d91912
Test
Feb 21, 2018
ff6f6d2
Test
Feb 21, 2018
7446afb
Merge branch 'master' of https://github.com/Microsoft/vscode-python i…
Feb 21, 2018
a7b2854
Fix dot spacing
Feb 21, 2018
a9457f2
Merge branch 'master' of https://github.com/Microsoft/vscode-python i…
Feb 21, 2018
c7c07de
Remove banner
Feb 21, 2018
e9eec39
Delete banner code
Feb 21, 2018
7b5d76b
Add pyenv and direnv folders
Feb 21, 2018
d62a8e0
Merge branch 'master' of https://github.com/Microsoft/vscode-python i…
Feb 21, 2018
66395c5
Basic venv path search tests
Feb 22, 2018
005f65e
PYENV_ROOT resolution
Feb 22, 2018
63e195c
PYENV_ROOT test
Feb 22, 2018
b142a49
Use ICurrentProcess
Feb 22, 2018
005875f
Fix travis
Feb 22, 2018
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
29 changes: 17 additions & 12 deletions src/client/interpreter/locators/services/globalVirtualEnvService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
'use strict';

import { inject, injectable, named } from 'inversify';
import * as os from 'os';
import * as path from 'path';
import { Uri } from 'vscode';
import { IPlatformService } from '../../../common/platform/types';
import { ICurrentProcess } from '../../../common/types';
import { IServiceContainer } from '../../../ioc/types';
import { IVirtualEnvironmentsSearchPathProvider } from '../../contracts';
import { BaseVirtualEnvService } from './baseVirtualEnvService';

// tslint:disable-next-line:no-require-imports no-var-requires
const untildify = require('untildify');

@injectable()
export class GlobalVirtualEnvService extends BaseVirtualEnvService {
public constructor(
Expand All @@ -24,16 +23,22 @@ export class GlobalVirtualEnvService extends BaseVirtualEnvService {

@injectable()
export class GlobalVirtualEnvironmentsSearchPathProvider implements IVirtualEnvironmentsSearchPathProvider {
public constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
private readonly process: ICurrentProcess;

constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
this.process = serviceContainer.get<ICurrentProcess>(ICurrentProcess);
}

public getSearchPaths(_resource?: Uri): string[] {
const platformService = this.serviceContainer.get<IPlatformService>(IPlatformService);
if (platformService.isWindows) {
return [];
} else {
return ['/Envs', '/.virtualenvs', '/.pyenv', '/.pyenv/versions']
.map(item => untildify(`~${item}`));
}
const homedir = os.homedir();
const folders = ['Envs', '.virtualenvs'].map(item => path.join(homedir, item));

// tslint:disable-next-line:no-string-literal
let pyenvRoot = this.process.env['PYENV_ROOT'];
pyenvRoot = pyenvRoot ? pyenvRoot : path.join(homedir, '.pyenv');

folders.push(pyenvRoot);
folders.push(path.join(pyenvRoot, 'versions'));
return folders;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
'use strict';

import { inject, injectable, named } from 'inversify';
import * as path from 'path';
// tslint:disable-next-line:no-require-imports
import untildify = require('untildify');
import { Uri } from 'vscode';
Expand Down Expand Up @@ -36,16 +37,20 @@ export class WorkspaceVirtualEnvironmentsSearchPathProvider implements IVirtualE
}
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
if (Array.isArray(workspaceService.workspaceFolders) && workspaceService.workspaceFolders.length > 0) {
let wsPath: string | undefined;
if (resource && workspaceService.workspaceFolders.length > 1) {
const wkspaceFolder = workspaceService.getWorkspaceFolder(resource);
if (wkspaceFolder) {
paths.push(wkspaceFolder.uri.fsPath);
wsPath = wkspaceFolder.uri.fsPath;
}
} else {
paths.push(workspaceService.workspaceFolders[0].uri.fsPath);
wsPath = workspaceService.workspaceFolders[0].uri.fsPath;
}
if (wsPath) {
paths.push(wsPath);
paths.push(path.join(wsPath, '.direnv'));
}
}
return paths;

}
}
82 changes: 82 additions & 0 deletions src/test/interpreters/venv.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { expect } from 'chai';
import { Container } from 'inversify';
import * as os from 'os';
import * as path from 'path';
import * as TypeMoq from 'typemoq';
import { Uri, WorkspaceFolder } from 'vscode';
import { IWorkspaceService } from '../../client/common/application/types';
import { IConfigurationService, ICurrentProcess, IPythonSettings } from '../../client/common/types';
import { EnvironmentVariables } from '../../client/common/variables/types';
import { GlobalVirtualEnvironmentsSearchPathProvider } from '../../client/interpreter/locators/services/globalVirtualEnvService';
import { WorkspaceVirtualEnvironmentsSearchPathProvider } from '../../client/interpreter/locators/services/workspaceVirtualEnvService';
import { ServiceContainer } from '../../client/ioc/container';
import { ServiceManager } from '../../client/ioc/serviceManager';

suite('Virtual environments', () => {
let serviceManager: ServiceManager;
let serviceContainer: ServiceContainer;
let settings: TypeMoq.IMock<IPythonSettings>;
let config: TypeMoq.IMock<IConfigurationService>;
let workspace: TypeMoq.IMock<IWorkspaceService>;
let process: TypeMoq.IMock<ICurrentProcess>;

setup(async () => {
const cont = new Container();
serviceManager = new ServiceManager(cont);
serviceContainer = new ServiceContainer(cont);

settings = TypeMoq.Mock.ofType<IPythonSettings>();
config = TypeMoq.Mock.ofType<IConfigurationService>();
workspace = TypeMoq.Mock.ofType<IWorkspaceService>();
process = TypeMoq.Mock.ofType<ICurrentProcess>();

config.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object);

serviceManager.addSingletonInstance<IConfigurationService>(IConfigurationService, config.object);
serviceManager.addSingletonInstance<IWorkspaceService>(IWorkspaceService, workspace.object);
serviceManager.addSingletonInstance<ICurrentProcess>(ICurrentProcess, process.object);
});

test('Global search paths', async () => {
const pathProvider = new GlobalVirtualEnvironmentsSearchPathProvider(serviceContainer);
const envMap: EnvironmentVariables = {};

const homedir = os.homedir();
let folders = ['Envs', '.virtualenvs', '.pyenv', path.join('.pyenv', 'versions')];
let paths = pathProvider.getSearchPaths();
let expected = folders.map(item => path.join(homedir, item));
expect(paths).to.deep.equal(expected, 'Global search folder list is incorrect.');

process.setup(x => x.env).returns(() => envMap);
// tslint:disable-next-line:no-string-literal
envMap['PYENV_ROOT'] = path.join(homedir, 'some_folder');
paths = pathProvider.getSearchPaths();

folders = ['Envs', '.virtualenvs', 'some_folder', path.join('some_folder', 'versions')];
expected = folders.map(item => path.join(homedir, item));
expect(paths).to.deep.equal(expected, 'PYENV_ROOT not resolved correctly.');
});

test('Workspace search paths', async () => {
settings.setup(x => x.venvPath).returns(() => `~${path.sep}foo`);

const wsRoot = TypeMoq.Mock.ofType<WorkspaceFolder>();
wsRoot.setup(x => x.uri).returns(() => Uri.file('root'));

const folder1 = TypeMoq.Mock.ofType<WorkspaceFolder>();
folder1.setup(x => x.uri).returns(() => Uri.file('dir1'));

workspace.setup(x => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsRoot.object);
workspace.setup(x => x.workspaceFolders).returns(() => [wsRoot.object, folder1.object]);

const pathProvider = new WorkspaceVirtualEnvironmentsSearchPathProvider(serviceContainer);
const paths = pathProvider.getSearchPaths(Uri.file(''));

const homedir = os.homedir();
const expected = [path.join(homedir, 'foo'), `${path.sep}root`, `${path.sep}root${path.sep}.direnv`];
expect(paths).to.deep.equal(expected, 'Workspace venv folder search list does not match.');
});
});