Skip to content

Commit f452c4d

Browse files
committed
use fixtures for starting servers
1 parent 3dde433 commit f452c4d

File tree

9 files changed

+253
-75
lines changed

9 files changed

+253
-75
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
# NOTE: See CONTRIBUTING.md for a local development setup that differs
6868
# slightly from this.
6969
#
70-
# Pytest options are set in tests/pytest.ini.
70+
# Pytest options are set in `pyproject.toml`.
7171
run: |
7272
pip install -vv $(ls ./dist/jupyter_server_proxy-*.whl)\[acceptance\] 'jupyterlab~=${{ matrix.jupyterlab-version }}.0' 'jupyter_server~=${{ matrix.jupyter_server-version }}.0'
7373
@@ -76,18 +76,8 @@ jobs:
7676
pip freeze
7777
pip check
7878
79-
- name: Run tests against jupyter-notebook
79+
- name: Run tests
8080
run: |
81-
JUPYTER_TOKEN=secret jupyter-notebook --config=./tests/resources/jupyter_server_config.py &
82-
sleep 5
83-
cd tests
84-
pytest -k "not acceptance"
85-
86-
- name: Run tests against jupyter-lab
87-
run: |
88-
JUPYTER_TOKEN=secret jupyter-lab --config=./tests/resources/jupyter_server_config.py &
89-
sleep 5
90-
cd tests
9181
pytest -k "not acceptance"
9282
9383
- name: Upload pytest and coverage reports

