From 69b843ed0b152b333a67bfcbfff57a082368dad9 Mon Sep 17 00:00:00 2001 From: Egor Eliseev Date: Mon, 18 Oct 2021 16:17:55 +0300 Subject: [PATCH] PY-28563 "Set value" doesn't work (cherry picked from commit 0c42984de25e97e748e719dccfe3657fc4eb79f1) IJ-CR-15473 GitOrigin-RevId: 93090f9d81d14009120682796c11862dbe9e3e98 --- .../pydev/_pydevd_bundle/pydevd_vars.py | 12 + python/testData/debug/test_set_value_class.py | 7 + python/testData/debug/test_set_value_dict.py | 7 + python/testData/debug/test_set_value_list.py | 3 + .../debug/test_set_value_locals_globals.py | 10 + .../debug/test_set_value_simple_var.py | 3 + .../env/debug/PythonDebuggerSetValueTest.java | 220 ++++++++++++++++++ 7 files changed, 262 insertions(+) create mode 100644 python/testData/debug/test_set_value_class.py create mode 100644 python/testData/debug/test_set_value_dict.py create mode 100644 python/testData/debug/test_set_value_list.py create mode 100644 python/testData/debug/test_set_value_locals_globals.py create mode 100644 python/testData/debug/test_set_value_simple_var.py create mode 100644 python/testSrc/com/jetbrains/env/debug/PythonDebuggerSetValueTest.java diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index aaccd0bfea70..40d06d92e665 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -457,11 +457,17 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg, value=SEN if attr[:7] == "Globals": attr = attr[8:] + if is_complex(attr): + Exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_globals) + return value if attr in frame.f_globals: frame.f_globals[attr] = value return frame.f_globals[attr] else: if pydevd_save_locals.is_save_locals_available(): + if is_complex(attr): + Exec('%s=%s' % (attr, expression), frame.f_locals, frame.f_locals) + return value frame.f_locals[attr] = value pydevd_save_locals.save_locals(frame) return frame.f_locals[attr] @@ -474,6 +480,12 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg, value=SEN except Exception: traceback.print_exc() +def is_complex(attr): + complex_indicators = ['[', ']', '.'] + for indicator in complex_indicators: + if attr.find(indicator) != -1: + return True + return False MAXIMUM_ARRAY_SIZE = float('inf') diff --git a/python/testData/debug/test_set_value_class.py b/python/testData/debug/test_set_value_class.py new file mode 100644 index 000000000000..2b8fab4c4a1a --- /dev/null +++ b/python/testData/debug/test_set_value_class.py @@ -0,0 +1,7 @@ +class Foo: + def __init__(self): + self.fst = 1 + self.snd = "hello" + +foo = Foo() +print(foo.fst, foo.snd) \ No newline at end of file diff --git a/python/testData/debug/test_set_value_dict.py b/python/testData/debug/test_set_value_dict.py new file mode 100644 index 000000000000..169503229564 --- /dev/null +++ b/python/testData/debug/test_set_value_dict.py @@ -0,0 +1,7 @@ +objs = { + 'one': 1, + 'two': 2, + 'three': 3 +} + +print(objs) \ No newline at end of file diff --git a/python/testData/debug/test_set_value_list.py b/python/testData/debug/test_set_value_list.py new file mode 100644 index 000000000000..e663a9511779 --- /dev/null +++ b/python/testData/debug/test_set_value_list.py @@ -0,0 +1,3 @@ +lst = [1, 2, 3, 4] + +print(lst) \ No newline at end of file diff --git a/python/testData/debug/test_set_value_locals_globals.py b/python/testData/debug/test_set_value_locals_globals.py new file mode 100644 index 000000000000..acb5f63369b2 --- /dev/null +++ b/python/testData/debug/test_set_value_locals_globals.py @@ -0,0 +1,10 @@ +lst = [1, 2, 3] + + +def test(): + lst = ['a', 'b', 'c'] + print(lst) + + +test() +print(lst) \ No newline at end of file diff --git a/python/testData/debug/test_set_value_simple_var.py b/python/testData/debug/test_set_value_simple_var.py new file mode 100644 index 000000000000..edfde45c95c0 --- /dev/null +++ b/python/testData/debug/test_set_value_simple_var.py @@ -0,0 +1,3 @@ +x = 10 + +print(x) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/env/debug/PythonDebuggerSetValueTest.java b/python/testSrc/com/jetbrains/env/debug/PythonDebuggerSetValueTest.java new file mode 100644 index 000000000000..fad985500114 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/debug/PythonDebuggerSetValueTest.java @@ -0,0 +1,220 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.jetbrains.env.debug; + +import com.intellij.xdebugger.XDebuggerTestUtil; +import com.intellij.xdebugger.frame.XValue; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.python.debugger.PyDebugValue; +import org.junit.Test; + +import java.util.List; + +public class PythonDebuggerSetValueTest extends PyEnvTestCase { + + /** + * setValue(x, 'change') + * assert(x == 'change') + */ + @Test + public void testSetValueSimpleVariable() { + runPythonTest(new PyDebuggerTask("/debug", "test_set_value_simple_var.py") { + @Override + public void before() { + toggleBreakpoint(getFilePath(getScriptName()), 2); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("x").hasValue("10"); + setVal("x", "\"change\""); + eval("x").hasValue("'change'"); + } + }); + } + + /** + * setValue(objs['two'], "change") + * assert(objs['two'] == "change") + *

