From a414a65ccccac35028cf235e995f5748dc9629d6 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Thu, 18 Jul 2024 15:18:39 -0700 Subject: [PATCH] More changes (broken ) state --- .../locators/common/nativePythonFinder.ts | 75 +++-------------- .../base/locators/common/nativePythonUtils.ts | 83 ++++++++++++------- .../composite/envsCollectionService.ts | 8 +- src/client/pythonEnvironments/nativeAPI.ts | 10 +-- .../envsCollectionService.unit.test.ts | 4 - .../pythonEnvironments/nativeAPI.unit.test.ts | 13 ++- .../nativePythonFinder.unit.test.ts | 4 +- 7 files changed, 79 insertions(+), 118 deletions(-) diff --git a/src/client/pythonEnvironments/base/locators/common/nativePythonFinder.ts b/src/client/pythonEnvironments/base/locators/common/nativePythonFinder.ts index 8e633abf03c7..e811badce553 100644 --- a/src/client/pythonEnvironments/base/locators/common/nativePythonFinder.ts +++ b/src/client/pythonEnvironments/base/locators/common/nativePythonFinder.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Disposable, EventEmitter, Event, Uri, LogOutputChannel } from 'vscode'; +import { Disposable, EventEmitter, Event, Uri } from 'vscode'; import * as ch from 'child_process'; import * as path from 'path'; import * as rpc from 'vscode-jsonrpc/node'; @@ -17,7 +17,7 @@ import { VENVFOLDERS_SETTING_KEY, VENVPATH_SETTING_KEY } from '../lowLevel/custo import { getUserHomeDir } from '../../../../common/utils/platform'; import { createLogOutputChannel } from '../../../../common/vscodeApis/windowApis'; import { sendNativeTelemetry, NativePythonTelemetry } from './nativePythonTelemetry'; -import { traceError } from '../../../../logging'; +import { NativePythonEnvironmentKind } from './nativePythonUtils'; const untildify = require('untildify'); @@ -29,7 +29,7 @@ export interface NativeEnvInfo { displayName?: string; name?: string; executable?: string; - kind?: PythonEnvironmentKind; + kind?: NativePythonEnvironmentKind; version?: string; prefix?: string; manager?: NativeEnvManagerInfo; @@ -41,32 +41,13 @@ export interface NativeEnvInfo { symlinks?: string[]; } -export enum PythonEnvironmentKind { - Conda = 'Conda', - Homebrew = 'Homebrew', - Pyenv = 'Pyenv', - GlobalPaths = 'GlobalPaths', - PyenvVirtualEnv = 'PyenvVirtualEnv', - Pipenv = 'Pipenv', - Poetry = 'Poetry', - MacPythonOrg = 'MacPythonOrg', - MacCommandLineTools = 'MacCommandLineTools', - LinuxGlobal = 'LinuxGlobal', - MacXCode = 'MacXCode', - Venv = 'Venv', - VirtualEnv = 'VirtualEnv', - VirtualEnvWrapper = 'VirtualEnvWrapper', - WindowsStore = 'WindowsStore', - WindowsRegistry = 'WindowsRegistry', -} - export interface NativeEnvManagerInfo { tool: string; executable: string; version?: string; } -export function isNativeInfoEnvironment(info: NativeEnvInfo | NativeEnvManagerInfo): info is NativeEnvInfo { +export function isNativeEnvInfo(info: NativeEnvInfo | NativeEnvManagerInfo): info is NativeEnvInfo { if ((info as NativeEnvManagerInfo).tool) { return false; } @@ -91,9 +72,9 @@ export interface NativePythonFinder extends Disposable { * * If a Uri is provided, then it will search for python environments in that location (ignoring workspaces). * Uri can be a file or a folder. - * If a PythonEnvironmentKind is provided, then it will search for python environments of that kind (ignoring workspaces). + * If a NativePythonEnvironmentKind is provided, then it will search for python environments of that kind (ignoring workspaces). */ - refresh(options?: PythonEnvironmentKind | Uri[]): AsyncIterable; + refresh(options?: NativePythonEnvironmentKind | Uri[]): AsyncIterable; /** * Will spawn the provided Python executable and return information about the environment. * @param executable @@ -105,48 +86,12 @@ export interface NativePythonFinder extends Disposable { getCondaInfo(): Promise; } -const mapping = new Map([ - [PythonEnvironmentKind.Conda, PythonEnvKind.Conda], - [PythonEnvironmentKind.GlobalPaths, PythonEnvKind.OtherGlobal], - [PythonEnvironmentKind.Pyenv, PythonEnvKind.Pyenv], - [PythonEnvironmentKind.PyenvVirtualEnv, PythonEnvKind.Pyenv], - [PythonEnvironmentKind.Pipenv, PythonEnvKind.Pipenv], - [PythonEnvironmentKind.Poetry, PythonEnvKind.Poetry], - [PythonEnvironmentKind.VirtualEnv, PythonEnvKind.VirtualEnv], - [PythonEnvironmentKind.VirtualEnvWrapper, PythonEnvKind.VirtualEnvWrapper], - [PythonEnvironmentKind.Venv, PythonEnvKind.Venv], - [PythonEnvironmentKind.WindowsRegistry, PythonEnvKind.System], - [PythonEnvironmentKind.WindowsStore, PythonEnvKind.MicrosoftStore], - [PythonEnvironmentKind.Homebrew, PythonEnvKind.System], - [PythonEnvironmentKind.LinuxGlobal, PythonEnvKind.System], - [PythonEnvironmentKind.MacCommandLineTools, PythonEnvKind.System], - [PythonEnvironmentKind.MacPythonOrg, PythonEnvKind.System], - [PythonEnvironmentKind.MacXCode, PythonEnvKind.System], -]); - -export function categoryToKind(category?: PythonEnvironmentKind, logger?: LogOutputChannel): PythonEnvKind { - if (!category) { - return PythonEnvKind.Unknown; - } - const kind = mapping.get(category); - if (kind) { - return kind; - } - - if (logger) { - logger.error(`Unknown Python Environment category '${category}' from Native Locator.`); - } else { - traceError(`Unknown Python Environment category '${category}' from Native Locator.`); - } - return PythonEnvKind.Unknown; -} - interface NativeLog { level: string; message: string; } -class NativeGlobalPythonFinderImpl extends DisposableBase implements NativePythonFinder { +class NativePythonFinderImpl extends DisposableBase implements NativePythonFinder { private readonly connection: rpc.MessageConnection; private firstRefreshResults: undefined | (() => AsyncGenerator); @@ -169,7 +114,7 @@ class NativeGlobalPythonFinderImpl extends DisposableBase implements NativePytho return environment; } - async *refresh(options?: PythonEnvironmentKind | Uri[]): AsyncIterable { + async *refresh(options?: NativePythonEnvironmentKind | Uri[]): AsyncIterable { if (this.firstRefreshResults) { // If this is the first time we are refreshing, // Then get the results from the first refresh. @@ -315,7 +260,7 @@ class NativeGlobalPythonFinderImpl extends DisposableBase implements NativePytho } private doRefresh( - options?: PythonEnvironmentKind | Uri[], + options?: NativePythonEnvironmentKind | Uri[], ): { completed: Promise; discovered: Event } { const disposable = this._register(new DisposableStore()); const discovered = disposable.add(new EventEmitter()); @@ -377,7 +322,7 @@ class NativeGlobalPythonFinderImpl extends DisposableBase implements NativePytho ); type RefreshOptions = { - searchKind?: PythonEnvironmentKind; + searchKind?: NativePythonEnvironmentKind; searchPaths?: string[]; }; diff --git a/src/client/pythonEnvironments/base/locators/common/nativePythonUtils.ts b/src/client/pythonEnvironments/base/locators/common/nativePythonUtils.ts index 25f504550c3b..f840ce9a41ec 100644 --- a/src/client/pythonEnvironments/base/locators/common/nativePythonUtils.ts +++ b/src/client/pythonEnvironments/base/locators/common/nativePythonUtils.ts @@ -1,42 +1,61 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { LogOutputChannel } from 'vscode'; import { PythonEnvKind } from '../../info'; +import { traceError } from '../../../../logging'; -export function categoryToKind(category?: string): PythonEnvKind { +export enum NativePythonEnvironmentKind { + Conda = 'Conda', + Homebrew = 'Homebrew', + Pyenv = 'Pyenv', + GlobalPaths = 'GlobalPaths', + PyenvVirtualEnv = 'PyenvVirtualEnv', + Pipenv = 'Pipenv', + Poetry = 'Poetry', + MacPythonOrg = 'MacPythonOrg', + MacCommandLineTools = 'MacCommandLineTools', + LinuxGlobal = 'LinuxGlobal', + MacXCode = 'MacXCode', + Venv = 'Venv', + VirtualEnv = 'VirtualEnv', + VirtualEnvWrapper = 'VirtualEnvWrapper', + WindowsStore = 'WindowsStore', + WindowsRegistry = 'WindowsRegistry', +} + +const mapping = new Map([ + [NativePythonEnvironmentKind.Conda, PythonEnvKind.Conda], + [NativePythonEnvironmentKind.GlobalPaths, PythonEnvKind.OtherGlobal], + [NativePythonEnvironmentKind.Pyenv, PythonEnvKind.Pyenv], + [NativePythonEnvironmentKind.PyenvVirtualEnv, PythonEnvKind.Pyenv], + [NativePythonEnvironmentKind.Pipenv, PythonEnvKind.Pipenv], + [NativePythonEnvironmentKind.Poetry, PythonEnvKind.Poetry], + [NativePythonEnvironmentKind.VirtualEnv, PythonEnvKind.VirtualEnv], + [NativePythonEnvironmentKind.VirtualEnvWrapper, PythonEnvKind.VirtualEnvWrapper], + [NativePythonEnvironmentKind.Venv, PythonEnvKind.Venv], + [NativePythonEnvironmentKind.WindowsRegistry, PythonEnvKind.System], + [NativePythonEnvironmentKind.WindowsStore, PythonEnvKind.MicrosoftStore], + [NativePythonEnvironmentKind.Homebrew, PythonEnvKind.System], + [NativePythonEnvironmentKind.LinuxGlobal, PythonEnvKind.System], + [NativePythonEnvironmentKind.MacCommandLineTools, PythonEnvKind.System], + [NativePythonEnvironmentKind.MacPythonOrg, PythonEnvKind.System], + [NativePythonEnvironmentKind.MacXCode, PythonEnvKind.System], +]); + +export function categoryToKind(category?: NativePythonEnvironmentKind, logger?: LogOutputChannel): PythonEnvKind { if (!category) { return PythonEnvKind.Unknown; } - switch (category.toLowerCase()) { - case 'conda': - return PythonEnvKind.Conda; - case 'system': - case 'homebrew': - case 'macpythonorg': - case 'maccommandlinetools': - case 'macxcode': - case 'windowsregistry': - case 'linuxglobal': - return PythonEnvKind.System; - case 'globalpaths': - return PythonEnvKind.OtherGlobal; - case 'pyenv': - return PythonEnvKind.Pyenv; - case 'poetry': - return PythonEnvKind.Poetry; - case 'pipenv': - return PythonEnvKind.Pipenv; - case 'pyenvvirtualenv': - return PythonEnvKind.VirtualEnv; - case 'venv': - return PythonEnvKind.Venv; - case 'virtualenv': - return PythonEnvKind.VirtualEnv; - case 'virtualenvwrapper': - return PythonEnvKind.VirtualEnvWrapper; - case 'windowsstore': - return PythonEnvKind.MicrosoftStore; - default: - return PythonEnvKind.Unknown; + const kind = mapping.get(category); + if (kind) { + return kind; + } + + if (logger) { + logger.error(`Unknown Python Environment category '${category}' from Native Locator.`); + } else { + traceError(`Unknown Python Environment category '${category}' from Native Locator.`); } + return PythonEnvKind.Unknown; } diff --git a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts index 21247a7103bd..5f1fc7c3bf05 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts @@ -27,7 +27,7 @@ import { PythonEnvCollectionChangedEvent, PythonEnvsWatcher } from '../../watche import { IEnvsCollectionCache } from './envsCollectionCache'; import { getNativePythonFinder, - isNativeInfoEnvironment, + isNativeEnvInfo, NativeEnvInfo, NativePythonFinder, } from '../common/nativePythonFinder'; @@ -300,7 +300,7 @@ export class EnvsCollectionService extends PythonEnvsWatcher(); const nativeStopWatch = new StopWatch(); for await (const data of this.nativeFinder.refresh()) { - if (isNativeInfoEnvironment(data)) { + if (isNativeEnvInfo(data)) { nativeEnvs.push(data); if (data.executable) { // Lowercase for purposes of comparison (safe). @@ -973,7 +973,7 @@ async function getCondaTelemetry( const rootPrefixEnvs = await flattenIterable(nativeFinder.refresh([Uri.file(rootPrefix)])); // Did we find an env with the same prefix? const rootPrefixEnv = rootPrefixEnvs - .filter(isNativeInfoEnvironment) + .filter(isNativeEnvInfo) .find((e) => fsPath.normalize(e.prefix || '').toLowerCase() === rootPrefix.toLowerCase()); condaTelemetry.condaRootPrefixEnvsAfterFind = rootPrefixEnvs.length; condaTelemetry.condaRootPrefixFoundInInfoAfterFind = !!rootPrefixEnv; @@ -1012,7 +1012,7 @@ async function getCondaTelemetry( const defaultPrefixEnvs = await flattenIterable(nativeFinder.refresh([Uri.file(defaultPrefix)])); // Did we find an env with the same prefix? const defaultPrefixEnv = defaultPrefixEnvs - .filter(isNativeInfoEnvironment) + .filter(isNativeEnvInfo) .find((e) => fsPath.normalize(e.prefix || '').toLowerCase() === defaultPrefix.toLowerCase()); condaTelemetry.condaDefaultPrefixEnvsAfterFind = defaultPrefixEnvs.length; condaTelemetry.condaDefaultPrefixFoundInInfoAfterFind = !!defaultPrefixEnv; diff --git a/src/client/pythonEnvironments/nativeAPI.ts b/src/client/pythonEnvironments/nativeAPI.ts index 0cf3a29bc014..6690beebf7c9 100644 --- a/src/client/pythonEnvironments/nativeAPI.ts +++ b/src/client/pythonEnvironments/nativeAPI.ts @@ -13,7 +13,7 @@ import { TriggerRefreshOptions, } from './base/locator'; import { PythonEnvCollectionChangedEvent } from './base/watcher'; -import { isNativeInfoEnvironment, NativeEnvInfo, NativePythonFinder } from './base/locators/common/nativePythonFinder'; +import { isNativeEnvInfo, NativeEnvInfo, NativePythonFinder } from './base/locators/common/nativePythonFinder'; import { createDeferred, Deferred } from '../common/utils/async'; import { Architecture } from '../common/utils/platform'; import { parseVersion } from './base/info/pythonVersion'; @@ -150,7 +150,7 @@ function getName(nativeEnv: NativeEnvInfo, kind: PythonEnvKind): string { return ''; } -function toPythonEnvInfo(finder: NativePythonFinder, nativeEnv: NativeEnvInfo): PythonEnvInfo | undefined { +function toPythonEnvInfo(nativeEnv: NativeEnvInfo): PythonEnvInfo | undefined { if (!validEnv(nativeEnv)) { return undefined; } @@ -230,7 +230,7 @@ class NativePythonEnvironments implements IDiscoveryAPI, Disposable { setImmediate(async () => { try { for await (const native of this.finder.refresh()) { - if (!isNativeInfoEnvironment(native) || !validEnv(native)) { + if (!isNativeEnvInfo(native) || !validEnv(native)) { // eslint-disable-next-line no-continue continue; } @@ -289,7 +289,7 @@ class NativePythonEnvironments implements IDiscoveryAPI, Disposable { } addEnv(native: NativeEnvInfo): void { - const info = toPythonEnvInfo(this.finder, native); + const info = toPythonEnvInfo(native); if (!info) { return; } @@ -311,7 +311,7 @@ class NativePythonEnvironments implements IDiscoveryAPI, Disposable { } const native = await this.finder.resolve(envPath); if (native) { - const env = toPythonEnvInfo(this.finder, native); + const env = toPythonEnvInfo(native); if (env) { const old = this._envs.find((item) => item.executable.filename === env.executable.filename); if (old) { diff --git a/src/test/pythonEnvironments/base/locators/composite/envsCollectionService.unit.test.ts b/src/test/pythonEnvironments/base/locators/composite/envsCollectionService.unit.test.ts index 45ce63cfbdfc..b807e337a4da 100644 --- a/src/test/pythonEnvironments/base/locators/composite/envsCollectionService.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/composite/envsCollectionService.unit.test.ts @@ -37,10 +37,6 @@ class MockNativePythonFinder implements nativeFinder.NativePythonFinder { throw new Error('Method not implemented.'); } - categoryToKind(_category: nativeFinder.PythonEnvironmentKind): PythonEnvKind { - throw new Error('Method not implemented.'); - } - resolve(_executable: string): Promise { throw new Error('Method not implemented.'); } diff --git a/src/test/pythonEnvironments/nativeAPI.unit.test.ts b/src/test/pythonEnvironments/nativeAPI.unit.test.ts index cc17a4bd82c6..89be5dc374e2 100644 --- a/src/test/pythonEnvironments/nativeAPI.unit.test.ts +++ b/src/test/pythonEnvironments/nativeAPI.unit.test.ts @@ -9,14 +9,13 @@ import * as sinon from 'sinon'; import * as nativeAPI from '../../client/pythonEnvironments/nativeAPI'; import { IDiscoveryAPI } from '../../client/pythonEnvironments/base/locator'; import { - categoryToKind, NativeEnvInfo, NativePythonFinder, - PythonEnvironmentKind, } from '../../client/pythonEnvironments/base/locators/common/nativePythonFinder'; import { Architecture } from '../../client/common/utils/platform'; import { PythonEnvInfo, PythonEnvKind, PythonEnvType } from '../../client/pythonEnvironments/base/info'; import { isWindows } from '../../client/common/platform/platformService'; +import { NativePythonEnvironmentKind } from '../../client/pythonEnvironments/base/locators/common/nativePythonUtils'; suite('Native Python API', () => { let api: IDiscoveryAPI; @@ -26,7 +25,7 @@ suite('Native Python API', () => { displayName: 'Basic Python', name: 'basic_python', executable: '/usr/bin/python', - kind: PythonEnvironmentKind.LinuxGlobal, + kind: NativePythonEnvironmentKind.LinuxGlobal, version: `3.12.0`, prefix: '/usr/bin', }; @@ -35,7 +34,7 @@ suite('Native Python API', () => { displayName: 'Basic Python', name: 'basic_python', executable: '/usr/bin/python', - kind: PythonEnvironmentKind.LinuxGlobal, + kind: NativePythonEnvironmentKind.LinuxGlobal, version: undefined, // this is intentionally set to trigger resolve prefix: '/usr/bin', }; @@ -58,7 +57,7 @@ suite('Native Python API', () => { displayName: 'Conda Python', name: 'conda_python', executable: '/home/user/.conda/envs/conda_python/python', - kind: PythonEnvironmentKind.Conda, + kind: NativePythonEnvironmentKind.Conda, version: `3.12.0`, prefix: '/home/user/.conda/envs/conda_python', }; @@ -67,7 +66,7 @@ suite('Native Python API', () => { displayName: 'Conda Python', name: 'conda_python', executable: '/home/user/.conda/envs/conda_python/python', - kind: PythonEnvironmentKind.Conda, + kind: NativePythonEnvironmentKind.Conda, version: undefined, // this is intentionally set to test conda without python prefix: '/home/user/.conda/envs/conda_python', }; @@ -76,7 +75,7 @@ suite('Native Python API', () => { displayName: 'Conda Python', name: 'conda_python', executable: undefined, // this is intentionally set to test env with no executable - kind: PythonEnvironmentKind.Conda, + kind: NativePythonEnvironmentKind.Conda, version: undefined, // this is intentionally set to test conda without python prefix: '/home/user/.conda/envs/conda_python', }; diff --git a/src/test/pythonEnvironments/nativePythonFinder.unit.test.ts b/src/test/pythonEnvironments/nativePythonFinder.unit.test.ts index 640ef1ecc0b0..dacf7c375d9f 100644 --- a/src/test/pythonEnvironments/nativePythonFinder.unit.test.ts +++ b/src/test/pythonEnvironments/nativePythonFinder.unit.test.ts @@ -7,6 +7,8 @@ import * as typemoq from 'typemoq'; import { WorkspaceConfiguration } from 'vscode'; import { getNativePythonFinder, + isNativeEnvInfo, + NativeEnvInfo, NativePythonFinder, } from '../../client/pythonEnvironments/base/locators/common/nativePythonFinder'; import * as windowsApis from '../../client/common/vscodeApis/windowApis'; @@ -66,7 +68,7 @@ suite('Native Python Finder', () => { assert.isNotEmpty(envs); // pick and env without version - let env = envs.find((e) => !e.version); + let env: NativeEnvInfo | undefined = envs.filter((e) => isNativeEnvInfo(e)).find((e) => !e.version); if (!env) { [env] = envs; env.version = undefined;