Skip to content

Commit dbea8f2

Browse files
authored
Increase coverage (#1021)
* increase coverage * add codecov file * add tk guard * add another guard * skip test on windows * skip more tests on windows * more skips
1 parent 85d0a3d commit dbea8f2

File tree

6 files changed

+257
-8
lines changed

6 files changed

+257
-8
lines changed

codecov.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
target: auto
6+
threshold: 1
7+
patch:
8+
default:
9+
target: 0%

ipykernel/kernelbase.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def _update_eventloop(self, change):
102102
banner: str
103103

104104
@default("shell_streams")
105-
def _shell_streams_default(self):
105+
def _shell_streams_default(self): # pragma: no cover
106106
warnings.warn(
107107
"Kernel.shell_streams is deprecated in ipykernel 6.0. Use Kernel.shell_stream",
108108
DeprecationWarning,
@@ -114,7 +114,7 @@ def _shell_streams_default(self):
114114
return []
115115

116116
@observe("shell_streams")
117-
def _shell_streams_changed(self, change):
117+
def _shell_streams_changed(self, change): # pragma: no cover
118118
warnings.warn(
119119
"Kernel.shell_streams is deprecated in ipykernel 6.0. Use Kernel.shell_stream",
120120
DeprecationWarning,
@@ -683,7 +683,6 @@ def finish_metadata(self, parent, metadata, reply_content):
683683

684684
async def execute_request(self, stream, ident, parent):
685685
"""handle an execute_request"""
686-
687686
try:
688687
content = parent["content"]
689688
code = content["code"]
@@ -947,7 +946,6 @@ def do_is_complete(self, code):
947946

948947
async def debug_request(self, stream, ident, parent):
949948
content = parent["content"]
950-
951949
reply_content = self.do_debug_request(content)
952950
if inspect.isawaitable(reply_content):
953951
reply_content = await reply_content
@@ -1006,7 +1004,7 @@ async def do_debug_request(self, msg):
10061004
# Engine methods (DEPRECATED)
10071005
# ---------------------------------------------------------------------------
10081006

1009-
async def apply_request(self, stream, ident, parent):
1007+
async def apply_request(self, stream, ident, parent): # pragma: no cover
10101008
self.log.warning("apply_request is deprecated in kernel_base, moving to ipyparallel.")
10111009
try:
10121010
content = parent["content"]
@@ -1044,7 +1042,7 @@ def do_apply(self, content, bufs, msg_id, reply_metadata):
10441042
# Control messages (DEPRECATED)
10451043
# ---------------------------------------------------------------------------
10461044

1047-
async def abort_request(self, stream, ident, parent):
1045+
async def abort_request(self, stream, ident, parent): # pragma: no cover
10481046
"""abort a specific msg by id"""
10491047
self.log.warning(
10501048
"abort_request is deprecated in kernel_base. It is only part of IPython parallel"
@@ -1063,7 +1061,7 @@ async def abort_request(self, stream, ident, parent):
10631061
)
10641062
self.log.debug("%s", reply_msg)
10651063

1066-
async def clear_request(self, stream, idents, parent):
1064+
async def clear_request(self, stream, idents, parent): # pragma: no cover
10671065
"""Clear our namespace."""
10681066
self.log.warning(
10691067
"clear_request is deprecated in kernel_base. It is only part of IPython parallel"

ipykernel/tests/conftest.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import asyncio
2+
import logging
23
import os
34

5+
import pytest
6+
import zmq
7+
from jupyter_client.session import Session
8+
from tornado.ioloop import IOLoop
9+
from zmq.eventloop.zmqstream import ZMQStream
10+
11+
from ipykernel.kernelbase import Kernel
12+
413
try:
514
import resource
615
except ImportError:
@@ -26,3 +35,97 @@
2635
# Enforce selector event loop on Windows.
2736
if os.name == "nt":
2837
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
38+
39+
40+
class TestKernel(Kernel):
41+
implementation = "test"
42+
implementation_version = "1.0"
43+
language = "no-op"
44+
language_version = "0.1"
45+
language_info = {
46+
"name": "test",
47+
"mimetype": "text/plain",
48+
"file_extension": ".txt",
49+
}
50+
banner = "test kernel"
51+
log = logging.getLogger()
52+
53+
def __init__(self, *args, **kwargs):
54+
self.context = context = zmq.Context()
55+
self.iopub_socket = context.socket(zmq.PUB)
56+
self.session = Session()
57+
self.test_sockets = [self.iopub_socket]
58+
self.test_streams = []
59+
60+
for name in ["shell", "control"]:
61+
socket = context.socket(zmq.ROUTER)
62+
stream = ZMQStream(socket)
63+
stream.on_send(self._on_send)
64+
self.test_sockets.append(socket)
65+
self.test_streams.append(stream)
66+
setattr(self, f"{name}_stream", stream)
67+
super().__init__(*args, **kwargs)
68+
69+
def do_execute(
70+
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
71+
):
72+
if not silent:
73+
stream_content = {"name": "stdout", "text": code}
74+
self.send_response(self.iopub_socket, "stream", stream_content)
75+
76+
return {
77+
"status": "ok",
78+
# The base class increments the execution count
79+
"execution_count": self.execution_count,
80+
"payload": [],
81+
"user_expressions": {},
82+
}
83+
84+
async def do_debug_request(self, msg):
85+
return {}
86+
87+
async def test_shell_message(self, *args, **kwargs):
88+
msg_list = self._prep_msg(*args, **kwargs)
89+
await self.dispatch_shell(msg_list)
90+
self.shell_stream.flush()
91+
return await self._wait_for_msg()
92+
93+
async def test_control_message(self, *args, **kwargs):
94+
msg_list = self._prep_msg(*args, **kwargs)
95+
await self.process_control(msg_list)
96+
self.control_stream.flush()
97+
return await self._wait_for_msg()
98+
99+
def destroy(self):
100+
for stream in self.test_streams:
101+
stream.close()
102+
for socket in self.test_sockets:
103+
socket.close()
104+
self.context.destroy()
105+
106+
def _on_send(self, msg, *args, **kwargs):
107+
self._reply = msg
108+
109+
def _prep_msg(self, *args, **kwargs):
110+
self._reply = None
111+
msg = self.session.msg(*args, **kwargs)
112+
msg = self.session.serialize(msg)
113+
return [zmq.Message(m) for m in msg]
114+
115+
async def _wait_for_msg(self):
116+
while not self._reply:
117+
await asyncio.sleep(0.1)
118+
_, msg = self.session.feed_identities(self._reply)
119+
return self.session.deserialize(msg)
120+
121+
def _send_interupt_children(self):
122+
# override to prevent deadlock
123+
pass
124+
125+
126+
@pytest.fixture
127+
async def kernel():
128+
kernel = TestKernel()
129+
kernel.io_loop = IOLoop.current()
130+
yield kernel
131+
kernel.destroy()

ipykernel/tests/test_eventloop.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
"""Test eventloop integration"""
22

3+
import asyncio
4+
import os
5+
import threading
6+
import time
7+
38
import pytest
49
import tornado
510

11+
from ipykernel.eventloops import enable_gui, loop_asyncio, loop_tk
12+
613
from .utils import execute, flush_channels, start_new_kernel
714

815
KC = KM = None
@@ -41,3 +48,41 @@ def test_asyncio_interrupt():
4148
flush_channels(KC)
4249
msg_id, content = execute(async_code, KC)
4350
assert content["status"] == "ok"
51+
52+
53+
windows_skip = pytest.mark.skipif(os.name == "nt", reason="causing failures on windows")
54+
55+
56+
@windows_skip
57+
def test_tk_loop(kernel):
58+
def do_thing():
59+
time.sleep(1)
60+
try:
61+
kernel.app_wrapper.app.quit()
62+
# guard for tk failing to start (if there is no display)
63+
except AttributeError:
64+
pass
65+
66+
t = threading.Thread(target=do_thing)
67+
t.start()
68+
# guard for tk failing to start (if there is no display)
69+
try:
70+
loop_tk(kernel)
71+
except Exception:
72+
pass
73+
t.join()
74+
75+
76+
@windows_skip
77+
def test_asyncio_loop(kernel):
78+
def do_thing():
79+
loop.call_soon(loop.stop)
80+
81+
loop = asyncio.get_event_loop()
82+
loop.call_soon(do_thing)
83+
loop_asyncio(kernel)
84+
85+
86+
@windows_skip
87+
def test_enable_gui(kernel):
88+
enable_gui("tk", kernel)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""test the IPython Kernel"""
2+
3+
# Copyright (c) IPython Development Team.
4+
# Distributed under the terms of the Modified BSD License.
5+
6+
import os
7+
8+
import pytest
9+
10+
if os.name == "nt":
11+
pytest.skip("skipping tests on windows", allow_module_level=True)
12+
13+
14+
async def test_direct_kernel_info_request(kernel):
15+
reply = await kernel.test_shell_message("kernel_info_request", {})
16+
assert reply["header"]["msg_type"] == "kernel_info_reply"
17+
18+
19+
async def test_direct_execute_request(kernel):
20+
reply = await kernel.test_shell_message("execute_request", dict(code="hello", silent=False))
21+
assert reply["header"]["msg_type"] == "execute_reply"
22+
23+
24+
async def test_direct_execute_request_aborting(kernel):
25+
kernel._aborting = True
26+
reply = await kernel.test_shell_message("execute_request", dict(code="hello", silent=False))
27+
assert reply["header"]["msg_type"] == "execute_reply"
28+
assert reply["content"]["status"] == "aborted"
29+
30+
31+
async def test_complete_request(kernel):
32+
reply = await kernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
33+
assert reply["header"]["msg_type"] == "complete_reply"
34+
35+
36+
async def test_inspect_request(kernel):
37+
reply = await kernel.test_shell_message("inspect_request", dict(code="hello", cursor_pos=0))
38+
assert reply["header"]["msg_type"] == "inspect_reply"
39+
40+
41+
async def test_history_request(kernel):
42+
reply = await kernel.test_shell_message(
43+
"history_request", dict(hist_access_type="", output="", raw="")
44+
)
45+
assert reply["header"]["msg_type"] == "history_reply"
46+
47+
48+
async def test_comm_info_request(kernel):
49+
reply = await kernel.test_shell_message("comm_info_request")
50+
assert reply["header"]["msg_type"] == "comm_info_reply"
51+
52+
53+
async def test_direct_interrupt_request(kernel):
54+
reply = await kernel.test_shell_message("interrupt_request", {})
55+
assert reply["header"]["msg_type"] == "interrupt_reply"
56+
57+
58+
async def test_direct_shutdown_request(kernel):
59+
reply = await kernel.test_shell_message("shutdown_request", dict(restart=False))
60+
assert reply["header"]["msg_type"] == "shutdown_reply"
61+
reply = await kernel.test_shell_message("shutdown_request", dict(restart=True))
62+
assert reply["header"]["msg_type"] == "shutdown_reply"
63+
64+
65+
async def test_is_complete_request(kernel):
66+
reply = await kernel.test_shell_message("is_complete_request", dict(code="hello"))
67+
assert reply["header"]["msg_type"] == "is_complete_reply"
68+
69+
70+
async def test_direct_debug_request(kernel):
71+
reply = await kernel.test_control_message("debug_request", {})
72+
assert reply["header"]["msg_type"] == "debug_reply"
73+
74+
75+
# TODO: this causes deadlock
76+
# async def test_direct_usage_request(kernel):
77+
# reply = await kernel.test_control_message("usage_request", {})
78+
# assert reply['header']['msg_type'] == 'usage_reply'

pyproject.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ test = [
5151
"flaky",
5252
"ipyparallel",
5353
"pre-commit",
54-
"pytest-timeout",
54+
"pytest-asyncio",
55+
"pytest-timeout"
5556
]
5657

5758
[tool.hatch.version]
@@ -110,6 +111,7 @@ testpaths = [
110111
"ipykernel/tests",
111112
"ipykernel/inprocess/tests"
112113
]
114+
asyncio_mode = "auto"
113115
timeout = 300
114116
# Restore this setting to debug failures
115117
# timeout_method = "thread"
@@ -127,6 +129,20 @@ filterwarnings= [
127129
"module:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning",
128130
]
129131

132+
[tool.coverage.report]
133+
exclude_lines = [
134+
"pragma: no cover",
135+
"def __repr__",
136+
"if self.debug:",
137+
"if settings.DEBUG",
138+
"raise AssertionError",
139+
"raise NotImplementedError",
140+
"if 0:",
141+
"if __name__ == .__main__.:",
142+
"class .*\bProtocol\\):",
143+
"@(abc\\.)?abstractmethod",
144+
]
145+
130146
[tool.flake8]
131147
ignore = "E501, W503, E402"
132148
builtins = "c, get_config"

0 commit comments

Comments
 (0)