Skip to content

Commit

Permalink
Backport PR ipython#13021: Don't access current_frame f_locals
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau authored and meeseeksmachine committed Jun 23, 2021
1 parent 104a0d1 commit dabe197
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 23 deletions.
43 changes: 33 additions & 10 deletions IPython/core/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def _hidden_predicate(self, frame):
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

Expand Down Expand Up @@ -424,6 +424,28 @@ def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
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
Expand Down Expand Up @@ -451,10 +473,11 @@ def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
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` + ')'
Expand All @@ -466,10 +489,10 @@ def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
else:
func = "<lambda>"

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)
Expand Down Expand Up @@ -660,7 +683,7 @@ def do_list(self, arg):

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):
Expand Down
93 changes: 80 additions & 13 deletions IPython/core/tests/test_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
"""
)

Expand All @@ -298,15 +298,15 @@ def g():

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()
"""
)

Expand All @@ -324,3 +324,70 @@ def g():
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()

0 comments on commit dabe197

Please sign in to comment.