@@ -721,45 +721,79 @@ def _lightweight_serialize(self, value):
721721 return f"<{ type_name } object>"
722722
723723 def set_variable (self , variables_ref : int , name : str , value : str ) -> dict [str , str | int ]:
724- """Set a variable to a new value and return the updated variable info."""
724+ """Set a variable to a new value and return the updated variable info.
725+
726+ This function can modify both global and local variables when using a MicroPython
727+ build with settrace and local variable modification support (sys._set_local_var).
728+
729+ For global variables: Works reliably on all MicroPython builds.
730+ For local variables: Requires MicroPython build with C-level local variable support.
731+ """
725732 # Handle complex variable references (not supported for setting)
726733 if variables_ref >= VARREF_COMPLEX_BASE :
727734 raise Exception ("Cannot set variables in complex object expansions" )
728735
729736 frame_id = variables_ref // 1000
730737 scope_type = variables_ref % 1000
731738
732- if frame_id not in self .variables_cache :
733- raise Exception ("Invalid frame reference" )
734-
735- frame = self .variables_cache [frame_id ]
739+ # Only allow setting variables in the topmost frame (frame_id = 0)
740+ if frame_id != 0 :
741+ raise Exception ("Variable modification is only allowed in the topmost frame" )
736742
737- # Determine the variable dictionary to modify
738- if scope_type == VARREF_LOCALS or scope_type == VARREF_LOCALS_SPECIAL :
739- var_dict = frame .f_locals if hasattr (frame , "f_locals" ) else {}
740- elif scope_type == VARREF_GLOBALS or scope_type == VARREF_GLOBALS_SPECIAL :
741- var_dict = frame .f_globals if hasattr (frame , "f_globals" ) else {}
742- else :
743- raise Exception ("Invalid scope reference" )
743+ # Use the current frame for modification
744+ frame = self .current_frame
745+ if frame is None :
746+ raise Exception ("No current frame available" )
744747
745- # Check if variable exists
746- if name not in var_dict :
747- raise Exception ( f"Variable ' { name } ' not found in the specified scope" )
748+ # Get the appropriate variable contexts
749+ globals_dict = frame . f_globals if hasattr ( frame , "f_globals" ) else {}
750+ locals_dict = frame . f_locals if hasattr ( frame , "f_locals" ) else {}
748751
749752 try :
750- # Evaluate the new value in the context of the frame
751- globals_dict = frame .f_globals if hasattr (frame , "f_globals" ) else {}
752- locals_dict = frame .f_locals if hasattr (frame , "f_locals" ) else {}
753-
754- # Try to evaluate the value as a Python expression
753+ # Try to evaluate the new value as a Python expression
755754 try :
756755 new_value = eval (value , globals_dict , locals_dict )
757756 except :
758757 # If evaluation fails, treat as string literal
759758 new_value = value
760759
761- # Set the variable
762- var_dict [name ] = new_value
760+ if scope_type == VARREF_GLOBALS or scope_type == VARREF_GLOBALS_SPECIAL :
761+ # Check if variable exists in globals
762+ if name not in globals_dict :
763+ raise Exception (f"Global variable '{ name } ' not found" )
764+
765+ # For global variables, direct assignment works reliably
766+ globals_dict [name ] = new_value
767+ self ._debug_print (f"[PDB] Successfully set global variable '{ name } ' = { new_value } " )
768+
769+ elif scope_type == VARREF_LOCALS or scope_type == VARREF_LOCALS_SPECIAL :
770+ # Check if variable exists in locals
771+ if name not in locals_dict :
772+ raise Exception (f"Local variable '{ name } ' not found" )
773+
774+ # Try to use the frame._set_local method to set local variables
775+ try :
776+ if hasattr (frame , '_set_local' ):
777+ # Use the frame._set_local method (CPython-compatible API)
778+ frame ._set_local (name , new_value )
779+ self ._debug_print (f"[PDB] Successfully set local variable '{ name } ' = { new_value } " )
780+ else :
781+ # Fallback error if the method is not available
782+ raise Exception (
783+ f"Cannot modify local variable '{ name } '. "
784+ f"This MicroPython build doesn't support local variable modification. "
785+ f"Please use a MicroPython build with settrace and local variable support."
786+ )
787+ except Exception as inner_e :
788+ # If frame.set_local fails, provide detailed error
789+ raise Exception (
790+ f"Failed to modify local variable '{ name } ': { str (inner_e )} . "
791+ f"Local variables in MicroPython are stored in internal code_state->state[] slots. "
792+ f"Consider using global variables for reliable modification during debugging."
793+ )
794+
795+ else :
796+ raise Exception ("Invalid scope reference" )
763797
764798 # Return the updated variable info
765799 return self ._get_variable_info (name , new_value )
0 commit comments