Skip to content

Commit 8dde7a6

Browse files
Zsailerecharles
andauthored
Enable ServerApp to discover ExtensionApps (and their config). (#180)
* enable extensionapp config to be discoverable from serveapp * add some comments for future devs * allow None in non-ExtensionApp extensions * adjust tests to capture changes * minor bug fixes * renamed config pytest fixture * standardize extension loading mechanism * pass serverapp and extesnionapp to extensionapp handlers * use static url prefix for static paths * iniitalize all enabled extension, then load later * split extension initialization and loading * Upgrade examples to align on discovery branch * Polish examples * Launch example via python module * Avoid to run initialisation methods twice * Add main for simple_ext2 and simple_ext11 * minor changes to extension toggler * adding some comments throughout the code * move all CLI handling to the ServerApp * remove old traits from extensionapp * update tests * update tests with changes to extensionapp * fix examples entrypoint * add test dependency: pytest-lazy-fixture * unpin pytest * import lazyfixture directly due to changes in pytest * drop pytest-lazy-fixture * cleaner error handling in init_server_extension * minor clean up * minor fixes after review * add underscore as prefix to extension function * remove load_jupyter_server_extension from examples * minor typo in example comment Co-authored-by: Eric Charles <[email protected]>
1 parent 65fa26e commit 8dde7a6

36 files changed

+840
-518
lines changed

examples/simple/README.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ open http://localhost:8888/simple_ext1/redirect
6767
open http://localhost:8888/static/simple_ext1/favicon.ico
6868
```
6969

70+
You can also start the server extension with python modules.
71+
72+
```bash
73+
python -m simple_ext1
74+
```
75+
7076
## Extension 1 and Extension 2
7177

7278
The following command starts both the `simple_ext1` and `simple_ext2` extensions.
@@ -90,9 +96,12 @@ open http://localhost:8888/simple_ext2/params/test?var1=foo
9096
Optionally, you can copy `simple_ext1.json` and `simple_ext2.json` configuration to your env `etc` folder and start only Extension 1, which will also start Extension 2.
9197

9298
```bash
93-
pip uninstall -y jupyter_simple_ext && \
99+
pip uninstall -y jupyter_server_example && \
94100
python setup.py install && \
95101
cp -r ./etc $(dirname $(which jupyter))/..
102+
```
103+
104+
```bash
96105
# Start the jupyter server extension simple_ext1, it will also load simple_ext2 because of load_other_extensions = True..
97106
# When you invoke with the entrypoint, the default url will be opened in your browser.
98107
jupyter simple-ext1
@@ -102,18 +111,20 @@ jupyter simple-ext1
102111

103112
Stop any running server (with `CTRL+C`) and start with additional configuration on the command line.
104113

105-
The provided settings via CLI will override the configuration that reside in the files (`jupyter_simple_ext1_config.py`...)
114+
The provided settings via CLI will override the configuration that reside in the files (`jupyter_server_example1_config.py`...)
106115

107116
```bash
108117
jupyter simple-ext1 --SimpleApp1.configA="ConfigA from command line"
109118
```
110119

111-
Check the log, it should return on startup something like the following base on the trait you have defined in the CLI and in the `jupyter_simple_ext1_config.py`.
120+
Check the log, it should return on startup print the Config object.
121+
122+
The content of the Config is based on the trait you have defined via the `CLI` and in the `jupyter_server_example1_config.py`.
112123

113124
```
114125
[SimpleApp1] Config {'SimpleApp1': {'configA': 'ConfigA from file', 'configB': 'ConfigB from file', 'configC': 'ConfigC from file'}}
115126
[SimpleApp1] Config {'SimpleApp1': {'configA': 'ConfigA from file', 'configB': 'ConfigB from file', 'configC': 'ConfigC from file'}}
116-
[SimpleApp2] WARNING | Config option `configD` not recognized by `SimpleApp2`. Did you mean `config_file`?
127+
[SimpleApp2] WARNING | Config option `configD` not recognized by `SimpleApp2`. Did you mean one of: `configA, configB, configC`?
117128
[SimpleApp2] Config {'SimpleApp2': {'configD': 'ConfigD from file'}}
118129
[SimpleApp1] Config {'SimpleApp1': {'configA': 'ConfigA from command line', 'configB': 'ConfigB from file', 'configC': 'ConfigC from file'}}
119130
```
@@ -133,27 +144,31 @@ Try with the above links to check that only Extension 2 is responding (Extension
133144

134145
`Extension 11` extends `Extension 1` and brings a few more configs.
135146

136-
Run `jupyter simple-ext11 --generate-config && vi ~/.jupyter/jupyter_config.py`.
137-
138-
> TODO `--generate-config` returns an exception `"The ExtensionApp has not ServerApp "`
147+
```bash
148+
# TODO `--generate-config` returns an exception `"The ExtensionApp has not ServerApp "`
149+
jupyter simple-ext11 --generate-config && vi ~/.jupyter/jupyter_config.py`.
150+
```
139151

140152
The generated configuration should contains the following.
141153

142154
```bash
143-
TBD
155+
# TODO
144156
```
145157

146158
The `hello`, `ignore_js` and `simple11_dir` are traits defined on the SimpleApp11 class.
147159

148160
It also implements additional flags and aliases for these traits.
149161

150-
+ The `--hello` flag will log on startup `Hello Simple11 - You have provided the --hello flag or defined a c.SimpleApp1.hello == True`.
151-
+ The `--simple11-dir` alias will set `SimpleExt11.simple11_dir` settings.
162+
- The `--hello` flag will log on startup `Hello Simple11 - You have provided the --hello flag or defined a c.SimpleApp1.hello == True`
163+
- The `ignore_js` flag
164+
- The `--simple11-dir` alias will set `SimpleExt11.simple11_dir` settings
152165

153166
Stop any running server and then start the simple-ext11.
154167

155168
```bash
156169
jupyter simple-ext11 --hello --simple11-dir any_folder
170+
# You can also launch with a module
171+
python -m simple_ext11 --hello
157172
# TODO FIX the following command, simple11 does not work launching with jpserver_extensions parameter.
158173
jupyter server --ServerApp.jpserver_extensions="{'simple_ext11': True}" --hello --simple11-dir any_folder
159174
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
c.SimpleApp1.configA = 'ConfigA from file'
22
c.SimpleApp1.configB = 'ConfigB from file'
33
c.SimpleApp1.configC = 'ConfigC from file'
4+
c.SimpleApp1.configD = 'ConfigD from file'

examples/simple/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "jupyter-simple-ext",
2+
"name": "jupyter-server-example",
33
"version": "0.0.1",
44
"private": true,
55
"scripts": {

examples/simple/setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ def add_data_files(path):
2323
return data_files
2424

2525
setuptools.setup(
26-
name = 'jupyter_simple_ext',
26+
name = 'jupyter_server_example',
2727
version = VERSION,
28-
description = 'Jupyter Simple Extension',
28+
description = 'Jupyter Server Example',
2929
long_description = open('README.md').read(),
3030
packages = find_packages(),
3131
python_requires = '>=3.5',
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from .application import SimpleApp1
22

3-
def _jupyter_server_extension_paths():
4-
return [
5-
{'module': 'simple_ext1'}
6-
]
73

8-
load_jupyter_server_extension = SimpleApp1.load_jupyter_server_extension
4+
def _jupyter_server_extension_paths():
5+
return [{
6+
'module': 'simple_ext1.application',
7+
'app': SimpleApp1
8+
}]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .application import main
2+
3+
if __name__ == "__main__":
4+
main()

examples/simple/simple_ext1/application.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import os, jinja2
22
from traitlets import Unicode
33
from jupyter_server.extension.application import ExtensionApp, ExtensionAppJinjaMixin
4-
from .handlers import (DefaultHandler, RedirectHandler,
4+
from .handlers import (DefaultHandler, RedirectHandler,
55
ParameterHandler, TemplateHandler, TypescriptHandler, ErrorHandler)
66

77
DEFAULT_STATIC_FILES_PATH = os.path.join(os.path.dirname(__file__), "static")
88
DEFAULT_TEMPLATE_FILES_PATH = os.path.join(os.path.dirname(__file__), "templates")
99

1010
class SimpleApp1(ExtensionAppJinjaMixin, ExtensionApp):
11-
11+
1212
# The name of the extension.
1313
extension_name = "simple_ext1"
1414

15-
# Te url that your extension will serve its homepage.
16-
default_url = '/simple_ext1/default'
15+
# The url that your extension will serve its homepage.
16+
extension_url = '/simple_ext1/default'
1717

1818
# Should your extension expose other server extensions when launched directly?
1919
load_other_extensions = True
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from .application import SimpleApp1
1+
from .application import SimpleApp11
2+
23

34
def _jupyter_server_extension_paths():
45
return [
5-
{'module': 'simple_ext1'}
6-
]
7-
8-
load_jupyter_server_extension = SimpleApp1.load_jupyter_server_extension
6+
{
7+
'module': 'simple_ext11.application',
8+
'app': SimpleApp11
9+
}
10+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .application import main
2+
3+
if __name__ == "__main__":
4+
main()

examples/simple/simple_ext11/application.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ class SimpleApp11(SimpleApp1):
1313
aliases.update({
1414
'simple11-dir': 'SimpleApp11.simple11_dir',
1515
})
16-
16+
1717
# The name of the extension.
1818
extension_name = "simple_ext11"
1919

2020
# Te url that your extension will serve its homepage.
21-
default_url = '/simple_ext11/default'
21+
extension_url = '/simple_ext11/default'
2222

2323
# Local path to static files directory.
2424
static_paths = [
@@ -37,12 +37,12 @@ class SimpleApp11(SimpleApp1):
3737

3838
hello = Bool(False,
3939
config=True,
40-
help='Say hello',
40+
help='Say hello',
4141
)
4242

4343
ignore_js = Bool(False,
4444
config=True,
45-
help='Ignore Javascript',
45+
help='Ignore Javascript',
4646
)
4747

4848
@observe('ignore_js')
@@ -56,8 +56,8 @@ def simple11_dir_formatted(self):
5656

5757
def initialize_settings(self):
5858
self.log.info('hello: {}'.format(self.hello))
59-
if self.config['hello'] == True:
60-
self.log.info("Hello Simple11 - You have provided the --hello flag or defined 'c.SimpleApp1.hello == True' in jupyter_server_config.py")
59+
if self.hello == True:
60+
self.log.info("Hello Simple11: You have launched with --hello flag or defined 'c.SimpleApp1.hello == True' in your config file")
6161
self.log.info('ignore_js: {}'.format(self.ignore_js))
6262
super().initialize_settings()
6363

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from .application import SimpleApp2
22

3+
34
def _jupyter_server_extension_paths():
45
return [
5-
{'module': 'simple_ext2'},
6-
]
7-
8-
load_jupyter_server_extension = SimpleApp2.load_jupyter_server_extension
6+
{
7+
'module': 'simple_ext2.application',
8+
'app': SimpleApp2
9+
},
10+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .application import main
2+
3+
if __name__ == "__main__":
4+
main()

examples/simple/simple_ext2/application.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
DEFAULT_TEMPLATE_FILES_PATH = os.path.join(os.path.dirname(__file__), "templates")
88

99
class SimpleApp2(ExtensionAppJinjaMixin, ExtensionApp):
10-
10+
1111
# The name of the extension.
1212
extension_name = "simple_ext2"
1313

1414
# Te url that your extension will serve its homepage.
15-
default_url = '/simple_ext2'
15+
extension_url = '/simple_ext2'
1616

1717
# Should your extension expose other server extensions when launched directly?
18-
load_other_extensions = False
18+
load_other_extensions = True
1919

2020
# Local path to static files directory.
2121
static_paths = [

jupyter_server/base/handlers.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def cookie_name(self):
154154
self.request.host
155155
))
156156
return self.settings.get('cookie_name', default_cookie_name)
157-
157+
158158
@property
159159
def logged_in(self):
160160
"""Is a user currently logged in?"""
@@ -203,23 +203,23 @@ def log(self):
203203
def jinja_template_vars(self):
204204
"""User-supplied values to supply to jinja templates."""
205205
return self.settings.get('jinja_template_vars', {})
206-
206+
207207
#---------------------------------------------------------------
208208
# URLs
209209
#---------------------------------------------------------------
210-
210+
211211
@property
212212
def version_hash(self):
213213
"""The version hash to use for cache hints for static files"""
214214
return self.settings.get('version_hash', '')
215-
215+
216216
@property
217217
def mathjax_url(self):
218218
url = self.settings.get('mathjax_url', '')
219219
if not url or url_is_absolute(url):
220220
return url
221221
return url_path_join(self.base_url, url)
222-
222+
223223
@property
224224
def mathjax_config(self):
225225
return self.settings.get('mathjax_config', 'TeX-AMS-MML_HTMLorMML-full,Safe')
@@ -241,27 +241,27 @@ def contents_js_source(self):
241241
self.log.debug("Using contents: %s", self.settings.get('contents_js_source',
242242
'services/contents'))
243243
return self.settings.get('contents_js_source', 'services/contents')
244-
244+
245245
#---------------------------------------------------------------
246246
# Manager objects
247247
#---------------------------------------------------------------
248-
248+
249249
@property
250250
def kernel_manager(self):
251251
return self.settings['kernel_manager']
252252

253253
@property
254254
def contents_manager(self):
255255
return self.settings['contents_manager']
256-
256+
257257
@property
258258
def session_manager(self):
259259
return self.settings['session_manager']
260-
260+
261261
@property
262262
def terminal_manager(self):
263263
return self.settings['terminal_manager']
264-
264+
265265
@property
266266
def kernel_spec_manager(self):
267267
return self.settings['kernel_spec_manager']
@@ -273,7 +273,7 @@ def config_manager(self):
273273
#---------------------------------------------------------------
274274
# CORS
275275
#---------------------------------------------------------------
276-
276+
277277
@property
278278
def allow_origin(self):
279279
"""Normal Access-Control-Allow-Origin"""
@@ -310,7 +310,7 @@ def set_default_headers(self):
310310

311311
if self.allow_credentials:
312312
self.set_header("Access-Control-Allow-Credentials", 'true')
313-
313+
314314
def set_attachment_header(self, filename):
315315
"""Set Content-Disposition: attachment header
316316

0 commit comments

Comments
 (0)