Skip to content

Commit 505fafc

Browse files
committed
Merge branch 'master' into example
2 parents cb046b5 + 3a98032 commit 505fafc

File tree

5 files changed

+66
-21
lines changed

5 files changed

+66
-21
lines changed

docs/source/extending/handlers.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ following:
115115
Called when the extension is loaded.
116116
117117
Args:
118-
nb_server_app (NotebookWebApplication): handle to the Notebook webserver instance.
118+
nb_server_app: handle to the Notebook webserver instance.
119119
"""
120120
web_app = nb_server_app.web_app
121121
host_pattern = '.*$'

jupyter_server/extension/application.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from traitlets import (
55
Unicode,
66
List,
7-
Dict,
7+
Dict,
8+
Bool,
89
default,
910
validate
1011
)
@@ -80,6 +81,12 @@ def _preparse_for_stopping_flags(Application, argv):
8081
app.exit(0)
8182

8283

84+
flags['no-browser']=(
85+
{'ExtensionApp' : {'open_browser' : True}},
86+
_("Prevent the opening of the default url in the browser.")
87+
)
88+
89+
8390
class ExtensionApp(JupyterApp):
8491
"""Base class for configurable Jupyter Server Extension Applications.
8592
@@ -95,21 +102,20 @@ class method. This method can be set as a entry_point in
95102

96103
# Name of the extension
97104
extension_name = Unicode(
98-
"",
99105
help="Name of extension."
100106
)
101107

102-
@default("extension_name")
103-
def _default_extension_name(self):
108+
def _extension_name_default(self):
104109
try:
105110
return self.name
106111
except AttributeError:
107112
raise ValueError("The extension must be given a `name`.")
108113

109114
INVALID_EXTENSION_NAME_CHARS = [' ', '.', '+', '/']
110115

111-
def _validate_extension_name(self):
112-
value = self.extension_name
116+
@validate('extension_name')
117+
def _validate_extension_name(self, value):
118+
#value = self.extension_name
113119
if isinstance(value, str):
114120
# Validate that extension_name doesn't contain any invalid characters.
115121
for c in ExtensionApp.INVALID_EXTENSION_NAME_CHARS:
@@ -157,10 +163,31 @@ def static_url_prefix(self):
157163
help=_("""Handlers appended to the server.""")
158164
).tag(config=True)
159165

166+
def _config_dir_default(self):
167+
"""Point the config directory at the server's config_dir by default."""
168+
try:
169+
return self.serverapp.config_dir
170+
except AttributeError:
171+
raise AttributeError(
172+
"The ExtensionApp has not ServerApp "
173+
"initialized. Try `.initialize_server()`."
174+
)
175+
176+
def _config_file_name_default(self):
177+
"""The default config file name."""
178+
if not self.extension_name:
179+
return ''
180+
return 'jupyter_{}_config'.format(self.extension_name.replace('-','_'))
181+
160182
default_url = Unicode('/', config=True,
161183
help=_("The default URL to redirect to from `/`")
162184
)
163185

