From dabe197e1213308d26e9fc8da6c4fb5e80fe77e3 2021-06-23 14:55:06 From: Matthias Bussonnier Date: 2021-06-23 14:55:06 Subject: [PATCH] Backport PR #13021: Don't access current_frame f_locals --- diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py index bb48c27..02d01ad 100644 --- a/IPython/core/debugger.py +++ b/IPython/core/debugger.py @@ -332,7 +332,7 @@ class Pdb(OldPdb): if frame in (self.curframe, getattr(self, "initial_frame", None)): return False else: - return frame.f_locals.get("__tracebackhide__", False) + return self._get_frame_locals(frame).get("__tracebackhide__", False) return False @@ -424,6 +424,28 @@ class Pdb(OldPdb): self.shell.hooks.synchronize_with_editor(filename, lineno, 0) # vds: << + def _get_frame_locals(self, frame): + """ " + Acessing f_local of current frame reset the namespace, so we want to avoid + that or the following can happend + + ipdb> foo + "old" + ipdb> foo = "new" + ipdb> foo + "new" + ipdb> where + ipdb> foo + "old" + + So if frame is self.current_frame we instead return self.curframe_locals + + """ + if frame is self.curframe: + return self.curframe_locals + else: + return frame.f_locals + def format_stack_entry(self, frame_lineno, lprefix=': ', context=None): if context is None: context = self.context @@ -451,10 +473,11 @@ class Pdb(OldPdb): frame, lineno = frame_lineno return_value = '' - if '__return__' in frame.f_locals: - rv = frame.f_locals['__return__'] - #return_value += '->' - return_value += reprlib.repr(rv) + '\n' + loc_frame = self._get_frame_locals(frame) + if "__return__" in loc_frame: + rv = loc_frame["__return__"] + # return_value += '->' + return_value += reprlib.repr(rv) + "\n" ret.append(return_value) #s = filename + '(' + `lineno` + ')' @@ -466,10 +489,10 @@ class Pdb(OldPdb): else: func = "" - call = '' - if func != '?': - if '__args__' in frame.f_locals: - args = reprlib.repr(frame.f_locals['__args__']) + call = "" + if func != "?": + if "__args__" in loc_frame: + args = reprlib.repr(loc_frame["__args__"]) else: args = '()' call = tpl_call % (func, args) @@ -660,7 +683,7 @@ class Pdb(OldPdb): def getsourcelines(self, obj): lines, lineno = inspect.findsource(obj) - if inspect.isframe(obj) and obj.f_globals is obj.f_locals: + if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj): # must be a module frame: do not try to cut a block out of it return lines, 1 elif inspect.ismodule(obj): diff --git a/IPython/core/tests/test_debugger.py b/IPython/core/tests/test_debugger.py index 9fdc944..7c94592 100644 --- a/IPython/core/tests/test_debugger.py +++ b/IPython/core/tests/test_debugger.py @@ -280,14 +280,14 @@ def test_xmode_skip(): block = dedent( """ -def f(): - __tracebackhide__ = True - g() + def f(): + __tracebackhide__ = True + g() -def g(): - raise ValueError + def g(): + raise ValueError -f() + f() """ ) @@ -298,15 +298,15 @@ f() block = dedent( """ -def f(): - __tracebackhide__ = True - g() + def f(): + __tracebackhide__ = True + g() -def g(): - from IPython.core.debugger import set_trace - set_trace() + def g(): + from IPython.core.debugger import set_trace + set_trace() -f() + f() """ ) @@ -324,3 +324,70 @@ f() child.expect("ipdb>") child.close() + + +@skip_win32 +def test_where_erase_value(): + """Test that `where` does not access f_locals and erase values.""" + import pexpect + + env = os.environ.copy() + env["IPY_TEST_SIMPLE_PROMPT"] = "1" + + child = pexpect.spawn( + sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env + ) + child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE + + child.expect("IPython") + child.expect("\n") + child.expect_exact("In [1]") + + block = dedent( + """ + def simple_f(): + myvar = 1 + print(myvar) + 1/0 + print(myvar) + simple_f() """ + ) + + for line in block.splitlines(): + child.sendline(line) + child.expect_exact(line) + child.expect_exact("ZeroDivisionError") + child.expect_exact("In [2]:") + + child.sendline("%debug") + + ## + child.expect("ipdb>") + + child.sendline("myvar") + child.expect("1") + + ## + child.expect("ipdb>") + + child.sendline("myvar = 2") + + ## + child.expect_exact("ipdb>") + + child.sendline("myvar") + + child.expect_exact("2") + + ## + child.expect("ipdb>") + child.sendline("where") + + ## + child.expect("ipdb>") + child.sendline("myvar") + + child.expect_exact("2") + child.expect("ipdb>") + + child.close()