Skip to content
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

Use ExtensionApp #592

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion etc/jupyter/jupyter_server_config.d/voila.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"ServerApp": {
"jpserver_extensions": {
"voila.server_extension": true
"voila": true
}
}
}
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ def get_data_files():
},
'entry_points': {
'console_scripts': [
'jupyter-voila = voila.app:main',
'voila = voila.app:main'
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

{%- block html_head_js -%}
<script
src="{{resources.base_url}}voila/static/require.min.js"
src="{{resources.base_url}}static/voila/require.min.js"
integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA="
crossorigin="anonymous">
</script>
Expand Down Expand Up @@ -47,12 +47,12 @@
{% block footer %}
{% block footer_js %}
<script>
requirejs.config({ baseUrl: '{{resources.base_url}}voila/', waitSeconds: 30})
requirejs.config({ baseUrl: '{{resources.base_url}}static/voila/', waitSeconds: 30})
requirejs(
[
"static/main",
"main",
{% for ext in resources.nbextensions -%}
"{{resources.base_url}}voila/nbextensions/{{ ext }}.js",
"{{resources.base_url}}static/voila/nbextensions/{{ ext }}.js",
{% endfor %}
]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
{%- block html_head_css -%}
{{ super() }}

<link rel="stylesheet" type="text/css" href="{{resources.base_url}}voila/static/index.css">
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}static/voila/index.css"></link>

{% if resources.theme == 'dark' %}
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}voila/static/theme-dark.css">
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}static/voila/theme-dark.css"></link>
{% else %}
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}voila/static/theme-light.css">
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}static/voila/theme-light.css"></link>
{% endif %}

{% for css in resources.inlining.css %}
Expand Down
2 changes: 1 addition & 1 deletion share/jupyter/voila/templates/default/static/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
****************************************************************************/

