3
3
import sys
4
4
import time
5
5
import os
6
+ import json
6
7
from ..common .constants import (
7
8
TRACE_CALL , TRACE_LINE , TRACE_RETURN , TRACE_EXCEPTION ,
8
9
SCOPE_LOCALS , SCOPE_GLOBALS
9
10
)
11
+ VARREF_LOCALS = 1
12
+ VARREF_GLOBALS = 2
13
+ VARREF_LOCALS_SPECIAL = 3
14
+ VARREF_GLOBALS_SPECIAL = 4
10
15
11
16
12
17
class PdbAdapter :
@@ -26,7 +31,7 @@ def __init__(self):
26
31
27
32
def _debug_print (self , message ):
28
33
"""Print debug message only if debug logging is enabled."""
29
- if hasattr (self , '_debug_session' ) and self ._debug_session .debug_logging :
34
+ if hasattr (self , '_debug_session' ) and self ._debug_session .debug_logging : # type: ignore
30
35
print (message )
31
36
32
37
def _normalize_path (self , path ):
@@ -197,7 +202,7 @@ def wait_for_continue(self):
197
202
while not self .continue_event :
198
203
# Process any pending DAP messages (scopes, variables, etc.)
199
204
if hasattr (self , '_debug_session' ):
200
- self ._debug_session .process_pending_messages ()
205
+ self ._debug_session .process_pending_messages () # type: ignore
201
206
time .sleep (0.01 )
202
207
203
208
def get_stack_trace (self ):
@@ -213,21 +218,25 @@ def get_stack_trace(self):
213
218
filename = frame .f_code .co_filename
214
219
name = frame .f_code .co_name
215
220
line = frame .f_lineno
216
-
221
+ if "<stdin>" in filename or filename .endswith ("debugpy.py" ) :
222
+ hint = 'subtle'
223
+ else :
224
+ hint = 'normal'
225
+
217
226
# Use the VS Code path if we have a mapping, otherwise use the original path
218
227
display_path = self .path_mapping .get (filename , filename )
219
228
if filename != display_path :
220
229
self ._debug_print (f"[PDB] Stack trace path mapping: { filename } -> { display_path } " )
221
-
222
- # Create frame info
230
+ # Create StackFrame info
223
231
frames .append ({
224
232
"id" : frame_id ,
225
- "name" : name ,
233
+ "name" : name + f" { type ( frame . f_code . co_filename ). __name__ } " ,
226
234
"source" : {"path" : display_path },
227
235
"line" : line ,
228
236
"column" : 1 ,
229
237
"endLine" : line ,
230
- "endColumn" : 1
238
+ "endColumn" : 1 ,
239
+ "presentationHint" : hint
231
240
})
232
241
233
242
# Cache frame for variable access
@@ -248,60 +257,97 @@ def get_scopes(self, frame_id):
248
257
scopes = [
249
258
{
250
259
"name" : "Locals" ,
251
- "variablesReference" : frame_id * 1000 + 1 ,
260
+ "variablesReference" : frame_id * 1000 + VARREF_LOCALS ,
252
261
"expensive" : False
253
262
},
254
263
{
255
264
"name" : "Globals" ,
256
- "variablesReference" : frame_id * 1000 + 2 ,
265
+ "variablesReference" : frame_id * 1000 + VARREF_GLOBALS ,
257
266
"expensive" : False
258
267
}
259
268
]
260
269
return scopes
261
270
262
- def get_variables (self , variables_ref ):
263
- """Get variables for a scope."""
264
- frame_id = variables_ref // 1000
265
- scope_type = variables_ref % 1000
266
-
267
- if frame_id not in self .variables_cache :
268
- return []
269
-
270
- frame = self .variables_cache [frame_id ]
271
+ def _process_special_variables (self , var_dict ):
272
+ """Process special variables (those starting and ending with __)."""
273
+ variables = []
274
+ for name , value in var_dict .items ():
275
+ if name .startswith ('__' ) and name .endswith ('__' ):
276
+ try :
277
+ value_str = json .dumps (value )
278
+ type_str = type (value ).__name__
279
+ variables .append ({
280
+ "name" : name ,
281
+ "value" : value_str ,
282
+ "type" : type_str ,
283
+ "variablesReference" : 0
284
+ })
285
+ except Exception :
286
+ variables .append (self ._var_error (name ))
287
+ return variables
288
+
289
+ def _process_regular_variables (self , var_dict ):
290
+ """Process regular variables (excluding special ones)."""
271
291
variables = []
272
-
273
- if scope_type == 1 : # Locals
274
- var_dict = frame .f_locals if hasattr (frame , 'f_locals' ) else {}
275
- elif scope_type == 2 : # Globals
276
- var_dict = frame .f_globals if hasattr (frame , 'f_globals' ) else {}
277
- else :
278
- return []
279
-
280
292
for name , value in var_dict .items ():
281
293
# Skip private/internal variables
282
294
if name .startswith ('__' ) and name .endswith ('__' ):
283
295
continue
284
-
285
296
try :
286
- value_str = repr (value )
297
+ value_str = json . dumps (value )
287
298
type_str = type (value ).__name__
288
-
289
299
variables .append ({
290
300
"name" : name ,
291
301
"value" : value_str ,
292
302
"type" : type_str ,
293
- "variablesReference" : 0 # Simple implementation - no nested objects
294
- })
295
- except Exception :
296
- variables .append ({
297
- "name" : name ,
298
- "value" : "<error>" ,
299
- "type" : "unknown" ,
300
303
"variablesReference" : 0
301
304
})
302
-
305
+ except Exception :
306
+ variables .append (self ._var_error (name ))
303
307
return variables
308
+
309
+ @staticmethod
310
+ def _var_error (name :str ):
311
+ return {"name" : name , "value" : "<error>" , "type" : "unknown" , "variablesReference" : 0 }
312
+
313
+ @staticmethod
314
+ def _special_vars (varref :int ):
315
+ return {"name" : "Special" , "value" : "" , "variablesReference" : varref }
316
+
317
+ def get_variables (self , variables_ref ):
318
+ """Get variables for a scope."""
319
+ frame_id = variables_ref // 1000
320
+ scope_type = variables_ref % 1000
321
+
322
+ if frame_id not in self .variables_cache :
323
+ return []
324
+
325
+ frame = self .variables_cache [frame_id ]
304
326
327
+ # Handle special scope types first
328
+ if scope_type == VARREF_LOCALS_SPECIAL :
329
+ var_dict = frame .f_locals if hasattr (frame , 'f_locals' ) else {}
330
+ return self ._process_special_variables (var_dict )
331
+ elif scope_type == VARREF_GLOBALS_SPECIAL :
332
+ var_dict = frame .f_globals if hasattr (frame , 'f_globals' ) else {}
333
+ return self ._process_special_variables (var_dict )
334
+
335
+ # Handle regular scope types with special folder
336
+ variables = []
337
+ if scope_type == VARREF_LOCALS :
338
+ var_dict = frame .f_locals if hasattr (frame , 'f_locals' ) else {}
339
+ variables .append (self ._special_vars ( VARREF_LOCALS_SPECIAL ))
340
+ elif scope_type == VARREF_GLOBALS :
341
+ var_dict = frame .f_globals if hasattr (frame , 'f_globals' ) else {}
342
+ variables .append (self ._special_vars ( VARREF_GLOBALS_SPECIAL ))
343
+ else :
344
+ # Invalid reference, return empty
345
+ return []
346
+
347
+ # Add regular variables
348
+ variables .extend (self ._process_regular_variables (var_dict ))
349
+ return variables
350
+
305
351
def evaluate_expression (self , expression , frame_id = None ):
306
352
"""Evaluate an expression in the context of a frame."""
307
353
if frame_id is not None and frame_id in self .variables_cache :
@@ -317,14 +363,13 @@ def evaluate_expression(self, expression, frame_id=None):
317
363
else :
318
364
globals_dict = globals ()
319
365
locals_dict = {}
320
-
321
366
try :
322
367
# Evaluate the expression
323
368
result = eval (expression , globals_dict , locals_dict )
324
369
return result
325
370
except Exception as e :
326
371
raise Exception (f"Evaluation error: { e } " )
327
-
372
+
328
373
def cleanup (self ):
329
374
"""Clean up resources."""
330
375
self .variables_cache .clear ()
0 commit comments