CONTRIBUTING.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,10 @@ jupyter labextension develop --overwrite .
2222
jupyter server extension enable jupyter_server_proxy
2323
```
2424

25-
Before running tests, you need a server that we can test against.
26-
27-
```bash
28-
JUPYTER_TOKEN=secret jupyter-lab --config=./tests/resources/jupyter_server_config.py --no-browser
29-
```
30-
3125
Run the tests:
3226

3327
```bash
34-
pytest --verbose
28+
pytest
3529
```
3630

3731
These generate test and coverage reports in `build/pytest` and `build/coverage`.

jupyter_server_proxy/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ def _load_jupyter_server_extension(nbapp):
7171
],
7272
)
7373

74+
nbapp.log.debug(
75+
"[jupyter-server-proxy] Started with known servers: %s",
76+
", ".join([p.name for p in server_processes]),
77+
)
78+
7479

7580
# For backward compatibility
7681
load_jupyter_server_extension = _load_jupyter_server_extension

jupyter_server_proxy/config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
from collections import namedtuple
55
from warnings import warn
66

7-
import pkg_resources
7+
import sys
8+
9+
if sys.version_info < (3, 10): # pragma: no cover
10+
from importlib_metadata import entry_points
11+
else: # pragma: no cover
12+
from importlib.metadata import entry_points
13+
814
from jupyter_server.utils import url_path_join as ujoin
915
from traitlets import Dict, List, Tuple, Union, default, observe
1016
from traitlets.config import Configurable
@@ -90,7 +96,7 @@ def get_timeout(self):
9096

9197
def get_entrypoint_server_processes(serverproxy_config):
9298
sps = []
93-
for entry_point in pkg_resources.iter_entry_points("jupyter_serverproxy_servers"):
99+
for entry_point in entry_points(group="jupyter_serverproxy_servers"):
94100
name = entry_point.name
95101
server_process_config = entry_point.load()()
96102
sps.append(make_server_process(name, server_process_config, serverproxy_config))

pyproject.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ classifiers = [
4747
]
4848
dependencies = [
4949
"aiohttp",
50+
"importlib_metadata >=4.8.3 ; python_version<\"3.10\"",
5051
"jupyter-server >=1.0",
5152
"simpervisor >=0.4",
5253
]
@@ -175,3 +176,26 @@ tag_template = "v{new_version}"
175176

176177
[[tool.tbump.file]]
177178
src = "labextension/package.json"
179+
180+
[tool.pytest.ini_options]
181+
cache_dir = "build/.cache/pytest"
182+
addopts = [
183+
"-vv",
184+
"--cov=jupyter_server_proxy",
185+
"--cov-branch",
186+
"--cov-context=test",
187+
"--cov-report=term-missing:skip-covered",
188+
"--cov-report=html:build/coverage",
189+
"--html=build/pytest/index.html",
190+
"--color=yes",
191+
]
192+
193+
[tool.coverage.run]
194+
data_file = "build/.coverage"
195+
concurrency = [
196+
"multiprocessing",
197+
"thread"
198+
]
199+
200+
[tool.coverage.html]
201+
show_contexts = true

tests/conftest.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""Reusable test fixtures for ``jupyter_server_proxy``."""
2+
import socket
3+
import sys
4+
import os
5+
from pytest import fixture
6+
from subprocess import Popen
7+
from typing import Generator, Any, Tuple
8+
from pathlib import Path
9+
from uuid import uuid4
10+
from urllib.error import URLError
11+
import time
12+
from urllib.request import urlopen
13+
import shutil
14+
15+
HERE = Path(__file__).parent
16+
RESOURCES = HERE / "resources"
17+
18+
@fixture
19+
def a_token() -> str:
20+
"""Get a random UUID to use for a token."""
21+
return str(uuid4())
22+
23+
24+
@fixture
25+
def an_unused_port() -> int:
26+
"""Get a random unused port."""
27+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28+
s.bind(("127.0.0.1", 0))
29+
s.listen(1)
30+
port = s.getsockname()[1]
31+
s.close()
32+
return port
33+
34+
35+
@fixture(params=["notebook", "lab"])
36+
def a_server_cmd(request: Any) -> str:
37+
"""Get a viable name for a command."""
38+
return request.param
39+
40+
41+
@fixture
42+
def a_server(
43+
a_server_cmd: str,
44+
tmp_path: Path,
45+
an_unused_port: int,
46+
a_token: str,
47+
) -> Generator[str, None, None]:
48+
"""Get a running server."""
49+
# get a copy of the resources
50+
tests = tmp_path / "tests"
51+
tests.mkdir()
52+
shutil.copytree(RESOURCES, tests / "resources")
53+
args = [
54+
sys.executable,
55+
"-m",
56+
"jupyter",
57+
a_server_cmd,
58+
f"--port={an_unused_port}",
59+
"--no-browser",
60+
"--config=./tests/resources/jupyter_server_config.py",
61+
"--debug",
62+
]
63+
64+
# prepare an env
65+
env = dict(os.environ)
66+
env.update(JUPYTER_TOKEN=a_token)
67+
68+
# start the process
69+
server_proc = Popen(args, cwd=str(tmp_path), env=env)
70+
71+
# prepare some URLss
72+
url = f"http://127.0.0.1:{an_unused_port}/"
73+
canary_url = f"{url}favicon.ico"
74+
shutdown_url = f"{url}api/shutdown?token={a_token}"
75+
76+
retries = 10
77+
78+
while retries:
79+
try:
80+
urlopen(canary_url)
81+
break
82+
except URLError as err:
83+
if "Connection refused" in str(err):
84+
print(
85+
f"{a_server_cmd} not ready, will try again in 0.5s [{retries} retries]",
86+
flush=True,
87+
)
88+
time.sleep(0.5)
89+
retries -= 1
90+
continue
91+
raise err
92+
93+
print(f"{a_server_cmd} is ready...", flush=True)
94+
95+
yield url
96+
97+
# clean up after server is no longer needed
98+
print(f"{a_server_cmd} shutting down...", flush=True)
99+
urlopen(shutdown_url, data=[])
100+
server_proc.wait()
101+
print(f"{a_server_cmd} is stopped", flush=True)
102+
103+
104+
@fixture
105+
def a_server_port_and_token(
106+
a_server: str, # noqa
107+
an_unused_port: int,
108+
a_token: str,
109+
) -> Tuple[int, str]:
110+
"""Get the port and token for a running server."""
111+
return an_unused_port, a_token

tests/pytest.ini

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/resources/jupyter_server_config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
from pathlib import Path
2+
import sys
3+
4+
HERE = Path(__file__).parent.resolve()
5+
6+
sys.path.append(str(HERE))
7+
18
# load the config object for traitlets based configuration
29
c = get_config() # noqa
310

@@ -116,9 +123,5 @@ def cats_only(response, path):
116123

117124
c.ServerProxy.non_service_rewrite_response = hello_to_foo
118125

119-
import sys
120-
121-
sys.path.append("./tests/resources")
122126
c.ServerApp.jpserver_extensions = {"proxyextension": True}
123127
c.NotebookApp.nbserver_extensions = {"proxyextension": True}
124-
# c.Application.log_level = 'DEBUG'

0 commit comments

Comments
 (0)