// NOTE: this file is not transpiled, async/await is the only modern feature we use here
require(['static/voila'], function(voila) {
require(['voila'], function(voila) {
// requirejs doesn't like to be passed an async function, so create one inside
(async function() {
var kernel = await voila.connectKernel()
Expand Down
14 changes: 7 additions & 7 deletions tests/app/config_paths_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@
@pytest.fixture
def voila_config_file_paths_arg():
path = os.path.join(BASE_DIR, '..', 'configs', 'general')
return '--VoilaTest.config_file_paths=[%r]' % path
return '--Voila.config_file_paths=[%r]' % path


def test_config_app(voila_app):
assert voila_app.voila_configuration.template == 'test_template'
assert voila_app.voila_configuration.enable_nbextensions is True
assert voila_app.template == 'test_template'
assert voila_app.enable_nbextensions is True


def test_config_kernel_manager(voila_app):
assert voila_app.kernel_manager.cull_interval == 10
assert voila_app.serverapp.kernel_manager.cull_interval == 10


def test_config_contents_manager(voila_app):
assert voila_app.contents_manager.use_atomic_writing is False
assert voila_app.serverapp.contents_manager.use_atomic_writing is False


async def test_template(http_server_client, base_url):
response = await http_server_client.fetch(base_url)
async def test_template(fetch):
response = await fetch('voila', method='GET')
assert response.code == 200
assert 'test_template.css' in response.body.decode('utf-8')
assert 'Hi Voila' in response.body.decode('utf-8')
48 changes: 24 additions & 24 deletions tests/app/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

import pytest

import voila.app
from voila.app import Voila

BASE_DIR = os.path.dirname(__file__)


class VoilaTest(voila.app.Voila):
def listen(self):
pass # the ioloop is taken care of by the pytest-tornado framework
@pytest.fixture
def voila_config(notebook_directory):
return dict(root_dir=notebook_directory)


@pytest.fixture
def voila_config():
return lambda app: None
def server_config(notebook_directory):
return dict(root_dir=notebook_directory, default_url="voila", log_level="DEBUG")


@pytest.fixture
Expand All @@ -25,26 +25,26 @@ def voila_args_extra():
@pytest.fixture
def voila_config_file_paths_arg():
# we don't want the tests to use any configuration on the system
return '--VoilaTest.config_file_paths=[]'
return '--Voila.config_file_paths=[]'


@pytest.fixture
def voila_args(voila_notebook, voila_args_extra, voila_config_file_paths_arg):
debug_args = ['--VoilaTest.log_level=DEBUG'] if os.environ.get('VOILA_TEST_DEBUG', False) else []
return [voila_notebook, voila_config_file_paths_arg] + voila_args_extra + debug_args


@pytest.fixture
def voila_app(voila_args, voila_config):
voila_app = VoilaTest.instance()
voila_app.initialize(voila_args + ['--no-browser'])
voila_config(voila_app)
voila_app.start()
debug_args = ['--Voila.log_level=DEBUG'] if os.environ.get('VOILA_TEST_DEBUG', False) else []
return [voila_notebook, voila_config_file_paths_arg] + voila_args_extra + debug_args + ['--no-browser']


@pytest.fixture(autouse=True)
def voila_app(server_config, voila_config, voila_args):
# Get an instance of Voila
voila_app = Voila(**voila_config)
# Get an instance of the underlying server. This is
# configured automatically when launched by command line.
serverapp = voila_app.initialize_server(voila_args, **server_config)
# silence the start method for pytests.
serverapp.start = lambda self: None
voila_app.link_to_serverapp(serverapp)
voila_app.initialize()
yield voila_app
voila_app.stop()
voila_app.clear_instance()


@pytest.fixture
def app(voila_app):
return voila_app.app
serverapp.cleanup_kernels()
serverapp.clear_instance()
11 changes: 3 additions & 8 deletions tests/app/cwd_subdir_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@
import pytest


@pytest.fixture
def cwd_subdir_notebook_url(base_url):
return base_url + "voila/render/subdir/cwd_subdir.ipynb"


@pytest.fixture
def voila_args(notebook_directory, voila_args_extra):
return ['--VoilaTest.root_dir=%r' % notebook_directory, '--VoilaTest.log_level=DEBUG'] + voila_args_extra
return ['--ServerApp.root_dir=%r' % notebook_directory, '--Voila.log_level=DEBUG'] + voila_args_extra + ['--no-browser']


async def test_hello_world(http_server_client, cwd_subdir_notebook_url):
response = await http_server_client.fetch(cwd_subdir_notebook_url)
async def test_hello_world(fetch):
response = await fetch('voila', 'render', 'subdir', 'cwd_subdir.ipynb', method='GET')
html_text = response.body.decode('utf-8')
assert 'check for the cwd' in html_text
4 changes: 2 additions & 2 deletions tests/app/cwd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def voila_notebook(notebook_directory):
return os.path.join(notebook_directory, 'cwd.ipynb')


async def test_template_cwd(http_server_client, base_url, notebook_directory):
response = await http_server_client.fetch(base_url)
async def test_template_cwd(voila_app, fetch):
response = await fetch('voila', method='GET')
html_text = response.body.decode('utf-8')
assert 'check for the cwd' in html_text
13 changes: 4 additions & 9 deletions tests/app/execute_cpp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@
TEST_XEUS_CLING = os.environ.get('VOILA_TEST_XEUS_CLING', '') == '1'


@pytest.fixture
def cpp_file_url(base_url):
return base_url + "voila/render/print.xcpp"


@pytest.fixture
def voila_args_extra():
return ['--VoilaConfiguration.extension_language_mapping={".xcpp": "C++11"}']
return ['--Voila.extension_language_mapping={".xcpp": "C++11"}']


@pytest.fixture
def voila_args(notebook_directory, voila_args_extra):
return ['--VoilaTest.root_dir=%r' % notebook_directory] + voila_args_extra
return ['--Voila.root_dir=%r' % notebook_directory] + voila_args_extra + ['--no-browser']


@pytest.mark.skipif(not TEST_XEUS_CLING, reason='opt in to avoid having to install xeus-cling')
async def test_non_existing_kernel(http_server_client, cpp_file_url):
response = await http_server_client.fetch(cpp_file_url)
async def test_cpp11_kernel(fetch):
response = await fetch('voila', 'render', 'print.xcpp', method='GET')
assert response.code == 200
assert 'Hello voila, from c++' in response.body.decode('utf-8')
59 changes: 42 additions & 17 deletions tests/app/execute_test.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
# test basics of voila running a notebook
import tornado.web

import re
import json
import asyncio

try:
from unittest import mock
except ImportError:
import mock


async def test_hello_world(http_server_client, base_url):
response = await http_server_client.fetch(base_url)
async def test_hello_world(voila_app, fetch):
response = await fetch('voila', method='GET')
assert response.code == 200
html_text = response.body.decode('utf-8')
assert 'Hi Voila' in html_text
assert 'print(' not in html_text, 'by default the source code should be stripped'
assert 'test_template.css' not in html_text, "test_template should not be the default"


async def test_no_execute_allowed(voila_app, app, http_server_client, base_url, http_server_port):
assert voila_app.app is app
response = (await http_server_client.fetch(base_url)).body.decode('utf-8')
async def test_no_execute_allowed(voila_app, fetch, http_port, ws_fetch):
response = (await fetch('voila', method='GET')).body.decode('utf-8')
pattern = r"""kernelId": ["']([0-9a-zA-Z-]+)["']"""
groups = re.findall(pattern, response)
kernel_id = groups[0]
print(kernel_id, base_url)
session_id = '445edd75-c6f5-45d2-8b58-5fe8f84a7123'
url = f'ws://localhost:{http_server_port[1]}{base_url}api/kernels/{kernel_id}/channels?session_id={session_id}'
conn = await tornado.websocket.websocket_connect(url)
kid = groups[0]

# session_id = '445edd75-c6f5-45d2-8b58-5fe8f84a7123'
# url = f'ws://localhost:{http_port}/api/kernels/{kernel_id}/channels?session_id={session_id}'
# conn = await tornado.websocket.websocket_connect(url)

# create kernel
# r = await fetch(
# 'api', 'kernels',
# method='POST',
# body=json.dumps({
# 'name': 'python'
# })
# )
# kid = json.loads(r.body.decode())['id']

# Get kernel info
r = await fetch(
'api', 'kernels', kid,
method='GET'
)
model = json.loads(r.body.decode())
assert model['connections'] == 0

# Open a websocket connection.
ws = await ws_fetch('api', 'kernels', kid, 'channels')

# Test that it was opened.
r = await fetch(
'api', 'kernels', kid,
method='GET'
)
model = json.loads(r.body.decode())
assert model['connections'] == 1

msg = {
"header": {
Expand All @@ -52,9 +77,9 @@ async def test_no_execute_allowed(voila_app, app, http_server_client, base_url,
"parent_header": {},
"channel": "shell",
}
with mock.patch.object(voila_app.log, 'warning') as mock_warning:
await conn.write_message(json.dumps(msg))
with mock.patch.object(voila_app.serverapp.log, 'warning') as mock_warning:
await ws.write_message(json.dumps(msg))
# make sure the warning method is called
while not mock_warning.called:
await asyncio.sleep(0.1)
# while not mock_warning.called:
# await asyncio.sleep(0.1)
mock_warning.assert_called_with('Received message of type "execute_request", which is not allowed. Ignoring.')
4 changes: 2 additions & 2 deletions tests/app/image_inlining_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def voila_notebook(notebook_directory):
return os.path.join(notebook_directory, 'images.ipynb')


async def test_image_inlining(http_server_client, base_url, notebook_directory):
response = await http_server_client.fetch(base_url)
async def test_image_inlining(fetch, notebook_directory):
response = await fetch('voila', method='GET')
html_text = response.body.decode('utf-8')

assert 'data:image/svg+xml;base64,' in html_text
Expand Down
4 changes: 2 additions & 2 deletions tests/app/many_iopub_messages_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def _close():
return client


async def test_template_cwd(http_server_client, base_url, notebook_directory):
response = await http_server_client.fetch(base_url)
async def test_template_cwd(fetch, notebook_directory):
response = await fetch('voila', method='GET')
html_text = response.body.decode('utf-8')
assert 'you should see me' in html_text
12 changes: 5 additions & 7 deletions tests/app/nbextensions_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,20 @@

@pytest.fixture
def voila_config():
def config(app):
pass
os.environ['JUPYTER_CONFIG_DIR'] = os.path.join(BASE_DIR, '../configs/general')
yield config
yield {}
del os.environ['JUPYTER_CONFIG_DIR']


@pytest.fixture
def voila_config_file_paths_arg():
# we don't want the tests to use any configuration on the system
path = os.path.abspath(os.path.join(BASE_DIR, '../configs/general'))
return '--VoilaTest.config_file_paths=[%r]' % path
path = os.path.join(BASE_DIR, '..', 'configs', 'general')
return '--Voila.config_file_paths=[%r]' % path


async def test_lists_extension(http_server_client, base_url):
response = await http_server_client.fetch(base_url)
async def test_lists_extension(fetch):
response = await fetch('voila', method='GET')
assert response.code == 200
html_text = response.body.decode('utf-8')
assert 'Hi Voila' in html_text
Expand Down
6 changes: 3 additions & 3 deletions tests/app/no_kernelspec_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ def non_existing_kernel_notebook(base_url):

@pytest.fixture
def voila_args(notebook_directory, voila_args_extra):
return ['--VoilaTest.root_dir=%r' % notebook_directory] + voila_args_extra
return ['--Voila.root_dir=%r' % notebook_directory] + voila_args_extra + ['--no-browser']


async def test_non_existing_kernel(http_server_client, non_existing_kernel_notebook):
response = await http_server_client.fetch(non_existing_kernel_notebook)
async def test_no_kernelspec(fetch):
response = await fetch('voila', 'render', 'no_kernelspec.ipynb', method='GET')
assert response.code == 200
assert 'Executing without a kernelspec' in response.body.decode('utf-8')
6 changes: 3 additions & 3 deletions tests/app/no_strip_sources_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

@pytest.fixture
def voila_args_extra():
return ['--VoilaConfiguration.strip_sources=False']
return ['--Voila.strip_sources=False']


async def test_no_strip_sources(http_server_client, base_url):
response = await http_server_client.fetch(base_url)
async def test_no_strip_sources(fetch):
response = await fetch('voila', method='GET')
assert response.code == 200
html_text = response.body.decode('utf-8')
assert 'Hi Voila' in html_text
Expand Down
Loading