diff --git a/src/client/pythonEnvironments/common/environmentIdentifier.ts b/src/client/pythonEnvironments/common/environmentIdentifier.ts index 4bc759869423..619343196904 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 { isVirtualenvEnvironment } from '../discovery/locators/services/virtualenvLocator'; import { isWindowsStoreEnvironment } from '../discovery/locators/services/windowsStoreLocator'; import { EnvironmentType } from '../info'; @@ -42,6 +43,10 @@ export async function identifyEnvironment(interpreterPath: string): Promise { + // Check if there are any activate.* files in the same directory as the interpreter. + // + // env + // |__ activate, activate.* <--- check if any of these files exist + // |__ python <--- interpreterPath + const directory = path.dirname(interpreterPath); + const files = await fsapi.readdir(directory); + const regex = /^activate(\.([A-z]|\d)+)?$/; + + return files.find((file) => regex.test(file)) !== undefined; +} diff --git a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts index f501c461374d..98e2f2f53672 100644 --- a/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentIdentifier.unit.test.ts @@ -109,4 +109,21 @@ suite('Environment Identifier', () => { assert.deepEqual(envType, EnvironmentType.Venv); }); }); + + suite('Virtualenv', () => { + const activateFiles = [ + { folder: 'virtualenv1', file: 'activate' }, + { folder: 'virtualenv2', file: 'activate.sh' }, + { folder: 'virtualenv3', file: 'activate.ps1' }, + ]; + + activateFiles.forEach(({ folder, file }) => { + test(`Folder contains ${file}`, async () => { + const interpreterPath = path.join(TEST_LAYOUT_ROOT, folder, 'bin', 'python'); + const envType = await identifyEnvironment(interpreterPath); + + assert.deepStrictEqual(envType, EnvironmentType.VirtualEnv); + }); + }); + }); }); diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenv1/bin/activate b/src/test/pythonEnvironments/common/envlayouts/virtualenv1/bin/activate new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenv2/bin/activate.sh b/src/test/pythonEnvironments/common/envlayouts/virtualenv2/bin/activate.sh new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/pythonEnvironments/common/envlayouts/virtualenv3/bin/activate.ps1 b/src/test/pythonEnvironments/common/envlayouts/virtualenv3/bin/activate.ps1 new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/pythonEnvironments/discovery/locators/virtualenvLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualenvLocator.unit.test.ts new file mode 100644 index 000000000000..a16a5c2139c9 --- /dev/null +++ b/src/test/pythonEnvironments/discovery/locators/virtualenvLocator.unit.test.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as assert from 'assert'; +import * as fsapi from 'fs-extra'; +import * as path from 'path'; +import * as sinon from 'sinon'; +import { isVirtualenvEnvironment } from '../../../../client/pythonEnvironments/discovery/locators/services/virtualenvLocator'; + +suite('Virtualenv Locator Tests', () => { + const envRoot = path.join('path', 'to', 'env'); + const interpreter = path.join(envRoot, 'python'); + let readDirStub: sinon.SinonStub; + + setup(() => { + readDirStub = sinon.stub(fsapi, 'readdir'); + }); + + teardown(() => { + readDirStub.restore(); + }); + + test('Interpreter folder contains an activate file', async () => { + readDirStub.resolves(['activate', 'python']); + + assert.ok(await isVirtualenvEnvironment(interpreter)); + }); + + test('Interpreter folder does not contain any activate.* files', async () => { + readDirStub.resolves(['mymodule', 'python']); + + assert.strictEqual(await isVirtualenvEnvironment(interpreter), false); + }); +});