@@ -96,6 +96,9 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
9696 uri ,
9797 ) ;
9898
99+ // Setup process handlers deferred (used by both execution paths)
100+ const deferredTillExecClose : Deferred < void > = createTestingDeferred ( ) ;
101+
99102 try {
100103 // Build pytest command and arguments
101104 const settings = this . configSettings . getSettings ( uri ) ;
@@ -111,7 +114,6 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
111114 const mutableEnv = await configureDiscoveryEnv ( this . envVarsService , uri , discoveryPipeName ) ;
112115
113116 // Setup process handlers (shared by both execution paths)
114- const deferredTillExecClose : Deferred < void > = createTestingDeferred ( ) ;
115117 const handlers = createProcessHandlers ( 'pytest' , uri , cwd , this . resultResolver , deferredTillExecClose , [ 5 ] ) ;
116118
117119 // Execute using environment extension if available
@@ -122,6 +124,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
122124 traceError (
123125 `Python environment not found for workspace ${ uri . fsPath } . Cannot proceed with test discovery.` ,
124126 ) ;
127+ deferredTillExecClose . resolve ( ) ;
125128 return ;
126129 }
127130 traceVerbose ( `Using Python environment: ${ JSON . stringify ( pythonEnv ) } ` ) ;
@@ -161,11 +164,19 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
161164 traceError (
162165 `Failed to create execution service for workspace ${ uri . fsPath } . Cannot proceed with test discovery.` ,
163166 ) ;
167+ deferredTillExecClose . resolve ( ) ;
164168 return ;
165169 }
166170 const execInfo = await execService . getExecutablePath ( ) ;
167171 traceVerbose ( `Using Python executable: ${ execInfo } for workspace ${ uri . fsPath } ` ) ;
168172
173+ // Check for cancellation before spawning process
174+ if ( token ?. isCancellationRequested ) {
175+ traceInfo ( `Pytest discovery cancelled before spawning process for workspace ${ uri . fsPath } ` ) ;
176+ deferredTillExecClose . resolve ( ) ;
177+ return ;
178+ }
179+
169180 const spawnOptions : SpawnOptions = {
170181 cwd,
171182 throwOnStdErr : true ,
@@ -174,30 +185,51 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
174185 } ;
175186
176187 let resultProc : ChildProcess | undefined ;
177- const result = execService . execObservable ( commandArgs , spawnOptions ) ;
178- resultProc = result ?. proc ;
179188
180- if ( ! resultProc ) {
181- traceError ( `Failed to spawn pytest discovery subprocess for workspace ${ uri . fsPath } ` ) ;
182- return ;
183- }
184- traceInfo ( `Started pytest discovery subprocess (execution factory) for workspace ${ uri . fsPath } ` ) ;
185-
186- // Wire up cancellation and process events
187- token ?. onCancellationRequested ( ( ) => {
189+ // Set up cancellation handler before execObservable to catch early cancellations
190+ const cancellationHandler = token ?. onCancellationRequested ( ( ) => {
191+ traceInfo ( `Cancellation requested during pytest discovery for workspace ${ uri . fsPath } ` ) ;
188192 cleanupOnCancellation ( 'pytest' , resultProc , deferredTillExecClose , discoveryPipeCancellation , uri ) ;
189193 } ) ;
194+
195+ try {
196+ const result = execService . execObservable ( commandArgs , spawnOptions ) ;
197+ resultProc = result ?. proc ;
198+
199+ if ( ! resultProc ) {
200+ traceError ( `Failed to spawn pytest discovery subprocess for workspace ${ uri . fsPath } ` ) ;
201+ deferredTillExecClose . resolve ( ) ;
202+ return ;
203+ }
204+ traceInfo ( `Started pytest discovery subprocess (execution factory) for workspace ${ uri . fsPath } ` ) ;
205+ } catch ( error ) {
206+ traceError ( `Error spawning pytest discovery subprocess for workspace ${ uri . fsPath } : ${ error } ` ) ;
207+ deferredTillExecClose . resolve ( ) ;
208+ cancellationHandler ?. dispose ( ) ;
209+ throw error ;
210+ }
190211 resultProc . stdout ?. on ( 'data' , handlers . onStdout ) ;
191212 resultProc . stderr ?. on ( 'data' , handlers . onStderr ) ;
192213 resultProc . on ( 'exit' , handlers . onExit ) ;
193214 resultProc . on ( 'close' , handlers . onClose ) ;
194215
216+ cancellationHandler ?. dispose ( ) ;
217+
218+ // Check for early cancellation before awaitingre awaiting
219+ if ( token ?. isCancellationRequested ) {
220+ traceInfo ( `Pytest discovery was cancelled before process completion for workspace ${ uri . fsPath } ` ) ;
221+ deferredTillExecClose . resolve ( ) ;
222+ }
223+
224+ traceVerbose ( `Waiting for pytest discovery subprocess to complete for workspace ${ uri . fsPath } ` ) ;
195225 await deferredTillExecClose . promise ;
196226 traceInfo ( `Pytest discovery completed for workspace ${ uri . fsPath } ` ) ;
197227 } catch ( error ) {
198228 traceError ( `Error during pytest discovery for workspace ${ uri . fsPath } : ${ error } ` ) ;
229+ deferredTillExecClose . resolve ( ) ;
199230 throw error ;
200231 } finally {
232+ traceVerbose ( `Cleaning up pytest discovery resources for workspace ${ uri . fsPath } ` ) ;
201233 discoveryPipeCancellation . dispose ( ) ;
202234 }
203235 }
0 commit comments