diff --git a/src/client/common/installer/pipEnvInstaller.ts b/src/client/common/installer/pipEnvInstaller.ts index 32dc883c6d41..432171769b17 100644 --- a/src/client/common/installer/pipEnvInstaller.ts +++ b/src/client/common/installer/pipEnvInstaller.ts @@ -4,7 +4,7 @@ import { inject, injectable } from 'inversify'; import { IInterpreterLocatorService, IInterpreterService, PIPENV_SERVICE } from '../../interpreter/contracts'; import { IServiceContainer } from '../../ioc/types'; -import { isPipenvEnvironmentRelatedToFolder } from '../../pythonEnvironments/discovery/locators/services/pipEnvHelper'; +import { isPipenvEnvironmentRelatedToFolder } from '../../pythonEnvironments/common/environmentManagers/pipenv'; import { EnvironmentType, ModuleInstallerType } from '../../pythonEnvironments/info'; import { IWorkspaceService } from '../application/types'; import { inDiscoveryExperiment } from '../experiments/helpers'; diff --git a/src/client/common/installer/poetryInstaller.ts b/src/client/common/installer/poetryInstaller.ts index 33f65a21124c..8ae795b43fe2 100644 --- a/src/client/common/installer/poetryInstaller.ts +++ b/src/client/common/installer/poetryInstaller.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import { Uri } from 'vscode'; import { IInterpreterService } from '../../interpreter/contracts'; import { IServiceContainer } from '../../ioc/types'; -import { isPoetryEnvironmentRelatedToFolder } from '../../pythonEnvironments/discovery/locators/services/poetry'; +import { isPoetryEnvironmentRelatedToFolder } from '../../pythonEnvironments/common/environmentManagers/poetry'; import { EnvironmentType, ModuleInstallerType } from '../../pythonEnvironments/info'; import { IWorkspaceService } from '../application/types'; import { inDiscoveryExperiment } from '../experiments/helpers'; diff --git a/src/client/common/process/pythonEnvironment.ts b/src/client/common/process/pythonEnvironment.ts index a039c3eca12e..a100f1c55351 100644 --- a/src/client/common/process/pythonEnvironment.ts +++ b/src/client/common/process/pythonEnvironment.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { CondaEnvironmentInfo } from '../../pythonEnvironments/discovery/locators/services/conda'; +import { CondaEnvironmentInfo } from '../../pythonEnvironments/common/environmentManagers/conda'; import { buildPythonExecInfo, PythonExecInfo } from '../../pythonEnvironments/exec'; import { InterpreterInformation } from '../../pythonEnvironments/info'; import { getExecutablePath } from '../../pythonEnvironments/info/executable'; diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index f78a736ed1f5..a3c74891ef59 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -7,7 +7,7 @@ import { Uri } from 'vscode'; import { IEnvironmentActivationService } from '../../interpreter/activation/types'; import { IComponentAdapter, ICondaLocatorService, ICondaService } from '../../interpreter/contracts'; import { IServiceContainer } from '../../ioc/types'; -import { CondaEnvironmentInfo } from '../../pythonEnvironments/discovery/locators/services/conda'; +import { CondaEnvironmentInfo } from '../../pythonEnvironments/common/environmentManagers/conda'; import { inDiscoveryExperiment } from '../experiments/helpers'; import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; diff --git a/src/client/common/terminal/environmentActivationProviders/pipEnvActivationProvider.ts b/src/client/common/terminal/environmentActivationProviders/pipEnvActivationProvider.ts index 49e60e109a03..de0e76fd6d42 100644 --- a/src/client/common/terminal/environmentActivationProviders/pipEnvActivationProvider.ts +++ b/src/client/common/terminal/environmentActivationProviders/pipEnvActivationProvider.ts @@ -7,7 +7,7 @@ import { inject, injectable, named } from 'inversify'; import { Uri } from 'vscode'; import '../../extensions'; import { IInterpreterService } from '../../../interpreter/contracts'; -import { isPipenvEnvironmentRelatedToFolder } from '../../../pythonEnvironments/discovery/locators/services/pipEnvHelper'; +import { isPipenvEnvironmentRelatedToFolder } from '../../../pythonEnvironments/common/environmentManagers/pipenv'; import { EnvironmentType } from '../../../pythonEnvironments/info'; import { IWorkspaceService } from '../../application/types'; import { inDiscoveryExperiment } from '../../experiments/helpers'; diff --git a/src/client/interpreter/contracts.ts b/src/client/interpreter/contracts.ts index dd21e0d20deb..09571a0a21db 100644 --- a/src/client/interpreter/contracts.ts +++ b/src/client/interpreter/contracts.ts @@ -3,7 +3,7 @@ import { CodeLensProvider, ConfigurationTarget, Disposable, Event, TextDocument, import { IExtensionSingleActivationService } from '../activation/types'; import { Resource } from '../common/types'; import { PythonEnvSource } from '../pythonEnvironments/base/info'; -import { CondaEnvironmentInfo, CondaInfo } from '../pythonEnvironments/discovery/locators/services/conda'; +import { CondaEnvironmentInfo, CondaInfo } from '../pythonEnvironments/common/environmentManagers/conda'; import { EnvironmentType, PythonEnvironment } from '../pythonEnvironments/info'; export const INTERPRETER_LOCATOR_SERVICE = 'IInterpreterLocatorService'; diff --git a/src/client/pythonEnvironments/info/environmentInfoService.ts b/src/client/pythonEnvironments/base/info/environmentInfoService.ts similarity index 90% rename from src/client/pythonEnvironments/info/environmentInfoService.ts rename to src/client/pythonEnvironments/base/info/environmentInfoService.ts index ad793f4ecaa7..490a556a50ba 100644 --- a/src/client/pythonEnvironments/info/environmentInfoService.ts +++ b/src/client/pythonEnvironments/base/info/environmentInfoService.ts @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { traceVerbose } from '../../common/logger'; -import { IDisposableRegistry } from '../../common/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; -import { createRunningWorkerPool, IWorkerPool, QueuePosition } from '../../common/utils/workerPool'; -import { getInterpreterInfo, InterpreterInformation } from '../base/info/interpreter'; -import { buildPythonExecInfo } from '../exec'; +import { traceVerbose } from '../../../common/logger'; +import { IDisposableRegistry } from '../../../common/types'; +import { createDeferred, Deferred } from '../../../common/utils/async'; +import { createRunningWorkerPool, IWorkerPool, QueuePosition } from '../../../common/utils/workerPool'; +import { getInterpreterInfo, InterpreterInformation } from './interpreter'; +import { buildPythonExecInfo } from '../../exec'; export enum EnvironmentInfoServiceQueuePriority { Default, diff --git a/src/client/pythonEnvironments/base/locators/composite/environmentsResolver.ts b/src/client/pythonEnvironments/base/locators/composite/environmentsResolver.ts index 4ed6e2d31285..17fe292b644f 100644 --- a/src/client/pythonEnvironments/base/locators/composite/environmentsResolver.ts +++ b/src/client/pythonEnvironments/base/locators/composite/environmentsResolver.ts @@ -5,7 +5,7 @@ import { cloneDeep } from 'lodash'; import { Event, EventEmitter } from 'vscode'; import { traceVerbose } from '../../../../common/logger'; import { identifyEnvironment } from '../../../common/environmentIdentifier'; -import { IEnvironmentInfoService } from '../../../info/environmentInfoService'; +import { IEnvironmentInfoService } from '../../info/environmentInfoService'; import { PythonEnvInfo } from '../../info'; import { getEnvDisplayString } from '../../info/env'; import { InterpreterInformation } from '../../info/interpreter'; diff --git a/src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts b/src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts index c7b11b3f7b58..6b63a3a7756f 100644 --- a/src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts +++ b/src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts @@ -13,8 +13,8 @@ import { getPythonVersionFromPath, } from '../../../common/commonUtils'; import { getFileInfo, getWorkspaceFolders, isParentPath } from '../../../common/externalDependencies'; -import { AnacondaCompanyName, Conda } from '../../../discovery/locators/services/conda'; -import { parsePyenvVersion } from '../../../discovery/locators/services/pyenvLocator'; +import { AnacondaCompanyName, Conda } from '../../../common/environmentManagers/conda'; +import { parsePyenvVersion } from '../../../common/environmentManagers/pyenv'; import { Architecture, getOSType, OSType } from '../../../../common/utils/platform'; import { getPythonVersionFromPath as parsePythonVersionFromPath, parseVersion } from '../../info/pythonVersion'; import { getRegistryInterpreters, getRegistryInterpretersSync } from '../../../common/windowsUtils'; @@ -169,7 +169,7 @@ async function resolvePyenvEnv(executablePath: string): Promise { // The sub-directory name sometimes can contain distro and python versions. // here we attempt to extract the texts out of the name. - const versionStrings = await parsePyenvVersion(name); + const versionStrings = parsePyenvVersion(name); const envInfo = buildEnvInfo({ kind: PythonEnvKind.Pyenv, diff --git a/src/client/pythonEnvironments/base/locators/index.ts b/src/client/pythonEnvironments/base/locators/index.ts new file mode 100644 index 000000000000..744444bf8338 --- /dev/null +++ b/src/client/pythonEnvironments/base/locators/index.ts @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// eslint-disable-next-line max-classes-per-file +import { Uri } from 'vscode'; +import { iterEmpty } from '../../../common/utils/async'; +import { getURIFilter } from '../../../common/utils/misc'; +import { Disposables, IDisposable } from '../../../common/utils/resourceLifecycle'; +import { PythonEnvInfo } from '../info'; +import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from '../locator'; +import { combineIterators, Locators } from '../locators'; +import { LazyResourceBasedLocator } from './common/resourceBasedLocator'; + +/** + * A wrapper around all locators used by the extension. + */ + +export class ExtensionLocators extends Locators { + constructor( + // These are expected to be low-level locators (e.g. system). + nonWorkspace: ILocator[], + // This is expected to be a locator wrapping any found in + // the workspace (i.e. WorkspaceLocators). + workspace: ILocator, + ) { + super([...nonWorkspace, workspace]); + } +} +type WorkspaceLocatorFactoryResult = ILocator & Partial; +type WorkspaceLocatorFactory = (root: Uri) => WorkspaceLocatorFactoryResult[]; +type RootURI = string; + +export type WatchRootsArgs = { + initRoot(root: Uri): void; + addRoot(root: Uri): void; + removeRoot(root: Uri): void; +}; +type WatchRootsFunc = (args: WatchRootsArgs) => IDisposable; +// XXX Factor out RootedLocators and MultiRootedLocators. +/** + * The collection of all workspace-specific locators used by the extension. + * + * The factories are used to produce the locators for each workspace folder. + */ + +export class WorkspaceLocators extends LazyResourceBasedLocator { + private readonly locators: Record, IDisposable]> = {}; + + private readonly roots: Record = {}; + + constructor(private readonly watchRoots: WatchRootsFunc, private readonly factories: WorkspaceLocatorFactory[]) { + super(); + } + + public async dispose(): Promise { + await super.dispose(); + + // Clear all the roots. + const roots = Object.keys(this.roots).map((key) => this.roots[key]); + roots.forEach((root) => this.removeRoot(root)); + } + + protected doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { + const iterators = Object.keys(this.locators).map((key) => { + if (query?.searchLocations !== undefined) { + const root = this.roots[key]; + // Match any related search location. + const filter = getURIFilter(root, { checkParent: true, checkChild: true, checkExact: true }); + // Ignore any requests for global envs. + if (!query.searchLocations.roots.some(filter)) { + // This workspace folder did not match the query, so skip it! + return iterEmpty(); + } + } + // The query matches or was not location-specific. + const [locator] = this.locators[key]; + return locator.iterEnvs(query); + }); + return combineIterators(iterators); + } + + protected async initResources(): Promise { + const disposable = this.watchRoots({ + initRoot: (root: Uri) => this.addRoot(root), + addRoot: (root: Uri) => { + // Drop the old one, if necessary. + this.removeRoot(root); + this.addRoot(root); + this.emitter.fire({ searchLocation: root }); + }, + removeRoot: (root: Uri) => { + this.removeRoot(root); + this.emitter.fire({ searchLocation: root }); + }, + }); + this.disposables.push(disposable); + } + + private addRoot(root: Uri): void { + // Create the root's locator, wrapping each factory-generated locator. + const locators: ILocator[] = []; + const disposables = new Disposables(); + this.factories.forEach((create) => { + create(root).forEach((loc) => { + locators.push(loc); + if (loc.dispose !== undefined) { + disposables.push(loc as IDisposable); + } + }); + }); + const locator = new Locators(locators); + // Cache it. + const key = root.toString(); + this.locators[key] = [locator, disposables]; + this.roots[key] = root; + // Hook up the watchers. + disposables.push( + locator.onChanged((e) => { + if (e.searchLocation === undefined) { + e.searchLocation = root; + } + this.emitter.fire(e); + }), + ); + } + + private removeRoot(root: Uri): void { + const key = root.toString(); + const found = this.locators[key]; + if (found === undefined) { + return; + } + const [, disposables] = found; + delete this.locators[key]; + delete this.roots[key]; + disposables.dispose(); + } +} diff --git a/src/client/pythonEnvironments/discovery/locators/services/condaLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts similarity index 89% rename from src/client/pythonEnvironments/discovery/locators/services/condaLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts index bbaba7f618dd..7e54e9efcb82 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/condaLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts @@ -1,17 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import '../../../../common/extensions'; -import { PythonEnvKind } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../../base/locator'; +import { PythonEnvKind } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator'; import { getInterpreterPathFromDir } from '../../../common/commonUtils'; -import { Conda } from './conda'; +import { Conda } from '../../../common/environmentManagers/conda'; import { traceError, traceVerbose } from '../../../../common/logger'; export class CondaEnvironmentLocator extends Locator { - public constructor() { - super(); - } - // eslint-disable-next-line class-methods-use-this public async *iterEnvs(): IPythonEnvsIterator { const conda = await Conda.getConda(); diff --git a/src/client/pythonEnvironments/discovery/locators/services/customVirtualEnvLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/customVirtualEnvLocator.ts similarity index 94% rename from src/client/pythonEnvironments/discovery/locators/services/customVirtualEnvLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/customVirtualEnvLocator.ts index fcac7f87c404..dcdc9aaa0685 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/customVirtualEnvLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/customVirtualEnvLocator.ts @@ -6,9 +6,9 @@ import * as path from 'path'; import { traceError, traceVerbose } from '../../../../common/logger'; import { chain, iterable } from '../../../../common/utils/async'; import { getUserHomeDir } from '../../../../common/utils/platform'; -import { PythonEnvKind } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator } from '../../../base/locator'; -import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator'; +import { PythonEnvKind } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; +import { FSWatchingLocator } from './fsWatchingLocator'; import { findInterpretersInDir, looksLikeBasicVirtualPython } from '../../../common/commonUtils'; import { getPythonSetting, @@ -16,12 +16,12 @@ import { pathExists, untildify, } from '../../../common/externalDependencies'; -import { isPipenvEnvironment } from './pipEnvHelper'; +import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv'; import { isVenvEnvironment, isVirtualenvEnvironment, isVirtualenvwrapperEnvironment, -} from './virtualEnvironmentIdentifier'; +} from '../../../common/environmentManagers/simplevirtualenvs'; import '../../../../common/extensions'; import { asyncFilter } from '../../../../common/utils/arrayUtils'; /** diff --git a/src/client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/globalVirtualEnvronmentLocator.ts similarity index 94% rename from src/client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/globalVirtualEnvronmentLocator.ts index 9541cfcaf106..2e17639dcb3a 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/globalVirtualEnvronmentLocator.ts @@ -6,17 +6,17 @@ import * as path from 'path'; import { traceError, traceVerbose } from '../../../../common/logger'; import { chain, iterable } from '../../../../common/utils/async'; import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform'; -import { PythonEnvKind } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator } from '../../../base/locator'; -import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator'; +import { PythonEnvKind } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; +import { FSWatchingLocator } from './fsWatchingLocator'; import { findInterpretersInDir, looksLikeBasicVirtualPython } from '../../../common/commonUtils'; import { pathExists, untildify } from '../../../common/externalDependencies'; -import { isPipenvEnvironment } from './pipEnvHelper'; +import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv'; import { isVenvEnvironment, isVirtualenvEnvironment, isVirtualenvwrapperEnvironment, -} from './virtualEnvironmentIdentifier'; +} from '../../../common/environmentManagers/simplevirtualenvs'; import '../../../../common/extensions'; import { asyncFilter } from '../../../../common/utils/arrayUtils'; diff --git a/src/client/pythonEnvironments/discovery/locators/services/poetryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts similarity index 94% rename from src/client/pythonEnvironments/discovery/locators/services/poetryLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts index 3627357a9301..9382ab12cc68 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/poetryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts @@ -6,12 +6,12 @@ import * as path from 'path'; import { traceError, traceVerbose } from '../../../../common/logger'; import { chain, iterable } from '../../../../common/utils/async'; -import { PythonEnvKind } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator } from '../../../base/locator'; -import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator'; +import { PythonEnvKind } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; +import { FSWatchingLocator } from './fsWatchingLocator'; import { getInterpreterPathFromDir } from '../../../common/commonUtils'; import { pathExists } from '../../../common/externalDependencies'; -import { isPoetryEnvironment, localPoetryEnvDirName, Poetry } from './poetry'; +import { isPoetryEnvironment, localPoetryEnvDirName, Poetry } from '../../../common/environmentManagers/poetry'; import '../../../../common/extensions'; import { asyncFilter } from '../../../../common/utils/arrayUtils'; diff --git a/src/client/pythonEnvironments/discovery/locators/services/posixKnownPathsLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator.ts similarity index 90% rename from src/client/pythonEnvironments/discovery/locators/services/posixKnownPathsLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator.ts index 2f37de628a3b..74070386355f 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/posixKnownPathsLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator.ts @@ -2,10 +2,10 @@ // Licensed under the MIT License. import { traceError } from '../../../../common/logger'; -import { PythonEnvKind, PythonEnvSource } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../../base/locator'; +import { PythonEnvKind, PythonEnvSource } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator'; import { commonPosixBinPaths, getPythonBinFromPosixPaths } from '../../../common/posixUtils'; -import { isPyenvShimDir } from './pyenvLocator'; +import { isPyenvShimDir } from '../../../common/environmentManagers/pyenv'; export class PosixKnownPathsLocator extends Locator { private kind: PythonEnvKind = PythonEnvKind.OtherGlobal; diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/pyenvLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/pyenvLocator.ts new file mode 100644 index 000000000000..00f71eaa65ab --- /dev/null +++ b/src/client/pythonEnvironments/base/locators/lowLevel/pyenvLocator.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as path from 'path'; +import { traceError } from '../../../../common/logger'; +import { PythonEnvKind } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; +import { FSWatchingLocator } from './fsWatchingLocator'; +import { getInterpreterPathFromDir } from '../../../common/commonUtils'; +import { getSubDirs } from '../../../common/externalDependencies'; +import { getPyenvDir } from '../../../common/environmentManagers/pyenv'; + +function getPyenvVersionsDir(): string { + return path.join(getPyenvDir(), 'versions'); +} + +/** + * Gets all the pyenv environments. + * + * Remarks: This function looks at the /versions directory and gets + * all the environments (global or virtual) in that directory. + */ +async function* getPyenvEnvironments(): AsyncIterableIterator { + const pyenvVersionDir = getPyenvVersionsDir(); + + const subDirs = getSubDirs(pyenvVersionDir, { resolveSymlinks: true }); + for await (const subDirPath of subDirs) { + const interpreterPath = await getInterpreterPathFromDir(subDirPath); + + if (interpreterPath) { + try { + yield { + kind: PythonEnvKind.Pyenv, + executablePath: interpreterPath, + }; + } catch (ex) { + traceError(`Failed to process environment: ${interpreterPath}`, ex); + } + } + } +} + +export class PyenvLocator extends FSWatchingLocator { + constructor() { + super(getPyenvVersionsDir, async () => PythonEnvKind.Pyenv); + } + + // eslint-disable-next-line class-methods-use-this + public doIterEnvs(): IPythonEnvsIterator { + return getPyenvEnvironments(); + } +} diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts index d7764e361d24..532cb89b8b6d 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts @@ -8,8 +8,8 @@ import { Event } from 'vscode'; import { getSearchPathEntries } from '../../../../common/utils/exec'; import { Disposables, IDisposable } from '../../../../common/utils/resourceLifecycle'; import { iterPythonExecutablesInDir, looksLikeBasicGlobalPython } from '../../../common/commonUtils'; -import { isPyenvShimDir } from '../../../discovery/locators/services/pyenvLocator'; -import { isWindowsStoreDir } from '../../../discovery/locators/services/windowsStoreLocator'; +import { isPyenvShimDir } from '../../../common/environmentManagers/pyenv'; +import { isWindowsStoreDir } from '../../../common/environmentManagers/windowsStoreEnv'; import { PythonEnvKind, PythonEnvSource } from '../../info'; import { BasicEnvInfo, ILocator, IPythonEnvsIterator, PythonLocatorQuery } from '../../locator'; import { Locators } from '../../locators'; diff --git a/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts similarity index 92% rename from src/client/pythonEnvironments/discovery/locators/services/windowsRegistryLocator.ts rename to src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts index 341cef43e8d0..fc493fc13e16 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts @@ -2,8 +2,8 @@ // Licensed under the MIT License. import { traceError } from '../../../../common/logger'; -import { PythonEnvKind, PythonEnvSource } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../../base/locator'; +import { PythonEnvKind, PythonEnvSource } from '../../info'; +import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator'; import { getRegistryInterpreters } from '../../../common/windowsUtils'; export class WindowsRegistryLocator extends Locator { diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsStoreLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsStoreLocator.ts new file mode 100644 index 000000000000..dd5ee551fbe8 --- /dev/null +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsStoreLocator.ts @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as fsapi from 'fs-extra'; +import * as minimatch from 'minimatch'; +import * as path from 'path'; +import { PythonEnvKind } from '../../info'; +import { IPythonEnvsIterator, BasicEnvInfo } from '../../locator'; +import { FSWatchingLocator } from './fsWatchingLocator'; +import { PythonEnvStructure } from '../../../common/pythonBinariesWatcher'; +import { isStorePythonInstalled, getWindowsStoreAppsRoot } from '../../../common/environmentManagers/windowsStoreEnv'; + +/** + * This is a glob pattern which matches following file names: + * python3.8.exe + * python3.9.exe + * python3.10.exe + * This pattern does not match: + * python.exe + * python2.7.exe + * python3.exe + * python38.exe + * Note chokidar fails to match multiple digits using +([0-9]), even though the underlying glob pattern matcher + * they use (picomatch), or any other glob matcher does. Hence why we had to use {[0-9],[0-9][0-9]} instead. + */ +const pythonExeGlob = 'python3.{[0-9],[0-9][0-9]}.exe'; + +/** + * This is a glob pattern which matches following dir names: + * PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0 + * PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0 + * + * Note chokidar fails to match multiple digits using +([0-9]), even though the underlying glob pattern matcher + * they use (picomatch), or any other glob matcher does. Hence why we had to use {[0-9],[0-9][0-9]} instead. + */ +const storePythonDirGlob = 'PythonSoftwareFoundation.Python.3.{[0-9],[0-9][0-9]}_*'; + +/** + * Checks if a given path ends with python3.*.exe. Not all python executables are matched as + * we do not want to return duplicate executables. + * @param {string} interpreterPath : Path to python interpreter. + * @returns {boolean} : Returns true if the path matches pattern for windows python executable. + */ +function isWindowsStorePythonExePattern(interpreterPath: string): boolean { + return minimatch(path.basename(interpreterPath), pythonExeGlob, { nocase: true }); +} + +/** + * Gets paths to the Python executable under Windows Store apps. + * @returns: Returns python*.exe for the windows store app root directory. + * + * Remarks: We don't need to find the path to the interpreter under the specific application + * directory. Such as: + * `%LOCALAPPDATA%/Microsoft/WindowsApps/PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0` + * The same python executable is also available at: + * `%LOCALAPPDATA%/Microsoft/WindowsApps` + * It would be a duplicate. + * + * All python executable under `%LOCALAPPDATA%/Microsoft/WindowsApps` or the sub-directories + * are 'reparse points' that point to the real executable at `%PROGRAMFILES%/WindowsApps`. + * However, that directory is off limits to users. So no need to populate interpreters from + * that location. + */ +export async function getWindowsStorePythonExes(): Promise { + if (await isStorePythonInstalled()) { + const windowsAppsRoot = getWindowsStoreAppsRoot(); + + // Collect python*.exe directly under %LOCALAPPDATA%/Microsoft/WindowsApps + const files = await fsapi.readdir(windowsAppsRoot); + return files + .map((filename: string) => path.join(windowsAppsRoot, filename)) + .filter(isWindowsStorePythonExePattern); + } + return []; +} + +export class WindowsStoreLocator extends FSWatchingLocator { + private readonly kind: PythonEnvKind = PythonEnvKind.WindowsStore; + + constructor() { + // We have to watch the directory instead of the executable here because + // FS events are not triggered for `*.exe` in the WindowsApps folder. The + // .exe files here are reparse points and not real files. Watching the + // PythonSoftwareFoundation directory will trigger both for new install + // and update case. Update is handled by deleting and recreating the + // PythonSoftwareFoundation directory. + super(getWindowsStoreAppsRoot, async () => this.kind, { + baseGlob: storePythonDirGlob, + searchLocation: getWindowsStoreAppsRoot(), + envStructure: PythonEnvStructure.Flat, + }); + } + + protected doIterEnvs(): IPythonEnvsIterator { + const iterator = async function* (kind: PythonEnvKind) { + const exes = await getWindowsStorePythonExes(); + yield* exes.map(async (executablePath: string) => ({ + kind, + executablePath, + })); + }; + return iterator(this.kind); + } +} diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts index 5403f281eec1..0b0e60dd94d0 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts @@ -6,11 +6,8 @@ import { traceVerbose } from '../../../../common/logger'; import { chain, iterable } from '../../../../common/utils/async'; import { findInterpretersInDir, looksLikeBasicVirtualPython } from '../../../common/commonUtils'; import { pathExists } from '../../../common/externalDependencies'; -import { isPipenvEnvironment } from '../../../discovery/locators/services/pipEnvHelper'; -import { - isVenvEnvironment, - isVirtualenvEnvironment, -} from '../../../discovery/locators/services/virtualEnvironmentIdentifier'; +import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv'; +import { isVenvEnvironment, isVirtualenvEnvironment } from '../../../common/environmentManagers/simplevirtualenvs'; import { PythonEnvKind } from '../../info'; import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; import { FSWatchingLocator } from './fsWatchingLocator'; diff --git a/src/client/pythonEnvironments/common/commonUtils.ts b/src/client/pythonEnvironments/common/commonUtils.ts index 22c81f825a90..f6b5ae810542 100644 --- a/src/client/pythonEnvironments/common/commonUtils.ts +++ b/src/client/pythonEnvironments/common/commonUtils.ts @@ -9,8 +9,8 @@ import { logError } from '../../logging'; import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../base/info'; import { comparePythonVersionSpecificity } from '../base/info/env'; import { parseVersion } from '../base/info/pythonVersion'; -import { getPythonVersionFromConda } from '../discovery/locators/services/conda'; -import { getPythonVersionFromPyvenvCfg } from '../discovery/locators/services/virtualEnvironmentIdentifier'; +import { getPythonVersionFromConda } from './environmentManagers/conda'; +import { getPythonVersionFromPyvenvCfg } from './environmentManagers/simplevirtualenvs'; import * as posix from './posixUtils'; import * as windows from './windowsUtils'; diff --git a/src/client/pythonEnvironments/common/environmentIdentifier.ts b/src/client/pythonEnvironments/common/environmentIdentifier.ts index 03756ee09c7e..0dcdbd37e68c 100644 --- a/src/client/pythonEnvironments/common/environmentIdentifier.ts +++ b/src/client/pythonEnvironments/common/environmentIdentifier.ts @@ -3,16 +3,16 @@ import { PythonEnvKind } from '../base/info'; import { getPrioritizedEnvKinds } from '../base/info/envKind'; -import { isCondaEnvironment } from '../discovery/locators/services/conda'; -import { isPipenvEnvironment } from '../discovery/locators/services/pipEnvHelper'; -import { isPoetryEnvironment } from '../discovery/locators/services/poetry'; -import { isPyenvEnvironment } from '../discovery/locators/services/pyenvLocator'; +import { isCondaEnvironment } from './environmentManagers/conda'; +import { isPipenvEnvironment } from './environmentManagers/pipenv'; +import { isPoetryEnvironment } from './environmentManagers/poetry'; +import { isPyenvEnvironment } from './environmentManagers/pyenv'; import { isVenvEnvironment, isVirtualenvEnvironment as isVirtualEnvEnvironment, isVirtualenvwrapperEnvironment as isVirtualEnvWrapperEnvironment, -} from '../discovery/locators/services/virtualEnvironmentIdentifier'; -import { isWindowsStoreEnvironment } from '../discovery/locators/services/windowsStoreLocator'; +} from './environmentManagers/simplevirtualenvs'; +import { isWindowsStoreEnvironment } from './environmentManagers/windowsStoreEnv'; function getIdentifiers(): Map Promise> { const notImplemented = () => Promise.resolve(false); diff --git a/src/client/pythonEnvironments/discovery/locators/services/conda.ts b/src/client/pythonEnvironments/common/environmentManagers/conda.ts similarity index 95% rename from src/client/pythonEnvironments/discovery/locators/services/conda.ts rename to src/client/pythonEnvironments/common/environmentManagers/conda.ts index f058f8cb6674..1eb577ee1b1f 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/conda.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/conda.ts @@ -1,17 +1,17 @@ import * as fsapi from 'fs-extra'; import * as path from 'path'; -import { traceVerbose } from '../../../../common/logger'; -import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform'; -import { exec, pathExists, readFile } from '../../../common/externalDependencies'; - -import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../../base/info'; -import { parseVersion } from '../../../base/info/pythonVersion'; - -import { getRegistryInterpreters } from '../../../common/windowsUtils'; -import { EnvironmentType, PythonEnvironment } from '../../../info'; -import { IDisposable } from '../../../../common/types'; -import { cache } from '../../../../common/utils/decorators'; -import { isTestExecution } from '../../../../common/constants'; +import { traceVerbose } from '../../../common/logger'; +import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../common/utils/platform'; +import { exec, pathExists, readFile } from '../externalDependencies'; + +import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../base/info'; +import { parseVersion } from '../../base/info/pythonVersion'; + +import { getRegistryInterpreters } from '../windowsUtils'; +import { EnvironmentType, PythonEnvironment } from '../../info'; +import { IDisposable } from '../../../common/types'; +import { cache } from '../../../common/utils/decorators'; +import { isTestExecution } from '../../../common/constants'; export const AnacondaCompanyName = 'Anaconda, Inc.'; diff --git a/src/client/pythonEnvironments/discovery/locators/services/pipEnvHelper.ts b/src/client/pythonEnvironments/common/environmentManagers/pipenv.ts similarity index 94% rename from src/client/pythonEnvironments/discovery/locators/services/pipEnvHelper.ts rename to src/client/pythonEnvironments/common/environmentManagers/pipenv.ts index 91ac1c2dcc3a..b17f99d6efa0 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/pipEnvHelper.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/pipenv.ts @@ -1,153 +1,153 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import * as path from 'path'; -import { traceError } from '../../../../common/logger'; -import { getEnvironmentVariable } from '../../../../common/utils/platform'; -import { arePathsSame, pathExists, readFile } from '../../../common/externalDependencies'; - -function getSearchHeight() { - // PIPENV_MAX_DEPTH tells pipenv the maximum number of directories to recursively search for - // a Pipfile, defaults to 3: https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_MAX_DEPTH - const maxDepthStr = getEnvironmentVariable('PIPENV_MAX_DEPTH'); - if (maxDepthStr === undefined) { - return 3; - } - const maxDepth = parseInt(maxDepthStr, 10); - // eslint-disable-next-line no-restricted-globals - if (isNaN(maxDepth)) { - traceError(`PIPENV_MAX_DEPTH is incorrectly set. Converting value '${maxDepthStr}' to number results in NaN`); - return 1; - } - return maxDepth; -} - -/** - * Returns the path to Pipfile associated with the provided directory. - * @param searchDir the directory to look into - * @param lookIntoParentDirectories set to true if we should also search for Pipfile in parent directory - */ -export async function _getAssociatedPipfile( - searchDir: string, - options: { lookIntoParentDirectories: boolean }, -): Promise { - const pipFileName = getEnvironmentVariable('PIPENV_PIPFILE') || 'Pipfile'; - let heightToSearch = options.lookIntoParentDirectories ? getSearchHeight() : 1; - while (heightToSearch > 0 && !arePathsSame(searchDir, path.dirname(searchDir))) { - const pipFile = path.join(searchDir, pipFileName); - if (await pathExists(pipFile)) { - return pipFile; - } - searchDir = path.dirname(searchDir); - heightToSearch -= 1; - } - return undefined; -} - -/** - * If interpreter path belongs to a pipenv environment which is located inside a project, return associated Pipfile, - * otherwise return `undefined`. - * @param interpreterPath Absolute path to any python interpreter. - */ -async function getPipfileIfLocal(interpreterPath: string): Promise { - // Local pipenv environments are created by setting PIPENV_VENV_IN_PROJECT to 1, which always names the environment - // folder '.venv': https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_VENV_IN_PROJECT - // This is the layout we wish to verify. - // project - // |__ Pipfile <--- check if Pipfile exists here - // |__ .venv <--- check if name of the folder is '.venv' - // |__ Scripts/bin - // |__ python <--- interpreterPath - const venvFolder = path.dirname(path.dirname(interpreterPath)); - if (path.basename(venvFolder) !== '.venv') { - return undefined; - } - const directoryWhereVenvResides = path.dirname(venvFolder); - return _getAssociatedPipfile(directoryWhereVenvResides, { lookIntoParentDirectories: false }); -} - -/** - * Returns the project directory for pipenv environments given the environment folder - * @param envFolder Path to the environment folder - */ -async function getProjectDir(envFolder: string): Promise { - // Global pipenv environments have a .project file with the absolute path to the project - // See https://github.com/pypa/pipenv/blob/v2018.6.25/CHANGELOG.rst#features--improvements - // This is the layout we expect - // - // |__ .project <--- check if .project exists here - // |__ Scripts/bin - // |__ python <--- interpreterPath - // We get the project by reading the .project file - const dotProjectFile = path.join(envFolder, '.project'); - if (!(await pathExists(dotProjectFile))) { - return undefined; - } - const projectDir = await readFile(dotProjectFile); - if (!(await pathExists(projectDir))) { - traceError( - `The .project file inside environment folder: ${envFolder} doesn't contain a valid path to the project`, - ); - return undefined; - } - return projectDir; -} - -/** - * If interpreter path belongs to a global pipenv environment, return associated Pipfile, otherwise return `undefined`. - * @param interpreterPath Absolute path to any python interpreter. - */ -async function getPipfileIfGlobal(interpreterPath: string): Promise { - const envFolder = path.dirname(path.dirname(interpreterPath)); - const projectDir = await getProjectDir(envFolder); - if (projectDir === undefined) { - return undefined; - } - - // This is the layout we expect to see. - // project - // |__ Pipfile <--- check if Pipfile exists here and return it - // The name of the project (directory where Pipfile resides) is used as a prefix in the environment folder - const envFolderName = path.basename(envFolder); - if (!envFolderName.startsWith(`${path.basename(projectDir)}-`)) { - return undefined; - } - - return _getAssociatedPipfile(projectDir, { lookIntoParentDirectories: false }); -} - -/** - * Checks if the given interpreter path belongs to a pipenv environment, by locating the Pipfile which was used to - * create the environment. - * @param interpreterPath: Absolute path to any python interpreter. - */ -export async function isPipenvEnvironment(interpreterPath: string): Promise { - if (await getPipfileIfLocal(interpreterPath)) { - return true; - } - if (await getPipfileIfGlobal(interpreterPath)) { - return true; - } - return false; -} - -/** - * Returns true if interpreter path belongs to a global pipenv environment which is associated with a particular folder, - * false otherwise. - * @param interpreterPath Absolute path to any python interpreter. - */ -export async function isPipenvEnvironmentRelatedToFolder(interpreterPath: string, folder: string): Promise { - const pipFileAssociatedWithEnvironment = await getPipfileIfGlobal(interpreterPath); - if (!pipFileAssociatedWithEnvironment) { - return false; - } - - // PIPENV_NO_INHERIT is used to tell pipenv not to look for Pipfile in parent directories - // https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_NO_INHERIT - const lookIntoParentDirectories = getEnvironmentVariable('PIPENV_NO_INHERIT') === undefined; - const pipFileAssociatedWithFolder = await _getAssociatedPipfile(folder, { lookIntoParentDirectories }); - if (!pipFileAssociatedWithFolder) { - return false; - } - return arePathsSame(pipFileAssociatedWithEnvironment, pipFileAssociatedWithFolder); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as path from 'path'; +import { traceError } from '../../../common/logger'; +import { getEnvironmentVariable } from '../../../common/utils/platform'; +import { arePathsSame, pathExists, readFile } from '../externalDependencies'; + +function getSearchHeight() { + // PIPENV_MAX_DEPTH tells pipenv the maximum number of directories to recursively search for + // a Pipfile, defaults to 3: https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_MAX_DEPTH + const maxDepthStr = getEnvironmentVariable('PIPENV_MAX_DEPTH'); + if (maxDepthStr === undefined) { + return 3; + } + const maxDepth = parseInt(maxDepthStr, 10); + // eslint-disable-next-line no-restricted-globals + if (isNaN(maxDepth)) { + traceError(`PIPENV_MAX_DEPTH is incorrectly set. Converting value '${maxDepthStr}' to number results in NaN`); + return 1; + } + return maxDepth; +} + +/** + * Returns the path to Pipfile associated with the provided directory. + * @param searchDir the directory to look into + * @param lookIntoParentDirectories set to true if we should also search for Pipfile in parent directory + */ +export async function _getAssociatedPipfile( + searchDir: string, + options: { lookIntoParentDirectories: boolean }, +): Promise { + const pipFileName = getEnvironmentVariable('PIPENV_PIPFILE') || 'Pipfile'; + let heightToSearch = options.lookIntoParentDirectories ? getSearchHeight() : 1; + while (heightToSearch > 0 && !arePathsSame(searchDir, path.dirname(searchDir))) { + const pipFile = path.join(searchDir, pipFileName); + if (await pathExists(pipFile)) { + return pipFile; + } + searchDir = path.dirname(searchDir); + heightToSearch -= 1; + } + return undefined; +} + +/** + * If interpreter path belongs to a pipenv environment which is located inside a project, return associated Pipfile, + * otherwise return `undefined`. + * @param interpreterPath Absolute path to any python interpreter. + */ +async function getPipfileIfLocal(interpreterPath: string): Promise { + // Local pipenv environments are created by setting PIPENV_VENV_IN_PROJECT to 1, which always names the environment + // folder '.venv': https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_VENV_IN_PROJECT + // This is the layout we wish to verify. + // project + // |__ Pipfile <--- check if Pipfile exists here + // |__ .venv <--- check if name of the folder is '.venv' + // |__ Scripts/bin + // |__ python <--- interpreterPath + const venvFolder = path.dirname(path.dirname(interpreterPath)); + if (path.basename(venvFolder) !== '.venv') { + return undefined; + } + const directoryWhereVenvResides = path.dirname(venvFolder); + return _getAssociatedPipfile(directoryWhereVenvResides, { lookIntoParentDirectories: false }); +} + +/** + * Returns the project directory for pipenv environments given the environment folder + * @param envFolder Path to the environment folder + */ +async function getProjectDir(envFolder: string): Promise { + // Global pipenv environments have a .project file with the absolute path to the project + // See https://github.com/pypa/pipenv/blob/v2018.6.25/CHANGELOG.rst#features--improvements + // This is the layout we expect + // + // |__ .project <--- check if .project exists here + // |__ Scripts/bin + // |__ python <--- interpreterPath + // We get the project by reading the .project file + const dotProjectFile = path.join(envFolder, '.project'); + if (!(await pathExists(dotProjectFile))) { + return undefined; + } + const projectDir = await readFile(dotProjectFile); + if (!(await pathExists(projectDir))) { + traceError( + `The .project file inside environment folder: ${envFolder} doesn't contain a valid path to the project`, + ); + return undefined; + } + return projectDir; +} + +/** + * If interpreter path belongs to a global pipenv environment, return associated Pipfile, otherwise return `undefined`. + * @param interpreterPath Absolute path to any python interpreter. + */ +async function getPipfileIfGlobal(interpreterPath: string): Promise { + const envFolder = path.dirname(path.dirname(interpreterPath)); + const projectDir = await getProjectDir(envFolder); + if (projectDir === undefined) { + return undefined; + } + + // This is the layout we expect to see. + // project + // |__ Pipfile <--- check if Pipfile exists here and return it + // The name of the project (directory where Pipfile resides) is used as a prefix in the environment folder + const envFolderName = path.basename(envFolder); + if (!envFolderName.startsWith(`${path.basename(projectDir)}-`)) { + return undefined; + } + + return _getAssociatedPipfile(projectDir, { lookIntoParentDirectories: false }); +} + +/** + * Checks if the given interpreter path belongs to a pipenv environment, by locating the Pipfile which was used to + * create the environment. + * @param interpreterPath: Absolute path to any python interpreter. + */ +export async function isPipenvEnvironment(interpreterPath: string): Promise { + if (await getPipfileIfLocal(interpreterPath)) { + return true; + } + if (await getPipfileIfGlobal(interpreterPath)) { + return true; + } + return false; +} + +/** + * Returns true if interpreter path belongs to a global pipenv environment which is associated with a particular folder, + * false otherwise. + * @param interpreterPath Absolute path to any python interpreter. + */ +export async function isPipenvEnvironmentRelatedToFolder(interpreterPath: string, folder: string): Promise { + const pipFileAssociatedWithEnvironment = await getPipfileIfGlobal(interpreterPath); + if (!pipFileAssociatedWithEnvironment) { + return false; + } + + // PIPENV_NO_INHERIT is used to tell pipenv not to look for Pipfile in parent directories + // https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_NO_INHERIT + const lookIntoParentDirectories = getEnvironmentVariable('PIPENV_NO_INHERIT') === undefined; + const pipFileAssociatedWithFolder = await _getAssociatedPipfile(folder, { lookIntoParentDirectories }); + if (!pipFileAssociatedWithFolder) { + return false; + } + return arePathsSame(pipFileAssociatedWithEnvironment, pipFileAssociatedWithFolder); +} diff --git a/src/client/pythonEnvironments/discovery/locators/services/poetry.ts b/src/client/pythonEnvironments/common/environmentManagers/poetry.ts similarity index 94% rename from src/client/pythonEnvironments/discovery/locators/services/poetry.ts rename to src/client/pythonEnvironments/common/environmentManagers/poetry.ts index 08d4fcd975d0..eb52c5b653d5 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/poetry.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/poetry.ts @@ -4,19 +4,14 @@ 'use strict'; import * as path from 'path'; -import { traceError, traceVerbose } from '../../../../common/logger'; -import { getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform'; -import { - getPythonSetting, - isParentPath, - pathExistsSync, - readFileSync, - shellExecute, -} from '../../../common/externalDependencies'; -import { getEnvironmentDirFromPath } from '../../../common/commonUtils'; -import { isVirtualenvEnvironment } from './virtualEnvironmentIdentifier'; -import { StopWatch } from '../../../../common/utils/stopWatch'; -import { cache } from '../../../../common/utils/decorators'; +import { traceError, traceVerbose } from '../../../common/logger'; +import { getOSType, getUserHomeDir, OSType } from '../../../common/utils/platform'; +import { getPythonSetting, isParentPath, pathExistsSync, readFileSync, shellExecute } from '../externalDependencies'; +import { getEnvironmentDirFromPath } from '../commonUtils'; +import { isVirtualenvEnvironment } from './simplevirtualenvs'; +import { StopWatch } from '../../../common/utils/stopWatch'; +import { cache } from '../../../common/utils/decorators'; +import { isTestExecution } from '../../../common/constants'; /** * Global virtual env dir for a project is named as: @@ -96,7 +91,7 @@ export class Poetry { * Locating poetry binary can be expensive, since it potentially involves spawning or * trying to spawn processes; so we only do it once per session. */ - public static _poetryPromise: Map> = new Map< + private static poetryPromise: Map> = new Map< string, Promise >(); @@ -126,10 +121,10 @@ export class Poetry { return undefined; } traceVerbose(`Getting poetry for cwd ${cwd}`); - if (Poetry._poetryPromise.get(cwd) === undefined) { - Poetry._poetryPromise.set(cwd, Poetry.locate(cwd)); + if (Poetry.poetryPromise.get(cwd) === undefined || isTestExecution()) { + Poetry.poetryPromise.set(cwd, Poetry.locate(cwd)); } - return Poetry._poetryPromise.get(cwd); + return Poetry.poetryPromise.get(cwd); } private static async locate(cwd: string): Promise { diff --git a/src/client/pythonEnvironments/discovery/locators/services/pyenvLocator.ts b/src/client/pythonEnvironments/common/environmentManagers/pyenv.ts similarity index 69% rename from src/client/pythonEnvironments/discovery/locators/services/pyenvLocator.ts rename to src/client/pythonEnvironments/common/environmentManagers/pyenv.ts index 188a067859f2..e4aa47e7154f 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/pyenvLocator.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/pyenv.ts @@ -1,16 +1,8 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - import * as path from 'path'; -import { traceError } from '../../../../common/logger'; -import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform'; -import { PythonEnvKind } from '../../../base/info'; -import { BasicEnvInfo, IPythonEnvsIterator } from '../../../base/locator'; -import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator'; -import { getInterpreterPathFromDir } from '../../../common/commonUtils'; -import { arePathsSame, getSubDirs, pathExists } from '../../../common/externalDependencies'; +import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../common/utils/platform'; +import { arePathsSame, pathExists } from '../externalDependencies'; -function getPyenvDir(): string { +export function getPyenvDir(): string { // Check if the pyenv environment variables exist: PYENV on Windows, PYENV_ROOT on Unix. // They contain the path to pyenv's installation folder. // If they don't exist, use the default path: ~/.pyenv/pyenv-win on Windows, ~/.pyenv on Unix. @@ -27,27 +19,23 @@ function getPyenvDir(): string { return pyenvDir; } - -function getPyenvVersionsDir(): string { - return path.join(getPyenvDir(), 'versions'); -} - /** * Checks if a given directory path is same as `pyenv` shims path. This checks * `~/.pyenv/shims` on posix and `~/.pyenv/pyenv-win/shims` on windows. * @param {string} dirPath: Absolute path to any directory * @returns {boolean}: Returns true if the patch is same as `pyenv` shims directory. */ + export function isPyenvShimDir(dirPath: string): boolean { const shimPath = path.join(getPyenvDir(), 'shims'); return arePathsSame(shimPath, dirPath) || arePathsSame(`${shimPath}${path.sep}`, dirPath); } - /** * Checks if the given interpreter belongs to a pyenv based environment. * @param {string} interpreterPath: Absolute path to the python interpreter. * @returns {boolean}: Returns true if the interpreter belongs to a pyenv environment. */ + export async function isPyenvEnvironment(interpreterPath: string): Promise { let pathToCheck = interpreterPath; let pyenvDir = getPyenvDir(); @@ -73,7 +61,6 @@ export interface IPyenvVersionStrings { distro?: string; distroVer?: string; } - /** * This function provides parsers for some of the common and known distributions * supported by pyenv. To get the list of supported pyenv distributions, run @@ -81,7 +68,7 @@ export interface IPyenvVersionStrings { * * The parsers below were written based on the list obtained from pyenv version 1.2.21 */ -function getKnownPyenvVersionParsers(): Map Promise> { +function getKnownPyenvVersionParsers(): Map IPyenvVersionStrings | undefined> { /** * This function parses versions that are plain python versions. * @param str string to parse @@ -90,12 +77,12 @@ function getKnownPyenvVersionParsers(): Map Promise { - return Promise.resolve({ + function pythonOnly(str: string): IPyenvVersionStrings { + return { pythonVer: str, distro: undefined, distroVer: undefined, - }); + }; } /** @@ -106,29 +93,29 @@ function getKnownPyenvVersionParsers(): Map Promise { + function distroOnly(str: string): IPyenvVersionStrings | undefined { const parts = str.split('-'); if (parts.length === 3) { - return Promise.resolve({ + return { pythonVer: undefined, distroVer: `${parts[1]}-${parts[2]}`, distro: parts[0], - }); + }; } if (parts.length === 2) { - return Promise.resolve({ + return { pythonVer: undefined, distroVer: parts[1], distro: parts[0], - }); + }; } - return Promise.resolve({ + return { pythonVer: undefined, distroVer: undefined, distro: str, - }); + }; } /** @@ -148,17 +135,17 @@ function getKnownPyenvVersionParsers(): Map Promise { + function pypyParser(str: string): IPyenvVersionStrings | undefined { const pattern = /[0-9\.]+/; const parts = str.split('-'); const pythonVer = parts[0].search(pattern) > 0 ? parts[0].substr('pypy'.length) : undefined; if (parts.length === 2) { - return Promise.resolve({ + return { pythonVer, distroVer: parts[1], distro: 'pypy', - }); + }; } if ( @@ -169,45 +156,45 @@ function getKnownPyenvVersionParsers(): Map Promise Promise> = new Map(); + const parsers: Map IPyenvVersionStrings | undefined> = new Map(); parsers.set('activepython', distroOnly); parsers.set('anaconda', distroOnly); parsers.set('graalpython', distroOnly); @@ -224,7 +211,6 @@ function getKnownPyenvVersionParsers(): Map Promise Promise { + +export function parsePyenvVersion(str: string): IPyenvVersionStrings | undefined { const allParsers = getKnownPyenvVersionParsers(); const knownPrefixes = Array.from(allParsers.keys()); @@ -246,42 +233,5 @@ export function parsePyenvVersion(str: string): Promise/versions directory and gets - * all the environments (global or virtual) in that directory. - */ -async function* getPyenvEnvironments(): AsyncIterableIterator { - const pyenvVersionDir = getPyenvVersionsDir(); - - const subDirs = getSubDirs(pyenvVersionDir, { resolveSymlinks: true }); - for await (const subDirPath of subDirs) { - const interpreterPath = await getInterpreterPathFromDir(subDirPath); - - if (interpreterPath) { - try { - yield { - kind: PythonEnvKind.Pyenv, - executablePath: interpreterPath, - }; - } catch (ex) { - traceError(`Failed to process environment: ${interpreterPath}`, ex); - } - } - } -} - -export class PyenvLocator extends FSWatchingLocator { - constructor() { - super(getPyenvVersionsDir, async () => PythonEnvKind.Pyenv); - } - - // eslint-disable-next-line class-methods-use-this - public doIterEnvs(): IPythonEnvsIterator { - return getPyenvEnvironments(); - } + return undefined; } diff --git a/src/client/pythonEnvironments/discovery/locators/services/virtualEnvironmentIdentifier.ts b/src/client/pythonEnvironments/common/environmentManagers/simplevirtualenvs.ts similarity index 95% rename from src/client/pythonEnvironments/discovery/locators/services/virtualEnvironmentIdentifier.ts rename to src/client/pythonEnvironments/common/environmentManagers/simplevirtualenvs.ts index 37387e092d1b..824c540689ed 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/virtualEnvironmentIdentifier.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/simplevirtualenvs.ts @@ -3,12 +3,12 @@ import * as fsapi from 'fs-extra'; import * as path from 'path'; -import '../../../../common/extensions'; -import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform'; -import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../../base/info'; -import { comparePythonVersionSpecificity } from '../../../base/info/env'; -import { parseBasicVersion, parseRelease, parseVersion } from '../../../base/info/pythonVersion'; -import { pathExists, readFile } from '../../../common/externalDependencies'; +import '../../../common/extensions'; +import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../common/utils/platform'; +import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../base/info'; +import { comparePythonVersionSpecificity } from '../../base/info/env'; +import { parseBasicVersion, parseRelease, parseVersion } from '../../base/info/pythonVersion'; +import { pathExists, readFile } from '../externalDependencies'; function getPyvenvConfigPathsFrom(interpreterPath: string): string[] { const pyvenvConfigFile = 'pyvenv.cfg'; diff --git a/src/client/pythonEnvironments/discovery/locators/services/windowsStoreLocator.ts b/src/client/pythonEnvironments/common/environmentManagers/windowsStoreEnv.ts similarity index 52% rename from src/client/pythonEnvironments/discovery/locators/services/windowsStoreLocator.ts rename to src/client/pythonEnvironments/common/environmentManagers/windowsStoreEnv.ts index 080ed9122457..098cb75eb6eb 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/windowsStoreLocator.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/windowsStoreEnv.ts @@ -1,27 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import * as fsapi from 'fs-extra'; -import * as minimatch from 'minimatch'; import * as path from 'path'; -import { traceWarning } from '../../../../common/logger'; -import { getEnvironmentVariable } from '../../../../common/utils/platform'; -import { PythonEnvKind } from '../../../base/info'; -import { IPythonEnvsIterator, BasicEnvInfo } from '../../../base/locator'; -import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator'; -import { pathExists } from '../../../common/externalDependencies'; -import { PythonEnvStructure } from '../../../common/pythonBinariesWatcher'; +import { traceWarning } from '../../../common/logger'; +import { getEnvironmentVariable } from '../../../common/utils/platform'; +import { pathExists } from '../externalDependencies'; /** * Gets path to the Windows Apps directory. * @returns {string} : Returns path to the Windows Apps directory under * `%LOCALAPPDATA%/Microsoft/WindowsApps`. */ -function getWindowsStoreAppsRoot(): string { +export function getWindowsStoreAppsRoot(): string { const localAppData = getEnvironmentVariable('LOCALAPPDATA') || ''; return path.join(localAppData, 'Microsoft', 'WindowsApps'); } - /** * Checks if a given path is under the forbidden windows store directory. * @param {string} absPath : Absolute path to a file or directory. @@ -35,7 +28,6 @@ function isForbiddenStorePath(absPath: string): boolean { .toUpperCase(); return path.normalize(absPath).toUpperCase().includes(programFilesStorePath); } - /** * Checks if a given directory is any one of the possible windows store directories, or * its sub-directory. @@ -46,11 +38,11 @@ function isForbiddenStorePath(absPath: string): boolean { * 1. %LOCALAPPDATA%/Microsoft/WindowsApps * 2. %ProgramFiles%/WindowsApps */ + export function isWindowsStoreDir(dirPath: string): boolean { const storeRootPath = path.normalize(getWindowsStoreAppsRoot()).toUpperCase(); return path.normalize(dirPath).toUpperCase().includes(storeRootPath) || isForbiddenStorePath(dirPath); } - /** * Checks if store python is installed. * @param {string} interpreterPath : Absolute path to a interpreter. @@ -59,7 +51,7 @@ export function isWindowsStoreDir(dirPath: string): boolean { * have idle.exe or pip.exe. We can use this as a way to identify the python.exe * found in the store apps directory is a real python or a store install shortcut. */ -async function isStorePythonInstalled(interpreterPath?: string): Promise { +export async function isStorePythonInstalled(interpreterPath?: string): Promise { let results = await Promise.all([ pathExists(path.join(getWindowsStoreAppsRoot(), 'idle.exe')), pathExists(path.join(getWindowsStoreAppsRoot(), 'pip.exe')), @@ -78,7 +70,6 @@ async function isStorePythonInstalled(interpreterPath?: string): Promise { if (await isStorePythonInstalled(interpreterPath)) { const pythonPathToCompare = path.normalize(interpreterPath).toUpperCase(); @@ -128,96 +120,3 @@ export async function isWindowsStoreEnvironment(interpreterPath: string): Promis } return false; } - -/** - * This is a glob pattern which matches following file names: - * python3.8.exe - * python3.9.exe - * python3.10.exe - * This pattern does not match: - * python.exe - * python2.7.exe - * python3.exe - * python38.exe - * Note chokidar fails to match multiple digits using +([0-9]), even though the underlying glob pattern matcher - * they use (picomatch), or any other glob matcher does. Hence why we had to use {[0-9],[0-9][0-9]} instead. - */ -const pythonExeGlob = 'python3.{[0-9],[0-9][0-9]}.exe'; - -/** - * This is a glob pattern which matches following dir names: - * PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0 - * PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0 - * - * Note chokidar fails to match multiple digits using +([0-9]), even though the underlying glob pattern matcher - * they use (picomatch), or any other glob matcher does. Hence why we had to use {[0-9],[0-9][0-9]} instead. - */ -const storePythonDirGlob = 'PythonSoftwareFoundation.Python.3.{[0-9],[0-9][0-9]}_*'; - -/** - * Checks if a given path ends with python3.*.exe. Not all python executables are matched as - * we do not want to return duplicate executables. - * @param {string} interpreterPath : Path to python interpreter. - * @returns {boolean} : Returns true if the path matches pattern for windows python executable. - */ -function isWindowsStorePythonExePattern(interpreterPath: string): boolean { - return minimatch(path.basename(interpreterPath), pythonExeGlob, { nocase: true }); -} - -/** - * Gets paths to the Python executable under Windows Store apps. - * @returns: Returns python*.exe for the windows store app root directory. - * - * Remarks: We don't need to find the path to the interpreter under the specific application - * directory. Such as: - * `%LOCALAPPDATA%/Microsoft/WindowsApps/PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0` - * The same python executable is also available at: - * `%LOCALAPPDATA%/Microsoft/WindowsApps` - * It would be a duplicate. - * - * All python executable under `%LOCALAPPDATA%/Microsoft/WindowsApps` or the sub-directories - * are 'reparse points' that point to the real executable at `%PROGRAMFILES%/WindowsApps`. - * However, that directory is off limits to users. So no need to populate interpreters from - * that location. - */ -export async function getWindowsStorePythonExes(): Promise { - if (await isStorePythonInstalled()) { - const windowsAppsRoot = getWindowsStoreAppsRoot(); - - // Collect python*.exe directly under %LOCALAPPDATA%/Microsoft/WindowsApps - const files = await fsapi.readdir(windowsAppsRoot); - return files - .map((filename: string) => path.join(windowsAppsRoot, filename)) - .filter(isWindowsStorePythonExePattern); - } - return []; -} - -export class WindowsStoreLocator extends FSWatchingLocator { - private readonly kind: PythonEnvKind = PythonEnvKind.WindowsStore; - - constructor() { - // We have to watch the directory instead of the executable here because - // FS events are not triggered for `*.exe` in the WindowsApps folder. The - // .exe files here are reparse points and not real files. Watching the - // PythonSoftwareFoundation directory will trigger both for new install - // and update case. Update is handled by deleting and recreating the - // PythonSoftwareFoundation directory. - super(getWindowsStoreAppsRoot, async () => this.kind, { - baseGlob: storePythonDirGlob, - searchLocation: getWindowsStoreAppsRoot(), - envStructure: PythonEnvStructure.Flat, - }); - } - - protected doIterEnvs(): IPythonEnvsIterator { - const iterator = async function* (kind: PythonEnvKind) { - const exes = await getWindowsStorePythonExes(); - yield* exes.map(async (executablePath: string) => ({ - kind, - executablePath, - })); - }; - return iterator(this.kind); - } -} diff --git a/src/client/pythonEnvironments/discovery/locators/index.ts b/src/client/pythonEnvironments/discovery/locators/index.ts index 824d7dfaf8bc..4038ddc39a6a 100644 --- a/src/client/pythonEnvironments/discovery/locators/index.ts +++ b/src/client/pythonEnvironments/discovery/locators/index.ts @@ -6,10 +6,8 @@ import { Disposable, Event, EventEmitter, Uri } from 'vscode'; import { traceDecorators } from '../../../common/logger'; import { IPlatformService } from '../../../common/platform/types'; import { IDisposableRegistry } from '../../../common/types'; -import { createDeferred, Deferred, iterEmpty } from '../../../common/utils/async'; -import { getURIFilter } from '../../../common/utils/misc'; +import { createDeferred, Deferred } from '../../../common/utils/async'; import { OSType } from '../../../common/utils/platform'; -import { Disposables, IDisposable } from '../../../common/utils/resourceLifecycle'; import { CONDA_ENV_FILE_SERVICE, CONDA_ENV_SERVICE, @@ -24,141 +22,9 @@ import { WORKSPACE_VIRTUAL_ENV_SERVICE, } from '../../../interpreter/contracts'; import { IServiceContainer } from '../../../ioc/types'; -import { PythonEnvInfo } from '../../base/info'; -import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from '../../base/locator'; -import { combineIterators, Locators } from '../../base/locators'; -import { LazyResourceBasedLocator } from '../../base/locators/common/resourceBasedLocator'; import { PythonEnvironment } from '../../info'; import { isHiddenInterpreter } from './services/interpreterFilter'; -/** - * A wrapper around all locators used by the extension. - */ -export class ExtensionLocators extends Locators { - constructor( - // These are expected to be low-level locators (e.g. system). - nonWorkspace: ILocator[], - // This is expected to be a locator wrapping any found in - // the workspace (i.e. WorkspaceLocators). - workspace: ILocator, - ) { - super([...nonWorkspace, workspace]); - } -} - -type WorkspaceLocatorFactoryResult = ILocator & Partial; -type WorkspaceLocatorFactory = (root: Uri) => WorkspaceLocatorFactoryResult[]; - -type RootURI = string; - -export type WatchRootsArgs = { - initRoot(root: Uri): void; - addRoot(root: Uri): void; - removeRoot(root: Uri): void; -}; -type WatchRootsFunc = (args: WatchRootsArgs) => IDisposable; - -// XXX Factor out RootedLocators and MultiRootedLocators. - -/** - * The collection of all workspace-specific locators used by the extension. - * - * The factories are used to produce the locators for each workspace folder. - */ -export class WorkspaceLocators extends LazyResourceBasedLocator { - private readonly locators: Record, IDisposable]> = {}; - - private readonly roots: Record = {}; - - constructor(private readonly watchRoots: WatchRootsFunc, private readonly factories: WorkspaceLocatorFactory[]) { - super(); - } - - public async dispose(): Promise { - await super.dispose(); - - // Clear all the roots. - const roots = Object.keys(this.roots).map((key) => this.roots[key]); - roots.forEach((root) => this.removeRoot(root)); - } - - protected doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { - const iterators = Object.keys(this.locators).map((key) => { - if (query?.searchLocations !== undefined) { - const root = this.roots[key]; - // Match any related search location. - const filter = getURIFilter(root, { checkParent: true, checkChild: true, checkExact: true }); - // Ignore any requests for global envs. - if (!query.searchLocations.roots.some(filter)) { - // This workspace folder did not match the query, so skip it! - return iterEmpty(); - } - } - // The query matches or was not location-specific. - const [locator] = this.locators[key]; - return locator.iterEnvs(query); - }); - return combineIterators(iterators); - } - - protected async initResources(): Promise { - const disposable = this.watchRoots({ - initRoot: (root: Uri) => this.addRoot(root), - addRoot: (root: Uri) => { - // Drop the old one, if necessary. - this.removeRoot(root); - this.addRoot(root); - this.emitter.fire({ searchLocation: root }); - }, - removeRoot: (root: Uri) => { - this.removeRoot(root); - this.emitter.fire({ searchLocation: root }); - }, - }); - this.disposables.push(disposable); - } - - private addRoot(root: Uri): void { - // Create the root's locator, wrapping each factory-generated locator. - const locators: ILocator[] = []; - const disposables = new Disposables(); - this.factories.forEach((create) => { - create(root).forEach((loc) => { - locators.push(loc); - if (loc.dispose !== undefined) { - disposables.push(loc as IDisposable); - } - }); - }); - const locator = new Locators(locators); - // Cache it. - const key = root.toString(); - this.locators[key] = [locator, disposables]; - this.roots[key] = root; - // Hook up the watchers. - disposables.push( - locator.onChanged((e) => { - if (e.searchLocation === undefined) { - e.searchLocation = root; - } - this.emitter.fire(e); - }), - ); - } - - private removeRoot(root: Uri): void { - const key = root.toString(); - const found = this.locators[key]; - if (found === undefined) { - return; - } - const [, disposables] = found; - delete this.locators[key]; - delete this.roots[key]; - disposables.dispose(); - } -} - /** * Facilitates locating Python interpreters. */ diff --git a/src/client/pythonEnvironments/discovery/locators/services/condaEnvFileService.ts b/src/client/pythonEnvironments/discovery/locators/services/condaEnvFileService.ts index 3d0678f169c0..83825d58af71 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/condaEnvFileService.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/condaEnvFileService.ts @@ -17,7 +17,7 @@ import { ICondaLocatorService, IInterpreterHelper } from '../../../../interprete import { IServiceContainer } from '../../../../ioc/types'; import { EnvironmentType, PythonEnvironment } from '../../../info'; import { CacheableLocatorService } from './cacheableLocatorService'; -import { AnacondaCompanyName } from './conda'; +import { AnacondaCompanyName } from '../../../common/environmentManagers/conda'; /** * Locate conda env interpreters based on the "conda environments file". diff --git a/src/client/pythonEnvironments/discovery/locators/services/condaEnvService.ts b/src/client/pythonEnvironments/discovery/locators/services/condaEnvService.ts index eb1f128c3fa9..d9de871996c2 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/condaEnvService.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/condaEnvService.ts @@ -9,7 +9,7 @@ import { ICondaLocatorService, IInterpreterHelper } from '../../../../interprete import { IServiceContainer } from '../../../../ioc/types'; import { PythonEnvironment } from '../../../info'; import { CacheableLocatorService } from './cacheableLocatorService'; -import { parseCondaInfo } from './conda'; +import { parseCondaInfo } from '../../../common/environmentManagers/conda'; /** * Locates conda env interpreters based on the conda service's info. diff --git a/src/client/pythonEnvironments/discovery/locators/services/condaLocatorService.ts b/src/client/pythonEnvironments/discovery/locators/services/condaLocatorService.ts index 83c6c8e18731..0283e0fffb70 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/condaLocatorService.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/condaLocatorService.ts @@ -26,7 +26,7 @@ import { import { IServiceContainer } from '../../../../ioc/types'; import { compareSemVerLikeVersions } from '../../../base/info/pythonVersion'; import { EnvironmentType, PythonEnvironment } from '../../../info'; -import { CondaEnvironmentInfo, CondaInfo } from './conda'; +import { CondaEnvironmentInfo, CondaInfo } from '../../../common/environmentManagers/conda'; import { parseCondaEnvFileContents } from './condaHelper'; const untildify: (value: string) => string = require('untildify'); diff --git a/src/client/pythonEnvironments/discovery/locators/services/condaService.ts b/src/client/pythonEnvironments/discovery/locators/services/condaService.ts index 097ecddcecd6..82551230809c 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/condaService.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/condaService.ts @@ -11,7 +11,7 @@ import { IExperimentService, IConfigurationService, IDisposableRegistry } from ' import { cache } from '../../../../common/utils/decorators'; import { ICondaService, ICondaLocatorService } from '../../../../interpreter/contracts'; import { IServiceContainer } from '../../../../ioc/types'; -import { Conda, CondaInfo } from './conda'; +import { Conda, CondaInfo } from '../../../common/environmentManagers/conda'; /** * A wrapper around a conda installation. diff --git a/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryService.ts b/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryService.ts index fbfa112772f0..8f9c622a4fae 100644 --- a/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryService.ts +++ b/src/client/pythonEnvironments/discovery/locators/services/windowsRegistryService.ts @@ -9,7 +9,7 @@ import { IServiceContainer } from '../../../../ioc/types'; import { EnvironmentType, PythonEnvironment } from '../../../info'; import { parsePythonVersion } from '../../../info/pythonVersion'; import { CacheableLocatorService } from './cacheableLocatorService'; -import { AnacondaCompanyName } from './conda'; +import { AnacondaCompanyName } from '../../../common/environmentManagers/conda'; import { isWindowsStoreInterpreter, isRestrictedWindowsStoreInterpreterPath } from './windowsStoreInterpreter'; const flatten = require('lodash/flatten'); diff --git a/src/client/pythonEnvironments/index.ts b/src/client/pythonEnvironments/index.ts index 454fd9a243e2..a31bbc7af3d9 100644 --- a/src/client/pythonEnvironments/index.ts +++ b/src/client/pythonEnvironments/index.ts @@ -17,17 +17,17 @@ import { WindowsPathEnvVarLocator } from './base/locators/lowLevel/windowsKnownP import { WorkspaceVirtualEnvironmentLocator } from './base/locators/lowLevel/workspaceVirtualEnvLocator'; import { getEnvs } from './base/locatorUtils'; import { initializeExternalDependencies as initializeLegacyExternalDependencies } from './common/externalDependencies'; -import { ExtensionLocators, WatchRootsArgs, WorkspaceLocators } from './discovery/locators'; -import { CustomVirtualEnvironmentLocator } from './discovery/locators/services/customVirtualEnvLocator'; -import { CondaEnvironmentLocator } from './discovery/locators/services/condaLocator'; -import { GlobalVirtualEnvironmentLocator } from './discovery/locators/services/globalVirtualEnvronmentLocator'; -import { PosixKnownPathsLocator } from './discovery/locators/services/posixKnownPathsLocator'; -import { PyenvLocator } from './discovery/locators/services/pyenvLocator'; -import { WindowsRegistryLocator } from './discovery/locators/services/windowsRegistryLocator'; -import { WindowsStoreLocator } from './discovery/locators/services/windowsStoreLocator'; -import { getEnvironmentInfoService } from './info/environmentInfoService'; +import { ExtensionLocators, WatchRootsArgs, WorkspaceLocators } from './base/locators/'; +import { CustomVirtualEnvironmentLocator } from './base/locators/lowLevel/customVirtualEnvLocator'; +import { CondaEnvironmentLocator } from './base/locators/lowLevel/condaLocator'; +import { GlobalVirtualEnvironmentLocator } from './base/locators/lowLevel/globalVirtualEnvronmentLocator'; +import { PosixKnownPathsLocator } from './base/locators/lowLevel/posixKnownPathsLocator'; +import { PyenvLocator } from './base/locators/lowLevel/pyenvLocator'; +import { WindowsRegistryLocator } from './base/locators/lowLevel/windowsRegistryLocator'; +import { WindowsStoreLocator } from './base/locators/lowLevel/windowsStoreLocator'; +import { getEnvironmentInfoService } from './base/info/environmentInfoService'; import { isComponentEnabled, registerLegacyDiscoveryForIOC, registerNewDiscoveryForIOC } from './legacyIOC'; -import { PoetryLocator } from './discovery/locators/services/poetryLocator'; +import { PoetryLocator } from './base/locators/lowLevel/poetryLocator'; /** * Set up the Python environments component (during extension activation).' diff --git a/src/client/pythonEnvironments/legacyIOC.ts b/src/client/pythonEnvironments/legacyIOC.ts index 828de589c238..5ecde18f2889 100644 --- a/src/client/pythonEnvironments/legacyIOC.ts +++ b/src/client/pythonEnvironments/legacyIOC.ts @@ -42,7 +42,7 @@ import { inExperiment, isParentPath } from './common/externalDependencies'; import { PythonInterpreterLocatorService } from './discovery/locators'; import { InterpreterLocatorHelper } from './discovery/locators/helpers'; import { InterpreterLocatorProgressService } from './discovery/locators/progressService'; -import { CondaEnvironmentInfo, isCondaEnvironment } from './discovery/locators/services/conda'; +import { CondaEnvironmentInfo, isCondaEnvironment } from './common/environmentManagers/conda'; import { CondaEnvFileService } from './discovery/locators/services/condaEnvFileService'; import { CondaEnvService } from './discovery/locators/services/condaEnvService'; import { CondaService } from './discovery/locators/services/condaService'; @@ -57,7 +57,7 @@ import { KnownPathsService, KnownSearchPathsForInterpreters } from './discovery/ import { PipEnvService } from './discovery/locators/services/pipEnvService'; import { PipEnvServiceHelper } from './discovery/locators/services/pipEnvServiceHelper'; import { WindowsRegistryService } from './discovery/locators/services/windowsRegistryService'; -import { isWindowsStoreEnvironment } from './discovery/locators/services/windowsStoreLocator'; +import { isWindowsStoreEnvironment } from './common/environmentManagers/windowsStoreEnv'; import { WorkspaceVirtualEnvironmentsSearchPathProvider, WorkspaceVirtualEnvService, @@ -67,7 +67,7 @@ import { EnvironmentType, PythonEnvironment } from './info'; import { toSemverLikeVersion } from './base/info/pythonVersion'; import { PythonVersion } from './info/pythonVersion'; import { IExtensionSingleActivationService } from '../activation/types'; -import { EnvironmentInfoServiceQueuePriority, getEnvironmentInfoService } from './info/environmentInfoService'; +import { EnvironmentInfoServiceQueuePriority, getEnvironmentInfoService } from './base/info/environmentInfoService'; const convertedKinds = new Map( Object.entries({ diff --git a/src/test/common/installer/condaInstaller.unit.test.ts b/src/test/common/installer/condaInstaller.unit.test.ts index 3fe9c83dbdae..f322031f81e1 100644 --- a/src/test/common/installer/condaInstaller.unit.test.ts +++ b/src/test/common/installer/condaInstaller.unit.test.ts @@ -20,7 +20,7 @@ import { import { ICondaService, ICondaLocatorService } from '../../../client/interpreter/contracts'; import { ServiceContainer } from '../../../client/ioc/container'; import { IServiceContainer } from '../../../client/ioc/types'; -import { CondaEnvironmentInfo } from '../../../client/pythonEnvironments/discovery/locators/services/conda'; +import { CondaEnvironmentInfo } from '../../../client/pythonEnvironments/common/environmentManagers/conda'; import { CondaService } from '../../../client/pythonEnvironments/discovery/locators/services/condaService'; suite('Common - Conda Installer', () => { diff --git a/src/test/common/installer/pipEnvInstaller.unit.test.ts b/src/test/common/installer/pipEnvInstaller.unit.test.ts index 7c35688f5148..c1858e2a2ee1 100644 --- a/src/test/common/installer/pipEnvInstaller.unit.test.ts +++ b/src/test/common/installer/pipEnvInstaller.unit.test.ts @@ -13,7 +13,7 @@ import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstalle import { IExperimentService } from '../../../client/common/types'; import { IInterpreterLocatorService, IInterpreterService, PIPENV_SERVICE } from '../../../client/interpreter/contracts'; import { IServiceContainer } from '../../../client/ioc/types'; -import * as pipEnvHelper from '../../../client/pythonEnvironments/discovery/locators/services/pipEnvHelper'; +import * as pipEnvHelper from '../../../client/pythonEnvironments/common/environmentManagers/pipenv'; import { EnvironmentType, PythonEnvironment } from '../../../client/pythonEnvironments/info'; suite('PipEnv installer', async () => { diff --git a/src/test/pythonEnvironments/base/locators/composite/environmentsResolver.unit.test.ts b/src/test/pythonEnvironments/base/locators/composite/environmentsResolver.unit.test.ts index ae8db581ca06..3b14d339514f 100644 --- a/src/test/pythonEnvironments/base/locators/composite/environmentsResolver.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/composite/environmentsResolver.unit.test.ts @@ -25,7 +25,7 @@ import * as externalDependencies from '../../../../../client/pythonEnvironments/ import { getEnvironmentInfoService, IEnvironmentInfoService, -} from '../../../../../client/pythonEnvironments/info/environmentInfoService'; +} from '../../../../../client/pythonEnvironments/base/info/environmentInfoService'; import { TEST_LAYOUT_ROOT } from '../../../common/commonTestConstants'; import { assertEnvEqual, assertEnvsEqual } from '../../../discovery/locators/envTestUtils'; import { createBasicEnv, getEnvs, getEnvsWithUpdates, SimpleLocator } from '../../common'; diff --git a/src/test/pythonEnvironments/base/locators/composite/resolverUtils.unit.test.ts b/src/test/pythonEnvironments/base/locators/composite/resolverUtils.unit.test.ts index 0abd1fa1a102..3ea5bb684265 100644 --- a/src/test/pythonEnvironments/base/locators/composite/resolverUtils.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/composite/resolverUtils.unit.test.ts @@ -23,7 +23,7 @@ import { Architecture } from '../../../../../client/common/utils/platform'; import { AnacondaCompanyName, CondaInfo, -} from '../../../../../client/pythonEnvironments/discovery/locators/services/conda'; +} from '../../../../../client/pythonEnvironments/common/environmentManagers/conda'; import { resolveBasicEnv } from '../../../../../client/pythonEnvironments/base/locators/composite/resolverUtils'; suite('Resolver Utils', () => { diff --git a/src/test/pythonEnvironments/discovery/locators/condaEnvFileService.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/condaEnvFileService.unit.test.ts index 66b4f45ed829..edda32cf2c2f 100644 --- a/src/test/pythonEnvironments/discovery/locators/condaEnvFileService.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/condaEnvFileService.unit.test.ts @@ -10,7 +10,7 @@ import { IInterpreterLocatorService, } from '../../../../client/interpreter/contracts'; import { IServiceContainer } from '../../../../client/ioc/types'; -import { AnacondaCompanyName } from '../../../../client/pythonEnvironments/discovery/locators/services/conda'; +import { AnacondaCompanyName } from '../../../../client/pythonEnvironments/common/environmentManagers/conda'; import { CondaEnvFileService } from '../../../../client/pythonEnvironments/discovery/locators/services/condaEnvFileService'; import { EnvironmentType } from '../../../../client/pythonEnvironments/info'; import { MockState } from '../../../interpreters/mocks'; diff --git a/src/test/pythonEnvironments/discovery/locators/condaEnvService.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/condaEnvService.unit.test.ts index 4ef17d920e91..4e530bed2aab 100644 --- a/src/test/pythonEnvironments/discovery/locators/condaEnvService.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/condaEnvService.unit.test.ts @@ -10,7 +10,7 @@ import { AnacondaCompanyName, CondaInfo, parseCondaInfo, -} from '../../../../client/pythonEnvironments/discovery/locators/services/conda'; +} from '../../../../client/pythonEnvironments/common/environmentManagers/conda'; import { CondaEnvService } from '../../../../client/pythonEnvironments/discovery/locators/services/condaEnvService'; import { EnvironmentType } from '../../../../client/pythonEnvironments/info'; import { MockState } from '../../../interpreters/mocks'; diff --git a/src/test/pythonEnvironments/discovery/locators/condaHelper.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/condaHelper.unit.test.ts index 4862192e369d..24ae55373f65 100644 --- a/src/test/pythonEnvironments/discovery/locators/condaHelper.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/condaHelper.unit.test.ts @@ -9,9 +9,9 @@ import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; import * as windowsUtils from '../../../../client/pythonEnvironments/common/windowsUtils'; -import { Conda, CondaInfo } from '../../../../client/pythonEnvironments/discovery/locators/services/conda'; +import { Conda, CondaInfo } from '../../../../client/pythonEnvironments/common/environmentManagers/conda'; import { parseCondaEnvFileContents } from '../../../../client/pythonEnvironments/discovery/locators/services/condaHelper'; -import { CondaEnvironmentLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/condaLocator'; +import { CondaEnvironmentLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/condaLocator'; import { createBasicEnv } from '../../base/common'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/condaLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/condaLocator.unit.test.ts index 9f720a865490..6de60391a186 100644 --- a/src/test/pythonEnvironments/discovery/locators/condaLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/condaLocator.unit.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { PythonReleaseLevel, PythonVersion } from '../../../../client/pythonEnvironments/base/info'; import * as externalDeps from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { getPythonVersionFromConda } from '../../../../client/pythonEnvironments/discovery/locators/services/conda'; +import { getPythonVersionFromConda } from '../../../../client/pythonEnvironments/common/environmentManagers/conda'; import { TEST_DATA_ROOT } from '../../common/commonTestConstants'; import { assertVersionsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/customVirtualEnvLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/customVirtualEnvLocator.unit.test.ts index b45381142f56..903293cdda31 100644 --- a/src/test/pythonEnvironments/discovery/locators/customVirtualEnvLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/customVirtualEnvLocator.unit.test.ts @@ -14,7 +14,7 @@ import { CustomVirtualEnvironmentLocator, VENVFOLDERS_SETTING_KEY, VENVPATH_SETTING_KEY, -} from '../../../../client/pythonEnvironments/discovery/locators/services/customVirtualEnvLocator'; +} from '../../../../client/pythonEnvironments/base/locators/lowLevel/customVirtualEnvLocator'; import { createBasicEnv } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.functional.test.ts b/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.functional.test.ts index 6a8913a2da15..44062ddb3f5d 100644 --- a/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.functional.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.functional.test.ts @@ -8,7 +8,7 @@ import * as platformUtils from '../../../../client/common/utils/platform'; import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { GlobalVirtualEnvironmentLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator'; +import { GlobalVirtualEnvironmentLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/globalVirtualEnvronmentLocator'; import { createBasicEnv } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.testvirtualenvs.ts b/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.testvirtualenvs.ts index c3484788cec6..cb6c10da9781 100644 --- a/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.testvirtualenvs.ts +++ b/src/test/pythonEnvironments/discovery/locators/globalVirtualEnvironmentLocator.testvirtualenvs.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import * as path from 'path'; -import { GlobalVirtualEnvironmentLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator'; +import { GlobalVirtualEnvironmentLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/globalVirtualEnvronmentLocator'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { testLocatorWatcher } from './watcherTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/index.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/index.unit.test.ts index 81abb89ff3ac..e0627126e598 100644 --- a/src/test/pythonEnvironments/discovery/locators/index.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/index.unit.test.ts @@ -28,11 +28,8 @@ import { import { IServiceContainer } from '../../../../client/ioc/types'; import { PythonEnvInfo, PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { PythonEnvsChangedEvent } from '../../../../client/pythonEnvironments/base/watcher'; -import { - PythonInterpreterLocatorService, - WatchRootsArgs, - WorkspaceLocators, -} from '../../../../client/pythonEnvironments/discovery/locators'; +import { PythonInterpreterLocatorService } from '../../../../client/pythonEnvironments/discovery/locators'; +import { WatchRootsArgs, WorkspaceLocators } from '../../../../client/pythonEnvironments/base/locators/'; import { EnvironmentType, PythonEnvironment } from '../../../../client/pythonEnvironments/info'; import { assertSameEnvs, createLocatedEnv, createNamedEnv, getEnvs, SimpleLocator } from '../../base/common'; diff --git a/src/test/pythonEnvironments/discovery/locators/lowLevel/poetry.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/lowLevel/poetry.unit.test.ts index 76d13d4ec908..49833dc80b01 100644 --- a/src/test/pythonEnvironments/discovery/locators/lowLevel/poetry.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/lowLevel/poetry.unit.test.ts @@ -10,7 +10,7 @@ import * as externalDependencies from '../../../../../client/pythonEnvironments/ import { isPoetryEnvironment, Poetry, -} from '../../../../../client/pythonEnvironments/discovery/locators/services/poetry'; +} from '../../../../../client/pythonEnvironments/common/environmentManagers/poetry'; import { TEST_LAYOUT_ROOT } from '../../../common/commonTestConstants'; const testPoetryDir = path.join(TEST_LAYOUT_ROOT, 'poetry'); @@ -22,10 +22,6 @@ suite('isPoetryEnvironment Tests', () => { let shellExecute: sinon.SinonStub; let getPythonSetting: sinon.SinonStub; - suiteTeardown(() => { - Poetry._poetryPromise = new Map(); - }); - suite('Global poetry environment', async () => { test('Return true if environment folder name matches global env pattern and environment is of virtual env type', async () => { const result = await isPoetryEnvironment( @@ -87,17 +83,12 @@ suite('Poetry binary is located correctly', async () => { let shellExecute: sinon.SinonStub; let getPythonSetting: sinon.SinonStub; - suiteSetup(() => { - Poetry._poetryPromise = new Map(); - }); - setup(() => { getPythonSetting = sinon.stub(externalDependencies, 'getPythonSetting'); shellExecute = sinon.stub(externalDependencies, 'shellExecute'); }); teardown(() => { - Poetry._poetryPromise = new Map(); sinon.restore(); }); diff --git a/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.functional.test.ts b/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.functional.test.ts index ed5e8e657977..8b20291d5376 100644 --- a/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.functional.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.functional.test.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import * as platformApis from '../../../../client/common/utils/platform'; import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { isPipenvEnvironmentRelatedToFolder } from '../../../../client/pythonEnvironments/discovery/locators/services/pipEnvHelper'; +import { isPipenvEnvironmentRelatedToFolder } from '../../../../client/pythonEnvironments/common/environmentManagers/pipenv'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; suite('Pipenv utils', () => { diff --git a/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.unit.test.ts index 1b75e598fc47..45291625be13 100644 --- a/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/pipEnvHelper.unit.test.ts @@ -7,7 +7,7 @@ import { _getAssociatedPipfile, isPipenvEnvironment, isPipenvEnvironmentRelatedToFolder, -} from '../../../../client/pythonEnvironments/discovery/locators/services/pipEnvHelper'; +} from '../../../../client/pythonEnvironments/common/environmentManagers/pipenv'; const path = platformApis.getOSType() === platformApis.OSType.Windows ? pathModule.win32 : pathModule.posix; diff --git a/src/test/pythonEnvironments/discovery/locators/poetryLocator.testvirtualenvs.ts b/src/test/pythonEnvironments/discovery/locators/poetryLocator.testvirtualenvs.ts index cf5d66767f49..6abff7c24239 100644 --- a/src/test/pythonEnvironments/discovery/locators/poetryLocator.testvirtualenvs.ts +++ b/src/test/pythonEnvironments/discovery/locators/poetryLocator.testvirtualenvs.ts @@ -9,7 +9,7 @@ import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { BasicEnvInfo, ILocator } from '../../../../client/pythonEnvironments/base/locator'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { PoetryLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/poetryLocator'; +import { PoetryLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/poetryLocator'; import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../../../constants'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { testLocatorWatcher } from './watcherTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/poetryLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/poetryLocator.unit.test.ts index 70bcce072e53..40d9ebd4e914 100644 --- a/src/test/pythonEnvironments/discovery/locators/poetryLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/poetryLocator.unit.test.ts @@ -7,11 +7,10 @@ import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; import * as platformUtils from '../../../../client/common/utils/platform'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; -import { PoetryLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/poetryLocator'; +import { PoetryLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/poetryLocator'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; import { ExecutionResult, ShellOptions } from '../../../../client/common/process/types'; -import { Poetry } from '../../../../client/pythonEnvironments/discovery/locators/services/poetry'; import { createBasicEnv } from '../../base/common'; suite('Poetry Locator', () => { @@ -21,10 +20,6 @@ suite('Poetry Locator', () => { const testPoetryDir = path.join(TEST_LAYOUT_ROOT, 'poetry'); let locator: PoetryLocator; - suiteTeardown(() => { - Poetry._poetryPromise = new Map(); - }); - suiteSetup(() => { getPythonSetting = sinon.stub(externalDependencies, 'getPythonSetting'); getPythonSetting.returns('poetry'); diff --git a/src/test/pythonEnvironments/discovery/locators/posixKnownPathsLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/posixKnownPathsLocator.unit.test.ts index 80e2cb310668..4187e3b503ca 100644 --- a/src/test/pythonEnvironments/discovery/locators/posixKnownPathsLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/posixKnownPathsLocator.unit.test.ts @@ -7,7 +7,7 @@ import * as executablesAPI from '../../../../client/common/utils/exec'; import { PythonEnvKind, PythonEnvSource } from '../../../../client/pythonEnvironments/base/info'; import { BasicEnvInfo } from '../../../../client/pythonEnvironments/base/locator'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; -import { PosixKnownPathsLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/posixKnownPathsLocator'; +import { PosixKnownPathsLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator'; import { createBasicEnv } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.functional.test.ts b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.functional.test.ts index da67fc66b035..873f41249216 100644 --- a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.functional.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.functional.test.ts @@ -7,7 +7,7 @@ import * as fsWatcher from '../../../../client/common/platform/fileSystemWatcher import * as platformUtils from '../../../../client/common/utils/platform'; import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; -import { PyenvLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/pyenvLocator'; +import { PyenvLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/pyenvLocator'; import { createBasicEnv } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.testvirtualenvs.ts b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.testvirtualenvs.ts index fc800c008a1a..c851d6a18ee2 100644 --- a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.testvirtualenvs.ts +++ b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.testvirtualenvs.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; -import { PyenvLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/pyenvLocator'; +import { PyenvLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/pyenvLocator'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { testLocatorWatcher } from './watcherTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.unit.test.ts index 6d2a06b1095d..e5902ae2b291 100644 --- a/src/test/pythonEnvironments/discovery/locators/pyenvLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/pyenvLocator.unit.test.ts @@ -11,7 +11,7 @@ import { isPyenvEnvironment, isPyenvShimDir, parsePyenvVersion, -} from '../../../../client/pythonEnvironments/discovery/locators/services/pyenvLocator'; +} from '../../../../client/pythonEnvironments/common/environmentManagers/pyenv'; suite('Pyenv Identifier Tests', () => { const home = platformUtils.getUserHomeDir() || ''; @@ -278,7 +278,7 @@ suite('Pyenv Versions Parser Test', () => { testData.forEach((data) => { test(`Parse pyenv version [${data.input}]`, async () => { - assert.deepStrictEqual(await parsePyenvVersion(data.input), data.expectedOutput); + assert.deepStrictEqual(parsePyenvVersion(data.input), data.expectedOutput); }); }); }); diff --git a/src/test/pythonEnvironments/discovery/locators/virtualEnvironmentIdentifier.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/virtualEnvironmentIdentifier.unit.test.ts index 8fee657d89ce..5c32870988c2 100644 --- a/src/test/pythonEnvironments/discovery/locators/virtualEnvironmentIdentifier.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/virtualEnvironmentIdentifier.unit.test.ts @@ -14,7 +14,7 @@ import { isVenvEnvironment, isVirtualenvEnvironment, isVirtualenvwrapperEnvironment, -} from '../../../../client/pythonEnvironments/discovery/locators/services/virtualEnvironmentIdentifier'; +} from '../../../../client/pythonEnvironments/common/environmentManagers/simplevirtualenvs'; import { TEST_DATA_ROOT, TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertVersionsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/windowsRegistryLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/windowsRegistryLocator.unit.test.ts index eac49214b9a4..f62f8de0aea7 100644 --- a/src/test/pythonEnvironments/discovery/locators/windowsRegistryLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/windowsRegistryLocator.unit.test.ts @@ -7,7 +7,7 @@ import * as sinon from 'sinon'; import { PythonEnvKind, PythonEnvSource } from '../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; import * as winreg from '../../../../client/pythonEnvironments/common/windowsRegistry'; -import { WindowsRegistryLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/windowsRegistryLocator'; +import { WindowsRegistryLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; import { createBasicEnv } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.test.ts b/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.test.ts index fac557769dc3..cab77f770524 100644 --- a/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.test.ts @@ -14,7 +14,7 @@ import { PythonEnvKind } from '../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../client/pythonEnvironments/base/locatorUtils'; import { PythonEnvsChangedEvent } from '../../../../client/pythonEnvironments/base/watcher'; import * as externalDeps from '../../../../client/pythonEnvironments/common/externalDependencies'; -import { WindowsStoreLocator } from '../../../../client/pythonEnvironments/discovery/locators/services/windowsStoreLocator'; +import { WindowsStoreLocator } from '../../../../client/pythonEnvironments/base/locators/lowLevel/windowsStoreLocator'; import { TEST_TIMEOUT } from '../../../constants'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; diff --git a/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.unit.test.ts b/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.unit.test.ts index 3218f1fab1c6..bcb06940b510 100644 --- a/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.unit.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/windowsStoreLocator.unit.test.ts @@ -12,9 +12,9 @@ import { BasicEnvInfo } from '../../../../client/pythonEnvironments/base/locator import * as externalDep from '../../../../client/pythonEnvironments/common/externalDependencies'; import { getWindowsStorePythonExes, - isWindowsStoreDir, WindowsStoreLocator, -} from '../../../../client/pythonEnvironments/discovery/locators/services/windowsStoreLocator'; +} from '../../../../client/pythonEnvironments/base/locators/lowLevel/windowsStoreLocator'; +import { isWindowsStoreDir } from '../../../../client/pythonEnvironments/common/environmentManagers/windowsStoreEnv'; import { getEnvs } from '../../base/common'; import { TEST_LAYOUT_ROOT } from '../../common/commonTestConstants'; import { assertBasicEnvsEqual } from './envTestUtils'; diff --git a/src/test/pythonEnvironments/info/environmentInfoService.functional.test.ts b/src/test/pythonEnvironments/info/environmentInfoService.functional.test.ts index fc8e2482978f..9b76b62ce1ab 100644 --- a/src/test/pythonEnvironments/info/environmentInfoService.functional.test.ts +++ b/src/test/pythonEnvironments/info/environmentInfoService.functional.test.ts @@ -15,7 +15,7 @@ import * as ExternalDep from '../../../client/pythonEnvironments/common/external import { EnvironmentInfoServiceQueuePriority, getEnvironmentInfoService, -} from '../../../client/pythonEnvironments/info/environmentInfoService'; +} from '../../../client/pythonEnvironments/base/info/environmentInfoService'; suite('Environment Info Service', () => { let stubShellExec: sinon.SinonStub;