-
Notifications
You must be signed in to change notification settings - Fork 371
Expose a jupyter_server pytest plugin #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
6a34c63
make a jupyter server pytest plugin
Zsailer 4bcd12d
rollback extension conftest
Zsailer 6cb53bc
add a create_notebook fixture
Zsailer d672020
check that windows 3.8 is applying patch in pytest
Zsailer 4c094e3
print sys info in contest
Zsailer 4fb445f
print sys info in contest
Zsailer 2d25ef0
move ioloop into serverapp
Zsailer 22510d7
use pip to install dependencies
Zsailer 9a454f3
remove unnecessary conftest
Zsailer f486512
put conftest back
Zsailer efd3756
always require patch which already checks sys and version
Zsailer 6cb9efc
temporarily slim down appveyor matrix
Zsailer bcdf63c
put conda installs back
Zsailer d21655c
add matrix back
Zsailer 90f4eed
Update jupyter_server/pytest_plugin.py
Zsailer 8460698
Update jupyter_server/pytest_plugin.py
Zsailer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| import os | ||
| import sys | ||
| import json | ||
| import pytest | ||
| import asyncio | ||
| from binascii import hexlify | ||
|
|
||
| import urllib.parse | ||
| import tornado | ||
| from tornado.escape import url_escape | ||
|
|
||
| from traitlets.config import Config | ||
|
|
||
| import jupyter_core.paths | ||
| from jupyter_server.extension import serverextension | ||
| from jupyter_server.serverapp import ServerApp | ||
| from jupyter_server.utils import url_path_join | ||
|
|
||
| import nbformat | ||
|
|
||
| # This shouldn't be needed anymore, since pytest_tornasync is found in entrypoints | ||
| # Removing to avoid race conditions. | ||
| pytest_plugins = "pytest_tornasync" | ||
|
|
||
| # NOTE: This is a temporary fix for Windows 3.8 | ||
| # We have to override the io_loop fixture with an | ||
| # asyncio patch. This will probably be removed in | ||
| # the future. | ||
| print("\n\n\n{} {} \n\n\n\n".format(sys.platform, sys.version_info)) | ||
Zsailer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @pytest.fixture | ||
| def asyncio_patch(): | ||
| ServerApp()._init_asyncio_patch() | ||
|
|
||
| @pytest.fixture | ||
| def io_loop(asyncio_patch): | ||
| loop = tornado.ioloop.IOLoop() | ||
| loop.make_current() | ||
| yield loop | ||
| loop.clear_current() | ||
| loop.close(all_fds=True) | ||
|
|
||
|
|
||
| def mkdir(tmp_path, *parts): | ||
| path = tmp_path.joinpath(*parts) | ||
| if not path.exists(): | ||
| path.mkdir(parents=True) | ||
| return path | ||
|
|
||
|
|
||
| config = pytest.fixture(lambda: {}) | ||
| home_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "home")) | ||
| data_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "data")) | ||
| config_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "config")) | ||
| runtime_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "runtime")) | ||
| root_dir = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "root_dir")) | ||
| system_jupyter_path = pytest.fixture( | ||
| lambda tmp_path: mkdir(tmp_path, "share", "jupyter") | ||
| ) | ||
| env_jupyter_path = pytest.fixture( | ||
| lambda tmp_path: mkdir(tmp_path, "env", "share", "jupyter") | ||
| ) | ||
| system_config_path = pytest.fixture(lambda tmp_path: mkdir(tmp_path, "etc", "jupyter")) | ||
| env_config_path = pytest.fixture( | ||
| lambda tmp_path: mkdir(tmp_path, "env", "etc", "jupyter") | ||
| ) | ||
| argv = pytest.fixture(lambda: []) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def environ( | ||
| monkeypatch, | ||
| tmp_path, | ||
| home_dir, | ||
| data_dir, | ||
| config_dir, | ||
| runtime_dir, | ||
| root_dir, | ||
| system_jupyter_path, | ||
| system_config_path, | ||
| env_jupyter_path, | ||
| env_config_path, | ||
| ): | ||
| monkeypatch.setenv("HOME", str(home_dir)) | ||
| monkeypatch.setenv("PYTHONPATH", os.pathsep.join(sys.path)) | ||
| monkeypatch.setenv("JUPYTER_NO_CONFIG", "1") | ||
| monkeypatch.setenv("JUPYTER_CONFIG_DIR", str(config_dir)) | ||
| monkeypatch.setenv("JUPYTER_DATA_DIR", str(data_dir)) | ||
| monkeypatch.setenv("JUPYTER_RUNTIME_DIR", str(runtime_dir)) | ||
| monkeypatch.setattr( | ||
| jupyter_core.paths, "SYSTEM_JUPYTER_PATH", [str(system_jupyter_path)] | ||
| ) | ||
| monkeypatch.setattr(jupyter_core.paths, "ENV_JUPYTER_PATH", [str(env_jupyter_path)]) | ||
| monkeypatch.setattr( | ||
| jupyter_core.paths, "SYSTEM_CONFIG_PATH", [str(system_config_path)] | ||
| ) | ||
| monkeypatch.setattr(jupyter_core.paths, "ENV_CONFIG_PATH", [str(env_config_path)]) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def extension_environ(env_config_path, monkeypatch): | ||
| """Monkeypatch a Jupyter Extension's config path into each test's environment variable""" | ||
| monkeypatch.setattr(serverextension, "ENV_CONFIG_PATH", [str(env_config_path)]) | ||
| monkeypatch.setattr(serverextension, "ENV_CONFIG_PATH", [str(env_config_path)]) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def configurable_serverapp( | ||
| environ, http_port, tmp_path, home_dir, data_dir, config_dir, runtime_dir, root_dir, io_loop | ||
| ): | ||
| def serverapp( | ||
| config={}, | ||
| argv=[], | ||
| environ=environ, | ||
| http_port=http_port, | ||
| tmp_path=tmp_path, | ||
| home_dir=home_dir, | ||
| data_dir=data_dir, | ||
| config_dir=config_dir, | ||
| runtime_dir=runtime_dir, | ||
| root_dir=root_dir, | ||
| **kwargs | ||
| ): | ||
| c = Config(config) | ||
| c.NotebookNotary.db_file = ":memory:" | ||
| token = hexlify(os.urandom(4)).decode("ascii") | ||
| url_prefix = "/" | ||
| app = ServerApp.instance( | ||
| port=http_port, | ||
| port_retries=0, | ||
| open_browser=False, | ||
| config_dir=str(config_dir), | ||
| data_dir=str(data_dir), | ||
| runtime_dir=str(runtime_dir), | ||
| root_dir=str(root_dir), | ||
| base_url=url_prefix, | ||
| config=c, | ||
| allow_root=True, | ||
| token=token, | ||
| **kwargs | ||
| ) | ||
| app.init_signal = lambda: None | ||
| app.log.propagate = True | ||
| app.log.handlers = [] | ||
| # Initialize app without httpserver | ||
| app.initialize(argv=argv, new_httpserver=False) | ||
| app.log.propagate = True | ||
| app.log.handlers = [] | ||
| # Start app without ioloop | ||
| app.start_app() | ||
| return app | ||
|
|
||
| yield serverapp | ||
| ServerApp.clear_instance() | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def serverapp(configurable_serverapp, config, argv): | ||
| app = configurable_serverapp(config=config, argv=argv) | ||
| yield app | ||
| app.remove_server_info_file() | ||
| app.remove_browser_open_file() | ||
| app.cleanup_kernels() | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def app(serverapp): | ||
| return serverapp.web_app | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def auth_header(serverapp): | ||
| return {"Authorization": "token {token}".format(token=serverapp.token)} | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def http_port(http_server_port): | ||
| return http_server_port[-1] | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def base_url(http_server_port): | ||
| return "/" | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def fetch(http_server_client, auth_header, base_url): | ||
| """fetch fixture that handles auth, base_url, and path""" | ||
| def client_fetch(*parts, headers={}, params={}, **kwargs): | ||
| # Handle URL strings | ||
| path_url = url_escape(url_path_join(base_url, *parts), plus=False) | ||
| params_url = urllib.parse.urlencode(params) | ||
| url = path_url + "?" + params_url | ||
| # Add auth keys to header | ||
| headers.update(auth_header) | ||
| # Make request. | ||
| return http_server_client.fetch( | ||
| url, headers=headers, request_timeout=20, **kwargs | ||
| ) | ||
| return client_fetch | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def create_notebook(root_dir): | ||
| """Create a notebook in the test's home directory.""" | ||
| def inner(nbpath): | ||
| nbpath = root_dir.joinpath(nbpath) | ||
| # Check that the notebook has the correct file extension. | ||
| if nbpath.suffix != '.ipynb': | ||
| raise Exception("File extension for notebook must be .ipynb") | ||
| # If the notebook path has a parent directory, make sure it's created. | ||
| parent = nbpath.parent | ||
| parent.mkdir(parents=True, exist_ok=True) | ||
| # Create a notebook string and write to file. | ||
| nb = nbformat.v4.new_notebook() | ||
| nbtext = nbformat.writes(nb, version=4) | ||
| nbpath.write_text(nbtext) | ||
| return inner | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.