+ * setValue(objs['three'], [1, 2, 3]) + * assert(objs['three'] == [1, 2, 3]) + *

+ * setValue(objs['three'][0], 'hello') + * assert(objs['three'][0] == 'hello') + */ + @Test + public void testSetValueDict() { + runPythonTest(new PyDebuggerTask("/debug", "test_set_value_dict.py") { + @Override + public void before() { + toggleBreakpoint(getFilePath(getScriptName()), 6); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + List frameVariables = loadFrame(); + PyDebugValue var = findDebugValueByName(frameVariables, "objs"); + List children = XDebuggerTestUtil.collectChildren(var); + PyDebugValue two = (PyDebugValue)children.get(1); + PyDebugValue three = (PyDebugValue)children.get(2); + + eval("objs['two']").hasValue("2"); + myDebugProcess.changeVariable(two, "\"change\""); + eval("objs['two']").hasValue("'change'"); + + eval("objs['three']").hasValue("3"); + myDebugProcess.changeVariable(three, "[1, 2, 3]"); + eval("objs['three']").hasValue("[1, 2, 3]"); + + children = XDebuggerTestUtil.collectChildren(var); + three = (PyDebugValue)children.get(2); + children = XDebuggerTestUtil.collectChildren(three); + PyDebugValue one = (PyDebugValue)children.get(0); + + eval("objs['three'][0]").hasValue("1"); + myDebugProcess.changeVariable(one, "\"hello\""); + eval("objs['three'][0]").hasValue("'hello'"); + } + }); + } + + /** + * setValue(lst[0], "change") + * assert(lst[0] == "change") + *

+ * setValue(lst[1], ['1', '2', '3']) + * assert(lst[1] == ['1', '2', '3']) + *

+ * setValue(lst[2], {'one': 1, 'two': 2}) + * assert(lst[2] == {'one': 1, 'two': 2}) + *

+ * setValue(lst[2]['one'], 'hello') + * assert(lst[2]['one'] == 'hello') + */ + @Test + public void testSetValueList() { + runPythonTest(new PyDebuggerTask("/debug", "test_set_value_list.py") { + @Override + public void before() { + toggleBreakpoint(getFilePath(getScriptName()), 2); + } + + @Override + public void testing() throws Exception { + waitForPause(); + List frameVariables = loadFrame(); + PyDebugValue var = findDebugValueByName(frameVariables, "lst"); + List children = XDebuggerTestUtil.collectChildren(var); + PyDebugValue one = (PyDebugValue)children.get(0); + PyDebugValue two = (PyDebugValue)children.get(1); + PyDebugValue three = (PyDebugValue)children.get(2); + + eval("lst[0]").hasValue("1"); + myDebugProcess.changeVariable(one, "\"change\""); + eval("lst[0]").hasValue("'change'"); + + eval("lst[1]").hasValue("2"); + myDebugProcess.changeVariable(two, "['1', '2', '3']"); + eval("lst[1]").hasValue("['1', '2', '3']"); + + eval("lst[2]").hasValue("3"); + myDebugProcess.changeVariable(three, "{'one': 1, 'two': 2}"); + eval("lst[2]").hasValue("{'one': 1, 'two': 2}"); + + children = XDebuggerTestUtil.collectChildren(var); + three = (PyDebugValue)children.get(2); + children = XDebuggerTestUtil.collectChildren(three); + one = (PyDebugValue)children.get(0); + + eval("lst[2]['one']").hasValue("1"); + myDebugProcess.changeVariable(one, "'hello'"); + eval("lst[2]['one']").hasValue("'hello'"); + } + }); + } + + /** + * setValue(foo.fst, 'change') + * assert(foo.fst == 'change') + *

+ * setValue(foo.snd, ['1', '2', '3']) + * assert(foo.snd == ['1', '2', '3']) + *

+ * setValue(foo.snd[0], 'hello') + * assert(foo.snd[0] == 'hello') + */ + @Test + public void testSetValueClass() { + runPythonTest(new PyDebuggerTask("/debug", "test_set_value_class.py") { + @Override + public void before() { + toggleBreakpoint(getFilePath(getScriptName()), 6); + } + + @Override + public void testing() throws Exception { + waitForPause(); + List frameVariables = loadFrame(); + PyDebugValue var = findDebugValueByName(frameVariables, "foo"); + List children = XDebuggerTestUtil.collectChildren(var); + PyDebugValue one = (PyDebugValue)children.get(0); + PyDebugValue two = (PyDebugValue)children.get(1); + + eval("foo.fst").hasValue("1"); + myDebugProcess.changeVariable(one, "\"change\""); + eval("foo.fst").hasValue("'change'"); + + eval("foo.snd").hasValue("'hello'"); + myDebugProcess.changeVariable(two, "['1', '2', '3']"); + eval("foo.snd").hasValue("['1', '2', '3']"); + + children = XDebuggerTestUtil.collectChildren(var); + two = (PyDebugValue)children.get(1); + children = XDebuggerTestUtil.collectChildren(two); + one = (PyDebugValue)children.get(0); + + eval("foo.snd[0]").hasValue("'1'"); + myDebugProcess.changeVariable(one, "'hello'"); + eval("foo.snd[0]").hasValue("'hello'"); + } + }); + } + + /** + * setValue(test().lst[0], 'change') + * assert(test().lst[0] == 'change') + *

+ * assert(lst == [1, 2, 3]) + */ + @Test + public void testSetValueLocalsGlobals() { + runPythonTest(new PyDebuggerTask("/debug", "test_set_value_locals_globals.py") { + @Override + public void before() { + toggleBreakpoint(getFilePath(getScriptName()), 5); + toggleBreakpoint(getFilePath(getScriptName()), 9); + } + + @Override + public void testing() throws Exception { + waitForPause(); + List frameVariables = loadFrame(); + PyDebugValue var = findDebugValueByName(frameVariables, "lst"); + List children = XDebuggerTestUtil.collectChildren(var); + PyDebugValue one = (PyDebugValue)children.get(0); + + eval("lst[0]").hasValue("'a'"); + myDebugProcess.changeVariable(one, "'change'"); + eval("lst[0]").hasValue("'change'"); + + resume(); + waitForPause(); + + eval("lst").hasValue("[1, 2, 3]"); + } + }); + } +} \ No newline at end of file