Skip to content

Commit fe8b7b0

Browse files
authored
Add more kernel tests (#1032)
1 parent a3285d6 commit fe8b7b0

File tree

5 files changed

+174
-32
lines changed

5 files changed

+174
-32
lines changed

ipykernel/ipkernel.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,6 @@ def do_complete(self, code, cursor_pos):
460460
cursor_pos = len(code)
461461
line, offset = line_at_cursor(code, cursor_pos)
462462
line_cursor = cursor_pos - offset
463-
464463
txt, matches = self.shell.complete("", line, line_cursor)
465464
return {
466465
"matches": matches,
@@ -589,14 +588,16 @@ def do_is_complete(self, code):
589588
return r
590589

591590
def do_apply(self, content, bufs, msg_id, reply_metadata):
592-
from .serialize import serialize_object, unpack_apply_message
591+
try:
592+
from ipyparallel.serialize import serialize_object, unpack_apply_message
593+
except ImportError:
594+
from .serialize import serialize_object, unpack_apply_message # type:ignore
593595

594596
shell = self.shell
595597
try:
596598
working = shell.user_ns
597599

598600
prefix = "_" + str(msg_id).replace("-", "") + "_"
599-
600601
f, args, kwargs = unpack_apply_message(bufs, working, copy=False)
601602

602603
fname = getattr(f, "__name__", "f")

ipykernel/tests/conftest.py

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class KernelMixin:
4545
def _initialize(self):
4646
self.context = context = zmq.Context()
4747
self.iopub_socket = context.socket(zmq.PUB)
48+
self.stdin_socket = context.socket(zmq.ROUTER)
4849
self.session = Session()
4950
self.test_sockets = [self.iopub_socket]
5051
self.test_streams = []
@@ -57,24 +58,16 @@ def _initialize(self):
5758
self.test_streams.append(stream)
5859
setattr(self, f"{name}_stream", stream)
5960

60-
def do_execute(
61-
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
62-
):
63-
if not silent:
64-
stream_content = {"name": "stdout", "text": code}
65-
self.send_response(self.iopub_socket, "stream", stream_content)
66-
67-
return {
68-
"status": "ok",
69-
# The base class increments the execution count
70-
"execution_count": self.execution_count,
71-
"payload": [],
72-
"user_expressions": {},
73-
}
74-
7561
async def do_debug_request(self, msg):
7662
return {}
7763

64+
def destroy(self):
65+
for stream in self.test_streams:
66+
stream.close()
67+
for socket in self.test_sockets:
68+
socket.close()
69+
self.context.destroy()
70+
7871
async def test_shell_message(self, *args, **kwargs):
7972
msg_list = self._prep_msg(*args, **kwargs)
8073
await self.dispatch_shell(msg_list)
@@ -87,13 +80,6 @@ async def test_control_message(self, *args, **kwargs):
8780
self.control_stream.flush()
8881
return await self._wait_for_msg()
8982

90-
def destroy(self):
91-
for stream in self.test_streams:
92-
stream.close()
93-
for socket in self.test_sockets:
94-
socket.close()
95-
self.context.destroy()
96-
9783
def _on_send(self, msg, *args, **kwargs):
9884
self._reply = msg
9985

@@ -114,7 +100,7 @@ def _send_interupt_children(self):
114100
pass
115101

116102

117-
class TestKernel(KernelMixin, Kernel):
103+
class MockKernel(KernelMixin, Kernel):
118104
implementation = "test"
119105
implementation_version = "1.0"
120106
language = "no-op"
@@ -130,24 +116,39 @@ def __init__(self, *args, **kwargs):
130116
self._initialize()
131117
super().__init__(*args, **kwargs)
132118

119+
def do_execute(
120+
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
121+
):
122+
if not silent:
123+
stream_content = {"name": "stdout", "text": code}
124+
self.send_response(self.iopub_socket, "stream", stream_content)
125+
126+
return {
127+
"status": "ok",
128+
# The base class increments the execution count
129+
"execution_count": self.execution_count,
130+
"payload": [],
131+
"user_expressions": {},
132+
}
133+
133134

134-
class TestIPyKernel(KernelMixin, IPythonKernel):
135+
class MockIPyKernel(KernelMixin, IPythonKernel):
135136
def __init__(self, *args, **kwargs):
136137
self._initialize()
137138
super().__init__(*args, **kwargs)
138139

139140

140141
@pytest.fixture
141142
async def kernel():
142-
kernel = TestKernel()
143+
kernel = MockKernel()
143144
kernel.io_loop = IOLoop.current()
144145
yield kernel
145146
kernel.destroy()
146147

147148

148149
@pytest.fixture
149150
async def ipkernel():
150-
kernel = TestIPyKernel()
151+
kernel = MockIPyKernel()
151152
kernel.io_loop = IOLoop.current()
152153
yield kernel
153154
kernel.destroy()

ipykernel/tests/test_ipkernel_direct.py

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import os
55

66
import pytest
7+
import zmq
8+
from IPython.core.history import DummyDB
79

8-
from ipykernel.ipkernel import IPythonKernel
10+
from ipykernel.ipkernel import BaseComm, IPythonKernel, create_comm
11+
12+
from .conftest import MockIPyKernel
913

1014
if os.name == "nt":
1115
pytest.skip("skipping tests on windows", allow_module_level=True)
@@ -25,7 +29,14 @@ async def test_direct_kernel_info_request(ipkernel):
2529
assert reply["header"]["msg_type"] == "kernel_info_reply"
2630

2731

28-
async def test_direct_execute_request(ipkernel):
32+
async def test_direct_execute_request(ipkernel: MockIPyKernel):
33+
reply = await ipkernel.test_shell_message("execute_request", dict(code="hello", silent=False))
34+
assert reply["header"]["msg_type"] == "execute_reply"
35+
reply = await ipkernel.test_shell_message(
36+
"execute_request", dict(code="trigger_error", silent=False)
37+
)
38+
assert reply["content"]["status"] == "aborted"
39+
2940
reply = await ipkernel.test_shell_message("execute_request", dict(code="hello", silent=False))
3041
assert reply["header"]["msg_type"] == "execute_reply"
3142

@@ -40,6 +51,11 @@ async def test_direct_execute_request_aborting(ipkernel):
4051
async def test_complete_request(ipkernel):
4152
reply = await ipkernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
4253
assert reply["header"]["msg_type"] == "complete_reply"
54+
ipkernel.use_experimental_completions = False
55+
reply = await ipkernel.test_shell_message(
56+
"complete_request", dict(code="hello", cursor_pos=None)
57+
)
58+
assert reply["header"]["msg_type"] == "complete_reply"
4359

4460

4561
async def test_inspect_request(ipkernel):
@@ -48,10 +64,23 @@ async def test_inspect_request(ipkernel):
4864

4965

5066
async def test_history_request(ipkernel):
67+
ipkernel.shell.history_manager.db = DummyDB()
5168
reply = await ipkernel.test_shell_message(
5269
"history_request", dict(hist_access_type="", output="", raw="")
5370
)
5471
assert reply["header"]["msg_type"] == "history_reply"
72+
reply = await ipkernel.test_shell_message(
73+
"history_request", dict(hist_access_type="tail", output="", raw="")
74+
)
75+
assert reply["header"]["msg_type"] == "history_reply"
76+
reply = await ipkernel.test_shell_message(
77+
"history_request", dict(hist_access_type="range", output="", raw="")
78+
)
79+
assert reply["header"]["msg_type"] == "history_reply"
80+
reply = await ipkernel.test_shell_message(
81+
"history_request", dict(hist_access_type="search", output="", raw="")
82+
)
83+
assert reply["header"]["msg_type"] == "history_reply"
5584

5685

5786
async def test_comm_info_request(ipkernel):
@@ -77,11 +106,25 @@ async def test_direct_interrupt_request(ipkernel):
77106
# assert reply['header']['msg_type'] == 'usage_reply'
78107

79108

80-
async def test_is_complete_request(ipkernel):
109+
async def test_is_complete_request(ipkernel: MockIPyKernel):
110+
reply = await ipkernel.test_shell_message("is_complete_request", dict(code="hello"))
111+
assert reply["header"]["msg_type"] == "is_complete_reply"
112+
setattr(ipkernel, "shell.input_transformer_manager", None)
81113
reply = await ipkernel.test_shell_message("is_complete_request", dict(code="hello"))
82114
assert reply["header"]["msg_type"] == "is_complete_reply"
83115

84116

117+
def test_do_apply(ipkernel: MockIPyKernel):
118+
from ipyparallel import pack_apply_message
119+
120+
def hello():
121+
pass
122+
123+
msg = pack_apply_message(hello, (), {})
124+
ipkernel.do_apply(None, msg, "1", {})
125+
ipkernel.do_apply(None, [], "1", {})
126+
127+
85128
async def test_direct_debug_request(ipkernel):
86129
reply = await ipkernel.test_control_message("debug_request", {})
87130
assert reply["header"]["msg_type"] == "debug_reply"
@@ -96,3 +139,64 @@ async def test_cancel_on_sigint(ipkernel: IPythonKernel):
96139
with ipkernel._cancel_on_sigint(future):
97140
pass
98141
future.set_result(None)
142+
143+
144+
def test_dispatch_debugpy(ipkernel: IPythonKernel):
145+
msg = ipkernel.session.msg("debug_request", {})
146+
msg_list = ipkernel.session.serialize(msg)
147+
ipkernel.dispatch_debugpy([zmq.Message(m) for m in msg_list])
148+
149+
150+
async def test_start(ipkernel: IPythonKernel):
151+
shell_future = asyncio.Future()
152+
control_future = asyncio.Future()
153+
154+
async def fake_dispatch_queue():
155+
shell_future.set_result(None)
156+
157+
async def fake_poll_control_queue():
158+
control_future.set_result(None)
159+
160+
ipkernel.dispatch_queue = fake_dispatch_queue
161+
ipkernel.poll_control_queue = fake_poll_control_queue
162+
ipkernel.start()
163+
ipkernel.debugpy_stream = None
164+
ipkernel.start()
165+
await ipkernel.process_one(False)
166+
await shell_future
167+
await control_future
168+
169+
170+
async def test_start_no_debugpy(ipkernel: IPythonKernel):
171+
shell_future = asyncio.Future()
172+
control_future = asyncio.Future()
173+
174+
async def fake_dispatch_queue():
175+
shell_future.set_result(None)
176+
177+
async def fake_poll_control_queue():
178+
control_future.set_result(None)
179+
180+
ipkernel.dispatch_queue = fake_dispatch_queue
181+
ipkernel.poll_control_queue = fake_poll_control_queue
182+
ipkernel.debugpy_stream = None
183+
ipkernel.start()
184+
185+
await shell_future
186+
await control_future
187+
188+
189+
def test_create_comm():
190+
assert isinstance(create_comm(), BaseComm)
191+
192+
193+
def test_finish_metadata(ipkernel: IPythonKernel):
194+
reply_content = dict(status="error", ename="UnmetDependency")
195+
metadata = ipkernel.finish_metadata({}, {}, reply_content)
196+
assert metadata["dependencies_met"] is False
197+
198+
199+
async def test_do_debug_request(ipkernel: IPythonKernel):
200+
msg = ipkernel.session.msg("debug_request", {})
201+
msg_list = ipkernel.session.serialize(msg)
202+
await ipkernel.do_debug_request(msg)

ipykernel/tests/test_kernel_direct.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ async def test_direct_execute_request_aborting(kernel):
3030
assert reply["content"]["status"] == "aborted"
3131

3232

33+
async def test_direct_execute_request_error(kernel):
34+
await kernel.execute_request(None, None, None)
35+
36+
3337
async def test_complete_request(kernel):
3438
reply = await kernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
3539
assert reply["header"]["msg_type"] == "complete_reply"
@@ -45,6 +49,18 @@ async def test_history_request(kernel):
4549
"history_request", dict(hist_access_type="", output="", raw="")
4650
)
4751
assert reply["header"]["msg_type"] == "history_reply"
52+
reply = await kernel.test_shell_message(
53+
"history_request", dict(hist_access_type="tail", output="", raw="")
54+
)
55+
assert reply["header"]["msg_type"] == "history_reply"
56+
reply = await kernel.test_shell_message(
57+
"history_request", dict(hist_access_type="range", output="", raw="")
58+
)
59+
assert reply["header"]["msg_type"] == "history_reply"
60+
reply = await kernel.test_shell_message(
61+
"history_request", dict(hist_access_type="search", output="", raw="")
62+
)
63+
assert reply["header"]["msg_type"] == "history_reply"
4864

4965

5066
async def test_comm_info_request(kernel):
@@ -137,6 +153,23 @@ def check_status():
137153
await asyncio.sleep(0.1)
138154

139155

156+
async def test_do_one_iteration(kernel):
157+
kernel.msg_queue = asyncio.Queue()
158+
await kernel.do_one_iteration()
159+
160+
161+
async def test_publish_debug_event(kernel):
162+
kernel._publish_debug_event({})
163+
164+
165+
async def test_connect_request(kernel):
166+
await kernel.connect_request(kernel.shell_stream, "foo", {})
167+
168+
169+
async def test_send_interupt_children(kernel):
170+
kernel._send_interupt_children()
171+
172+
140173
# TODO: this causes deadlock
141174
# async def test_direct_usage_request(kernel):
142175
# reply = await kernel.test_control_message("usage_request", {})

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ filterwarnings= [
128128
# Ignore our own warnings
129129
"ignore:The `stream` parameter of `getpass.getpass` will have no effect:UserWarning",
130130

131+
# IPython warnings
132+
"ignore: `Completer.complete` is pending deprecation since IPython 6.0 and will be replaced by `Completer.completions`:PendingDeprecationWarning",
133+
131134
# Ignore jupyter_client warnings
132135
"ignore:unclosed <socket.socket:ResourceWarning",
133136
"ignore:unclosed event loop:ResourceWarning",

0 commit comments

Comments
 (0)