33import sys
44import time
55import os
6+ import json
67from ..common .constants import (
78 TRACE_CALL , TRACE_LINE , TRACE_RETURN , TRACE_EXCEPTION ,
89 SCOPE_LOCALS , SCOPE_GLOBALS
910)
11+ VARREF_LOCALS = 1
12+ VARREF_GLOBALS = 2
13+ VARREF_LOCALS_SPECIAL = 3
14+ VARREF_GLOBALS_SPECIAL = 4
1015
1116
1217class PdbAdapter :
@@ -26,7 +31,7 @@ def __init__(self):
2631
2732 def _debug_print (self , message ):
2833 """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
3035 print (message )
3136
3237 def _normalize_path (self , path ):
@@ -197,7 +202,7 @@ def wait_for_continue(self):
197202 while not self .continue_event :
198203 # Process any pending DAP messages (scopes, variables, etc.)
199204 if hasattr (self , '_debug_session' ):
200- self ._debug_session .process_pending_messages ()
205+ self ._debug_session .process_pending_messages () # type: ignore
201206 time .sleep (0.01 )
202207
203208 def get_stack_trace (self ):
@@ -213,21 +218,25 @@ def get_stack_trace(self):
213218 filename = frame .f_code .co_filename
214219 name = frame .f_code .co_name
215220 line = frame .f_lineno
216-
221+ if "<stdin>" in filename or filename .endswith ("debugpy.py" ) :
222+ hint = 'subtle'
223+ else :
224+ hint = 'normal'
225+
217226 # Use the VS Code path if we have a mapping, otherwise use the original path
218227 display_path = self .path_mapping .get (filename , filename )
219228 if filename != display_path :
220229 self ._debug_print (f"[PDB] Stack trace path mapping: { filename } -> { display_path } " )
221-
222- # Create frame info
230+ # Create StackFrame info
223231 frames .append ({
224232 "id" : frame_id ,
225- "name" : name ,
233+ "name" : name + f" { type ( frame . f_code . co_filename ). __name__ } " ,
226234 "source" : {"path" : display_path },
227235 "line" : line ,
228236 "column" : 1 ,
229237 "endLine" : line ,
230- "endColumn" : 1
238+ "endColumn" : 1 ,
239+ "presentationHint" : hint
231240 })
232241
233242 # Cache frame for variable access
@@ -248,60 +257,97 @@ def get_scopes(self, frame_id):
248257 scopes = [
249258 {
250259 "name" : "Locals" ,
251- "variablesReference" : frame_id * 1000 + 1 ,
260+ "variablesReference" : frame_id * 1000 + VARREF_LOCALS ,
252261 "expensive" : False
253262 },
254263 {
255264 "name" : "Globals" ,
256- "variablesReference" : frame_id * 1000 + 2 ,
265+ "variablesReference" : frame_id * 1000 + VARREF_GLOBALS ,
257266 "expensive" : False
258267 }
259268 ]
260269 return scopes
261270
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)."""
271291 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-
280292 for name , value in var_dict .items ():
281293 # Skip private/internal variables
282294 if name .startswith ('__' ) and name .endswith ('__' ):
283295 continue
284-
285296 try :
286- value_str = repr (value )
297+ value_str = json . dumps (value )
287298 type_str = type (value ).__name__
288-
289299 variables .append ({
290300 "name" : name ,
291301 "value" : value_str ,
292302 "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" ,
300303 "variablesReference" : 0
301304 })
302-
305+ except Exception :
306+ variables .append (self ._var_error (name ))
303307 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 ]
304326
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+
305351 def evaluate_expression (self , expression , frame_id = None ):
306352 """Evaluate an expression in the context of a frame."""
307353 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):
317363 else :
318364 globals_dict = globals ()
319365 locals_dict = {}
320-
321366 try :
322367 # Evaluate the expression
323368 result = eval (expression , globals_dict , locals_dict )
324369 return result
325370 except Exception as e :
326371 raise Exception (f"Evaluation error: { e } " )
327-
372+
328373 def cleanup (self ):
329374 """Clean up resources."""
330375 self .variables_cache .clear ()
0 commit comments