mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 21:43:01 +00:00
options_form is a regular configurable
now that we can assume User.spawner exists at all times
This commit is contained in:
@@ -452,12 +452,12 @@ class UserSpawnHandler(BaseHandler):
|
|||||||
# spawn has supposedly finished, check on the status
|
# spawn has supposedly finished, check on the status
|
||||||
status = yield current_user.spawner.poll()
|
status = yield current_user.spawner.poll()
|
||||||
if status is not None:
|
if status is not None:
|
||||||
if self.spawner_class.options_form:
|
if current_user.spawner.options_form:
|
||||||
self.redirect(url_path_join(self.hub.server.base_url, 'spawn'))
|
self.redirect(url_path_join(self.hub.server.base_url, 'spawn'))
|
||||||
else:
|
else:
|
||||||
yield self.spawn_single_user(current_user)
|
yield self.spawn_single_user(current_user)
|
||||||
else:
|
else:
|
||||||
if self.spawner_class.options_form:
|
if current_user.spawner.options_form:
|
||||||
self.redirect(url_path_join(self.hub.server.base_url, 'spawn'))
|
self.redirect(url_path_join(self.hub.server.base_url, 'spawn'))
|
||||||
else:
|
else:
|
||||||
yield self.spawn_single_user(current_user)
|
yield self.spawn_single_user(current_user)
|
||||||
|
@@ -68,7 +68,7 @@ class LoginHandler(BaseHandler):
|
|||||||
if user.spawner:
|
if user.spawner:
|
||||||
status = yield user.spawner.poll()
|
status = yield user.spawner.poll()
|
||||||
already_running = (status == None)
|
already_running = (status == None)
|
||||||
if not already_running and not self.spawner_class.options_form:
|
if not already_running and not user.spawner.options_form:
|
||||||
yield self.spawn_single_user(user)
|
yield self.spawn_single_user(user)
|
||||||
self.set_login_cookie(user)
|
self.set_login_cookie(user)
|
||||||
next_url = self.get_argument('next', default='')
|
next_url = self.get_argument('next', default='')
|
||||||
|
@@ -63,10 +63,10 @@ class SpawnHandler(BaseHandler):
|
|||||||
self.log.debug("User is running: %s", url)
|
self.log.debug("User is running: %s", url)
|
||||||
self.redirect(url)
|
self.redirect(url)
|
||||||
return
|
return
|
||||||
if self.spawner_class.options_form:
|
if user.spawner.options_form:
|
||||||
html = self.render_template('spawn.html',
|
html = self.render_template('spawn.html',
|
||||||
user=self.get_current_user(),
|
user=self.get_current_user(),
|
||||||
spawner_options_form=self.spawner_class.options_form,
|
spawner_options_form=user.spawner.options_form,
|
||||||
)
|
)
|
||||||
self.finish(html)
|
self.finish(html)
|
||||||
else:
|
else:
|
||||||
@@ -87,7 +87,7 @@ class SpawnHandler(BaseHandler):
|
|||||||
form_options = {}
|
form_options = {}
|
||||||
for key, byte_list in self.request.body_arguments.items():
|
for key, byte_list in self.request.body_arguments.items():
|
||||||
form_options[key] = [ bs.decode('utf8') for bs in byte_list ]
|
form_options[key] = [ bs.decode('utf8') for bs in byte_list ]
|
||||||
options = self.spawner_class.options_from_form(form_options)
|
options = user.spawner.options_from_form(form_options)
|
||||||
yield self.spawn_single_user(user, options=options)
|
yield self.spawn_single_user(user, options=options)
|
||||||
url = user.server.base_url
|
url = user.server.base_url
|
||||||
self.redirect(url)
|
self.redirect(url)
|
||||||
|
@@ -73,24 +73,33 @@ class Spawner(LoggingConfigurable):
|
|||||||
help="Enable debug-logging of the single-user server"
|
help="Enable debug-logging of the single-user server"
|
||||||
)
|
)
|
||||||
|
|
||||||
# options_form is a class attribute, defining an HTML form snippet,
|
options_form = Unicode("", config=True, help="""
|
||||||
# which can be used to specify whether
|
An HTML form for options a user can specify on launching their server.
|
||||||
# (i.e. just the <input> elements, not submit button or the <form> tag).
|
The surrounding `<form>` element and the submit button are already provided.
|
||||||
# This is **not** a configurable,
|
|
||||||
options_form = ""
|
For example:
|
||||||
@classmethod
|
|
||||||
def options_from_form(cls, form_data):
|
Set your key:
|
||||||
|
<input name="key" val="default_key"></input>
|
||||||
|
<br>
|
||||||
|
Choose a letter:
|
||||||
|
<select name="letter" multiple="true">
|
||||||
|
<option value="A">The letter A</option>
|
||||||
|
<option value="B">The letter B</option>
|
||||||
|
</select>
|
||||||
|
""")
|
||||||
|
|
||||||
|
def options_from_form(self, form_data):
|
||||||
"""Interpret HTTP form data
|
"""Interpret HTTP form data
|
||||||
|
|
||||||
Form data will always arrive as a dict of lists of strings.
|
Form data will always arrive as a dict of lists of strings.
|
||||||
Override this function to understand single-values, numbers, etc.
|
Override this function to understand single-values, numbers, etc.
|
||||||
|
|
||||||
This should coerce form data into the structure expected by self.options,
|
This should coerce form data into the structure expected by self.user_options,
|
||||||
which must be a dict.
|
which must be a dict.
|
||||||
|
|
||||||
Instances will receive this data on self.user_options, after passing through this function.
|
Instances will receive this data on self.user_options, after passing through this function,
|
||||||
|
prior to `Spawner.start`.
|
||||||
This must be a @classmethod.
|
|
||||||
"""
|
"""
|
||||||
return form_data
|
return form_data
|
||||||
|
|
||||||
|
@@ -76,8 +76,7 @@ class NeverSpawner(MockSpawner):
|
|||||||
class FormSpawner(MockSpawner):
|
class FormSpawner(MockSpawner):
|
||||||
options_form = "IMAFORM"
|
options_form = "IMAFORM"
|
||||||
|
|
||||||
@classmethod
|
def options_from_form(self, form_data):
|
||||||
def options_from_form(cls, form_data):
|
|
||||||
options = {}
|
options = {}
|
||||||
options['notspecified'] = 5
|
options['notspecified'] = 5
|
||||||
if 'bounds' in form_data:
|
if 'bounds' in form_data:
|
||||||
|
@@ -65,14 +65,14 @@ def test_spawn_redirect(app):
|
|||||||
assert r.url.endswith('/wash')
|
assert r.url.endswith('/wash')
|
||||||
|
|
||||||
def test_spawn_page(app):
|
def test_spawn_page(app):
|
||||||
with mock.patch.dict(app.tornado_application.settings, {'spawner_class': FormSpawner}):
|
with mock.patch.dict(app.users.settings, {'spawner_class': FormSpawner}):
|
||||||
cookies = app.login_user('jones')
|
cookies = app.login_user('jones')
|
||||||
r = get_page('spawn', app, cookies=cookies)
|
r = get_page('spawn', app, cookies=cookies)
|
||||||
assert r.url.endswith('/spawn')
|
assert r.url.endswith('/spawn')
|
||||||
assert FormSpawner.options_form in r.text
|
assert FormSpawner.options_form in r.text
|
||||||
|
|
||||||
def test_spawn_form(app, io_loop):
|
def test_spawn_form(app, io_loop):
|
||||||
with mock.patch.dict(app.tornado_application.settings, {'spawner_class': FormSpawner}):
|
with mock.patch.dict(app.users.settings, {'spawner_class': FormSpawner}):
|
||||||
base_url = ujoin(app.proxy.public_server.host, app.hub.server.base_url)
|
base_url = ujoin(app.proxy.public_server.host, app.hub.server.base_url)
|
||||||
cookies = app.login_user('jones')
|
cookies = app.login_user('jones')
|
||||||
orm_u = orm.User.find(app.db, 'jones')
|
orm_u = orm.User.find(app.db, 'jones')
|
||||||
|
Reference in New Issue
Block a user