Show More
@@ -0,0 +1,4 b'' | |||||
|
1 | IPython.core.debugger.Pdb is now interruptible | |||
|
2 | ============================================== | |||
|
3 | ||||
|
4 | A ``KeyboardInterrupt`` will now interrupt IPython's extended debugger, in order to make Jupyter able to interrupt it. |
@@ -632,6 +632,34 b' class Pdb(OldPdb):' | |||||
632 | do_w = do_where |
|
632 | do_w = do_where | |
633 |
|
633 | |||
634 |
|
634 | |||
|
635 | class InterruptiblePdb(Pdb): | |||
|
636 | """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" | |||
|
637 | ||||
|
638 | def cmdloop(self): | |||
|
639 | """Wrap cmdloop() such that KeyboardInterrupt stops the debugger.""" | |||
|
640 | try: | |||
|
641 | return OldPdb.cmdloop(self) | |||
|
642 | except KeyboardInterrupt: | |||
|
643 | self.stop_here = lambda frame: False | |||
|
644 | self.do_quit("") | |||
|
645 | sys.settrace(None) | |||
|
646 | self.quitting = False | |||
|
647 | raise | |||
|
648 | ||||
|
649 | def _cmdloop(self): | |||
|
650 | while True: | |||
|
651 | try: | |||
|
652 | # keyboard interrupts allow for an easy way to cancel | |||
|
653 | # the current command, so allow them during interactive input | |||
|
654 | self.allow_kbdint = True | |||
|
655 | self.cmdloop() | |||
|
656 | self.allow_kbdint = False | |||
|
657 | break | |||
|
658 | except KeyboardInterrupt: | |||
|
659 | self.message('--KeyboardInterrupt--') | |||
|
660 | raise | |||
|
661 | ||||
|
662 | ||||
635 | def set_trace(frame=None): |
|
663 | def set_trace(frame=None): | |
636 | """ |
|
664 | """ | |
637 | Start debugging from `frame`. |
|
665 | Start debugging from `frame`. |
@@ -4,8 +4,16 b'' | |||||
4 | # Copyright (c) IPython Development Team. |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
|
7 | import signal | |||
7 | import sys |
|
8 | import sys | |
|
9 | import time | |||
8 | import warnings |
|
10 | import warnings | |
|
11 | from tempfile import NamedTemporaryFile | |||
|
12 | from subprocess import check_output, CalledProcessError, PIPE | |||
|
13 | import subprocess | |||
|
14 | from unittest.mock import patch | |||
|
15 | import builtins | |||
|
16 | import bdb | |||
9 |
|
17 | |||
10 | import nose.tools as nt |
|
18 | import nose.tools as nt | |
11 |
|
19 | |||
@@ -220,3 +228,26 b' def can_exit():' | |||||
220 |
|
228 | |||
221 | >>> sys.settrace(old_trace) |
|
229 | >>> sys.settrace(old_trace) | |
222 | ''' |
|
230 | ''' | |
|
231 | ||||
|
232 | ||||
|
233 | def test_interruptible_core_debugger(): | |||
|
234 | """The debugger can be interrupted. | |||
|
235 | ||||
|
236 | The presumption is there is some mechanism that causes a KeyboardInterrupt | |||
|
237 | (this is implemented in ipykernel). We want to ensure the | |||
|
238 | KeyboardInterrupt cause debugging to cease. | |||
|
239 | """ | |||
|
240 | def raising_input(msg="", called=[0]): | |||
|
241 | called[0] += 1 | |||
|
242 | if called[0] == 1: | |||
|
243 | raise KeyboardInterrupt() | |||
|
244 | else: | |||
|
245 | raise AssertionError("input() should only be called once!") | |||
|
246 | ||||
|
247 | with patch.object(builtins, "input", raising_input): | |||
|
248 | debugger.InterruptiblePdb().set_trace() | |||
|
249 | # The way this test will fail is by set_trace() never exiting, | |||
|
250 | # resulting in a timeout by the test runner. The alternative | |||
|
251 | # implementation would involve a subprocess, but that adds issues with | |||
|
252 | # interrupting subprocesses that are rather complex, so it's simpler | |||
|
253 | # just to do it this way. |
General Comments 0
You need to be logged in to leave comments.
Login now