From cb58136443b40b4227a8dc7c50f7c54731bf3c0e Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Fri, 11 Sep 2020 15:57:04 -0700 Subject: [PATCH 01/10] virtualenvwrapper locator --- .../common/environmentIdentifier.ts | 5 ++ .../services/virtualenvwrapperLocator.ts | 31 +++++++++ .../common/environmentIdentifier.unit.test.ts | 46 +++++++++++++ .../virtualenvwrapperLocator.unit.test.ts | 67 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts create mode 100644 src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts diff --git a/src/client/pythonEnvironments/common/environmentIdentifier.ts b/src/client/pythonEnvironments/common/environmentIdentifier.ts index 4bc759869423..e80f44e4ee19 100644 --- a/src/client/pythonEnvironments/common/environmentIdentifier.ts +++ b/src/client/pythonEnvironments/common/environmentIdentifier.ts @@ -3,6 +3,7 @@ import { isCondaEnvironment } from '../discovery/locators/services/condaLocator'; import { isVenvEnvironment } from '../discovery/locators/services/venvLocator'; +import { isVirtualenvwrapperEnvironment } from '../discovery/locators/services/virtualenvwrapperLocator'; import { isWindowsStoreEnvironment } from '../discovery/locators/services/windowsStoreLocator'; import { EnvironmentType } from '../info'; @@ -42,6 +43,10 @@ export async function identifyEnvironment(interpreterPath: string): Promise { assert.deepEqual(envType, EnvironmentType.Venv); }); }); + + suite('Virtualenvwrapper', () => { + const homeDir = platformApis.getUserHomeDir() || path.join('path', 'to', 'home'); + + let getEnvVarStub: sinon.SinonStub; + let getOsTypeStub: sinon.SinonStub; + + suiteSetup(() => { + getEnvVarStub = sinon.stub(platformApis, 'getEnvironmentVariable'); + getOsTypeStub = sinon.stub(platformApis, 'getOSType'); + }); + + suiteTeardown(() => { + getEnvVarStub.restore(); + getOsTypeStub.restore(); + }); + + test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async () => { + const interpreterPath = path.join(homeDir, '.virtualenvs', 'myenv', 'python'); + + getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); + + const envType = await identifyEnvironment(interpreterPath); + assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); + }); + + test('WORKON_HOME is set to its default value %USERPROFILE%\\Envs on Windows', async () => { + const interpreterPath = path.join(homeDir, 'Envs', 'myenv', 'python'); + + getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); + getOsTypeStub.returns(platformApis.OSType.Windows); + + const envType = await identifyEnvironment(interpreterPath); + assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); + }); + + test('WORKON_HOME is set to a custom value', async () => { + const workonHomeDir = path.join('path', 'to', 'envs'); + const interpreterPath = path.join(workonHomeDir, 'myenv', 'python'); + + getEnvVarStub.withArgs('WORKON_HOME').returns(workonHomeDir); + + const envType = await identifyEnvironment(interpreterPath); + assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); + }); + }); }); diff --git a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts new file mode 100644 index 000000000000..a44c565693d7 --- /dev/null +++ b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as assert from 'assert'; +import * as path from 'path'; +import * as sinon from 'sinon'; +import * as platformUtils from '../../../../client/common/utils/platform'; +import { isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; + +suite('Virtualenvwrapper Locator Tests', () => { + const envDirectory = 'myenv'; + const homeDir = path.join('path', 'to', 'home'); + + let getOsTypeStub: sinon.SinonStub; + let getEnvVariableStub: sinon.SinonStub; + let getHomeDirStub: sinon.SinonStub; + + setup(() => { + getOsTypeStub = sinon.stub(platformUtils, 'getOSType'); + getEnvVariableStub = sinon.stub(platformUtils, 'getEnvironmentVariable'); + getHomeDirStub = sinon.stub(platformUtils, 'getUserHomeDir'); + + getHomeDirStub.returns(homeDir); + }); + + teardown(() => { + getOsTypeStub.restore(); + getEnvVariableStub.restore(); + getHomeDirStub.restore(); + }); + + test('WORKON_HOME is set to a custom value, and the interpreter is is in a subfolder', () => { + const workonHomeDirectory = path.join('path', 'to', 'workonHome'); + const interpreter = path.join(workonHomeDirectory, envDirectory, 'python'); + + getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + + assert.ok(isVirtualenvwrapperEnvironment(interpreter)); + }); + + test('The interpreter is not in a subfolder of WORKON_HOME', () => { + const workonHomeDirectory = path.join('path', 'to', 'workonHome'); + const interpreter = path.join('some', 'path', envDirectory, 'python'); + + getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + + assert.deepStrictEqual(isVirtualenvwrapperEnvironment(interpreter), false); + }); + + test('WORKON_HOME is not set on non-Windows, and the interpreter is in a subfolder', () => { + const interpreter = path.join(homeDir, '.virtualenvs', envDirectory, 'python'); + + getEnvVariableStub.withArgs('WORKON_HOME').returns(undefined); + getOsTypeStub.returns(platformUtils.OSType.Linux); + + assert.ok(isVirtualenvwrapperEnvironment(interpreter)); + }); + + test('WORKON_HOME is not set on Windows, and the interpreter is in a subfolder', () => { + const interpreter = path.join(homeDir, 'Envs', envDirectory, 'python'); + + getEnvVariableStub.withArgs('WORKON_HOME').returns(undefined); + getOsTypeStub.returns(platformUtils.OSType.Windows); + + assert.ok(isVirtualenvwrapperEnvironment(interpreter)); + }); +}); From 99b3f31972a058641bfee594c9b2c57b7df5e3b4 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Fri, 11 Sep 2020 16:01:03 -0700 Subject: [PATCH 02/10] skip os-specific tests --- .../common/environmentIdentifier.unit.test.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts index f7fcfe92c9d3..79206a59184c 100644 --- a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts @@ -7,6 +7,7 @@ import * as sinon from 'sinon'; import * as platformApis from '../../../client/common/utils/platform'; import { identifyEnvironment } from '../../../client/pythonEnvironments/common/environmentIdentifier'; import { EnvironmentType } from '../../../client/pythonEnvironments/info'; +import { getOSType as getOSTypeForTest, OSType } from '../../common'; import { TEST_LAYOUT_ROOT } from './commonTestConstants'; suite('Environment Identifier', () => { @@ -126,16 +127,28 @@ suite('Environment Identifier', () => { getOsTypeStub.restore(); }); - test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async () => { + test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async function () { + if (getOSTypeForTest() === OSType.Windows) { + // tslint:disable-next-line: no-invalid-this + return this.skip(); + } + const interpreterPath = path.join(homeDir, '.virtualenvs', 'myenv', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); + + return undefined; }); - test('WORKON_HOME is set to its default value %USERPROFILE%\\Envs on Windows', async () => { + test('WORKON_HOME is set to its default value %USERPROFILE%\\Envs on Windows', async function () { + if (getOSTypeForTest() !== OSType.Windows) { + // tslint:disable-next-line: no-invalid-this + return this.skip(); + } + const interpreterPath = path.join(homeDir, 'Envs', 'myenv', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); @@ -143,6 +156,8 @@ suite('Environment Identifier', () => { const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); + + return undefined; }); test('WORKON_HOME is set to a custom value', async () => { From 6fe393291443389ed669e85330c714dde31d7185 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Fri, 11 Sep 2020 16:01:38 -0700 Subject: [PATCH 03/10] Test getDefaultVirtualenvwrapperDir --- .../services/virtualenvwrapperLocator.ts | 2 +- .../virtualenvwrapperLocator.unit.test.ts | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts index 822f3a8f30ba..8e516929dd19 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts @@ -6,7 +6,7 @@ import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType, } from '../../../../common/utils/platform'; -function getDefaultVirtualenvwrapperDir(): string { +export function getDefaultVirtualenvwrapperDir(): string { const homeDir = getUserHomeDir() || ''; // In Windows, the default path for WORKON_HOME is %USERPROFILE%\Envs. diff --git a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts index a44c565693d7..79d9bdef38a4 100644 --- a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as sinon from 'sinon'; import * as platformUtils from '../../../../client/common/utils/platform'; -import { isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; +import { getDefaultVirtualenvwrapperDir, isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; suite('Virtualenvwrapper Locator Tests', () => { const envDirectory = 'myenv'; @@ -47,21 +47,19 @@ suite('Virtualenvwrapper Locator Tests', () => { assert.deepStrictEqual(isVirtualenvwrapperEnvironment(interpreter), false); }); - test('WORKON_HOME is not set on non-Windows, and the interpreter is in a subfolder', () => { - const interpreter = path.join(homeDir, '.virtualenvs', envDirectory, 'python'); - - getEnvVariableStub.withArgs('WORKON_HOME').returns(undefined); + test('Default virtualenvwrapper directory on non-Windows should be ~/.virtualenvs', () => { getOsTypeStub.returns(platformUtils.OSType.Linux); - assert.ok(isVirtualenvwrapperEnvironment(interpreter)); - }); + const directory = getDefaultVirtualenvwrapperDir(); - test('WORKON_HOME is not set on Windows, and the interpreter is in a subfolder', () => { - const interpreter = path.join(homeDir, 'Envs', envDirectory, 'python'); + assert.deepStrictEqual(directory, path.join(homeDir, '.virtualenvs')); + }); - getEnvVariableStub.withArgs('WORKON_HOME').returns(undefined); + test('Default virtualenvwrapper directory on Windows should be %USERPROFILE%\\Envs', () => { getOsTypeStub.returns(platformUtils.OSType.Windows); - assert.ok(isVirtualenvwrapperEnvironment(interpreter)); + const directory = getDefaultVirtualenvwrapperDir(); + + assert.deepStrictEqual(directory, path.join(homeDir, 'Envs')); }); }); From fbb3b81698493e60557d77a639ca85c7a2026b11 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Date: Mon, 14 Sep 2020 13:13:32 -0700 Subject: [PATCH 04/10] Update src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts Co-authored-by: Karthik Nadig --- .../discovery/locators/services/virtualenvwrapperLocator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts index 8e516929dd19..b9afcebdae9a 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts @@ -27,5 +27,5 @@ export function isVirtualenvwrapperEnvironment(interpreterPath:string): boolean const workonHomeFolder = getEnvironmentVariable('WORKON_HOME') || getDefaultVirtualenvwrapperDir(); - return interpreterPath.startsWith(workonHomeFolder); + return interpreterPath.toUpperCase().startsWith(workonHomeFolder.toUpperCase()); } From 38b41d93265fe8ef8decc0a3b16872312af0b450 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 14 Sep 2020 14:02:15 -0700 Subject: [PATCH 05/10] add pathExists check to WORKON_HOME dir --- .../common/environmentIdentifier.ts | 2 +- .../services/virtualenvwrapperLocator.ts | 7 +++++- .../common/environmentIdentifier.unit.test.ts | 7 ++++++ .../virtualenvwrapperLocator.unit.test.ts | 23 +++++++++++++++---- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/client/pythonEnvironments/common/environmentIdentifier.ts b/src/client/pythonEnvironments/common/environmentIdentifier.ts index e80f44e4ee19..e4d661cb16ed 100644 --- a/src/client/pythonEnvironments/common/environmentIdentifier.ts +++ b/src/client/pythonEnvironments/common/environmentIdentifier.ts @@ -43,7 +43,7 @@ export async function identifyEnvironment(interpreterPath: string): Promise { // The WORKON_HOME variable contains the path to the root directory of all virtualenvwrapper environments. // If the interpreter path belongs to one of them then it is a virtualenvwrapper type of environment. const workonHomeFolder = getEnvironmentVariable('WORKON_HOME') || getDefaultVirtualenvwrapperDir(); + if (!await pathExists(workonHomeFolder)) { + return false; + } + return interpreterPath.toUpperCase().startsWith(workonHomeFolder.toUpperCase()); } diff --git a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts index 79206a59184c..30c8d2eed28f 100644 --- a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import * as platformApis from '../../../client/common/utils/platform'; import { identifyEnvironment } from '../../../client/pythonEnvironments/common/environmentIdentifier'; +import * as fileApis from '../../../client/pythonEnvironments/common/externalDependencies'; import { EnvironmentType } from '../../../client/pythonEnvironments/info'; import { getOSType as getOSTypeForTest, OSType } from '../../common'; import { TEST_LAYOUT_ROOT } from './commonTestConstants'; @@ -116,15 +117,18 @@ suite('Environment Identifier', () => { let getEnvVarStub: sinon.SinonStub; let getOsTypeStub: sinon.SinonStub; + let pathExistsStub:sinon.SinonStub; suiteSetup(() => { getEnvVarStub = sinon.stub(platformApis, 'getEnvironmentVariable'); getOsTypeStub = sinon.stub(platformApis, 'getOSType'); + pathExistsStub = sinon.stub(fileApis, 'pathExists'); }); suiteTeardown(() => { getEnvVarStub.restore(); getOsTypeStub.restore(); + pathExistsStub.restore(); }); test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async function () { @@ -136,6 +140,7 @@ suite('Environment Identifier', () => { const interpreterPath = path.join(homeDir, '.virtualenvs', 'myenv', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); + pathExistsStub.withArgs(path.join(homeDir, '.virtualenvs')).resolves(true); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); @@ -153,6 +158,7 @@ suite('Environment Identifier', () => { getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); getOsTypeStub.returns(platformApis.OSType.Windows); + pathExistsStub.withArgs(path.join(homeDir, 'Envs')).resolves(true); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); @@ -165,6 +171,7 @@ suite('Environment Identifier', () => { const interpreterPath = path.join(workonHomeDir, 'myenv', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(workonHomeDir); + pathExistsStub.withArgs(workonHomeDir).resolves(true); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); diff --git a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts index 79d9bdef38a4..1efe68c6c7f4 100644 --- a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as sinon from 'sinon'; import * as platformUtils from '../../../../client/common/utils/platform'; +import * as fileUtils from '../../../../client/pythonEnvironments/common/externalDependencies'; import { getDefaultVirtualenvwrapperDir, isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; suite('Virtualenvwrapper Locator Tests', () => { @@ -14,37 +15,51 @@ suite('Virtualenvwrapper Locator Tests', () => { let getOsTypeStub: sinon.SinonStub; let getEnvVariableStub: sinon.SinonStub; let getHomeDirStub: sinon.SinonStub; + let pathExistsStub:sinon.SinonStub; setup(() => { getOsTypeStub = sinon.stub(platformUtils, 'getOSType'); getEnvVariableStub = sinon.stub(platformUtils, 'getEnvironmentVariable'); getHomeDirStub = sinon.stub(platformUtils, 'getUserHomeDir'); + pathExistsStub = sinon.stub(fileUtils, 'pathExists'); getHomeDirStub.returns(homeDir); + pathExistsStub.withArgs(path.join('path', 'to', 'workonHome')).resolves(true); + pathExistsStub.resolves(false); }); teardown(() => { getOsTypeStub.restore(); getEnvVariableStub.restore(); getHomeDirStub.restore(); + pathExistsStub.restore(); }); - test('WORKON_HOME is set to a custom value, and the interpreter is is in a subfolder', () => { + test('WORKON_HOME is set to a custom value, and the interpreter is is in a subfolder', async () => { const workonHomeDirectory = path.join('path', 'to', 'workonHome'); const interpreter = path.join(workonHomeDirectory, envDirectory, 'python'); getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); - assert.ok(isVirtualenvwrapperEnvironment(interpreter)); + assert.ok(await isVirtualenvwrapperEnvironment(interpreter)); }); - test('The interpreter is not in a subfolder of WORKON_HOME', () => { + test('The interpreter is not in a subfolder of WORKON_HOME', async () => { const workonHomeDirectory = path.join('path', 'to', 'workonHome'); const interpreter = path.join('some', 'path', envDirectory, 'python'); getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); - assert.deepStrictEqual(isVirtualenvwrapperEnvironment(interpreter), false); + assert.deepStrictEqual(await isVirtualenvwrapperEnvironment(interpreter), false); + }); + + test('WORKON_HOME doesn\'t exist', async () => { + const workonHomeDirectory = path.join('nonexistent', 'workonHome'); + const interpreter = path.join('some', 'path', envDirectory, 'python'); + + getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + + assert.deepStrictEqual(await isVirtualenvwrapperEnvironment(interpreter), false); }); test('Default virtualenvwrapper directory on non-Windows should be ~/.virtualenvs', () => { From 26df20c63a868ecb63b75a8baf1643aec370019d Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 15 Sep 2020 10:18:00 -0700 Subject: [PATCH 06/10] Change how the locator works + move util func out --- .../common/virtualenvwrapperUtils.ts | 14 +++++++++++ .../services/virtualenvwrapperLocator.ts | 25 +++++++------------ 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 src/client/pythonEnvironments/common/virtualenvwrapperUtils.ts diff --git a/src/client/pythonEnvironments/common/virtualenvwrapperUtils.ts b/src/client/pythonEnvironments/common/virtualenvwrapperUtils.ts new file mode 100644 index 000000000000..1849934ff59e --- /dev/null +++ b/src/client/pythonEnvironments/common/virtualenvwrapperUtils.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import * as path from 'path'; +import { getOSType, getUserHomeDir, OSType } from '../../common/utils/platform'; + +export function getDefaultVirtualenvwrapperDir(): string { + const homeDir = getUserHomeDir() || ''; + + // In Windows, the default path for WORKON_HOME is %USERPROFILE%\Envs. + if (getOSType() === OSType.Windows) { + return path.join(homeDir, 'Envs'); + } + return path.join(homeDir, '.virtualenvs'); +} diff --git a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts index 776de165fa87..a1ad3fa7ed26 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator.ts @@ -3,34 +3,27 @@ import * as path from 'path'; import { - getEnvironmentVariable, getOSType, getUserHomeDir, OSType, + getEnvironmentVariable, getOSType, OSType, } from '../../../../common/utils/platform'; import { pathExists } from '../../../common/externalDependencies'; - -export function getDefaultVirtualenvwrapperDir(): string { - const homeDir = getUserHomeDir() || ''; - - // In Windows, the default path for WORKON_HOME is %USERPROFILE%\Envs. - if (getOSType() === OSType.Windows) { - return path.join(homeDir, 'Envs'); - } - return path.join(homeDir, '.virtualenvs'); -} +import { getDefaultVirtualenvwrapperDir } from '../../../common/virtualenvwrapperUtils'; /** * Checks if the given interpreter belongs to a virtualenvWrapper based environment. * @param {string} interpreterPath: Absolute path to the python interpreter. - * @returns {boolean} : Returns true if the interpreter belongs to a virtualenvWrapper environment. + * @returns {boolean}: Returns true if the interpreter belongs to a virtualenvWrapper environment. */ export async function isVirtualenvwrapperEnvironment(interpreterPath:string): Promise { // The WORKON_HOME variable contains the path to the root directory of all virtualenvwrapper environments. // If the interpreter path belongs to one of them then it is a virtualenvwrapper type of environment. + const workonHomeDir = getEnvironmentVariable('WORKON_HOME') || getDefaultVirtualenvwrapperDir(); + const environmentName = path.basename(path.dirname(path.dirname(interpreterPath))); - const workonHomeFolder = getEnvironmentVariable('WORKON_HOME') || getDefaultVirtualenvwrapperDir(); + let environmentDir = path.join(workonHomeDir, environmentName); - if (!await pathExists(workonHomeFolder)) { - return false; + if (getOSType() === OSType.Windows) { + environmentDir = environmentDir.toUpperCase(); } - return interpreterPath.toUpperCase().startsWith(workonHomeFolder.toUpperCase()); + return await pathExists(environmentDir) && interpreterPath.startsWith(`${environmentDir}${path.sep}`); } From bd642d709f6ee480adaaf4b19d470df081932105 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 15 Sep 2020 10:18:28 -0700 Subject: [PATCH 07/10] Fix functional tests --- .../common/environmentIdentifier.unit.test.ts | 23 ++++++++----------- .../.virtualenvs/myenv/bin/python | 0 .../Envs/myenv/Scripts/python.exe | 0 .../virtualenvwrapper2/myenv/bin/python | 0 4 files changed, 10 insertions(+), 13 deletions(-) create mode 100644 src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/.virtualenvs/myenv/bin/python create mode 100644 src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/Envs/myenv/Scripts/python.exe create mode 100644 src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper2/myenv/bin/python diff --git a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts index 30c8d2eed28f..6ed14b6d6cd5 100644 --- a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import * as platformApis from '../../../client/common/utils/platform'; import { identifyEnvironment } from '../../../client/pythonEnvironments/common/environmentIdentifier'; -import * as fileApis from '../../../client/pythonEnvironments/common/externalDependencies'; +import * as virtualenvwrapperUtils from '../../../client/pythonEnvironments/common/virtualenvwrapperUtils'; import { EnvironmentType } from '../../../client/pythonEnvironments/info'; import { getOSType as getOSTypeForTest, OSType } from '../../common'; import { TEST_LAYOUT_ROOT } from './commonTestConstants'; @@ -113,22 +113,20 @@ suite('Environment Identifier', () => { }); suite('Virtualenvwrapper', () => { - const homeDir = platformApis.getUserHomeDir() || path.join('path', 'to', 'home'); - let getEnvVarStub: sinon.SinonStub; let getOsTypeStub: sinon.SinonStub; - let pathExistsStub:sinon.SinonStub; + let defaultDirStub: sinon.SinonStub; suiteSetup(() => { getEnvVarStub = sinon.stub(platformApis, 'getEnvironmentVariable'); getOsTypeStub = sinon.stub(platformApis, 'getOSType'); - pathExistsStub = sinon.stub(fileApis, 'pathExists'); + defaultDirStub = sinon.stub(virtualenvwrapperUtils, 'getDefaultVirtualenvwrapperDir'); }); suiteTeardown(() => { getEnvVarStub.restore(); getOsTypeStub.restore(); - pathExistsStub.restore(); + defaultDirStub.restore(); }); test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async function () { @@ -137,10 +135,10 @@ suite('Environment Identifier', () => { return this.skip(); } - const interpreterPath = path.join(homeDir, '.virtualenvs', 'myenv', 'python'); + const interpreterPath = path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', '.virtualenvs', 'myenv', 'bin', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); - pathExistsStub.withArgs(path.join(homeDir, '.virtualenvs')).resolves(true); + defaultDirStub.returns(path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', '.virtualenvs')); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); @@ -154,11 +152,11 @@ suite('Environment Identifier', () => { return this.skip(); } - const interpreterPath = path.join(homeDir, 'Envs', 'myenv', 'python'); + const interpreterPath = path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', 'Envs', 'myenv', 'Scripts', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); getOsTypeStub.returns(platformApis.OSType.Windows); - pathExistsStub.withArgs(path.join(homeDir, 'Envs')).resolves(true); + defaultDirStub.returns(path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', 'Envs')); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); @@ -167,11 +165,10 @@ suite('Environment Identifier', () => { }); test('WORKON_HOME is set to a custom value', async () => { - const workonHomeDir = path.join('path', 'to', 'envs'); - const interpreterPath = path.join(workonHomeDir, 'myenv', 'python'); + const workonHomeDir = path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper2'); + const interpreterPath = path.join(workonHomeDir, 'myenv', 'bin', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(workonHomeDir); - pathExistsStub.withArgs(workonHomeDir).resolves(true); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/.virtualenvs/myenv/bin/python b/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/.virtualenvs/myenv/bin/python new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/Envs/myenv/Scripts/python.exe b/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper1/Envs/myenv/Scripts/python.exe new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper2/myenv/bin/python b/src/test/pythonEnvironments/common/envlayouts/virtualenvwrapper2/myenv/bin/python new file mode 100644 index 000000000000..e69de29bb2d1 From 5f10e080b8dea84ce0d789d7c685ccaead333421 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 15 Sep 2020 10:26:32 -0700 Subject: [PATCH 08/10] utils unit tests --- .../virtualenvwrapperUtils.unit.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/test/pythonEnvironments/common/virtualenvwrapperUtils.unit.test.ts diff --git a/src/test/pythonEnvironments/common/virtualenvwrapperUtils.unit.test.ts b/src/test/pythonEnvironments/common/virtualenvwrapperUtils.unit.test.ts new file mode 100644 index 000000000000..77daf8aeeb7e --- /dev/null +++ b/src/test/pythonEnvironments/common/virtualenvwrapperUtils.unit.test.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as assert from 'assert'; +import * as path from 'path'; +import * as sinon from 'sinon'; +import * as platformUtils from '../../../client/common/utils/platform'; +import { getDefaultVirtualenvwrapperDir } from '../../../client/pythonEnvironments/common/virtualenvwrapperUtils'; + +suite('Virtualenvwrapper Utils tests', () => { + const homeDir = path.join('path', 'to', 'home'); + + let getOsTypeStub: sinon.SinonStub; + let getHomeDirStub: sinon.SinonStub; + + setup(() => { + getOsTypeStub = sinon.stub(platformUtils, 'getOSType'); + getHomeDirStub = sinon.stub(platformUtils, 'getUserHomeDir'); + + getHomeDirStub.returns(homeDir); + }); + + teardown(() => { + getOsTypeStub.restore(); + getHomeDirStub.restore(); + }); + + test('Default virtualenvwrapper directory on non-Windows should be ~/.virtualenvs', () => { + getOsTypeStub.returns(platformUtils.OSType.Linux); + + const directory = getDefaultVirtualenvwrapperDir(); + + assert.deepStrictEqual(directory, path.join(homeDir, '.virtualenvs')); + }); + + test('Default virtualenvwrapper directory on Windows should be %USERPROFILE%\\Envs', () => { + getOsTypeStub.returns(platformUtils.OSType.Windows); + + const directory = getDefaultVirtualenvwrapperDir(); + + assert.deepStrictEqual(directory, path.join(homeDir, 'Envs')); + }); +}); From 74b168dd33cff5dddcc0112d98b7fc466bd83f61 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 15 Sep 2020 11:05:10 -0700 Subject: [PATCH 09/10] Update locator tests --- .../virtualenvwrapperLocator.unit.test.ts | 53 +++++++------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts index 1efe68c6c7f4..05d73758e2a4 100644 --- a/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/virtualenvwrapperLocator.unit.test.ts @@ -6,75 +6,58 @@ import * as path from 'path'; import * as sinon from 'sinon'; import * as platformUtils from '../../../../client/common/utils/platform'; import * as fileUtils from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { getDefaultVirtualenvwrapperDir, isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; +import * as virtualenvwrapperUtils from '../../../../client/pythonEnvironments/common/virtualenvwrapperUtils'; +import { isVirtualenvwrapperEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvwrapperLocator'; suite('Virtualenvwrapper Locator Tests', () => { const envDirectory = 'myenv'; const homeDir = path.join('path', 'to', 'home'); - let getOsTypeStub: sinon.SinonStub; let getEnvVariableStub: sinon.SinonStub; - let getHomeDirStub: sinon.SinonStub; let pathExistsStub:sinon.SinonStub; + let getDefaultDirStub:sinon.SinonStub; setup(() => { - getOsTypeStub = sinon.stub(platformUtils, 'getOSType'); getEnvVariableStub = sinon.stub(platformUtils, 'getEnvironmentVariable'); - getHomeDirStub = sinon.stub(platformUtils, 'getUserHomeDir'); pathExistsStub = sinon.stub(fileUtils, 'pathExists'); + getDefaultDirStub = sinon.stub(virtualenvwrapperUtils, 'getDefaultVirtualenvwrapperDir'); - getHomeDirStub.returns(homeDir); - pathExistsStub.withArgs(path.join('path', 'to', 'workonHome')).resolves(true); + pathExistsStub.withArgs(path.join(homeDir, envDirectory)).resolves(true); pathExistsStub.resolves(false); }); teardown(() => { - getOsTypeStub.restore(); getEnvVariableStub.restore(); - getHomeDirStub.restore(); pathExistsStub.restore(); + getDefaultDirStub.restore(); }); - test('WORKON_HOME is set to a custom value, and the interpreter is is in a subfolder', async () => { - const workonHomeDirectory = path.join('path', 'to', 'workonHome'); - const interpreter = path.join(workonHomeDirectory, envDirectory, 'python'); + test('WORKON_HOME is not set, and the interpreter is is in a subfolder', async () => { + const interpreter = path.join(homeDir, envDirectory, 'bin', 'python'); - getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + getEnvVariableStub.withArgs('WORKON_HOME').returns(undefined); + getDefaultDirStub.returns(homeDir); assert.ok(await isVirtualenvwrapperEnvironment(interpreter)); }); - test('The interpreter is not in a subfolder of WORKON_HOME', async () => { + test('WORKON_HOME is set to a custom value, and the interpreter is is in a subfolder', async () => { const workonHomeDirectory = path.join('path', 'to', 'workonHome'); - const interpreter = path.join('some', 'path', envDirectory, 'python'); + const interpreter = path.join(workonHomeDirectory, envDirectory, 'bin', 'python'); getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + pathExistsStub.withArgs(path.join(workonHomeDirectory, envDirectory)).resolves(true); - assert.deepStrictEqual(await isVirtualenvwrapperEnvironment(interpreter), false); + assert.ok(await isVirtualenvwrapperEnvironment(interpreter)); }); - test('WORKON_HOME doesn\'t exist', async () => { - const workonHomeDirectory = path.join('nonexistent', 'workonHome'); - const interpreter = path.join('some', 'path', envDirectory, 'python'); + test('The interpreter is not in a subfolder of WORKON_HOME', async () => { + const workonHomeDirectory = path.join('path', 'to', 'workonHome'); + const interpreter = path.join('some', 'path', envDirectory, 'bin', 'python'); getEnvVariableStub.withArgs('WORKON_HOME').returns(workonHomeDirectory); + pathExistsStub.withArgs(path.join(workonHomeDirectory, envDirectory)).resolves(false); assert.deepStrictEqual(await isVirtualenvwrapperEnvironment(interpreter), false); }); - - test('Default virtualenvwrapper directory on non-Windows should be ~/.virtualenvs', () => { - getOsTypeStub.returns(platformUtils.OSType.Linux); - - const directory = getDefaultVirtualenvwrapperDir(); - - assert.deepStrictEqual(directory, path.join(homeDir, '.virtualenvs')); - }); - - test('Default virtualenvwrapper directory on Windows should be %USERPROFILE%\\Envs', () => { - getOsTypeStub.returns(platformUtils.OSType.Windows); - - const directory = getDefaultVirtualenvwrapperDir(); - - assert.deepStrictEqual(directory, path.join(homeDir, 'Envs')); - }); }); From 590341e5f0a89be677fb4aeb16eb68085a681e28 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 15 Sep 2020 15:00:06 -0700 Subject: [PATCH 10/10] Stub getUserHomeDir instead of getDefaultetc --- .../common/environmentIdentifier.unit.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts index e59d0040bea0..6f6bd4cbc818 100644 --- a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts @@ -7,7 +7,6 @@ import * as sinon from 'sinon'; import * as platformApis from '../../../client/common/utils/platform'; import { identifyEnvironment } from '../../../client/pythonEnvironments/common/environmentIdentifier'; import * as externalDependencies from '../../../client/pythonEnvironments/common/externalDependencies'; -import * as virtualenvwrapperUtils from '../../../client/pythonEnvironments/common/virtualenvwrapperUtils'; import { EnvironmentType } from '../../../client/pythonEnvironments/info'; import { getOSType as getOSTypeForTest, OSType } from '../../common'; import { TEST_LAYOUT_ROOT } from './commonTestConstants'; @@ -150,18 +149,20 @@ suite('Environment Identifier', () => { suite('Virtualenvwrapper', () => { let getEnvVarStub: sinon.SinonStub; let getOsTypeStub: sinon.SinonStub; - let defaultDirStub: sinon.SinonStub; + let getUserHomeDirStub: sinon.SinonStub; suiteSetup(() => { getEnvVarStub = sinon.stub(platformApis, 'getEnvironmentVariable'); getOsTypeStub = sinon.stub(platformApis, 'getOSType'); - defaultDirStub = sinon.stub(virtualenvwrapperUtils, 'getDefaultVirtualenvwrapperDir'); + getUserHomeDirStub = sinon.stub(platformApis, 'getUserHomeDir'); + + getUserHomeDirStub.returns(path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1')); }); suiteTeardown(() => { getEnvVarStub.restore(); getOsTypeStub.restore(); - defaultDirStub.restore(); + getUserHomeDirStub.restore(); }); test('WORKON_HOME is set to its default value ~/.virtualenvs on non-Windows', async function () { @@ -173,7 +174,6 @@ suite('Environment Identifier', () => { const interpreterPath = path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', '.virtualenvs', 'myenv', 'bin', 'python'); getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); - defaultDirStub.returns(path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', '.virtualenvs')); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper); @@ -191,7 +191,6 @@ suite('Environment Identifier', () => { getEnvVarStub.withArgs('WORKON_HOME').returns(undefined); getOsTypeStub.returns(platformApis.OSType.Windows); - defaultDirStub.returns(path.join(TEST_LAYOUT_ROOT, 'virtualenvwrapper1', 'Envs')); const envType = await identifyEnvironment(interpreterPath); assert.deepStrictEqual(envType, EnvironmentType.VirtualEnvWrapper);