diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index 4727e8bd..fd43d56b 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -883,7 +883,7 @@ async def comm_info_request(self, stream, ident, parent): msg = self.session.send(stream, "comm_info_reply", reply_content, parent, ident) self.log.debug("%s", msg) - def _send_interupt_children(self): + def _send_interrupt_children(self): if os.name == "nt": self.log.error("Interrupt message not supported on Windows") else: @@ -892,20 +892,24 @@ def _send_interupt_children(self): # Prefer process-group over process # but only if the kernel is the leader of the process group if pgid and pgid == pid and hasattr(os, "killpg"): - try: - os.killpg(pgid, SIGINT) - return - except OSError: - pass - try: + os.killpg(pgid, SIGINT) + else: os.kill(pid, SIGINT) - except OSError: - pass async def interrupt_request(self, stream, ident, parent): """Handle an interrupt request.""" - self._send_interupt_children() - content = parent["content"] + try: + self._send_interrupt_children() + content = { "status": "ok" } + except OSError as err: + import traceback + content = { + "status": "error", + "traceback": traceback.format_stack(), + "ename": str(type(err).__name__), + "evalue": str(err), + } + self.session.send(stream, "interrupt_reply", content, parent, ident=ident) return diff --git a/ipykernel/tests/conftest.py b/ipykernel/tests/conftest.py index 45341cef..00f8394f 100644 --- a/ipykernel/tests/conftest.py +++ b/ipykernel/tests/conftest.py @@ -99,7 +99,7 @@ async def _wait_for_msg(self): _, msg = self.session.feed_identities(self._reply) return self.session.deserialize(msg) - def _send_interupt_children(self): + def _send_interrupt_children(self): # override to prevent deadlock pass diff --git a/ipykernel/tests/test_ipkernel_direct.py b/ipykernel/tests/test_ipkernel_direct.py index 0e65c9a8..254b3a99 100644 --- a/ipykernel/tests/test_ipkernel_direct.py +++ b/ipykernel/tests/test_ipkernel_direct.py @@ -90,9 +90,20 @@ async def test_comm_info_request(ipkernel): async def test_direct_interrupt_request(ipkernel): - reply = await ipkernel.test_shell_message("interrupt_request", {}) + reply = await ipkernel.test_control_message("interrupt_request", {}) assert reply["header"]["msg_type"] == "interrupt_reply" + assert reply["content"] == { "status": "ok" } + # test failure on interrupt request + def raiseOSError(): + raise OSError("evalue") + ipkernel._send_interrupt_children = raiseOSError + reply = await ipkernel.test_control_message("interrupt_request", {}) + assert reply["header"]["msg_type"] == "interrupt_reply" + assert reply["content"]["status"] == "error" + assert reply["content"]["ename"] == "OSError" + assert reply["content"]["evalue"] == "evalue" + assert len(reply["content"]["traceback"]) > 0 # TODO: this causes deadlock # async def test_direct_shutdown_request(ipkernel): diff --git a/ipykernel/tests/test_kernel_direct.py b/ipykernel/tests/test_kernel_direct.py index f75d8b6d..900f17ba 100644 --- a/ipykernel/tests/test_kernel_direct.py +++ b/ipykernel/tests/test_kernel_direct.py @@ -69,8 +69,20 @@ async def test_comm_info_request(kernel): async def test_direct_interrupt_request(kernel): - reply = await kernel.test_shell_message("interrupt_request", {}) + reply = await kernel.test_control_message("interrupt_request", {}) assert reply["header"]["msg_type"] == "interrupt_reply" + assert reply["content"] == { "status": "ok" } + + # test failure on interrupt request + def raiseOSError(): + raise OSError("evalue") + kernel._send_interrupt_children = raiseOSError + reply = await kernel.test_control_message("interrupt_request", {}) + assert reply["header"]["msg_type"] == "interrupt_reply" + assert reply["content"]["status"] == "error" + assert reply["content"]["ename"] == "OSError" + assert reply["content"]["evalue"] == "evalue" + assert len(reply["content"]["traceback"]) > 0 async def test_direct_shutdown_request(kernel): @@ -145,8 +157,8 @@ async def test_connect_request(kernel): await kernel.connect_request(kernel.shell_stream, "foo", {}) -async def test_send_interupt_children(kernel): - kernel._send_interupt_children() +async def test_send_interrupt_children(kernel): + kernel._send_interrupt_children() # TODO: this causes deadlock