Skip to content

Commit e6b05fe

Browse files
authored
Refactor fixtures (#26)
1 parent 8b054c0 commit e6b05fe

File tree

5 files changed

+101
-88
lines changed

5 files changed

+101
-88
lines changed

pytest_jupyter/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
import asyncio
5-
import os
6-
74
from .jupyter_core import * # noqa
8-
9-
if os.name == "nt":
10-
asyncio.set_event_loop_policy(
11-
asyncio.WindowsSelectorEventLoopPolicy() # type:ignore[attr-defined]
12-
)

pytest_jupyter/jupyter_client.py

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
import json
5-
import os
6-
import sys
7-
from pathlib import Path
8-
94
import pytest
10-
from jupyter_core import paths
115

126
try:
137
import ipykernel # noqa
@@ -22,32 +16,12 @@
2216
"you need. Try: `pip install 'pytest-jupyter[client]'`"
2317
)
2418

19+
# Bring in core plugins.
2520
from pytest_jupyter import * # noqa
2621

27-
try:
28-
import resource
29-
except ImportError:
30-
# Windows
31-
resource = None # type: ignore
32-
33-
34-
# Handle resource limit
35-
# Ensure a minimal soft limit of DEFAULT_SOFT if the current hard limit is at least that much.
36-
if resource is not None:
37-
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
38-
39-
DEFAULT_SOFT = 4096
40-
if hard >= DEFAULT_SOFT:
41-
soft = DEFAULT_SOFT
42-
43-
if hard < soft:
44-
hard = soft
45-
46-
resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
47-
4822

4923
@pytest.fixture
50-
def zmq_context():
24+
def jp_zmq_context():
5125
import zmq
5226

5327
ctx = zmq.asyncio.Context()
@@ -56,7 +30,7 @@ def zmq_context():
5630

5731

5832
@pytest.fixture
59-
def start_kernel(echo_kernel_spec, asyncio_loop):
33+
def jp_start_kernel(jp_environ, jp_asyncio_loop):
6034
kms = []
6135
kcs = []
6236

@@ -72,21 +46,5 @@ async def inner(kernel_name="echo", **kwargs):
7246
kc.stop_channels()
7347

7448
for km in kms:
75-
asyncio_loop.run_until_complete(km.shutdown_kernel(now=True))
49+
jp_asyncio_loop.run_until_complete(km.shutdown_kernel(now=True))
7650
assert km.context.closed
77-
78-
79-
@pytest.fixture()
80-
def kernel_dir():
81-
return os.path.join(paths.jupyter_data_dir(), "kernels")
82-
83-
84-
@pytest.fixture
85-
def echo_kernel_spec(kernel_dir):
86-
test_dir = Path(kernel_dir) / "echo"
87-
test_dir.mkdir(parents=True, exist_ok=True)
88-
argv = [sys.executable, "-m", "pytest_jupyter.echo_kernel", "-f", "{connection_file}"]
89-
kernel_data = {"argv": argv, "display_name": "echo", "language": "echo"}
90-
spec_file_path = Path(test_dir / "kernel.json")
91-
spec_file_path.write_text(json.dumps(kernel_data), "utf8")
92-
return str(test_dir)

pytest_jupyter/jupyter_core.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,67 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
3+
import asyncio
4+
import json
35
import os
46
import sys
7+
from pathlib import Path
58

69
import jupyter_core
710
import pytest
811

912
from .utils import mkdir
1013

