@@ -96,13 +96,87 @@ import { collectEnvironmentInfo, getEnvManagerAndPackageManagerConfigLevels, run
9696import { EnvironmentManagers , ProjectCreators , PythonProjectManager } from './internal.api' ;
9797import { registerSystemPythonFeatures } from './managers/builtin/main' ;
9898import { SysPythonManager } from './managers/builtin/sysPythonManager' ;
99- import { createNativePythonFinder , NativePythonFinder } from './managers/common/nativePythonFinder' ;
99+ import {
100+ createNativePythonFinder ,
101+ NativePythonEnvironmentKind ,
102+ NativePythonFinder ,
103+ } from './managers/common/nativePythonFinder' ;
100104import { IDisposable } from './managers/common/types' ;
101105import { registerCondaFeatures } from './managers/conda/main' ;
102106import { registerPipenvFeatures } from './managers/pipenv/main' ;
103107import { registerPoetryFeatures } from './managers/poetry/main' ;
104108import { registerPyenvFeatures } from './managers/pyenv/main' ;
105109
110+ /**
111+ * Map from PET NativePythonEnvironmentKind to the manager name used in registration.
112+ * Only includes kinds that have a dedicated manager (not system/venv/etc.).
113+ */
114+ const PET_KIND_TO_MANAGER : ReadonlyMap < NativePythonEnvironmentKind , string > = new Map ( [
115+ [ NativePythonEnvironmentKind . conda , 'conda' ] ,
116+ [ NativePythonEnvironmentKind . pyenv , 'pyenv' ] ,
117+ [ NativePythonEnvironmentKind . pyenvVirtualEnv , 'pyenv' ] ,
118+ [ NativePythonEnvironmentKind . pipenv , 'pipenv' ] ,
119+ [ NativePythonEnvironmentKind . poetry , 'poetry' ] ,
120+ ] ) ;
121+
122+ /**
123+ * Checks whether PET discovered environments whose corresponding manager did not register.
124+ * Uses a soft (cached) refresh so it doesn't trigger additional PET work.
125+ */
126+ async function checkPetManagerMismatch (
127+ nativeFinder : NativePythonFinder ,
128+ envManagers : EnvironmentManagers ,
129+ ) : Promise < void > {
130+ const registeredIds = new Set ( envManagers . managers . map ( ( m ) => m . id ) ) ;
131+
132+ // Map manager names to their expected managerId prefix
133+ const managerIdPrefixes : ReadonlyMap < string , string > = new Map ( [
134+ [ 'conda' , 'ms-python.python:conda' ] ,
135+ [ 'pyenv' , 'ms-python.python:pyenv' ] ,
136+ [ 'pipenv' , 'ms-python.python:pipenv' ] ,
137+ [ 'poetry' , 'ms-python.python:poetry' ] ,
138+ ] ) ;
139+
140+ // Group PET kinds by manager name to avoid double-counting (e.g. pyenv + pyenvVirtualEnv)
141+ const kindsByManager = new Map < string , NativePythonEnvironmentKind [ ] > ( ) ;
142+ for ( const [ kind , managerName ] of PET_KIND_TO_MANAGER ) {
143+ const existing = kindsByManager . get ( managerName ) ?? [ ] ;
144+ existing . push ( kind ) ;
145+ kindsByManager . set ( managerName , existing ) ;
146+ }
147+
148+ for ( const [ managerName , kinds ] of kindsByManager ) {
149+ const expectedPrefix = managerIdPrefixes . get ( managerName ) ;
150+ if ( ! expectedPrefix ) {
151+ continue ;
152+ }
153+
154+ // Check if the corresponding manager registered
155+ const isRegistered = Array . from ( registeredIds ) . some ( ( id ) => id . startsWith ( expectedPrefix ) ) ;
156+ if ( isRegistered ) {
157+ continue ;
158+ }
159+
160+ // Manager not registered — check if PET found environments of any related kind
161+ let totalPetEnvs = 0 ;
162+ for ( const kind of kinds ) {
163+ try {
164+ const petEnvs = await nativeFinder . refresh ( false , kind ) ;
165+ totalPetEnvs += petEnvs . length ;
166+ } catch {
167+ // PET query failed — don't block post-init; skip this kind
168+ }
169+ }
170+
171+ if ( totalPetEnvs > 0 ) {
172+ sendTelemetryEvent ( EventNames . MANAGER_DISCOVERY_MISMATCH , undefined , {
173+ managerName,
174+ petEnvCount : totalPetEnvs ,
175+ } ) ;
176+ }
177+ }
178+ }
179+
106180export async function activate ( context : ExtensionContext ) : Promise < PythonEnvironmentApi | undefined > {
107181 // Only skip activation if user explicitly set useEnvironmentsExtension to false.
108182 // When disabled, the main Python extension handles environments instead (legacy mode).
@@ -594,6 +668,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
594668
595669 // Log discovery summary to help users troubleshoot environment detection issues
596670 await logDiscoverySummary ( envManagers ) ;
671+
672+ // Check for PET-vs-manager mismatches (PET found envs but manager didn't register)
673+ await checkPetManagerMismatch ( nativeFinder , envManagers ) ;
597674 } catch ( postInitError ) {
598675 traceError ( 'Post-initialization tasks failed:' , postInitError ) ;
599676 }
0 commit comments