Show More
@@ -103,7 +103,7 b' if tornado.version_info <= (2,1,1):' | |||||
103 |
|
103 | |||
104 | @decorator |
|
104 | @decorator | |
105 | def not_if_readonly(f, self, *args, **kwargs): |
|
105 | def not_if_readonly(f, self, *args, **kwargs): | |
106 |
if self. |
|
106 | if self.settings.get('read_only', False): | |
107 | raise web.HTTPError(403, "Notebook server is read-only") |
|
107 | raise web.HTTPError(403, "Notebook server is read-only") | |
108 | else: |
|
108 | else: | |
109 | return f(self, *args, **kwargs) |
|
109 | return f(self, *args, **kwargs) | |
@@ -120,13 +120,13 b' def authenticate_unless_readonly(f, self, *args, **kwargs):' | |||||
120 | def auth_f(self, *args, **kwargs): |
|
120 | def auth_f(self, *args, **kwargs): | |
121 | return f(self, *args, **kwargs) |
|
121 | return f(self, *args, **kwargs) | |
122 |
|
122 | |||
123 |
if self. |
|
123 | if self.settings.get('read_only', False): | |
124 | return f(self, *args, **kwargs) |
|
124 | return f(self, *args, **kwargs) | |
125 | else: |
|
125 | else: | |
126 | return auth_f(self, *args, **kwargs) |
|
126 | return auth_f(self, *args, **kwargs) | |
127 |
|
127 | |||
128 | def urljoin(*pieces): |
|
128 | def urljoin(*pieces): | |
129 |
"""Join componen |
|
129 | """Join components of url into a relative url | |
130 |
|
130 | |||
131 | Use to prevent double slash when joining subpath |
|
131 | Use to prevent double slash when joining subpath | |
132 | """ |
|
132 | """ | |
@@ -147,19 +147,31 b' class RequestHandler(web.RequestHandler):' | |||||
147 | class AuthenticatedHandler(RequestHandler): |
|
147 | class AuthenticatedHandler(RequestHandler): | |
148 | """A RequestHandler with an authenticated user.""" |
|
148 | """A RequestHandler with an authenticated user.""" | |
149 |
|
149 | |||
|
150 | def clear_login_cookie(self): | |||
|
151 | self.clear_cookie(self.cookie_name) | |||
|
152 | ||||
150 | def get_current_user(self): |
|
153 | def get_current_user(self): | |
151 |
user_id = self.get_secure_cookie(self. |
|
154 | user_id = self.get_secure_cookie(self.cookie_name) | |
152 | # For now the user_id should not return empty, but it could eventually |
|
155 | # For now the user_id should not return empty, but it could eventually | |
153 | if user_id == '': |
|
156 | if user_id == '': | |
154 | user_id = 'anonymous' |
|
157 | user_id = 'anonymous' | |
155 | if user_id is None: |
|
158 | if user_id is None: | |
156 | # prevent extra Invalid cookie sig warnings: |
|
159 | # prevent extra Invalid cookie sig warnings: | |
157 |
self.clear_cookie( |
|
160 | self.clear_login_cookie() | |
158 |
if not self. |
|
161 | if not self.read_only and not self.login_available: | |
159 | user_id = 'anonymous' |
|
162 | user_id = 'anonymous' | |
160 | return user_id |
|
163 | return user_id | |
161 |
|
164 | |||
162 | @property |
|
165 | @property | |
|
166 | def cookie_name(self): | |||
|
167 | return self.settings.get('cookie_name', '') | |||
|
168 | ||||
|
169 | @property | |||
|
170 | def password(self): | |||
|
171 | """our password""" | |||
|
172 | return self.settings.get('password', '') | |||
|
173 | ||||
|
174 | @property | |||
163 | def logged_in(self): |
|
175 | def logged_in(self): | |
164 | """Is a user currently logged in? |
|
176 | """Is a user currently logged in? | |
165 |
|
177 | |||
@@ -175,19 +187,34 b' class AuthenticatedHandler(RequestHandler):' | |||||
175 | whether the user is already logged in or not. |
|
187 | whether the user is already logged in or not. | |
176 |
|
188 | |||
177 | """ |
|
189 | """ | |
178 |
return bool(self. |
|
190 | return bool(self.settings.get('password', '')) | |
179 |
|
191 | |||
180 | @property |
|
192 | @property | |
181 | def read_only(self): |
|
193 | def read_only(self): | |
182 | """Is the notebook read-only? |
|
194 | """Is the notebook read-only? | |
183 |
|
195 | |||
184 | """ |
|
196 | """ | |
185 |
return self. |
|
197 | return self.settings.get('read_only', False) | |
|
198 | ||||
|
199 | ||||
|
200 | class IPythonHandler(AuthenticatedHandler): | |||
|
201 | """IPython-specific extensions to authenticated handling | |||
|
202 | ||||
|
203 | Mostly property shortcuts to IPython-specific settings. | |||
|
204 | """ | |||
|
205 | ||||
|
206 | @property | |||
|
207 | def config(self): | |||
|
208 | return self.settings.get('config', None) | |||
186 |
|
209 | |||
187 | @property |
|
210 | @property | |
188 | def use_less(self): |
|
211 | def use_less(self): | |
189 | """Use less instead of css in templates""" |
|
212 | """Use less instead of css in templates""" | |
190 |
return self. |
|
213 | return self.settings.get('use_less', False) | |
|
214 | ||||
|
215 | #--------------------------------------------------------------- | |||
|
216 | # URLs | |||
|
217 | #--------------------------------------------------------------- | |||
191 |
|
218 | |||
192 | @property |
|
219 | @property | |
193 | def ws_url(self): |
|
220 | def ws_url(self): | |
@@ -197,13 +224,69 b' class AuthenticatedHandler(RequestHandler):' | |||||
197 | ws[s]://host[:port] |
|
224 | ws[s]://host[:port] | |
198 | """ |
|
225 | """ | |
199 | proto = self.request.protocol.replace('http', 'ws') |
|
226 | proto = self.request.protocol.replace('http', 'ws') | |
200 | host = self.application.ipython_app.websocket_host # default to config value |
|
227 | host = self.settings.get('websocket_host', '') | |
|
228 | # default to config value | |||
201 | if host == '': |
|
229 | if host == '': | |
202 | host = self.request.host # get from request |
|
230 | host = self.request.host # get from request | |
203 | return "%s://%s" % (proto, host) |
|
231 | return "%s://%s" % (proto, host) | |
204 |
|
|
232 | ||
|
233 | @property | |||
|
234 | def mathjax_url(self): | |||
|
235 | return self.settings.get('mathjax_url', '') | |||
|
236 | ||||
|
237 | @property | |||
|
238 | def base_project_url(self): | |||
|
239 | return self.settings.get('base_project_url', '/') | |||
|
240 | ||||
|
241 | @property | |||
|
242 | def base_kernel_url(self): | |||
|
243 | return self.settings.get('base_kernel_url', '/') | |||
|
244 | ||||
|
245 | #--------------------------------------------------------------- | |||
|
246 | # Manager objects | |||
|
247 | #--------------------------------------------------------------- | |||
|
248 | ||||
|
249 | @property | |||
|
250 | def kernel_manager(self): | |||
|
251 | return self.settings['kernel_manager'] | |||
|
252 | ||||
|
253 | @property | |||
|
254 | def notebook_manager(self): | |||
|
255 | return self.settings['notebook_manager'] | |||
|
256 | ||||
|
257 | @property | |||
|
258 | def cluster_manager(self): | |||
|
259 | return self.settings['cluster_manager'] | |||
205 |
|
260 | |||
206 | class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler): |
|
261 | @property | |
|
262 | def project(self): | |||
|
263 | return self.notebook_manager.notebook_dir | |||
|
264 | ||||
|
265 | #--------------------------------------------------------------- | |||
|
266 | # template rendering | |||
|
267 | #--------------------------------------------------------------- | |||
|
268 | ||||
|
269 | def get_template(self, name): | |||
|
270 | """Return the jinja template object for a given name""" | |||
|
271 | return self.settings['jinja2_env'].get_template(name) | |||
|
272 | ||||
|
273 | def render_template(self, name, **ns): | |||
|
274 | ns.update(self.template_namespace) | |||
|
275 | template = self.get_template(name) | |||
|
276 | return template.render(**ns) | |||
|
277 | ||||
|
278 | @property | |||
|
279 | def template_namespace(self): | |||
|
280 | return dict( | |||
|
281 | base_project_url=self.base_project_url, | |||
|
282 | base_kernel_url=self.base_kernel_url, | |||
|
283 | read_only=self.read_only, | |||
|
284 | logged_in=self.logged_in, | |||
|
285 | login_available=self.login_available, | |||
|
286 | use_less=self.use_less, | |||
|
287 | ) | |||
|
288 | ||||
|
289 | class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler): | |||
207 | """static files should only be accessible when logged in""" |
|
290 | """static files should only be accessible when logged in""" | |
208 |
|
291 | |||
209 | @authenticate_unless_readonly |
|
292 | @authenticate_unless_readonly | |
@@ -211,125 +294,89 b' class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler):' | |||||
211 | return web.StaticFileHandler.get(self, path) |
|
294 | return web.StaticFileHandler.get(self, path) | |
212 |
|
295 | |||
213 |
|
296 | |||
214 |
class ProjectDashboardHandler( |
|
297 | class ProjectDashboardHandler(IPythonHandler): | |
215 |
|
298 | |||
216 | @authenticate_unless_readonly |
|
299 | @authenticate_unless_readonly | |
217 | def get(self): |
|
300 | def get(self): | |
218 | nbm = self.application.notebook_manager |
|
301 | self.write(self.render_template('projectdashboard.html', | |
219 | project = nbm.notebook_dir |
|
302 | project=self.project, | |
220 | template = self.application.jinja2_env.get_template('projectdashboard.html') |
|
303 | project_component=self.project.split('/'), | |
221 | self.write( template.render( |
|
304 | )) | |
222 | project=project, |
|
|||
223 | project_component=project.split('/'), |
|
|||
224 | base_project_url=self.application.ipython_app.base_project_url, |
|
|||
225 | base_kernel_url=self.application.ipython_app.base_kernel_url, |
|
|||
226 | read_only=self.read_only, |
|
|||
227 | logged_in=self.logged_in, |
|
|||
228 | use_less=self.use_less, |
|
|||
229 | login_available=self.login_available)) |
|
|||
230 |
|
305 | |||
231 |
|
306 | |||
232 |
class LoginHandler( |
|
307 | class LoginHandler(IPythonHandler): | |
233 |
|
308 | |||
234 |
def _render(self, message=None): |
|
309 | def _render(self, message=None): | |
235 |
|
|
310 | self.write(self.render_template('login.html', | |
236 | self.write( template.render( |
|
311 | next=url_escape(self.get_argument('next', default=self.base_project_url)), | |
237 | next=url_escape(self.get_argument('next', default=self.application.ipython_app.base_project_url)), |
|
312 | message=message, | |
238 | read_only=self.read_only, |
|
|||
239 | logged_in=self.logged_in, |
|
|||
240 | login_available=self.login_available, |
|
|||
241 | base_project_url=self.application.ipython_app.base_project_url, |
|
|||
242 | message=message |
|
|||
243 | )) |
|
313 | )) | |
244 |
|
314 | |||
245 | def get(self): |
|
315 | def get(self): | |
246 | if self.current_user: |
|
316 | if self.current_user: | |
247 |
self.redirect(self.get_argument('next', default=self. |
|
317 | self.redirect(self.get_argument('next', default=self.base_project_url)) | |
248 | else: |
|
318 | else: | |
249 | self._render() |
|
319 | self._render() | |
250 |
|
320 | |||
251 | def post(self): |
|
321 | def post(self): | |
252 | pwd = self.get_argument('password', default=u'') |
|
322 | pwd = self.get_argument('password', default=u'') | |
253 | if self.application.password: |
|
323 | if self.login_available: | |
254 |
if passwd_check(self. |
|
324 | if passwd_check(self.password, pwd): | |
255 |
self.set_secure_cookie(self. |
|
325 | self.set_secure_cookie(self.cookie_name, str(uuid.uuid4())) | |
256 | else: |
|
326 | else: | |
257 | self._render(message={'error': 'Invalid password'}) |
|
327 | self._render(message={'error': 'Invalid password'}) | |
258 | return |
|
328 | return | |
259 |
|
329 | |||
260 |
self.redirect(self.get_argument('next', default=self. |
|
330 | self.redirect(self.get_argument('next', default=self.base_project_url)) | |
261 |
|
331 | |||
262 |
|
332 | |||
263 |
class LogoutHandler( |
|
333 | class LogoutHandler(IPythonHandler): | |
264 |
|
334 | |||
265 | def get(self): |
|
335 | def get(self): | |
266 |
self.clear_cookie( |
|
336 | self.clear_login_cookie() | |
267 | if self.login_available: |
|
337 | if self.login_available: | |
268 | message = {'info': 'Successfully logged out.'} |
|
338 | message = {'info': 'Successfully logged out.'} | |
269 | else: |
|
339 | else: | |
270 | message = {'warning': 'Cannot log out. Notebook authentication ' |
|
340 | message = {'warning': 'Cannot log out. Notebook authentication ' | |
271 | 'is disabled.'} |
|
341 | 'is disabled.'} | |
272 |
|
|
342 | self.write(self.render_template('logout.html', | |
273 | self.write( template.render( |
|
|||
274 | read_only=self.read_only, |
|
|||
275 | logged_in=self.logged_in, |
|
|||
276 | login_available=self.login_available, |
|
|||
277 | base_project_url=self.application.ipython_app.base_project_url, |
|
|||
278 | message=message)) |
|
343 | message=message)) | |
279 |
|
344 | |||
280 |
|
345 | |||
281 |
class NewHandler( |
|
346 | class NewHandler(IPythonHandler): | |
282 |
|
347 | |||
283 | @web.authenticated |
|
348 | @web.authenticated | |
284 | def get(self): |
|
349 | def get(self): | |
285 |
n |
|
350 | notebook_id = self.notebook_manager.new_notebook() | |
286 | project = nbm.notebook_dir |
|
351 | self.redirect('/' + urljoin(self.base_project_url, notebook_id)) | |
287 | notebook_id = nbm.new_notebook() |
|
|||
288 | self.redirect('/'+urljoin(self.application.ipython_app.base_project_url, notebook_id)) |
|
|||
289 |
|
352 | |||
290 |
class NamedNotebookHandler( |
|
353 | class NamedNotebookHandler(IPythonHandler): | |
291 |
|
354 | |||
292 | @authenticate_unless_readonly |
|
355 | @authenticate_unless_readonly | |
293 | def get(self, notebook_id): |
|
356 | def get(self, notebook_id): | |
294 |
nbm = self. |
|
357 | nbm = self.notebook_manager | |
295 | project = nbm.notebook_dir |
|
|||
296 | if not nbm.notebook_exists(notebook_id): |
|
358 | if not nbm.notebook_exists(notebook_id): | |
297 | raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) |
|
359 | raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) | |
298 |
|
|
360 | self.write(self.render_template('notebook.html', | |
299 | self.write( template.render( |
|
361 | project=self.project, | |
300 | project=project, |
|
|||
301 | notebook_id=notebook_id, |
|
362 | notebook_id=notebook_id, | |
302 | base_project_url=self.application.ipython_app.base_project_url, |
|
|||
303 | base_kernel_url=self.application.ipython_app.base_kernel_url, |
|
|||
304 | kill_kernel=False, |
|
363 | kill_kernel=False, | |
305 | read_only=self.read_only, |
|
364 | mathjax_url=self.mathjax_url, | |
306 | logged_in=self.logged_in, |
|
|||
307 | login_available=self.login_available, |
|
|||
308 | mathjax_url=self.application.ipython_app.mathjax_url, |
|
|||
309 | use_less=self.use_less |
|
|||
310 | ) |
|
365 | ) | |
311 | ) |
|
366 | ) | |
312 |
|
367 | |||
313 |
|
368 | |||
314 |
class PrintNotebookHandler( |
|
369 | class PrintNotebookHandler(IPythonHandler): | |
315 |
|
370 | |||
316 | @authenticate_unless_readonly |
|
371 | @authenticate_unless_readonly | |
317 | def get(self, notebook_id): |
|
372 | def get(self, notebook_id): | |
318 | nbm = self.application.notebook_manager |
|
373 | if not self.notebook_manager.notebook_exists(notebook_id): | |
319 | project = nbm.notebook_dir |
|
|||
320 | if not nbm.notebook_exists(notebook_id): |
|
|||
321 | raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) |
|
374 | raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) | |
322 |
|
|
375 | self.write( self.render_template('printnotebook.html', | |
323 | self.write( template.render( |
|
376 | project=self.project, | |
324 | project=project, |
|
|||
325 | notebook_id=notebook_id, |
|
377 | notebook_id=notebook_id, | |
326 | base_project_url=self.application.ipython_app.base_project_url, |
|
|||
327 | base_kernel_url=self.application.ipython_app.base_kernel_url, |
|
|||
328 | kill_kernel=False, |
|
378 | kill_kernel=False, | |
329 | read_only=self.read_only, |
|
379 | mathjax_url=self.mathjax_url, | |
330 | logged_in=self.logged_in, |
|
|||
331 | login_available=self.login_available, |
|
|||
332 | mathjax_url=self.application.ipython_app.mathjax_url, |
|
|||
333 | )) |
|
380 | )) | |
334 |
|
381 | |||
335 | #----------------------------------------------------------------------------- |
|
382 | #----------------------------------------------------------------------------- | |
@@ -337,17 +384,17 b' class PrintNotebookHandler(AuthenticatedHandler):' | |||||
337 | #----------------------------------------------------------------------------- |
|
384 | #----------------------------------------------------------------------------- | |
338 |
|
385 | |||
339 |
|
386 | |||
340 |
class MainKernelHandler( |
|
387 | class MainKernelHandler(IPythonHandler): | |
341 |
|
388 | |||
342 | @web.authenticated |
|
389 | @web.authenticated | |
343 | def get(self): |
|
390 | def get(self): | |
344 |
km = self. |
|
391 | km = self.kernel_manager | |
345 | self.finish(jsonapi.dumps(km.list_kernel_ids())) |
|
392 | self.finish(jsonapi.dumps(km.list_kernel_ids())) | |
346 |
|
393 | |||
347 | @web.authenticated |
|
394 | @web.authenticated | |
348 | def post(self): |
|
395 | def post(self): | |
349 |
km = self. |
|
396 | km = self.kernel_manager | |
350 |
nbm = self. |
|
397 | nbm = self.notebook_manager | |
351 | notebook_id = self.get_argument('notebook', default=None) |
|
398 | notebook_id = self.get_argument('notebook', default=None) | |
352 | kernel_id = km.start_kernel(notebook_id, cwd=nbm.notebook_dir) |
|
399 | kernel_id = km.start_kernel(notebook_id, cwd=nbm.notebook_dir) | |
353 | data = {'ws_url':self.ws_url,'kernel_id':kernel_id} |
|
400 | data = {'ws_url':self.ws_url,'kernel_id':kernel_id} | |
@@ -355,23 +402,23 b' class MainKernelHandler(AuthenticatedHandler):' | |||||
355 | self.finish(jsonapi.dumps(data)) |
|
402 | self.finish(jsonapi.dumps(data)) | |
356 |
|
403 | |||
357 |
|
404 | |||
358 |
class KernelHandler( |
|
405 | class KernelHandler(IPythonHandler): | |
359 |
|
406 | |||
360 | SUPPORTED_METHODS = ('DELETE') |
|
407 | SUPPORTED_METHODS = ('DELETE') | |
361 |
|
408 | |||
362 | @web.authenticated |
|
409 | @web.authenticated | |
363 | def delete(self, kernel_id): |
|
410 | def delete(self, kernel_id): | |
364 |
km = self. |
|
411 | km = self.kernel_manager | |
365 | km.shutdown_kernel(kernel_id) |
|
412 | km.shutdown_kernel(kernel_id) | |
366 | self.set_status(204) |
|
413 | self.set_status(204) | |
367 | self.finish() |
|
414 | self.finish() | |
368 |
|
415 | |||
369 |
|
416 | |||
370 |
class KernelActionHandler( |
|
417 | class KernelActionHandler(IPythonHandler): | |
371 |
|
418 | |||
372 | @web.authenticated |
|
419 | @web.authenticated | |
373 | def post(self, kernel_id, action): |
|
420 | def post(self, kernel_id, action): | |
374 |
km = self. |
|
421 | km = self.kernel_manager | |
375 | if action == 'interrupt': |
|
422 | if action == 'interrupt': | |
376 | km.interrupt_kernel(kernel_id) |
|
423 | km.interrupt_kernel(kernel_id) | |
377 | self.set_status(204) |
|
424 | self.set_status(204) | |
@@ -413,7 +460,7 b' class ZMQStreamHandler(websocket.WebSocketHandler):' | |||||
413 | try: |
|
460 | try: | |
414 | msg = self._reserialize_reply(msg_list) |
|
461 | msg = self._reserialize_reply(msg_list) | |
415 | except Exception: |
|
462 | except Exception: | |
416 |
|
|
463 | logging.critical("Malformed message: %r" % msg_list, exc_info=True) | |
417 | else: |
|
464 | else: | |
418 | self.write_message(msg) |
|
465 | self.write_message(msg) | |
419 |
|
466 | |||
@@ -426,26 +473,14 b' class ZMQStreamHandler(websocket.WebSocketHandler):' | |||||
426 | return True |
|
473 | return True | |
427 |
|
474 | |||
428 |
|
475 | |||
429 | class AuthenticatedZMQStreamHandler(ZMQStreamHandler): |
|
476 | class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): | |
430 |
|
477 | |||
431 | def open(self, kernel_id): |
|
478 | def open(self, kernel_id): | |
432 | self.kernel_id = kernel_id.decode('ascii') |
|
479 | self.kernel_id = kernel_id.decode('ascii') | |
433 | try: |
|
480 | self.session = Session(config=self.config) | |
434 | cfg = self.application.config |
|
|||
435 | except AttributeError: |
|
|||
436 | # protect from the case where this is run from something other than |
|
|||
437 | # the notebook app: |
|
|||
438 | cfg = None |
|
|||
439 | self.session = Session(config=cfg) |
|
|||
440 | self.save_on_message = self.on_message |
|
481 | self.save_on_message = self.on_message | |
441 | self.on_message = self.on_first_message |
|
482 | self.on_message = self.on_first_message | |
442 |
|
483 | |||
443 | def get_current_user(self): |
|
|||
444 | user_id = self.get_secure_cookie(self.settings['cookie_name']) |
|
|||
445 | if user_id == '' or (user_id is None and not self.application.password): |
|
|||
446 | user_id = 'anonymous' |
|
|||
447 | return user_id |
|
|||
448 |
|
||||
449 | def _inject_cookie_message(self, msg): |
|
484 | def _inject_cookie_message(self, msg): | |
450 | """Inject the first message, which is the document cookie, |
|
485 | """Inject the first message, which is the document cookie, | |
451 | for authentication.""" |
|
486 | for authentication.""" | |
@@ -477,7 +512,7 b' class IOPubHandler(AuthenticatedZMQStreamHandler):' | |||||
477 | except web.HTTPError: |
|
512 | except web.HTTPError: | |
478 | self.close() |
|
513 | self.close() | |
479 | return |
|
514 | return | |
480 |
km = self. |
|
515 | km = self.kernel_manager | |
481 | kernel_id = self.kernel_id |
|
516 | kernel_id = self.kernel_id | |
482 | km.add_restart_callback(kernel_id, self.on_kernel_restarted) |
|
517 | km.add_restart_callback(kernel_id, self.on_kernel_restarted) | |
483 | km.add_restart_callback(kernel_id, self.on_restart_failed, 'dead') |
|
518 | km.add_restart_callback(kernel_id, self.on_restart_failed, 'dead') | |
@@ -513,7 +548,7 b' class IOPubHandler(AuthenticatedZMQStreamHandler):' | |||||
513 | # This method can be called twice, once by self.kernel_died and once |
|
548 | # This method can be called twice, once by self.kernel_died and once | |
514 | # from the WebSocket close event. If the WebSocket connection is |
|
549 | # from the WebSocket close event. If the WebSocket connection is | |
515 | # closed before the ZMQ streams are setup, they could be None. |
|
550 | # closed before the ZMQ streams are setup, they could be None. | |
516 |
km = self. |
|
551 | km = self.kernel_manager | |
517 | if self.kernel_id in km: |
|
552 | if self.kernel_id in km: | |
518 | km.remove_restart_callback( |
|
553 | km.remove_restart_callback( | |
519 | self.kernel_id, self.on_kernel_restarted, |
|
554 | self.kernel_id, self.on_kernel_restarted, | |
@@ -528,6 +563,10 b' class IOPubHandler(AuthenticatedZMQStreamHandler):' | |||||
528 |
|
563 | |||
529 | class ShellHandler(AuthenticatedZMQStreamHandler): |
|
564 | class ShellHandler(AuthenticatedZMQStreamHandler): | |
530 |
|
565 | |||
|
566 | @property | |||
|
567 | def max_msg_size(self): | |||
|
568 | return self.settings.get('max_msg_size', 65535) | |||
|
569 | ||||
531 | def initialize(self, *args, **kwargs): |
|
570 | def initialize(self, *args, **kwargs): | |
532 | self.shell_stream = None |
|
571 | self.shell_stream = None | |
533 |
|
572 | |||
@@ -537,8 +576,7 b' class ShellHandler(AuthenticatedZMQStreamHandler):' | |||||
537 | except web.HTTPError: |
|
576 | except web.HTTPError: | |
538 | self.close() |
|
577 | self.close() | |
539 | return |
|
578 | return | |
540 |
km = self. |
|
579 | km = self.kernel_manager | |
541 | self.max_msg_size = km.max_msg_size |
|
|||
542 | kernel_id = self.kernel_id |
|
580 | kernel_id = self.kernel_id | |
543 | try: |
|
581 | try: | |
544 | self.shell_stream = km.connect_shell(kernel_id) |
|
582 | self.shell_stream = km.connect_shell(kernel_id) | |
@@ -566,26 +604,26 b' class ShellHandler(AuthenticatedZMQStreamHandler):' | |||||
566 | # Notebook web service handlers |
|
604 | # Notebook web service handlers | |
567 | #----------------------------------------------------------------------------- |
|
605 | #----------------------------------------------------------------------------- | |
568 |
|
606 | |||
569 |
class NotebookRedirectHandler( |
|
607 | class NotebookRedirectHandler(IPythonHandler): | |
570 |
|
608 | |||
571 | @authenticate_unless_readonly |
|
609 | @authenticate_unless_readonly | |
572 | def get(self, notebook_name): |
|
610 | def get(self, notebook_name): | |
573 | app = self.application |
|
|||
574 | # strip trailing .ipynb: |
|
611 | # strip trailing .ipynb: | |
575 | notebook_name = os.path.splitext(notebook_name)[0] |
|
612 | notebook_name = os.path.splitext(notebook_name)[0] | |
576 |
notebook_id = |
|
613 | notebook_id = self.notebook_manager.rev_mapping.get(notebook_name, '') | |
577 | if notebook_id: |
|
614 | if notebook_id: | |
578 | url = self.settings.get('base_project_url', '/') + notebook_id |
|
615 | url = self.settings.get('base_project_url', '/') + notebook_id | |
579 | return self.redirect(url) |
|
616 | return self.redirect(url) | |
580 | else: |
|
617 | else: | |
581 | raise HTTPError(404) |
|
618 | raise HTTPError(404) | |
582 |
|
619 | |||
583 | class NotebookRootHandler(AuthenticatedHandler): |
|
620 | ||
|
621 | class NotebookRootHandler(IPythonHandler): | |||
584 |
|
622 | |||
585 | @authenticate_unless_readonly |
|
623 | @authenticate_unless_readonly | |
586 | def get(self): |
|
624 | def get(self): | |
587 |
nbm = self. |
|
625 | nbm = self.notebook_manager | |
588 |
km = self. |
|
626 | km = self.kernel_manager | |
589 | files = nbm.list_notebooks() |
|
627 | files = nbm.list_notebooks() | |
590 | for f in files : |
|
628 | for f in files : | |
591 | f['kernel_id'] = km.kernel_for_notebook(f['notebook_id']) |
|
629 | f['kernel_id'] = km.kernel_for_notebook(f['notebook_id']) | |
@@ -593,7 +631,7 b' class NotebookRootHandler(AuthenticatedHandler):' | |||||
593 |
|
631 | |||
594 | @web.authenticated |
|
632 | @web.authenticated | |
595 | def post(self): |
|
633 | def post(self): | |
596 |
nbm = self. |
|
634 | nbm = self.notebook_manager | |
597 | body = self.request.body.strip() |
|
635 | body = self.request.body.strip() | |
598 | format = self.get_argument('format', default='json') |
|
636 | format = self.get_argument('format', default='json') | |
599 | name = self.get_argument('name', default=None) |
|
637 | name = self.get_argument('name', default=None) | |
@@ -605,13 +643,13 b' class NotebookRootHandler(AuthenticatedHandler):' | |||||
605 | self.finish(jsonapi.dumps(notebook_id)) |
|
643 | self.finish(jsonapi.dumps(notebook_id)) | |
606 |
|
644 | |||
607 |
|
645 | |||
608 |
class NotebookHandler( |
|
646 | class NotebookHandler(IPythonHandler): | |
609 |
|
647 | |||
610 | SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE') |
|
648 | SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE') | |
611 |
|
649 | |||
612 | @authenticate_unless_readonly |
|
650 | @authenticate_unless_readonly | |
613 | def get(self, notebook_id): |
|
651 | def get(self, notebook_id): | |
614 |
nbm = self. |
|
652 | nbm = self.notebook_manager | |
615 | format = self.get_argument('format', default='json') |
|
653 | format = self.get_argument('format', default='json') | |
616 | last_mod, name, data = nbm.get_notebook(notebook_id, format) |
|
654 | last_mod, name, data = nbm.get_notebook(notebook_id, format) | |
617 |
|
655 | |||
@@ -626,7 +664,7 b' class NotebookHandler(AuthenticatedHandler):' | |||||
626 |
|
664 | |||
627 | @web.authenticated |
|
665 | @web.authenticated | |
628 | def put(self, notebook_id): |
|
666 | def put(self, notebook_id): | |
629 |
nbm = self. |
|
667 | nbm = self.notebook_manager | |
630 | format = self.get_argument('format', default='json') |
|
668 | format = self.get_argument('format', default='json') | |
631 | name = self.get_argument('name', default=None) |
|
669 | name = self.get_argument('name', default=None) | |
632 | nbm.save_notebook(notebook_id, self.request.body, name=name, format=format) |
|
670 | nbm.save_notebook(notebook_id, self.request.body, name=name, format=format) | |
@@ -635,20 +673,17 b' class NotebookHandler(AuthenticatedHandler):' | |||||
635 |
|
673 | |||
636 | @web.authenticated |
|
674 | @web.authenticated | |
637 | def delete(self, notebook_id): |
|
675 | def delete(self, notebook_id): | |
638 | nbm = self.application.notebook_manager |
|
676 | self.notebook_manager.delete_notebook(notebook_id) | |
639 | nbm.delete_notebook(notebook_id) |
|
|||
640 | self.set_status(204) |
|
677 | self.set_status(204) | |
641 | self.finish() |
|
678 | self.finish() | |
642 |
|
679 | |||
643 |
|
680 | |||
644 |
class NotebookCopyHandler( |
|
681 | class NotebookCopyHandler(IPythonHandler): | |
645 |
|
682 | |||
646 | @web.authenticated |
|
683 | @web.authenticated | |
647 | def get(self, notebook_id): |
|
684 | def get(self, notebook_id): | |
648 | nbm = self.application.notebook_manager |
|
685 | notebook_id = self.notebook_manager.copy_notebook(notebook_id) | |
649 | project = nbm.notebook_dir |
|
686 | self.redirect('/'+urljoin(self.base_project_url, notebook_id)) | |
650 | notebook_id = nbm.copy_notebook(notebook_id) |
|
|||
651 | self.redirect('/'+urljoin(self.application.ipython_app.base_project_url, notebook_id)) |
|
|||
652 |
|
687 | |||
653 |
|
688 | |||
654 | #----------------------------------------------------------------------------- |
|
689 | #----------------------------------------------------------------------------- | |
@@ -656,27 +691,25 b' class NotebookCopyHandler(AuthenticatedHandler):' | |||||
656 | #----------------------------------------------------------------------------- |
|
691 | #----------------------------------------------------------------------------- | |
657 |
|
692 | |||
658 |
|
693 | |||
659 |
class MainClusterHandler( |
|
694 | class MainClusterHandler(IPythonHandler): | |
660 |
|
695 | |||
661 | @web.authenticated |
|
696 | @web.authenticated | |
662 | def get(self): |
|
697 | def get(self): | |
663 | cm = self.application.cluster_manager |
|
698 | self.finish(jsonapi.dumps(self.cluster_manager.list_profiles())) | |
664 | self.finish(jsonapi.dumps(cm.list_profiles())) |
|
|||
665 |
|
699 | |||
666 |
|
700 | |||
667 |
class ClusterProfileHandler( |
|
701 | class ClusterProfileHandler(IPythonHandler): | |
668 |
|
702 | |||
669 | @web.authenticated |
|
703 | @web.authenticated | |
670 | def get(self, profile): |
|
704 | def get(self, profile): | |
671 | cm = self.application.cluster_manager |
|
705 | self.finish(jsonapi.dumps(self.cluster_manager.profile_info(profile))) | |
672 | self.finish(jsonapi.dumps(cm.profile_info(profile))) |
|
|||
673 |
|
706 | |||
674 |
|
707 | |||
675 |
class ClusterActionHandler( |
|
708 | class ClusterActionHandler(IPythonHandler): | |
676 |
|
709 | |||
677 | @web.authenticated |
|
710 | @web.authenticated | |
678 | def post(self, profile, action): |
|
711 | def post(self, profile, action): | |
679 |
cm = self. |
|
712 | cm = self.cluster_manager | |
680 | if action == 'start': |
|
713 | if action == 'start': | |
681 | n = self.get_argument('n',default=None) |
|
714 | n = self.get_argument('n',default=None) | |
682 | if n is None: |
|
715 | if n is None: | |
@@ -693,7 +726,7 b' class ClusterActionHandler(AuthenticatedHandler):' | |||||
693 | #----------------------------------------------------------------------------- |
|
726 | #----------------------------------------------------------------------------- | |
694 |
|
727 | |||
695 |
|
728 | |||
696 |
class RSTHandler( |
|
729 | class RSTHandler(IPythonHandler): | |
697 |
|
730 | |||
698 | @web.authenticated |
|
731 | @web.authenticated | |
699 | def post(self): |
|
732 | def post(self): |
@@ -29,18 +29,13 b' from IPython.utils.traitlets import (' | |||||
29 |
|
29 | |||
30 |
|
30 | |||
31 | class MappingKernelManager(MultiKernelManager): |
|
31 | class MappingKernelManager(MultiKernelManager): | |
32 | """A KernelManager that handles notebok mapping and HTTP error handling""" |
|
32 | """A KernelManager that handles notebook mapping and HTTP error handling""" | |
33 |
|
33 | |||
34 | def _kernel_manager_class_default(self): |
|
34 | def _kernel_manager_class_default(self): | |
35 | return "IPython.kernel.ioloop.IOLoopKernelManager" |
|
35 | return "IPython.kernel.ioloop.IOLoopKernelManager" | |
36 |
|
36 | |||
37 | kernel_argv = List(Unicode) |
|
37 | kernel_argv = List(Unicode) | |
38 |
|
38 | |||
39 | max_msg_size = Integer(65536, config=True, help=""" |
|
|||
40 | The max raw message size accepted from the browser |
|
|||
41 | over a WebSocket connection. |
|
|||
42 | """) |
|
|||
43 |
|
||||
44 | _notebook_mapping = Dict() |
|
39 | _notebook_mapping = Dict() | |
45 |
|
40 | |||
46 | #------------------------------------------------------------------------- |
|
41 | #------------------------------------------------------------------------- |
@@ -178,16 +178,33 b' class NotebookWebApplication(web.Application):' | |||||
178 | # Note that the URLs these patterns check against are escaped, |
|
178 | # Note that the URLs these patterns check against are escaped, | |
179 | # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'. |
|
179 | # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'. | |
180 | base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii') |
|
180 | base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii') | |
181 |
|
181 | template_path = os.path.join(os.path.dirname(__file__), "templates") | ||
182 | settings = dict( |
|
182 | settings = dict( | |
183 | template_path=os.path.join(os.path.dirname(__file__), "templates"), |
|
183 | # basics | |
|
184 | base_project_url=base_project_url, | |||
|
185 | base_kernel_url=ipython_app.base_kernel_url, | |||
|
186 | template_path=template_path, | |||
184 | static_path=ipython_app.static_file_path, |
|
187 | static_path=ipython_app.static_file_path, | |
185 | static_handler_class = FileFindHandler, |
|
188 | static_handler_class = FileFindHandler, | |
186 | static_url_prefix = url_path_join(base_project_url,'/static/'), |
|
189 | static_url_prefix = url_path_join(base_project_url,'/static/'), | |
|
190 | ||||
|
191 | # authentication | |||
187 | cookie_secret=os.urandom(1024), |
|
192 | cookie_secret=os.urandom(1024), | |
188 | login_url=url_path_join(base_project_url,'/login'), |
|
193 | login_url=url_path_join(base_project_url,'/login'), | |
189 | cookie_name='username-%s' % uuid.uuid4(), |
|
194 | cookie_name='username-%s' % uuid.uuid4(), | |
190 | base_project_url = base_project_url, |
|
195 | read_only=ipython_app.read_only, | |
|
196 | password=ipython_app.password, | |||
|
197 | ||||
|
198 | # managers | |||
|
199 | kernel_manager=kernel_manager, | |||
|
200 | notebook_manager=notebook_manager, | |||
|
201 | cluster_manager=cluster_manager, | |||
|
202 | ||||
|
203 | # IPython stuff | |||
|
204 | max_msg_size=ipython_app.max_msg_size, | |||
|
205 | config=ipython_app.config, | |||
|
206 | use_less=ipython_app.use_less, | |||
|
207 | jinja2_env=Environment(loader=FileSystemLoader(template_path)), | |||
191 | ) |
|
208 | ) | |
192 |
|
209 | |||
193 | # allow custom overrides for the tornado web app. |
|
210 | # allow custom overrides for the tornado web app. | |
@@ -202,16 +219,6 b' class NotebookWebApplication(web.Application):' | |||||
202 |
|
219 | |||
203 | super(NotebookWebApplication, self).__init__(new_handlers, **settings) |
|
220 | super(NotebookWebApplication, self).__init__(new_handlers, **settings) | |
204 |
|
221 | |||
205 | self.kernel_manager = kernel_manager |
|
|||
206 | self.notebook_manager = notebook_manager |
|
|||
207 | self.cluster_manager = cluster_manager |
|
|||
208 | self.ipython_app = ipython_app |
|
|||
209 | self.read_only = self.ipython_app.read_only |
|
|||
210 | self.config = self.ipython_app.config |
|
|||
211 | self.use_less = self.ipython_app.use_less |
|
|||
212 | self.log = log |
|
|||
213 | self.jinja2_env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates"))) |
|
|||
214 |
|
||||
215 |
|
222 | |||
216 |
|
223 | |||
217 | #----------------------------------------------------------------------------- |
|
224 | #----------------------------------------------------------------------------- | |
@@ -301,10 +308,13 b' class NotebookApp(BaseIPythonApplication):' | |||||
301 |
|
308 | |||
302 | kernel_argv = List(Unicode) |
|
309 | kernel_argv = List(Unicode) | |
303 |
|
310 | |||
304 | log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'), |
|
311 | max_msg_size = Integer(65536, config=True, help=""" | |
305 | default_value=logging.INFO, |
|
312 | The max raw message size accepted from the browser | |
306 | config=True, |
|
313 | over a WebSocket connection. | |
307 | help="Set the log level by value or name.") |
|
314 | """) | |
|
315 | ||||
|
316 | def _log_level_default(self): | |||
|
317 | return logging.INFO | |||
308 |
|
318 | |||
309 | # create requested profiles by default, if they don't exist: |
|
319 | # create requested profiles by default, if they don't exist: | |
310 | auto_create = Bool(True) |
|
320 | auto_create = Bool(True) |
General Comments 0
You need to be logged in to leave comments.
Login now