Skip to content

Commit 7a7789c

Browse files
authored
Merge pull request #285 from Zsailer/base_url-static
Prefix all extension URLs with base_url (including static)
2 parents 321588d + fa6c0d1 commit 7a7789c

File tree

6 files changed

+62
-8
lines changed

6 files changed

+62
-8
lines changed

jupyter_server/extension/application.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import sys
22
import re
33
import logging
4+
from urllib.parse import urljoin
45

56
from jinja2 import Environment, FileSystemLoader
67

78
from traitlets import (
89
Unicode,
910
List,
1011
Dict,
11-
Bool,
12-
default,
13-
validate
12+
default
1413
)
1514
from traitlets.config import Config
1615
from tornado.log import LogFormatter
@@ -191,8 +190,10 @@ def _default_log_format(self):
191190

192191
@property
193192
def static_url_prefix(self):
194-
return "/static/{name}/".format(
195-
name=self.name)
193+
static_url = "static/{name}".format(
194+
name=self.name
195+
)
196+
return urljoin(self.serverapp.base_url, static_url)
196197

197198
static_paths = List(Unicode(),
198199
help="""paths to search for serving static files.

jupyter_server/extension/handler.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from urllib.parse import urljoin
12
from jupyter_server.base.handlers import FileFindHandler
23

34

@@ -15,7 +16,7 @@ class ExtensionHandlerMixin:
1516
"""Base class for Jupyter server extension handlers.
1617
1718
Subclasses can serve static files behind a namespaced
18-
endpoint: "/static/<name>/"
19+
endpoint: "<base_url>/static/<name>/"
1920
2021
This allows multiple extensions to serve static files under
2122
their own namespace and avoid intercepting requests for
@@ -49,9 +50,16 @@ def config(self):
4950
def server_config(self):
5051
return self.settings["config"]
5152

53+
@property
54+
def base_url(self):
55+
return self.settings.get('base_url', '/')
56+
5257
@property
5358
def static_url_prefix(self):
54-
return "/static/{name}/".format(name=self.name)
59+
static_url = "static/{name}".format(
60+
name=self.name
61+
)
62+
return urljoin(self.serverapp.base_url, static_url)
5563

5664
@property
5765
def static_path(self):

jupyter_server/extension/serverextension.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _get_config_dir(user=False, sys_prefix=False):
4545
return extdir
4646

4747

48-
def _get_extmanager_for_context(user=False, sys_prefix=False):
48+
def _get_extmanager_for_context(write_dir="jupyter_server_config.d", user=False, sys_prefix=False):
4949
"""Get an extension manager pointing at the current context
5050
5151
Returns the path to the current context and an ExtensionManager object.
@@ -295,6 +295,7 @@ def list_server_extensions(self):
295295
{"user": False, "sys_prefix": True},
296296
{"user": False, "sys_prefix": False}
297297
)
298+
298299
for option in configurations:
299300
config_dir, ext_manager = _get_extmanager_for_context(**option)
300301
self.log.info("Config dir: {}".format(config_dir))

tests/extension/mockextensions/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from traitlets import Unicode, List
23

34
from jupyter_server.base.handlers import JupyterHandler
@@ -10,6 +11,8 @@
1011
ExtensionHandlerJinjaMixin
1112
)
1213

14+
STATIC_PATH = os.path.join(os.path.dirname(__file__), "static")
15+
1316

1417
class MockExtensionHandler(ExtensionHandlerMixin, JupyterHandler):
1518

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

3235
name = 'mockextension'
3336
template_paths = List().tag(config=True)
37+
static_paths = [STATIC_PATH]
3438
mock_trait = Unicode('mock trait', config=True)
3539
loaded = False
3640

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mock static content

tests/extension/test_handler.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,42 @@ async def test_handler_argv(fetch):
7272
)
7373
assert r.code == 200
7474
assert r.body.decode() == 'test mock trait'
75+
76+
77+
@pytest.mark.parametrize(
78+
'server_config',
79+
[
80+
{
81+
"ServerApp": {
82+
"jpserver_extensions": {
83+
"tests.extension.mockextensions": True
84+
},
85+
# Move extension handlers behind a url prefix
86+
"base_url": "test_prefix"
87+
},
88+
"MockExtensionApp": {
89+
# Change a trait in the MockExtensionApp using
90+
# the following config value.
91+
"mock_trait": "test mock trait"
92+
}
93+
}
94+
]
95+
)
96+
async def test_base_url(fetch):
97+
# Test that the extension's handlers were properly prefixed
98+
r = await fetch(
99+
'test_prefix', 'mock',
100+
method='GET'
101+
)
102+
assert r.code == 200
103+
assert r.body.decode() == 'test mock trait'
104+
105+
# Test that the static namespace was prefixed by base_url
106+
r = await fetch(
107+
'test_prefix',
108+
'static', 'mockextension', 'mock.txt',
109+
method='GET'
110+
)
111+
assert r.code == 200
112+
body = r.body.decode()
113+
assert "mock static content" in body

0 commit comments

Comments
 (0)