Skip to content

Commit 5071b0c

Browse files
bollwyvlZsailer
authored andcommitted
move min tornado outside translation block
1 parent 13fbe06 commit 5071b0c

File tree

2 files changed

+286
-1
lines changed

2 files changed

+286
-1
lines changed

jupyter_server/pytest_plugin.py

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
import os
2+
import sys
3+
import json
4+
import shutil
5+
import pytest
6+
import asyncio
7+
from binascii import hexlify
8+
9+
import urllib.parse
10+
import tornado
11+
from tornado.escape import url_escape
12+
13+
from traitlets.config import Config
14+
15+
import jupyter_core.paths
16+
from jupyter_server.extension import serverextension
17+
from jupyter_server.serverapp import ServerApp
18+
from jupyter_server.utils import url_path_join
19+
from jupyter_server.services.contents.filemanager import FileContentsManager
20+
21+
import nbformat
22+
23+
# This shouldn't be needed anymore, since pytest_tornasync is found in entrypoints
24+
pytest_plugins = "pytest_tornasync"
25+
26+
# NOTE: This is a temporary fix for Windows 3.8
27+
# We have to override the io_loop fixture with an
28+
# asyncio patch. This will probably be removed in
29+
# the future.
30+
31+
@pytest.fixture
32+
def asyncio_patch():
33+
ServerApp()._init_asyncio_patch()
34+
35+
@pytest.fixture
36+
def io_loop(asyncio_patch):
37+
loop = tornado.ioloop.IOLoop()
38+
loop.make_current()
39+
yield loop
40+
loop.clear_current()
41+
loop.close(all_fds=True)
42+
43+
44+
def mkdir(tmp_path, *parts):
45+
path = tmp_path.joinpath(*parts)
46+
if not path.exists():
47+
path.mkdir(parents=True)
48+
return path
49+
50+
51+
server_config = pytest.fixture(lambda: {})
52+
home_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "home"))
53+
data_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "data"))
54+
config_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "config"))
55+
runtime_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "runtime"))
56+
root_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "root_dir"))
57+
template_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "templates"))
58+
system_jupyter_path = pytest.fixture(
59+
lambda tmp_path: mkdir(tmp_path, "share", "jupyter")
60+
)
61+
env_jupyter_path = pytest.fixture(
62+
lambda tmp_path: mkdir(tmp_path, "env", "share", "jupyter")
63+
)
64+
system_config_path = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "etc", "jupyter"))
65+
env_config_path = pytest.fixture(
66+
lambda tmp_path: mkdir(tmp_path, "env", "etc", "jupyter")
67+
)
68+
some_resource = u"The very model of a modern major general"
69+
sample_kernel_json = {
70+
'argv':['cat', '{connection_file}'],
71+
'display_name': 'Test kernel',
72+
}
73+
argv = pytest.fixture(lambda: [])
74+
75+
@pytest.fixture
76+
def environ(
77+
monkeypatch,
78+
tmp_path,
79+
home_dir,
80+
data_dir,
81+
config_dir,
82+
runtime_dir,
83+
root_dir,
84+
system_jupyter_path,
85+
system_config_path,
86+
env_jupyter_path,
87+
env_config_path,
88+
):
89+
monkeypatch.setenv("HOME", str(home_dir))
90+
monkeypatch.setenv("PYTHONPATH", os.pathsep.join(sys.path))
91+
92+
# Get path to nbconvert template directory *before*
93+
# monkeypatching the paths env variable.
94+
possible_paths = jupyter_core.paths.jupyter_path('nbconvert', 'templates')
95+
nbconvert_path = None
96+
for path in possible_paths:
97+
if os.path.exists(path):
98+
nbconvert_path = path
99+
break
100+
101+
nbconvert_target = data_dir / 'nbconvert' / 'templates'
102+
103+
# monkeypatch.setenv("JUPYTER_NO_CONFIG", "1")
104+
monkeypatch.setenv("JUPYTER_CONFIG_DIR", str(config_dir))
105+
monkeypatch.setenv("JUPYTER_DATA_DIR", str(data_dir))
106+
monkeypatch.setenv("JUPYTER_RUNTIME_DIR", str(runtime_dir))
107+
monkeypatch.setattr(
108+
jupyter_core.paths, "SYSTEM_JUPYTER_PATH", [str(system_jupyter_path)]
109+
)
110+
monkeypatch.setattr(jupyter_core.paths, "ENV_JUPYTER_PATH", [str(env_jupyter_path)])
111+
monkeypatch.setattr(
112+
jupyter_core.paths, "SYSTEM_CONFIG_PATH", [str(system_config_path)]
113+
)
114+
monkeypatch.setattr(jupyter_core.paths, "ENV_CONFIG_PATH", [str(env_config_path)])
115+
116+
# copy nbconvert templates to new tmp data_dir.
117+
if nbconvert_path:
118+
shutil.copytree(nbconvert_path, str(nbconvert_target))
119+
120+
121+
@pytest.fixture
122+
def extension_environ(env_config_path, monkeypatch):
123+
"""Monkeypatch a Jupyter Extension's config path into each test's environment variable"""
124+
monkeypatch.setattr(serverextension, "ENV_CONFIG_PATH", [str(env_config_path)])
125+
126+
127+
@pytest.fixture(scope='function')
128+
def configurable_serverapp(
129+
environ,
130+
server_config,
131+
argv,
132+
http_port,
133+
tmp_path,
134+
root_dir,
135+
io_loop,
136+
):
137+
ServerApp.clear_instance()
138+
139+
def _configurable_serverapp(
140+
config=server_config,
141+
argv=argv,
142+
environ=environ,
143+
http_port=http_port,
144+
tmp_path=tmp_path,
145+
root_dir=root_dir,
146+
**kwargs
147+
):
148+
c = Config(config)
149+
c.NotebookNotary.db_file = ":memory:"
150+
token = hexlify(os.urandom(4)).decode("ascii")
151+
url_prefix = "/"
152+
app = ServerApp.instance(
153+
# Set the log level to debug for testing purposes
154+
log_level='DEBUG',
155+
port=http_port,
156+
port_retries=0,
157+
open_browser=False,
158+
root_dir=str(root_dir),
159+
base_url=url_prefix,
160+
config=c,
161+
allow_root=True,
162+
token=token,
163+
**kwargs
164+
)
165+
166+
app.init_signal = lambda: None
167+
app.log.propagate = True
168+
app.log.handlers = []
169+
# Initialize app without httpserver
170+
app.initialize(argv=argv, new_httpserver=False)
171+
app.log.propagate = True
172+
app.log.handlers = []
173+
# Start app without ioloop
174+
app.start_app()
175+
return app
176+
177+
return _configurable_serverapp
178+
179+
180+
@pytest.fixture(scope="function")
181+
def serverapp(server_config, argv, configurable_serverapp):
182+
app = configurable_serverapp(config=server_config, argv=argv)
183+
yield app
184+
app.remove_server_info_file()
185+
app.remove_browser_open_file()
186+
app.cleanup_kernels()
187+
188+
189+
@pytest.fixture
190+
def app(serverapp):
191+
"""app fixture is needed by pytest_tornasync plugin"""
192+
return serverapp.web_app
193+
194+
195+
@pytest.fixture
196+
def auth_header(serverapp):
197+
return {"Authorization": "token {token}".format(token=serverapp.token)}
198+
199+
200+
@pytest.fixture
201+
def http_port(http_server_port):
202+
return http_server_port[-1]
203+
204+
205+
@pytest.fixture
206+
def base_url():
207+
return "/"
208+
209+
210+
@pytest.fixture
211+
def fetch(http_server_client, auth_header, base_url):
212+
"""fetch fixture that handles auth, base_url, and path"""
213+
def client_fetch(*parts, headers={}, params={}, **kwargs):
214+
# Handle URL strings
215+
path_url = url_escape(url_path_join(base_url, *parts), plus=False)
216+
params_url = urllib.parse.urlencode(params)
217+
url = path_url + "?" + params_url
218+
# Add auth keys to header
219+
headers.update(auth_header)
220+
# Make request.
221+
return http_server_client.fetch(
222+
url, headers=headers, request_timeout=20, **kwargs
223+
)
224+
return client_fetch
225+
226+
227+
@pytest.fixture
228+
def ws_fetch(auth_header, http_port):
229+
"""websocket fetch fixture that handles auth, base_url, and path"""
230+
def client_fetch(*parts, headers={}, params={}, **kwargs):
231+
# Handle URL strings
232+
path = url_escape(url_path_join(*parts), plus=False)
233+
urlparts = urllib.parse.urlparse('ws://localhost:{}'.format(http_port))
234+
urlparts = urlparts._replace(
235+
path=path,
236+
query=urllib.parse.urlencode(params)
237+
)
238+
url = urlparts.geturl()
239+
# Add auth keys to header
240+
headers.update(auth_header)
241+
# Make request.
242+
req = tornado.httpclient.HTTPRequest(
243+
url,
244+
headers=auth_header,
245+
connect_timeout=120
246+
)
247+
return tornado.websocket.websocket_connect(req)
248+
return client_fetch
249+
250+
251+
@pytest.fixture
252+
def kernelspecs(data_dir):
253+
spec_names = ['sample', 'sample 2']
254+
for name in spec_names:
255+
sample_kernel_dir = data_dir.joinpath('kernels', name)
256+
sample_kernel_dir.mkdir(parents=True)
257+
# Create kernel json file
258+
sample_kernel_file = sample_kernel_dir.joinpath('kernel.json')
259+
sample_kernel_file.write_text(json.dumps(sample_kernel_json))
260+
# Create resources text
261+
sample_kernel_resources = sample_kernel_dir.joinpath('resource.txt')
262+
sample_kernel_resources.write_text(some_resource)
263+
264+
265+
@pytest.fixture(params=[True, False])
266+
def contents_manager(request, tmp_path):
267+
return FileContentsManager(root_dir=str(tmp_path), use_atomic_writing=request.param)
268+
269+
270+
@pytest.fixture
271+
def create_notebook(root_dir):
272+
"""Create a notebook in the test's home directory."""
273+
def inner(nbpath):
274+
nbpath = root_dir.joinpath(nbpath)
275+
# Check that the notebook has the correct file extension.
276+
if nbpath.suffix != '.ipynb':
277+
raise Exception("File extension for notebook must be .ipynb")
278+
# If the notebook path has a parent directory, make sure it's created.
279+
parent = nbpath.parent
280+
parent.mkdir(parents=True, exist_ok=True)
281+
# Create a notebook string and write to file.
282+
nb = nbformat.v4.new_notebook()
283+
nbtext = nbformat.writes(nb, version=4)
284+
nbpath.write_text(nbtext)
285+
return inner

jupyter_server/serverapp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
assert tornado.version_info >= MIN_TORNADO
5050
except (ImportError, AttributeError, AssertionError) as e: # pragma: no cover
5151
raise ImportError(
52-
_("The Jupyter Server requires tornado >=%s.%s.%s", *MIN_TORNADO)
52+
_("The Jupyter Server requires tornado >=%s.%s.%s") % MIN_TORNADO
5353
) from e
5454

5555
from tornado import httpserver

0 commit comments

Comments
 (0)