Skip to content

Commit e419ff4

Browse files
authored
Use pytest-jupyter (#891)
* clean up * bump version * cleanup * fix * fixup * debug * bump ipykernel dep * fix tests * try to fix windows * define override earlier * bump dep * try to fix windows * clean up coverage handling * fix mypy errors
1 parent bb29df0 commit e419ff4

13 files changed

+89
-278
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
hatch run cov:nowarn || hatch run test:nowarn --lf
7575
- name: Code coverage
7676
run: |
77-
pip install codecov
77+
pip install codecov coverage[toml]
7878
codecov
7979
8080
docs:
@@ -95,7 +95,7 @@ jobs:
9595
dependency_type: minimum
9696
- name: Run the unit tests
9797
run: |
98-
hatch run test:nowarn || hatch run test:nowarn --lf
98+
hatch -vv run test:nowarn || hatch run test:nowarn --lf
9999
100100
test_prereleases:
101101
name: Test Prereleases

jupyter_client/manager.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -657,20 +657,20 @@ def _context_default(self) -> zmq.asyncio.Context:
657657
self._created_context = True
658658
return zmq.asyncio.Context()
659659

660-
_launch_kernel = KernelManager._async_launch_kernel
661-
start_kernel = KernelManager._async_start_kernel
662-
pre_start_kernel = KernelManager._async_pre_start_kernel
663-
post_start_kernel = KernelManager._async_post_start_kernel
664-
request_shutdown = KernelManager._async_request_shutdown
665-
finish_shutdown = KernelManager._async_finish_shutdown
666-
cleanup_resources = KernelManager._async_cleanup_resources
667-
shutdown_kernel = KernelManager._async_shutdown_kernel
668-
restart_kernel = KernelManager._async_restart_kernel
669-
_send_kernel_sigterm = KernelManager._async_send_kernel_sigterm
670-
_kill_kernel = KernelManager._async_kill_kernel
671-
interrupt_kernel = KernelManager._async_interrupt_kernel
672-
signal_kernel = KernelManager._async_signal_kernel
673-
is_alive = KernelManager._async_is_alive
660+
_launch_kernel = KernelManager._async_launch_kernel # type:ignore[assignment]
661+
start_kernel = KernelManager._async_start_kernel # type:ignore[assignment]
662+
pre_start_kernel = KernelManager._async_pre_start_kernel # type:ignore[assignment]
663+
post_start_kernel = KernelManager._async_post_start_kernel # type:ignore[assignment]
664+
request_shutdown = KernelManager._async_request_shutdown # type:ignore[assignment]
665+
finish_shutdown = KernelManager._async_finish_shutdown # type:ignore[assignment]
666+
cleanup_resources = KernelManager._async_cleanup_resources # type:ignore[assignment]
667+
shutdown_kernel = KernelManager._async_shutdown_kernel # type:ignore[assignment]
668+
restart_kernel = KernelManager._async_restart_kernel # type:ignore[assignment]
669+
_send_kernel_sigterm = KernelManager._async_send_kernel_sigterm # type:ignore[assignment]
670+
_kill_kernel = KernelManager._async_kill_kernel # type:ignore[assignment]
671+
interrupt_kernel = KernelManager._async_interrupt_kernel # type:ignore[assignment]
672+
signal_kernel = KernelManager._async_signal_kernel # type:ignore[assignment]
673+
is_alive = KernelManager._async_is_alive # type:ignore[assignment]
674674

675675

676676
KernelManagerABC.register(KernelManager)
@@ -699,14 +699,14 @@ async def start_new_async_kernel(
699699
) -> t.Tuple[AsyncKernelManager, KernelClient]:
700700
"""Start a new kernel, and return its Manager and Client"""
701701
km = AsyncKernelManager(kernel_name=kernel_name)
702-
await km.start_kernel(**kwargs)
702+
await km.start_kernel(**kwargs) # type:ignore[has-type]
703703
kc = km.client()
704704
kc.start_channels()
705705
try:
706706
await kc.wait_for_ready(timeout=startup_timeout) # type:ignore[attr-defined]
707707
except RuntimeError:
708708
kc.stop_channels()
709-
await km.shutdown_kernel()
709+
await km.shutdown_kernel() # type:ignore[has-type]
710710
raise
711711

712712
return (km, kc)

jupyter_client/multikernelmanager.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ def _context_default(self) -> zmq.asyncio.Context:
562562
self._created_context = True
563563
return zmq.asyncio.Context()
564564

565-
start_kernel = MultiKernelManager._async_start_kernel
566-
restart_kernel = MultiKernelManager._async_restart_kernel
567-
shutdown_kernel = MultiKernelManager._async_shutdown_kernel
568-
shutdown_all = MultiKernelManager._async_shutdown_all
565+
start_kernel = MultiKernelManager._async_start_kernel # type:ignore[assignment]
566+
restart_kernel = MultiKernelManager._async_restart_kernel # type:ignore[assignment]
567+
shutdown_kernel = MultiKernelManager._async_shutdown_kernel # type:ignore[assignment]
568+
shutdown_all = MultiKernelManager._async_shutdown_all # type:ignore[assignment]

pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ Homepage = "https://jupyter.org"
5050
test = [
5151
"codecov",
5252
"coverage",
53-
"ipykernel>=6.11",
53+
"ipykernel>=6.14",
5454
"mypy",
5555
"paramiko; sys_platform == 'win32'",
5656
"pre-commit",
5757
"pytest",
58-
"pytest-asyncio>=0.19",
58+
"pytest-jupyter[client]>=0.4.1",
5959
"pytest-cov",
6060
"pytest-timeout",
6161
]
@@ -92,7 +92,7 @@ nowarn = "test -W default {args}"
9292

9393
[tool.hatch.envs.cov]
9494
features = ["test"]
95-
dependencies = ["coverage", "pytest-cov"]
95+
dependencies = ["coverage[toml]", "pytest-cov"]
9696
[tool.hatch.envs.cov.scripts]
9797
test = "python -m pytest -vv --cov jupyter_client --cov-branch --cov-report term-missing:skip-covered {args}"
9898
nowarn = "test -W default {args}"
@@ -116,7 +116,6 @@ testpaths = [
116116
timeout = 100
117117
# Restore this setting to debug failures
118118
timeout_method = "thread"
119-
asyncio_mode = "auto"
120119
filterwarnings= [
121120
# Fail on warnings
122121
"error",

tests/conftest.py

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,18 @@
11
import asyncio
2-
import json
32
import os
4-
import sys
5-
from pathlib import Path
3+
4+
if os.name == "nt":
5+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
66

77
import pytest
88

99
# Must be set before importing from `jupyter_core`.
1010
os.environ['JUPYTER_PLATFORM_DIRS'] = '1'
1111

12-
from jupyter_core import paths
13-
14-
from .utils import test_env
15-
16-
try:
17-
import resource
18-
except ImportError:
19-
# Windows
20-
resource = None
21-
22-
pjoin = os.path.join
23-
24-
25-
# Handle resource limit
26-
# Ensure a minimal soft limit of DEFAULT_SOFT if the current hard limit is at least that much.
27-
if resource is not None:
28-
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
29-
30-
DEFAULT_SOFT = 4096
31-
if hard >= DEFAULT_SOFT:
32-
soft = DEFAULT_SOFT
33-
34-
if hard < soft:
35-
hard = soft
3612

37-
resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
38-
39-
40-
if os.name == "nt" and sys.version_info >= (3, 7):
41-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
42-
43-
44-
@pytest.fixture
45-
def event_loop():
46-
# Make sure we test against a selector event loop
47-
# since pyzmq doesn't like the proactor loop.
48-
# This fixture is picked up by pytest-asyncio
49-
if os.name == "nt" and sys.version_info >= (3, 7):
50-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
51-
loop = asyncio.SelectorEventLoop()
52-
try:
53-
yield loop
54-
finally:
55-
loop.close()
13+
pytest_plugins = ["pytest_jupyter", "pytest_jupyter.jupyter_client"]
5614

5715

5816
@pytest.fixture(autouse=True)
59-
def env():
60-
env_patch = test_env()
61-
env_patch.start()
62-
yield
63-
env_patch.stop()
64-
65-
66-
@pytest.fixture()
67-
def kernel_dir():
68-
return pjoin(paths.jupyter_data_dir(), 'kernels')
69-
70-
71-
@pytest.fixture
72-
def kernel_spec(tmpdir):
73-
test_dir = Path(tmpdir / "test")
74-
test_dir.mkdir()
75-
kernel_data = {"argv": [], "display_name": "test", "language": "test"}
76-
spec_file_path = Path(test_dir / "kernel.json")
77-
spec_file_path.write_text(json.dumps(kernel_data), 'utf8')
78-
return str(test_dir)
17+
def setup_environ(jp_environ):
18+
pass

tests/test_client.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
from traitlets import DottedObjectName
1414
from traitlets import Type
1515

16-
from .utils import test_env
1716
from jupyter_client.client import validate_string_dict
1817
from jupyter_client.kernelspec import KernelSpecManager
19-
from jupyter_client.kernelspec import NATIVE_KERNEL_NAME
2018
from jupyter_client.kernelspec import NoSuchKernel
2119
from jupyter_client.manager import KernelManager
2220
from jupyter_client.manager import start_new_async_kernel
@@ -31,17 +29,13 @@
3129

3230
class TestKernelClient(TestCase):
3331
def setUp(self):
34-
self.env_patch = test_env()
35-
self.env_patch.start()
36-
self.addCleanup(self.env_patch.stop)
3732
try:
38-
KernelSpecManager().get_kernel_spec(NATIVE_KERNEL_NAME)
33+
KernelSpecManager().get_kernel_spec("echo")
3934
except NoSuchKernel:
4035
pytest.skip()
41-
self.km, self.kc = start_new_kernel(kernel_name=NATIVE_KERNEL_NAME)
36+
self.km, self.kc = start_new_kernel(kernel_name="echo")
4237

4338
def tearDown(self):
44-
self.env_patch.stop()
4539
self.km.shutdown_kernel()
4640
self.kc.stop_channels()
4741
return super().tearDown()
@@ -106,25 +100,28 @@ def test_shutdown_id(self):
106100

107101

108102
@pytest.fixture
109-
async def kc():
110-
env_patch = test_env()
111-
env_patch.start()
103+
def kc(jp_asyncio_loop):
112104
try:
113-
KernelSpecManager().get_kernel_spec(NATIVE_KERNEL_NAME)
105+
KernelSpecManager().get_kernel_spec("echo")
114106
except NoSuchKernel:
115107
pytest.skip()
116-
km, kc = await start_new_async_kernel(kernel_name=NATIVE_KERNEL_NAME)
108+
109+
async def start():
110+
return await start_new_async_kernel(kernel_name="echo")
111+
112+
km, kc = jp_asyncio_loop.run_until_complete(start())
117113
yield kc
118-
env_patch.stop()
119-
await km.shutdown_kernel()
114+
115+
async def stop():
116+
await km.shutdown_kernel()
117+
118+
jp_asyncio_loop.run_until_complete(stop())
120119
kc.stop_channels()
121120

122121

123122
class TestAsyncKernelClient:
124123
async def test_execute_interactive(self, kc):
125-
with capture_output() as io:
126-
reply = await kc.execute_interactive("print('hello')", timeout=TIMEOUT)
127-
assert "hello" in io.stdout
124+
reply = await kc.execute_interactive("hello", timeout=TIMEOUT)
128125
assert reply["content"]["status"] == "ok"
129126

130127
def _check_reply(self, reply_type, reply):
@@ -225,20 +222,16 @@ class CustomThreadedKernelClient(ThreadedKernelClient):
225222

226223
class TestThreadedKernelClient(TestKernelClient):
227224
def setUp(self):
228-
self.env_patch = test_env()
229-
self.env_patch.start()
230-
self.addCleanup(self.env_patch.stop)
231225
try:
232-
KernelSpecManager().get_kernel_spec(NATIVE_KERNEL_NAME)
226+
KernelSpecManager().get_kernel_spec("echo")
233227
except NoSuchKernel:
234228
pytest.skip()
235-
self.km = km = ThreadedKernelManager(kernel_name=NATIVE_KERNEL_NAME)
229+
self.km = km = ThreadedKernelManager(kernel_name="echo")
236230
km.start_kernel()
237231
self.kc = kc = km.client()
238232
kc.start_channels()
239233

240234
def tearDown(self):
241-
self.env_patch.stop()
242235
self.km.shutdown_kernel()
243236
self.kc.stop_channels()
244237

0 commit comments

Comments
 (0)