#7245: Add a SIGINT handler on continue in pdb that allows to break a program again by pressing Ctrl-C.

This commit is contained in:
Georg Brandl 2010-12-04 16:00:47 +00:00
parent 1ed77f300b
commit 44f2b640ff
4 changed files with 47 additions and 7 deletions

View File

@ -135,7 +135,8 @@ The ``run_*`` functions and :func:`set_trace` are aliases for instantiating the
:class:`Pdb` class and calling the method of the same name. If you want to :class:`Pdb` class and calling the method of the same name. If you want to
access further features, you have to do this yourself: access further features, you have to do this yourself:
.. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None) .. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None, \
nosigint=False)
:class:`Pdb` is the debugger class. :class:`Pdb` is the debugger class.
@ -146,6 +147,11 @@ access further features, you have to do this yourself:
patterns. The debugger will not step into frames that originate in a module patterns. The debugger will not step into frames that originate in a module
that matches one of these patterns. [1]_ that matches one of these patterns. [1]_
By default, Pdb sets a handler for the SIGINT signal (which is sent when the
user presses Ctrl-C on the console) when you give a ``continue`` command.
This allows you to break into the debugger again by pressing Ctrl-C. If you
want Pdb not to touch the SIGINT handler, set *nosigint* tot true.
Example call to enable tracing with *skip*:: Example call to enable tracing with *skip*::
import pdb; pdb.Pdb(skip=['django.*']).set_trace() import pdb; pdb.Pdb(skip=['django.*']).set_trace()
@ -153,6 +159,10 @@ access further features, you have to do this yourself:
.. versionadded:: 3.1 .. versionadded:: 3.1
The *skip* argument. The *skip* argument.
.. versionadded:: 3.2
The *nosigint* argument. Previously, a SIGINT handler was never set by
Pdb.
.. method:: run(statement, globals=None, locals=None) .. method:: run(statement, globals=None, locals=None)
runeval(expression, globals=None, locals=None) runeval(expression, globals=None, locals=None)
runcall(function, *args, **kwds) runcall(function, *args, **kwds)

View File

@ -214,7 +214,7 @@ class Bdb:
def set_continue(self): def set_continue(self):
# Don't stop except at breakpoints or when finished # Don't stop except at breakpoints or when finished
self._set_stopinfo(self.botframe, None, -1) self._set_stopinfo(self.botframe, None, -1)
if not self.breaks: if not self.breaks and not self.watching:
# no breakpoints; run without debugger overhead # no breakpoints; run without debugger overhead
sys.settrace(None) sys.settrace(None)
frame = sys._getframe().f_back frame = sys._getframe().f_back

View File

@ -66,14 +66,15 @@ Debugger commands
# NOTE: the actual command documentation is collected from docstrings of the # NOTE: the actual command documentation is collected from docstrings of the
# commands and is appended to __doc__ after the class has been defined. # commands and is appended to __doc__ after the class has been defined.
import os
import re
import sys import sys
import cmd import cmd
import bdb import bdb
import dis import dis
import os
import re
import code import code
import pprint import pprint
import signal
import inspect import inspect
import traceback import traceback
import linecache import linecache
@ -133,7 +134,8 @@ line_prefix = '\n-> ' # Probably a better default
class Pdb(bdb.Bdb, cmd.Cmd): class Pdb(bdb.Bdb, cmd.Cmd):
def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None): def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
nosigint=False):
bdb.Bdb.__init__(self, skip=skip) bdb.Bdb.__init__(self, skip=skip)
cmd.Cmd.__init__(self, completekey, stdin, stdout) cmd.Cmd.__init__(self, completekey, stdin, stdout)
if stdout: if stdout:
@ -148,6 +150,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
import readline import readline
except ImportError: except ImportError:
pass pass
self.allow_kbdint = False
self.nosigint = nosigint
# Read $HOME/.pdbrc and ./.pdbrc # Read $HOME/.pdbrc and ./.pdbrc
self.rcLines = [] self.rcLines = []
@ -174,6 +178,15 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.commands_bnum = None # The breakpoint number for which we are self.commands_bnum = None # The breakpoint number for which we are
# defining a list # defining a list
def sigint_handler(self, signum, frame):
if self.allow_kbdint:
raise KeyboardInterrupt
self.message("\nProgram interrupted. (Use 'cont' to resume).")
self.set_step()
self.set_trace(frame)
# restore previous signal handler
signal.signal(signal.SIGINT, self._previous_sigint_handler)
def reset(self): def reset(self):
bdb.Bdb.reset(self) bdb.Bdb.reset(self)
self.forget() self.forget()
@ -261,7 +274,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if not self.commands_silent[currentbp]: if not self.commands_silent[currentbp]:
self.print_stack_entry(self.stack[self.curindex]) self.print_stack_entry(self.stack[self.curindex])
if self.commands_doprompt[currentbp]: if self.commands_doprompt[currentbp]:
self.cmdloop() self._cmdloop()
self.forget() self.forget()
return return
return 1 return 1
@ -286,6 +299,17 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.interaction(frame, exc_traceback) self.interaction(frame, exc_traceback)
# General interaction function # General interaction function
def _cmdloop(self):
while True:
try:
# keyboard interrupts allow for an easy way to cancel
# the current command, so allow them during interactive input
self.allow_kbdint = True
self.cmdloop()
self.allow_kbdint = False
break
except KeyboardInterrupt:
self.message('--KeyboardInterrupt--')
def interaction(self, frame, traceback): def interaction(self, frame, traceback):
if self.setup(frame, traceback): if self.setup(frame, traceback):
@ -294,7 +318,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.forget() self.forget()
return return
self.print_stack_entry(self.stack[self.curindex]) self.print_stack_entry(self.stack[self.curindex])
self.cmdloop() self._cmdloop()
self.forget() self.forget()
def displayhook(self, obj): def displayhook(self, obj):
@ -909,6 +933,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
"""c(ont(inue)) """c(ont(inue))
Continue execution, only stop when a breakpoint is encountered. Continue execution, only stop when a breakpoint is encountered.
""" """
if not self.nosigint:
self._previous_sigint_handler = \
signal.signal(signal.SIGINT, self.sigint_handler)
self.set_continue() self.set_continue()
return 1 return 1
do_c = do_cont = do_continue do_c = do_cont = do_continue

View File

@ -49,6 +49,9 @@ Core and Builtins
Library Library
------- -------
- Issue #7245: Add a SIGINT handler in pdb that allows to break a program
again after a "continue" command.
- Add the "interact" pdb command. - Add the "interact" pdb command.
- Issue #7905: Actually respect the keyencoding parameter to shelve.Shelf. - Issue #7905: Actually respect the keyencoding parameter to shelve.Shelf.