Skip to content

Commit

Permalink
Add more kernel tests (ipython#1032)
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Nov 26, 2022
1 parent a3285d6 commit fe8b7b0
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 32 deletions.
7 changes: 4 additions & 3 deletions ipykernel/ipkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,6 @@ def do_complete(self, code, cursor_pos):
cursor_pos = len(code)
line, offset = line_at_cursor(code, cursor_pos)
line_cursor = cursor_pos - offset

txt, matches = self.shell.complete("", line, line_cursor)
return {
"matches": matches,
Expand Down Expand Up @@ -589,14 +588,16 @@ def do_is_complete(self, code):
return r

def do_apply(self, content, bufs, msg_id, reply_metadata):
from .serialize import serialize_object, unpack_apply_message
try:
from ipyparallel.serialize import serialize_object, unpack_apply_message
except ImportError:
from .serialize import serialize_object, unpack_apply_message # type:ignore

shell = self.shell
try:
working = shell.user_ns

prefix = "_" + str(msg_id).replace("-", "") + "_"

f, args, kwargs = unpack_apply_message(bufs, working, copy=False)

fname = getattr(f, "__name__", "f")
Expand Down
53 changes: 27 additions & 26 deletions ipykernel/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class KernelMixin:
def _initialize(self):
self.context = context = zmq.Context()
self.iopub_socket = context.socket(zmq.PUB)
self.stdin_socket = context.socket(zmq.ROUTER)
self.session = Session()
self.test_sockets = [self.iopub_socket]
self.test_streams = []
Expand All @@ -57,24 +58,16 @@ def _initialize(self):
self.test_streams.append(stream)
setattr(self, f"{name}_stream", stream)

def do_execute(
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
):
if not silent:
stream_content = {"name": "stdout", "text": code}
self.send_response(self.iopub_socket, "stream", stream_content)

return {
"status": "ok",
# The base class increments the execution count
"execution_count": self.execution_count,
"payload": [],
"user_expressions": {},
}

async def do_debug_request(self, msg):
return {}

def destroy(self):
for stream in self.test_streams:
stream.close()
for socket in self.test_sockets:
socket.close()
self.context.destroy()

async def test_shell_message(self, *args, **kwargs):
msg_list = self._prep_msg(*args, **kwargs)
await self.dispatch_shell(msg_list)
Expand All @@ -87,13 +80,6 @@ async def test_control_message(self, *args, **kwargs):
self.control_stream.flush()
return await self._wait_for_msg()

def destroy(self):
for stream in self.test_streams:
stream.close()
for socket in self.test_sockets:
socket.close()
self.context.destroy()

def _on_send(self, msg, *args, **kwargs):
self._reply = msg

Expand All @@ -114,7 +100,7 @@ def _send_interupt_children(self):
pass


class TestKernel(KernelMixin, Kernel):
class MockKernel(KernelMixin, Kernel):
implementation = "test"
implementation_version = "1.0"
language = "no-op"
Expand All @@ -130,24 +116,39 @@ def __init__(self, *args, **kwargs):
self._initialize()
super().__init__(*args, **kwargs)

def do_execute(
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
):
if not silent:
stream_content = {"name": "stdout", "text": code}
self.send_response(self.iopub_socket, "stream", stream_content)

return {
"status": "ok",
# The base class increments the execution count
"execution_count": self.execution_count,
"payload": [],
"user_expressions": {},
}


class TestIPyKernel(KernelMixin, IPythonKernel):
class MockIPyKernel(KernelMixin, IPythonKernel):
def __init__(self, *args, **kwargs):
self._initialize()
super().__init__(*args, **kwargs)


@pytest.fixture
async def kernel():
kernel = TestKernel()
kernel = MockKernel()
kernel.io_loop = IOLoop.current()
yield kernel
kernel.destroy()


@pytest.fixture
async def ipkernel():
kernel = TestIPyKernel()
kernel = MockIPyKernel()
kernel.io_loop = IOLoop.current()
yield kernel
kernel.destroy()
Expand Down
110 changes: 107 additions & 3 deletions ipykernel/tests/test_ipkernel_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import os

import pytest
import zmq
from IPython.core.history import DummyDB

from ipykernel.ipkernel import IPythonKernel
from ipykernel.ipkernel import BaseComm, IPythonKernel, create_comm

from .conftest import MockIPyKernel

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


async def test_direct_execute_request(ipkernel):
async def test_direct_execute_request(ipkernel: MockIPyKernel):
reply = await ipkernel.test_shell_message("execute_request", dict(code="hello", silent=False))
assert reply["header"]["msg_type"] == "execute_reply"
reply = await ipkernel.test_shell_message(
"execute_request", dict(code="trigger_error", silent=False)
)
assert reply["content"]["status"] == "aborted"

reply = await ipkernel.test_shell_message("execute_request", dict(code="hello", silent=False))
assert reply["header"]["msg_type"] == "execute_reply"

Expand All @@ -40,6 +51,11 @@ async def test_direct_execute_request_aborting(ipkernel):
async def test_complete_request(ipkernel):
reply = await ipkernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
assert reply["header"]["msg_type"] == "complete_reply"
ipkernel.use_experimental_completions = False
reply = await ipkernel.test_shell_message(
"complete_request", dict(code="hello", cursor_pos=None)
)
assert reply["header"]["msg_type"] == "complete_reply"


async def test_inspect_request(ipkernel):
Expand All @@ -48,10 +64,23 @@ async def test_inspect_request(ipkernel):


async def test_history_request(ipkernel):
ipkernel.shell.history_manager.db = DummyDB()
reply = await ipkernel.test_shell_message(
"history_request", dict(hist_access_type="", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await ipkernel.test_shell_message(
"history_request", dict(hist_access_type="tail", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await ipkernel.test_shell_message(
"history_request", dict(hist_access_type="range", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await ipkernel.test_shell_message(
"history_request", dict(hist_access_type="search", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"


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


async def test_is_complete_request(ipkernel):
async def test_is_complete_request(ipkernel: MockIPyKernel):
reply = await ipkernel.test_shell_message("is_complete_request", dict(code="hello"))
assert reply["header"]["msg_type"] == "is_complete_reply"
setattr(ipkernel, "shell.input_transformer_manager", None)
reply = await ipkernel.test_shell_message("is_complete_request", dict(code="hello"))
assert reply["header"]["msg_type"] == "is_complete_reply"


def test_do_apply(ipkernel: MockIPyKernel):
from ipyparallel import pack_apply_message

def hello():
pass

msg = pack_apply_message(hello, (), {})
ipkernel.do_apply(None, msg, "1", {})
ipkernel.do_apply(None, [], "1", {})


async def test_direct_debug_request(ipkernel):
reply = await ipkernel.test_control_message("debug_request", {})
assert reply["header"]["msg_type"] == "debug_reply"
Expand All @@ -96,3 +139,64 @@ async def test_cancel_on_sigint(ipkernel: IPythonKernel):
with ipkernel._cancel_on_sigint(future):
pass
future.set_result(None)


def test_dispatch_debugpy(ipkernel: IPythonKernel):
msg = ipkernel.session.msg("debug_request", {})
msg_list = ipkernel.session.serialize(msg)
ipkernel.dispatch_debugpy([zmq.Message(m) for m in msg_list])


async def test_start(ipkernel: IPythonKernel):
shell_future = asyncio.Future()
control_future = asyncio.Future()

async def fake_dispatch_queue():
shell_future.set_result(None)

async def fake_poll_control_queue():
control_future.set_result(None)

ipkernel.dispatch_queue = fake_dispatch_queue
ipkernel.poll_control_queue = fake_poll_control_queue
ipkernel.start()
ipkernel.debugpy_stream = None
ipkernel.start()
await ipkernel.process_one(False)
await shell_future
await control_future


async def test_start_no_debugpy(ipkernel: IPythonKernel):
shell_future = asyncio.Future()
control_future = asyncio.Future()

async def fake_dispatch_queue():
shell_future.set_result(None)

async def fake_poll_control_queue():
control_future.set_result(None)

ipkernel.dispatch_queue = fake_dispatch_queue
ipkernel.poll_control_queue = fake_poll_control_queue
ipkernel.debugpy_stream = None
ipkernel.start()

await shell_future
await control_future


def test_create_comm():
assert isinstance(create_comm(), BaseComm)


def test_finish_metadata(ipkernel: IPythonKernel):
reply_content = dict(status="error", ename="UnmetDependency")
metadata = ipkernel.finish_metadata({}, {}, reply_content)
assert metadata["dependencies_met"] is False


async def test_do_debug_request(ipkernel: IPythonKernel):
msg = ipkernel.session.msg("debug_request", {})
msg_list = ipkernel.session.serialize(msg)
await ipkernel.do_debug_request(msg)
33 changes: 33 additions & 0 deletions ipykernel/tests/test_kernel_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ async def test_direct_execute_request_aborting(kernel):
assert reply["content"]["status"] == "aborted"


async def test_direct_execute_request_error(kernel):
await kernel.execute_request(None, None, None)


async def test_complete_request(kernel):
reply = await kernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
assert reply["header"]["msg_type"] == "complete_reply"
Expand All @@ -45,6 +49,18 @@ async def test_history_request(kernel):
"history_request", dict(hist_access_type="", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await kernel.test_shell_message(
"history_request", dict(hist_access_type="tail", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await kernel.test_shell_message(
"history_request", dict(hist_access_type="range", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"
reply = await kernel.test_shell_message(
"history_request", dict(hist_access_type="search", output="", raw="")
)
assert reply["header"]["msg_type"] == "history_reply"


async def test_comm_info_request(kernel):
Expand Down Expand Up @@ -137,6 +153,23 @@ def check_status():
await asyncio.sleep(0.1)


async def test_do_one_iteration(kernel):
kernel.msg_queue = asyncio.Queue()
await kernel.do_one_iteration()


async def test_publish_debug_event(kernel):
kernel._publish_debug_event({})


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()


# TODO: this causes deadlock
# async def test_direct_usage_request(kernel):
# reply = await kernel.test_control_message("usage_request", {})
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ filterwarnings= [
# Ignore our own warnings
"ignore:The `stream` parameter of `getpass.getpass` will have no effect:UserWarning",

# IPython warnings
"ignore: `Completer.complete` is pending deprecation since IPython 6.0 and will be replaced by `Completer.completions`:PendingDeprecationWarning",

# Ignore jupyter_client warnings
"ignore:unclosed <socket.socket:ResourceWarning",
"ignore:unclosed event loop:ResourceWarning",
Expand Down

0 comments on commit fe8b7b0

Please sign in to comment.