14+
try:
15+
import resource
16+
except ImportError:
17+
# Windows
18+
resource = None # type: ignore
19+
20+
21+
# Handle resource limit
22+
# Ensure a minimal soft limit of DEFAULT_SOFT if the current hard limit is at least that much.
23+
if resource is not None:
24+
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
25+
26+
DEFAULT_SOFT = 4096
27+
if hard >= DEFAULT_SOFT:
28+
soft = DEFAULT_SOFT
29+
30+
if hard < soft:
31+
hard = soft
32+
33+
resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
34+
35+
36+
if os.name == "nt":
37+
asyncio.set_event_loop_policy(
38+
asyncio.WindowsSelectorEventLoopPolicy() # type:ignore[attr-defined]
39+
)
40+
41+
42+
@pytest.fixture
43+
def jp_asyncio_loop():
44+
loop = asyncio.new_event_loop()
45+
asyncio.set_event_loop(loop)
46+
yield loop
47+
loop.close()
48+
49+
50+
@pytest.fixture(autouse=True)
51+
def io_loop(jp_asyncio_loop):
52+
"""Override the io_loop for pytest_tornasync. This is a no-op
53+
if tornado is not installed."""
54+
55+
async def get_tornado_loop():
56+
try:
57+
from tornado.ioloop import IOLoop
58+
59+
return IOLoop.current()
60+
except ImportError:
61+
pass
62+
63+
return jp_asyncio_loop.run_until_complete(get_tornado_loop())
64+
1165

1266
@pytest.fixture
1367
def jp_home_dir(tmp_path):
@@ -57,6 +111,22 @@ def jp_env_config_path(tmp_path):
57111
return mkdir(tmp_path, "env", "etc", "jupyter")
58112

59113

