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