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

Prefix all extension URLs with base_url (including static) #285

Merged
merged 3 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
12 changes: 7 additions & 5 deletions jupyter_server/extension/application.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import sys
import re
import logging
from urllib.parse import urljoin

from jinja2 import Environment, FileSystemLoader

from traitlets import (
Unicode,
List,
Dict,
Bool,
default,
validate
default
)
from traitlets.config import Config
from tornado.log import LogFormatter
Expand Down Expand Up @@ -191,8 +190,10 @@ def _default_log_format(self):

@property
def static_url_prefix(self):
return "/static/{name}/".format(
name=self.name)
static_url = "static/{name}".format(
name=self.name
)
return urljoin(self.serverapp.base_url, static_url)

static_paths = List(Unicode(),
help="""paths to search for serving static files.
Expand Down Expand Up @@ -288,6 +289,7 @@ def _prepare_handlers(self):
# Add static endpoint for this extension, if static paths are given.
if len(self.static_paths) > 0:
# Append the extension's static directory to server handlers.
print(self.static_url_prefix)
Zsailer marked this conversation as resolved.
Show resolved Hide resolved
static_url = url_path_join(self.static_url_prefix, "(.*)")

# Construct handler.
Expand Down
12 changes: 10 additions & 2 deletions jupyter_server/extension/handler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from urllib.parse import urljoin
from jupyter_server.base.handlers import FileFindHandler


Expand All @@ -15,7 +16,7 @@ class ExtensionHandlerMixin:
"""Base class for Jupyter server extension handlers.

Subclasses can serve static files behind a namespaced
endpoint: "/static/<name>/"
endpoint: "<base_url>/static/<name>/"

This allows multiple extensions to serve static files under
their own namespace and avoid intercepting requests for
Expand Down Expand Up @@ -49,9 +50,16 @@ def config(self):
def server_config(self):
return self.settings["config"]

@property
def base_url(self):
return self.settings.get('base_url', '/')

@property
def static_url_prefix(self):
return "/static/{name}/".format(name=self.name)
static_url = "static/{name}".format(
name=self.name
)
return urljoin(self.serverapp.base_url, static_url)

@property
def static_path(self):
Expand Down
4 changes: 3 additions & 1 deletion jupyter_server/extension/serverextension.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _get_config_dir(user=False, sys_prefix=False):
return extdir


def _get_extmanager_for_context(user=False, sys_prefix=False):
def _get_extmanager_for_context(write_dir="jupyter_server_config.d", user=False, sys_prefix=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for commenting on an old, merged PR, but what is this argument for? It is unused and undocumented in docstring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, @vidartf. I've updated this function to use the argument and added a docstring in #297.

"""Get an extension manager pointing at the current context

Returns the path to the current context and an ExtensionManager object.
Expand Down Expand Up @@ -295,6 +295,8 @@ def list_server_extensions(self):
{"user": False, "sys_prefix": True},
{"user": False, "sys_prefix": False}
)
self.log.info
Zsailer marked this conversation as resolved.
Show resolved Hide resolved

for option in configurations:
config_dir, ext_manager = _get_extmanager_for_context(**option)
self.log.info("Config dir: {}".format(config_dir))
Expand Down
4 changes: 4 additions & 0 deletions tests/extension/mockextensions/app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from traitlets import Unicode, List

from jupyter_server.base.handlers import JupyterHandler
Expand All @@ -10,6 +11,8 @@
ExtensionHandlerJinjaMixin
)

STATIC_PATH = os.path.join(os.path.dirname(__file__), "static")


class MockExtensionHandler(ExtensionHandlerMixin, JupyterHandler):

Expand All @@ -31,6 +34,7 @@ class MockExtensionApp(ExtensionAppJinjaMixin, ExtensionApp):

name = 'mockextension'
template_paths = List().tag(config=True)
static_paths = [STATIC_PATH]
mock_trait = Unicode('mock trait', config=True)
loaded = False

Expand Down
1 change: 1 addition & 0 deletions tests/extension/mockextensions/static/mock.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mock static content
39 changes: 39 additions & 0 deletions tests/extension/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,42 @@ async def test_handler_argv(fetch):
)
assert r.code == 200
assert r.body.decode() == 'test mock trait'


@pytest.mark.parametrize(
'server_config',
[
{
"ServerApp": {
"jpserver_extensions": {
"tests.extension.mockextensions": True
},
# Move extension handlers behind a url prefix
"base_url": "test_prefix"
},
"MockExtensionApp": {
# Change a trait in the MockExtensionApp using
# the following config value.
"mock_trait": "test mock trait"
}
}
]
)
async def test_base_url(fetch):
# Test that the extension's handlers were properly prefixed
r = await fetch(
'test_prefix', 'mock',
method='GET'
)
assert r.code == 200
assert r.body.decode() == 'test mock trait'

# Test that the static namespace was prefixed by base_url
r = await fetch(
'test_prefix',
'static', 'mockextension', 'mock.txt',
method='GET'
)
assert r.code == 200
body = r.body.decode()
assert "mock static content" in body