186+
open_browser = Bool(
187+
True,
188+
help=_("Should the extension open a browser window?")
189+
)
190+
164191
custom_display_url = Unicode(u'', config=True,
165192
help=_("""Override URL shown to users.
166193
@@ -298,10 +325,11 @@ def initialize(self, serverapp, argv=[]):
298325
- Passes settings to webapp
299326
- Appends handlers to webapp.
300327
"""
301-
self._validate_extension_name()
328+
# Initialize ServerApp.
329+
self.serverapp = serverapp
330+
302331
# Initialize the extension application
303332
super(ExtensionApp, self).initialize(argv=argv)
304-
self.serverapp = serverapp
305333

306334
# Initialize config, settings, templates, and handlers.
307335
self._prepare_config()
@@ -319,7 +347,7 @@ def start(self):
319347
# Override the server's display url to show extension's display URL.
320348
self.serverapp.custom_display_url = self.custom_display_url
321349
# Override the server's default option and open a broswer window.
322-
self.serverapp.open_browser = True
350+
self.serverapp.open_browser = self.open_browser
323351
# Hijack the server's browser-open file to land on
324352
# the extensions home page.
325353
self.serverapp._write_browser_open_file = self._write_browser_open_file

jupyter_server/serverapp.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,16 @@ def _update_pylab(self, change):
11241124
)
11251125
self.exit(1)
11261126

1127+
notebook_dir = Unicode(
1128+
config=True,
1129+
help=_("DEPRECATED, use root_dir.")
1130+
)
1131+
1132+
@observe('notebook_dir')
1133+
def _update_notebook_dir(self, change):
1134+
self.log.warning(_("notebook_dir is deprecated, use root_dir"))
1135+
self.root_dir = change['new']
1136+
11271137
root_dir = Unicode(config=True,
11281138
help=_("The directory to use for notebooks and kernels.")
11291139
)
@@ -1151,14 +1161,6 @@ def _root_dir_validate(self, proposal):
11511161
raise TraitError(trans.gettext("No such notebook dir: '%r'") % value)
11521162
return value
11531163

1154-
@observe('root_dir')
1155-
def _update_root_dir(self, change):
1156-
"""Do a bit of validation of the notebook dir."""
1157-
# setting App.root_dir implies setting notebook and kernel dirs as well
1158-
new = change['new']
1159-
self.config.FileContentsManager.root_dir = new
1160-
self.config.MappingKernelManager.root_dir = new
1161-
11621164
@observe('server_extensions')
11631165
def _update_server_extensions(self, change):
11641166
self.log.warning(_("server_extensions is deprecated, use jpserver_extensions"))
@@ -1209,6 +1211,7 @@ def _update_server_extensions(self, change):
12091211
"""))
12101212

12111213
def parse_command_line(self, argv=None):
1214+
12121215
super(ServerApp, self).parse_command_line(argv)
12131216

12141217
if self.extra_args:
@@ -1504,7 +1507,7 @@ def init_server_extensions(self):
15041507
# Add debug log for loaded extensions.
15051508
self.log.debug("%s is enabled and loaded." % modulename)
15061509
else:
1507-
self.log.warning("%s is enabled but no `load_jupyter_server_extension` function was found")
1510+
self.log.warning("%s is enabled but no `load_jupyter_server_extension` function was found" % modulename)
15081511
except Exception:
15091512
if self.reraise_server_extension_failures:
15101513
raise

tests/extension/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ def initialize_handlers(self):
2020
self.handlers.append(('/mock', MockExtensionHandler))
2121

2222

23+
@pytest.fixture
24+
def config_file(config_dir):
25+
f = config_dir.joinpath('jupyter_mock_config.py')
26+
f.write_text("c.MockExtension.mock_trait ='config from file'")
27+
return f
28+
29+
2330
@pytest.fixture
2431
def extended_serverapp(serverapp):
2532
m = MockExtension()

tests/extension/test_app.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,18 @@ def test_instance_creation_with_instance_args(trait_name, trait_value):
4949
def test_instance_creation_with_argv(serverapp, trait_name, trait_value):
5050
kwarg = {}
5151
kwarg.setdefault(trait_name, trait_value)
52-
5352
argv = [
5453
'--MockExtension.{name}={value}'.format(name=trait_name, value=trait_value)
5554
]
56-
5755
mock_extension = MockExtension()
5856
mock_extension.initialize(serverapp, argv=argv)
5957
assert getattr(mock_extension, trait_name) == trait_value
58+
59+
60+
def test_extensionapp_load_config_file(config_file, serverapp, extended_serverapp):
61+
# Assert default config_file_paths is the same in the app and extension.
62+
assert extended_serverapp.config_file_paths == serverapp.config_file_paths
63+
assert extended_serverapp.config_file_name == 'jupyter_mock_config'
64+
assert extended_serverapp.config_dir == serverapp.config_dir
65+
# Assert that the trait is updated by config file
66+
assert extended_serverapp.mock_trait == 'config from file'

0 commit comments

Comments
 (0)