Skip to content

Commit

Permalink
Merge pull request #490 from itamarst/10516-pdb-freezes
Browse files Browse the repository at this point in the history
Make pdb on Windows interruptible
  • Loading branch information
blink1073 authored May 27, 2020
2 parents 8fd6297 + d212586 commit c87ce69
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
15 changes: 15 additions & 0 deletions ipykernel/kernelapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,28 @@ def _init_asyncio_patch(self):
# fallback to the pre-3.8 default of Selector
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())

def init_pdb(self):
"""Replace pdb with IPython's version that is interruptible.
With the non-interruptible version, stopping pdb() locks up the kernel in a
non-recoverable state.
"""
import pdb
from IPython.core import debugger
if hasattr(debugger, "InterruptiblePdb"):
# Only available in newer IPython releases:
debugger.Pdb = debugger.InterruptiblePdb
pdb.Pdb = debugger.Pdb
pdb.set_trace = debugger.set_trace

@catch_config_error
def initialize(self, argv=None):
self._init_asyncio_patch()
super(IPKernelApp, self).initialize(argv)
if self.subapp is not None:
return

self.init_pdb()
self.init_blackhole()
self.init_connection_file()
self.init_poller()
Expand Down
30 changes: 30 additions & 0 deletions ipykernel/tests/test_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@

import nose.tools as nt
from flaky import flaky
import pytest
from packaging import version

from IPython.testing import decorators as dec, tools as tt
import IPython
from ipython_genutils import py3compat
from IPython.paths import locate_profile
from ipython_genutils.tempdir import TemporaryDirectory
Expand Down Expand Up @@ -379,3 +382,30 @@ def test_interrupt_during_input():
reply = kc.get_shell_msg(timeout=TIMEOUT)
from .test_message_spec import validate_message
validate_message(reply, 'execute_reply', msg_id)


@pytest.mark.skipif(
version.parse(IPython.__version__) < version.parse("7.14.0"),
reason="Need new IPython"
)
def test_interrupt_during_pdb_set_trace():
"""
The kernel exits after being interrupted while waiting in pdb.set_trace().
Merely testing input() isn't enough, pdb has its own issues that need
to be handled in addition.
This test will fail with versions of IPython < 7.14.0.
"""
with new_kernel() as kc:
km = kc.parent
msg_id = kc.execute("import pdb; pdb.set_trace()")
msg_id2 = kc.execute("3 + 4")
time.sleep(1) # Make sure it's actually waiting for input.
km.interrupt_kernel()
# If we failed to interrupt interrupt, this will timeout:
from .test_message_spec import validate_message
reply = kc.get_shell_msg(timeout=TIMEOUT)
validate_message(reply, 'execute_reply', msg_id)
reply = kc.get_shell_msg(timeout=TIMEOUT)
validate_message(reply, 'execute_reply', msg_id2)

0 comments on commit c87ce69

Please sign in to comment.