diff --git a/src/managers/common/nativePythonFinder.ts b/src/managers/common/nativePythonFinder.ts index 76701625..b0534079 100644 --- a/src/managers/common/nativePythonFinder.ts +++ b/src/managers/common/nativePythonFinder.ts @@ -568,6 +568,7 @@ class NativePythonFinderImpl implements NativePythonFinder { workspaceDirectories: this.api.getPythonProjects().map((item) => item.uri.fsPath), environmentDirectories: extraSearchPaths, condaExecutable: getPythonSettingAndUntildify('condaPath'), + pipenvExecutable: getPythonSettingAndUntildify('pipenvPath'), poetryExecutable: getPythonSettingAndUntildify('poetryPath'), cacheDirectory: this.cacheDirectory?.fsPath, }; @@ -602,6 +603,9 @@ class NativePythonFinderImpl implements NativePythonFinder { if (a.condaExecutable !== b.condaExecutable) { return false; } + if (a.pipenvExecutable !== b.pipenvExecutable) { + return false; + } if (a.poetryExecutable !== b.poetryExecutable) { return false; } @@ -634,6 +638,7 @@ type ConfigurationOptions = { workspaceDirectories: string[]; environmentDirectories: string[]; condaExecutable: string | undefined; + pipenvExecutable: string | undefined; poetryExecutable: string | undefined; cacheDirectory?: string; }; diff --git a/src/managers/pipenv/main.ts b/src/managers/pipenv/main.ts index b84094d9..d87d0d9b 100644 --- a/src/managers/pipenv/main.ts +++ b/src/managers/pipenv/main.ts @@ -5,7 +5,7 @@ import { getPythonApi } from '../../features/pythonApi'; import { PythonProjectManager } from '../../internal.api'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { PipenvManager } from './pipenvManager'; -import { getPipenv } from './pipenvUtils'; +import { getPipenv, hasPipenvEnvironments } from './pipenvUtils'; import { notifyMissingManagerIfDefault } from '../common/utils'; @@ -19,15 +19,29 @@ export async function registerPipenvFeatures( try { const pipenv = await getPipenv(nativeFinder); - if (pipenv) { + // Register the manager if the CLI is found, or if there are existing pipenv environments. + // This allows users with existing pipenv environments to still see and use them. + const hasPipenvEnvs = !pipenv && (await hasPipenvEnvironments(nativeFinder)); + + if (pipenv || hasPipenvEnvs) { const mgr = new PipenvManager(nativeFinder, api); disposables.push(mgr, api.registerEnvironmentManager(mgr)); + if (!pipenv) { + traceInfo( + 'Pipenv CLI not found, but pipenv environments were discovered. Registering manager for read-only environment management. To enable full pipenv features, set the "python.pipenvPath" setting to the path of your pipenv executable.', + ); + } } else { - traceInfo('Pipenv not found, turning off pipenv features.'); + traceInfo( + 'Pipenv not found, turning off pipenv features. If you have pipenv installed in a non-standard location, set the "python.pipenvPath" setting.', + ); await notifyMissingManagerIfDefault('ms-python.python:pipenv', projectManager, api); } } catch (ex) { - traceInfo('Pipenv not found, turning off pipenv features.', ex); + traceInfo( + 'Pipenv not found, turning off pipenv features. If you have pipenv installed in a non-standard location, set the "python.pipenvPath" setting.', + ex, + ); await notifyMissingManagerIfDefault('ms-python.python:pipenv', projectManager, api); } } diff --git a/src/managers/pipenv/pipenvUtils.ts b/src/managers/pipenv/pipenvUtils.ts index 865ca8c4..79a41640 100644 --- a/src/managers/pipenv/pipenvUtils.ts +++ b/src/managers/pipenv/pipenvUtils.ts @@ -49,6 +49,17 @@ export async function clearPipenvCache(): Promise { pipenvPath = undefined; } +/** + * Check if any pipenv environments exist without requiring the pipenv CLI. + * This allows the manager to be registered even if the CLI is not found. + */ +export async function hasPipenvEnvironments(nativeFinder: NativePythonFinder): Promise { + const data = await nativeFinder.refresh(false); + return data + .filter((e) => isNativeEnvInfo(e)) + .some((e) => (e as NativeEnvInfo).kind === NativePythonEnvironmentKind.pipenv); +} + function getPipenvPathFromSettings(): string | undefined { const pipenvPath = getSettingWorkspaceScope('python', 'pipenvPath'); return pipenvPath ? pipenvPath : undefined; @@ -191,12 +202,13 @@ export async function refreshPipenv( const collection: PythonEnvironment[] = []; + // Add environments even if pipenv CLI is not found. + // This allows users with existing pipenv environments to still see them + // for read-only management (e.g., selecting the environment, viewing info). for (const e of envs) { - if (pipenv) { - const environment = await nativeToPythonEnv(e, api, manager); - if (environment) { - collection.push(environment); - } + const environment = await nativeToPythonEnv(e, api, manager); + if (environment) { + collection.push(environment); } } @@ -212,11 +224,10 @@ export async function resolvePipenvPath( ): Promise { const resolved = await nativeFinder.resolve(fsPath); + // Resolve pipenv environments even if the pipenv CLI is not found. + // This allows proper environment identification for read-only scenarios. if (resolved.kind === NativePythonEnvironmentKind.pipenv) { - const pipenv = await getPipenv(nativeFinder); - if (pipenv) { - return await nativeToPythonEnv(resolved, api, manager); - } + return await nativeToPythonEnv(resolved, api, manager); } return undefined;