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 | do_w = do_where |
|
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 | def set_trace(frame=None): |
|
665 | def set_trace(frame=None): | |
638 | """ |
|
666 | """ | |
639 | Start debugging from `frame`. |
|
667 | 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 | |||
@@ -223,3 +231,26 b' def can_exit():' | |||||
223 |
|
231 | |||
224 | >>> sys.settrace(old_trace) |
|
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