Index: Lib/pdb.py =================================================================== --- Lib/pdb.py (revision 84373) +++ Lib/pdb.py (working copy) @@ -188,6 +188,7 @@ def forget(self): self.lineno = None self.stack = [] + self.frame_locals = [] self.curindex = 0 self.curframe = None self.tb_lineno.clear() @@ -205,7 +206,11 @@ self.curframe = self.stack[self.curindex][0] # The f_locals dictionary is updated from the actual frame # locals whenever the .f_locals accessor is called, so we - # cache it here to ensure that modifications are not overwritten. + # cache all of the frame locals here to ensure that modifications + # are not overwritten. Note that we have to cache all of them so + # that we can switch frames properly with the 'up and 'down' commands. + self.frame_locals = [stack_entry[0].f_locals + for stack_entry in self.stack] self.curframe_locals = self.curframe.f_locals return self.execRcLines() @@ -266,7 +271,8 @@ self.onecmd(line) self.lastcmd = lastcmd_back if not self.commands_silent[currentbp]: - self.print_stack_entry(self.stack[self.curindex]) + self.print_stack_entry(frame_lineno = self.stack[self.curindex], + locals = self.frame_locals[self.curindex]) if self.commands_doprompt[currentbp]: self.cmdloop() self.forget() @@ -300,7 +306,8 @@ # a command like "continue") self.forget() return - self.print_stack_entry(self.stack[self.curindex]) + self.print_stack_entry(frame_lineno = self.stack[self.curindex], + locals = self.frame_locals[self.curindex]) self.cmdloop() self.forget() @@ -800,8 +807,9 @@ assert 0 <= number < len(self.stack) self.curindex = number self.curframe = self.stack[self.curindex][0] - self.curframe_locals = self.curframe.f_locals - self.print_stack_entry(self.stack[self.curindex]) + self.curframe_locals = self.frame_locals[self.curindex] + self.print_stack_entry(frame_lineno = self.stack[self.curindex], + locals = self.frame_locals[self.curindex]) self.lineno = None def do_up(self, arg): @@ -944,7 +952,8 @@ # new position self.curframe.f_lineno = arg self.stack[self.curindex] = self.stack[self.curindex][0], arg - self.print_stack_entry(self.stack[self.curindex]) + self.print_stack_entry(frame_lineno = self.stack[self.curindex], + locals = self.frame_locals[self.curindex]) except ValueError as e: self.error('Jump failed: %s' % e) do_j = do_jump @@ -1238,14 +1247,16 @@ except KeyboardInterrupt: pass - def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix): + def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix, + locals=None): frame, lineno = frame_lineno if frame is self.curframe: prefix = '> ' else: prefix = ' ' self.message(prefix + - self.format_stack_entry(frame_lineno, prompt_prefix)) + self.format_stack_entry(frame_lineno, prompt_prefix, + locals)) # Provide help Index: Lib/bdb.py =================================================================== --- Lib/bdb.py (revision 84373) +++ Lib/bdb.py (working copy) @@ -352,25 +352,27 @@ # - def format_stack_entry(self, frame_lineno, lprefix=': '): + def format_stack_entry(self, frame_lineno, lprefix=': ', locals=None): import linecache, reprlib frame, lineno = frame_lineno filename = self.canonic(frame.f_code.co_filename) s = '%s(%r)' % (filename, lineno) + if not locals: + locals = frame.f_locals if frame.f_code.co_name: s = s + frame.f_code.co_name else: s = s + "" - if '__args__' in frame.f_locals: - args = frame.f_locals['__args__'] + if '__args__' in locals: + args = locals['__args__'] else: args = None if args: s = s + reprlib.repr(args) else: s = s + '()' - if '__return__' in frame.f_locals: - rv = frame.f_locals['__return__'] + if '__return__' in locals: + rv = locals['__return__'] s = s + '->' s = s + reprlib.repr(rv) line = linecache.getline(filename, lineno, frame.f_globals) Index: Lib/test/test_pdb.py =================================================================== --- Lib/test/test_pdb.py (revision 84373) +++ Lib/test/test_pdb.py (working copy) @@ -578,6 +578,46 @@ """ +def test_pdb_set_frame_locals(): + """This tests setting local variables in the current stack frame. + + >>> def foo(n): + ... x = n + ... bar(x) + >>> def bar(n): + ... y = n + 1 + ... import pdb; pdb.Pdb().set_trace() + ... print(y) + + >>> with PdbTestInput([ + ... 'y', + ... '!y = 42', + ... 'y', + ... 'u', + ... 'd', + ... 'y', + ... 'continue' + ... ]): + ... foo(1) + > (4)bar() + -> print(y) + (Pdb) y + 2 + (Pdb) !y = 42 + (Pdb) y + 42 + (Pdb) u + > (3)foo() + -> bar(x) + (Pdb) d + > (4)bar() + -> print(y) + (Pdb) y + 42 + (Pdb) continue + 42 + """ + class PdbTestCase(unittest.TestCase): def test_issue7964(self):