Add display/undisplay pdb commands.
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index 959fde1..8335993 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -417,6 +417,22 @@
.. versionadded:: 3.2
+.. pdbcommand:: display [expression]
+
+ Display the value of the expression if it changed, each time execution stops
+ in the current frame.
+
+ Without expression, list all display expressions for the current frame.
+
+ .. versionadded:: 3.2
+
+.. pdbcommand:: undisplay [expression]
+
+ Do not display the expression any more in the current frame. Without
+ expression, clear all display expressions for the current frame.
+
+ .. versionadded:: 3.2
+
.. pdbcommand:: interact
Start an interative interpreter (using the :mod:`code` module) whose global
diff --git a/Lib/pdb.py b/Lib/pdb.py
index ac53eba..6776a3f 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -125,6 +125,12 @@
return 0
+class _rstr(str):
+ """String that doesn't quote its repr."""
+ def __repr__(self):
+ return self
+
+
# Interaction prompt line will separate file and call info from code
# text using value of line_prefix string. A newline and arrow may
# be to your liking. You can set it once pdb is imported using the
@@ -142,6 +148,7 @@
self.use_rawinput = 0
self.prompt = '(Pdb) '
self.aliases = {}
+ self.displaying = {}
self.mainpyfile = ''
self._wait_for_mainpyfile = False
self.tb_lineno = {}
@@ -311,6 +318,20 @@
except KeyboardInterrupt:
self.message('--KeyboardInterrupt--')
+ # Called before loop, handles display expressions
+ def preloop(self):
+ displaying = self.displaying.get(self.curframe)
+ if displaying:
+ for expr, oldvalue in displaying.items():
+ newvalue = self._getval_except(expr)
+ # check for identity first; this prevents custom __eq__ to
+ # be called at every loop, and also prevents instances whose
+ # fields are changed to be displayed
+ if newvalue is not oldvalue and newvalue != oldvalue:
+ displaying[expr] = newvalue
+ self.message('display %s: %r [old: %r]' %
+ (expr, newvalue, oldvalue))
+
def interaction(self, frame, traceback):
if self.setup(frame, traceback):
# no interaction desired at this time (happens if .pdbrc contains
@@ -1041,6 +1062,17 @@
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
raise
+ def _getval_except(self, arg, frame=None):
+ try:
+ if frame is None:
+ return eval(arg, self.curframe.f_globals, self.curframe_locals)
+ else:
+ return eval(arg, frame.f_globals, frame.f_locals)
+ except:
+ exc_info = sys.exc_info()[:2]
+ err = traceback.format_exception_only(*exc_info)[-1].strip()
+ return _rstr('** raised %s **' % err)
+
def do_p(self, arg):
"""p(rint) expression
Print the value of the expression.
@@ -1195,6 +1227,38 @@
# None of the above...
self.message(type(value))
+ def do_display(self, arg):
+ """display [expression]
+
+ Display the value of the expression if it changed, each time execution
+ stops in the current frame.
+
+ Without expression, list all display expressions for the current frame.
+ """
+ if not arg:
+ self.message('Currently displaying:')
+ for item in self.displaying.get(self.curframe, {}).items():
+ self.message('%s: %r' % item)
+ else:
+ val = self._getval_except(arg)
+ self.displaying.setdefault(self.curframe, {})[arg] = val
+ self.message('display %s: %r' % (arg, val))
+
+ def do_undisplay(self, arg):
+ """undisplay [expression]
+
+ Do not display the expression any more in the current frame.
+
+ Without expression, clear all display expressions for the current frame.
+ """
+ if arg:
+ try:
+ del self.displaying.get(self.curframe, {})[arg]
+ except KeyError:
+ self.error('not displaying %s' % arg)
+ else:
+ self.displaying.pop(self.curframe, None)
+
def do_interact(self, arg):
"""interact
@@ -1380,8 +1444,8 @@
'help', 'where', 'down', 'up', 'break', 'tbreak', 'clear', 'disable',
'enable', 'ignore', 'condition', 'commands', 'step', 'next', 'until',
'jump', 'return', 'retval', 'run', 'continue', 'list', 'longlist',
- 'args', 'print', 'pp', 'whatis', 'source', 'interact', 'alias',
- 'unalias', 'debug', 'quit',
+ 'args', 'print', 'pp', 'whatis', 'source', 'display', 'undisplay',
+ 'interact', 'alias', 'unalias', 'debug', 'quit',
]
for _command in _help_order:
diff --git a/Misc/NEWS b/Misc/NEWS
index a741d6e..0d1c1d5 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,8 @@
Library
-------
+- Add the "display" and "undisplay" pdb commands.
+
- Issue #7245: Add a SIGINT handler in pdb that allows to break a program
again after a "continue" command.