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