114+
@pytest.fixture()
115+
def jp_kernel_dir(jp_data_dir):
116+
return mkdir(jp_data_dir, "kernels")
117+
118+
119+
@pytest.fixture
120+
def echo_kernel_spec(jp_kernel_dir):
121+
test_dir = Path(jp_kernel_dir) / "echo"
122+
test_dir.mkdir(parents=True, exist_ok=True)
123+
argv = [sys.executable, "-m", "pytest_jupyter.echo_kernel", "-f", "{connection_file}"]
124+
kernel_data = {"argv": argv, "display_name": "echo", "language": "echo"}
125+
spec_file_path = Path(test_dir / "kernel.json")
126+
spec_file_path.write_text(json.dumps(kernel_data), "utf8")
127+
return str(test_dir)
128+
129+
60130
@pytest.fixture
61131
def jp_environ(
62132
monkeypatch,
@@ -65,6 +135,7 @@ def jp_environ(
65135
jp_data_dir,
66136
jp_config_dir,
67137
jp_runtime_dir,
138+
echo_kernel_spec,
68139
jp_system_jupyter_path,
69140
jp_system_config_path,
70141
jp_env_jupyter_path,

pytest_jupyter/jupyter_server.py

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
import asyncio
54
import importlib
65
import io
76
import logging
@@ -45,26 +44,17 @@
4544
)
4645

4746

47+
# Bring in core plugins.
48+
from pytest_jupyter import * # noqa
4849
from pytest_jupyter.utils import mkdir
4950

5051
# List of dependencies needed for this plugin.
51-
pytest_plugins = ["pytest_tornasync", "pytest_jupyter.jupyter_client"]
52+
pytest_plugins = ["pytest_tornasync"]
5253

5354

54-
@pytest.fixture
55-
def asyncio_loop():
56-
loop = asyncio.new_event_loop()
57-
asyncio.set_event_loop(loop)
58-
yield loop
59-
loop.close()
60-
61-
62-
@pytest.fixture(autouse=True)
63-
def io_loop(asyncio_loop):
64-
async def get_tornado_loop():
65-
return tornado.ioloop.IOLoop.current()
66-
67-
return asyncio_loop.run_until_complete(get_tornado_loop())
55+
# Override some of the fixtures from pytest_tornasync
56+
# The io_loop fixture is overidden in jupyter_core.py so it
57+
# can be shared by other plugins that need it (e.g. jupyter_client.py).
6858

6959

7060
@pytest.fixture
@@ -100,6 +90,9 @@ async def get_server():
10090
http_server_port[0].close()
10191

10292

93+
# End pytest_tornasync overrides
94+
95+
10396
@pytest.fixture
10497
def jp_server_config():
10598
"""Allows tests to setup their specific configuration values."""
@@ -132,19 +125,19 @@ def jp_argv():
132125
return []
133126

134127

135-
@pytest.fixture
136-
def jp_extension_environ(jp_env_config_path, monkeypatch):
137-
"""Monkeypatch a Jupyter Extension's config path into each test's environment variable"""
138-
monkeypatch.setattr(serverextension, "ENV_CONFIG_PATH", [str(jp_env_config_path)])
139-
140-
141-
@pytest.fixture
128+
@pytest.fixture()
142129
def jp_http_port(http_server_port):
143130
"""Returns the port value from the http_server_port fixture."""
144131
yield http_server_port[-1]
145132
http_server_port[0].close()
146133

147134

135+
@pytest.fixture
136+
def jp_extension_environ(jp_env_config_path, monkeypatch):
137+
"""Monkeypatch a Jupyter Extension's config path into each test's environment variable"""
138+
monkeypatch.setattr(serverextension, "ENV_CONFIG_PATH", [str(jp_env_config_path)])
139+
140+
148141
@pytest.fixture
149142
def jp_nbconvert_templates(jp_data_dir):
150143
"""Setups up a temporary directory consisting of the nbconvert templates."""
@@ -192,9 +185,8 @@ def jp_configurable_serverapp(
192185
tmp_path,
193186
jp_root_dir,
194187
jp_logging_stream,
195-
asyncio_loop,
188+
jp_asyncio_loop,
196189
io_loop,
197-
echo_kernel_spec, # noqa
198190
):
199191
"""Starts a Jupyter Server instance based on
200192
the provided configuration values.
@@ -261,14 +253,14 @@ def _configurable_serverapp(
261253
app.log.propagate = True
262254
app.log.handlers = []
263255
# Initialize app without httpserver
264-
if asyncio_loop.is_running():
256+
if jp_asyncio_loop.is_running():
265257
app.initialize(argv=argv, new_httpserver=False)
266258
else:
267259

268260
async def initialize_app():
269261
app.initialize(argv=argv, new_httpserver=False)
270262

271-
asyncio_loop.run_until_complete(initialize_app())
263+
jp_asyncio_loop.run_until_complete(initialize_app())
272264
# Reroute all logging StreamHandlers away from stdin/stdout since pytest hijacks
273265
# these streams and closes them at unfortunate times.
274266
stream_handlers = [h for h in app.log.handlers if isinstance(h, logging.StreamHandler)]
@@ -410,11 +402,11 @@ def inner(nbpath):
410402

411403

412404
@pytest.fixture(autouse=True)
413-
def jp_server_cleanup(asyncio_loop):
405+
def jp_server_cleanup(jp_asyncio_loop):
414406
yield
415407
app: ServerApp = ServerApp.instance()
416408
try:
417-
asyncio_loop.run_until_complete(app._cleanup())
409+
jp_asyncio_loop.run_until_complete(app._cleanup())
418410
except (RuntimeError, SystemExit) as e:
419411
print("ignoring cleanup error", e)
420412
if hasattr(app, "kernel_manager"):

tests/test_jupyter_client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
from pytest_jupyter.echo_kernel import EchoKernel
66

77

8-
def test_zmq_context(zmq_context):
9-
assert isinstance(zmq_context.underlying, int)
8+
def test_zmq_context(jp_zmq_context):
9+
assert isinstance(jp_zmq_context.underlying, int)
1010

1111

12-
async def test_start_kernel(start_kernel):
13-
km, kc = await start_kernel()
12+
async def test_start_kernel(jp_start_kernel):
13+
km, kc = await jp_start_kernel()
1414
assert km.kernel_name == "echo"
1515
msg = await kc.execute("hello", reply=True)
1616
assert msg["content"]["status"] == "ok"
1717

18-
km, kc = await start_kernel("python3")
18+
km, kc = await jp_start_kernel("python3")
1919
assert km.kernel_name == "python3"
2020
msg = await kc.execute("print('hi')", reply=True)
2121
assert msg["content"]["status"] == "ok"

0 commit comments

Comments
 (0)