diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index d855b3966..64ac454f2 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -934,7 +934,7 @@ def internal_change_variable_json(py_db, request): ) return - child_var = variable.change_variable(arguments.name, arguments.value, py_db, fmt=fmt) + child_var = variable.change_variable(arguments.name, arguments.value, py_db, fmt=fmt, scope=scope) if child_var is None: _write_variable_response(py_db, request, value="", success=False, message="Unable to change: %s." % (arguments.name,)) diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_plugin_utils.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_plugin_utils.py index df1f50c18..e3da713f1 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_plugin_utils.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_plugin_utils.py @@ -199,9 +199,9 @@ def exception_break(self, py_db, frame, thread, arg, is_unwind=False): return None - def change_variable(self, frame, attr, expression): + def change_variable(self, frame, attr, expression, scope=None): for plugin in self.active_plugins: - ret = plugin.change_variable(frame, attr, expression, self.EMPTY_SENTINEL) + ret = plugin.change_variable(frame, attr, expression, self.EMPTY_SENTINEL, scope) if ret is not self.EMPTY_SENTINEL: return ret diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py index b82823229..7bf8dc8a5 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py @@ -200,7 +200,7 @@ def get_children_variables(self, fmt=None, scope=None): return children_variables - def change_variable(self, name, value, py_db, fmt=None): + def change_variable(self, name, value, py_db, fmt=None, scope: Optional[ScopeRequest]=None): children_variable = self.get_child_variable_named(name) if children_variable is None: return None @@ -255,12 +255,10 @@ def __init__(self, py_db, frame, register_variable): self._register_variable = register_variable self._register_variable(self) - def change_variable(self, name, value, py_db, fmt=None): + def change_variable(self, name, value, py_db, fmt=None, scope: Optional[ScopeRequest]=None): frame = self.frame - - pydevd_vars.change_attr_expression(frame, name, value, py_db) - - return self.get_child_variable_named(name, fmt=fmt) + pydevd_vars.change_attr_expression(frame, name, value, py_db, scope=scope) + return self.get_child_variable_named(name, fmt=fmt, scope=scope) @silence_warnings_decorator @overrides(_AbstractVariable.get_children_variables) diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py index 8ec4dec4a..799dd07d1 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py @@ -15,11 +15,12 @@ from _pydev_bundle._pydev_saved_modules import threading from _pydevd_bundle import pydevd_save_locals, pydevd_timeout, pydevd_constants from _pydev_bundle.pydev_imports import Exec, execfile -from _pydevd_bundle.pydevd_utils import to_string +from _pydevd_bundle.pydevd_utils import to_string, ScopeRequest import inspect from _pydevd_bundle.pydevd_daemon_thread import PyDBDaemonThread from _pydevd_bundle.pydevd_save_locals import update_globals_and_locals from functools import lru_cache +from typing import Optional SENTINEL_VALUE = [] @@ -595,11 +596,15 @@ def method(): del frame -def change_attr_expression(frame, attr, expression, dbg, value=SENTINEL_VALUE): +def change_attr_expression(frame, attr, expression, dbg, value=SENTINEL_VALUE, /, scope: Optional[ScopeRequest]=None): """Changes some attribute in a given frame.""" if frame is None: return + if scope is not None: + assert isinstance(scope, ScopeRequest) + scope = scope.scope + try: expression = expression.replace("@LINE@", "\n") @@ -608,13 +613,15 @@ def change_attr_expression(frame, attr, expression, dbg, value=SENTINEL_VALUE): if result is not dbg.plugin.EMPTY_SENTINEL: return result - if attr[:7] == "Globals": - attr = attr[8:] + if attr[:7] == "Globals" or scope == "globals": + attr = attr[8:] if attr.startswith("Globals") else attr if attr in frame.f_globals: if value is SENTINEL_VALUE: value = eval(expression, frame.f_globals, frame.f_locals) frame.f_globals[attr] = value return frame.f_globals[attr] + else: + raise VariableError("Attribute %s not found in globals" % attr) else: if "." not in attr: # i.e.: if we have a '.', we're changing some attribute of a local var. if pydevd_save_locals.is_save_locals_available(): @@ -631,8 +638,9 @@ def change_attr_expression(frame, attr, expression, dbg, value=SENTINEL_VALUE): Exec("%s=%s" % (attr, expression), frame.f_globals, frame.f_locals) return result - except Exception: - pydev_log.exception() + except Exception as e: + pydev_log.exception(e) + MAXIMUM_ARRAY_SIZE = 100 diff --git a/src/debugpy/_vendored/pydevd/pydevd_plugins/django_debug.py b/src/debugpy/_vendored/pydevd/pydevd_plugins/django_debug.py index 39fcb111d..bacd4f7af 100644 --- a/src/debugpy/_vendored/pydevd/pydevd_plugins/django_debug.py +++ b/src/debugpy/_vendored/pydevd/pydevd_plugins/django_debug.py @@ -427,10 +427,10 @@ def __init__(self, frame, original_filename, lineno, f_locals): self.f_trace = None -def change_variable(frame, attr, expression, default): +def change_variable(frame, attr, expression, default, scope=None): if isinstance(frame, DjangoTemplateFrame): result = eval(expression, frame.f_globals, frame.f_locals) - frame._change_variable(attr, result) + frame._change_variable(attr, result, scope=scope) return result return default diff --git a/src/debugpy/_vendored/pydevd/pydevd_plugins/jinja2_debug.py b/src/debugpy/_vendored/pydevd/pydevd_plugins/jinja2_debug.py index 5d4bb589e..861275c17 100644 --- a/src/debugpy/_vendored/pydevd/pydevd_plugins/jinja2_debug.py +++ b/src/debugpy/_vendored/pydevd/pydevd_plugins/jinja2_debug.py @@ -249,10 +249,10 @@ def __init__(self, frame, exception_cls_name, filename, lineno, f_locals): self.f_trace = None -def change_variable(frame, attr, expression, default): +def change_variable(frame, attr, expression, default, scope=None): if isinstance(frame, Jinja2TemplateFrame): result = eval(expression, frame.f_globals, frame.f_locals) - frame._change_variable(frame.f_back, attr, result) + frame._change_variable(frame.f_back, attr, result, scope=scope) return result return default diff --git a/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_globals.py b/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_globals.py index 17bf0b182..834242984 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_globals.py +++ b/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_globals.py @@ -9,4 +9,5 @@ def method(self): if __name__ == '__main__': SomeClass().method() + print('second breakpoint') print('TEST SUCEEDED') diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py index b0b63fda8..c992f078e 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py @@ -5931,13 +5931,28 @@ def test_send_json_message(case_setup_dap): def test_global_scope(case_setup_dap): with case_setup_dap.test_file("_debugger_case_globals.py") as writer: json_facade = JsonFacade(writer) - json_facade.write_set_breakpoints(writer.get_line_index_with_content("breakpoint here")) + break1 = writer.get_line_index_with_content("breakpoint here") + break2 = writer.get_line_index_with_content("second breakpoint") + json_facade.write_set_breakpoints([break1, break2]) json_facade.write_make_initial_run() json_hit = json_facade.wait_for_thread_stopped() local_var = json_facade.get_global_var(json_hit.frame_id, "in_global_scope") assert local_var.value == "'in_global_scope_value'" + + scopes_request = json_facade.write_request(pydevd_schema.ScopesRequest(pydevd_schema.ScopesArguments(json_hit.frame_id))) + scopes_response = json_facade.wait_for_response(scopes_request) + assert len(scopes_response.body.scopes) == 2 + assert scopes_response.body.scopes[0]["name"] == "Locals" + assert scopes_response.body.scopes[1]["name"] == "Globals" + globals_varreference = scopes_response.body.scopes[1]["variablesReference"] + + json_facade.write_set_variable(globals_varreference, "in_global_scope", "'new_value'") + json_facade.write_continue() + json_hit2 = json_facade.wait_for_thread_stopped() + global_var = json_facade.get_global_var(json_hit2.frame_id, "in_global_scope") + assert global_var.value == "'new_value'" json_facade.write_continue() writer.finished_ok = True