##// END OF EJS Templates
fix: changed keyword input field to type password
Satrajit Ghosh -
Show More
@@ -1,357 +1,357 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from tornado import web
19 from tornado import web
20 from tornado import websocket
20 from tornado import websocket
21
21
22 from zmq.eventloop import ioloop
22 from zmq.eventloop import ioloop
23 from zmq.utils import jsonapi
23 from zmq.utils import jsonapi
24
24
25 from IPython.zmq.session import Session
25 from IPython.zmq.session import Session
26
26
27 try:
27 try:
28 from docutils.core import publish_string
28 from docutils.core import publish_string
29 except ImportError:
29 except ImportError:
30 publish_string = None
30 publish_string = None
31
31
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Top-level handlers
35 # Top-level handlers
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class BaseHandler(web.RequestHandler):
38 class BaseHandler(web.RequestHandler):
39 def get_current_user(self):
39 def get_current_user(self):
40 user_id = self.get_secure_cookie("user")
40 user_id = self.get_secure_cookie("user")
41 keyword = self.get_secure_cookie("keyword")
41 keyword = self.get_secure_cookie("keyword")
42 if self.application.keyword and self.application.keyword != keyword:
42 if self.application.keyword and self.application.keyword != keyword:
43 return None
43 return None
44 if not user_id:
44 if not user_id:
45 user_id = 'anonymous'
45 user_id = 'anonymous'
46 return user_id
46 return user_id
47
47
48 class NBBrowserHandler(BaseHandler):
48 class NBBrowserHandler(BaseHandler):
49 @web.authenticated
49 @web.authenticated
50 def get(self):
50 def get(self):
51 nbm = self.application.notebook_manager
51 nbm = self.application.notebook_manager
52 project = nbm.notebook_dir
52 project = nbm.notebook_dir
53 self.render('nbbrowser.html', project=project)
53 self.render('nbbrowser.html', project=project)
54
54
55 class LoginHandler(BaseHandler):
55 class LoginHandler(BaseHandler):
56 def get(self):
56 def get(self):
57 user_id = self.get_secure_cookie("user")
57 user_id = self.get_secure_cookie("user")
58 self.write('<html><body><form action="/login" method="post">'
58 self.write('<html><body><form action="/login" method="post">'
59 'Name: <input type="text" name="name" value=%s>'
59 'Name: <input type="text" name="name" value=%s>'
60 'Keyword: <input type="text" name="keyword">'
60 'Keyword: <input type="password" name="keyword">'
61 '<input type="submit" value="Sign in">'
61 '<input type="submit" value="Sign in">'
62 '</form></body></html>'%user_id)
62 '</form></body></html>'%user_id)
63
63
64 def post(self):
64 def post(self):
65 self.set_secure_cookie("user", self.get_argument("name", default=u''))
65 self.set_secure_cookie("user", self.get_argument("name", default=u''))
66 self.set_secure_cookie("keyword", self.get_argument("keyword", default=u''))
66 self.set_secure_cookie("keyword", self.get_argument("keyword", default=u''))
67 self.redirect("/")
67 self.redirect("/")
68
68
69 class NewHandler(web.RequestHandler):
69 class NewHandler(web.RequestHandler):
70 def get(self):
70 def get(self):
71 notebook_id = self.application.notebook_manager.new_notebook()
71 notebook_id = self.application.notebook_manager.new_notebook()
72 self.render('notebook.html', notebook_id=notebook_id)
72 self.render('notebook.html', notebook_id=notebook_id)
73
73
74
74
75 class NamedNotebookHandler(web.RequestHandler):
75 class NamedNotebookHandler(web.RequestHandler):
76 def get(self, notebook_id):
76 def get(self, notebook_id):
77 nbm = self.application.notebook_manager
77 nbm = self.application.notebook_manager
78 if not nbm.notebook_exists(notebook_id):
78 if not nbm.notebook_exists(notebook_id):
79 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
79 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
80 self.render('notebook.html', notebook_id=notebook_id)
80 self.render('notebook.html', notebook_id=notebook_id)
81
81
82
82
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84 # Kernel handlers
84 # Kernel handlers
85 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
86
86
87
87
88 class MainKernelHandler(web.RequestHandler):
88 class MainKernelHandler(web.RequestHandler):
89
89
90 def get(self):
90 def get(self):
91 km = self.application.kernel_manager
91 km = self.application.kernel_manager
92 self.finish(jsonapi.dumps(km.kernel_ids))
92 self.finish(jsonapi.dumps(km.kernel_ids))
93
93
94 def post(self):
94 def post(self):
95 km = self.application.kernel_manager
95 km = self.application.kernel_manager
96 notebook_id = self.get_argument('notebook', default=None)
96 notebook_id = self.get_argument('notebook', default=None)
97 kernel_id = km.start_kernel(notebook_id)
97 kernel_id = km.start_kernel(notebook_id)
98 ws_url = self.application.ipython_app.get_ws_url()
98 ws_url = self.application.ipython_app.get_ws_url()
99 data = {'ws_url':ws_url,'kernel_id':kernel_id}
99 data = {'ws_url':ws_url,'kernel_id':kernel_id}
100 self.set_header('Location', '/'+kernel_id)
100 self.set_header('Location', '/'+kernel_id)
101 self.finish(jsonapi.dumps(data))
101 self.finish(jsonapi.dumps(data))
102
102
103
103
104 class KernelHandler(web.RequestHandler):
104 class KernelHandler(web.RequestHandler):
105
105
106 SUPPORTED_METHODS = ('DELETE')
106 SUPPORTED_METHODS = ('DELETE')
107
107
108 def delete(self, kernel_id):
108 def delete(self, kernel_id):
109 km = self.application.kernel_manager
109 km = self.application.kernel_manager
110 km.kill_kernel(kernel_id)
110 km.kill_kernel(kernel_id)
111 self.set_status(204)
111 self.set_status(204)
112 self.finish()
112 self.finish()
113
113
114
114
115 class KernelActionHandler(web.RequestHandler):
115 class KernelActionHandler(web.RequestHandler):
116
116
117 def post(self, kernel_id, action):
117 def post(self, kernel_id, action):
118 km = self.application.kernel_manager
118 km = self.application.kernel_manager
119 if action == 'interrupt':
119 if action == 'interrupt':
120 km.interrupt_kernel(kernel_id)
120 km.interrupt_kernel(kernel_id)
121 self.set_status(204)
121 self.set_status(204)
122 if action == 'restart':
122 if action == 'restart':
123 new_kernel_id = km.restart_kernel(kernel_id)
123 new_kernel_id = km.restart_kernel(kernel_id)
124 ws_url = self.application.ipython_app.get_ws_url()
124 ws_url = self.application.ipython_app.get_ws_url()
125 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
125 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
126 self.set_header('Location', '/'+new_kernel_id)
126 self.set_header('Location', '/'+new_kernel_id)
127 self.write(jsonapi.dumps(data))
127 self.write(jsonapi.dumps(data))
128 self.finish()
128 self.finish()
129
129
130
130
131 class ZMQStreamHandler(websocket.WebSocketHandler):
131 class ZMQStreamHandler(websocket.WebSocketHandler):
132
132
133 def _reserialize_reply(self, msg_list):
133 def _reserialize_reply(self, msg_list):
134 """Reserialize a reply message using JSON.
134 """Reserialize a reply message using JSON.
135
135
136 This takes the msg list from the ZMQ socket, unserializes it using
136 This takes the msg list from the ZMQ socket, unserializes it using
137 self.session and then serializes the result using JSON. This method
137 self.session and then serializes the result using JSON. This method
138 should be used by self._on_zmq_reply to build messages that can
138 should be used by self._on_zmq_reply to build messages that can
139 be sent back to the browser.
139 be sent back to the browser.
140 """
140 """
141 idents, msg_list = self.session.feed_identities(msg_list)
141 idents, msg_list = self.session.feed_identities(msg_list)
142 msg = self.session.unserialize(msg_list)
142 msg = self.session.unserialize(msg_list)
143 try:
143 try:
144 msg['header'].pop('date')
144 msg['header'].pop('date')
145 except KeyError:
145 except KeyError:
146 pass
146 pass
147 try:
147 try:
148 msg['parent_header'].pop('date')
148 msg['parent_header'].pop('date')
149 except KeyError:
149 except KeyError:
150 pass
150 pass
151 msg.pop('buffers')
151 msg.pop('buffers')
152 return jsonapi.dumps(msg)
152 return jsonapi.dumps(msg)
153
153
154 def _on_zmq_reply(self, msg_list):
154 def _on_zmq_reply(self, msg_list):
155 try:
155 try:
156 msg = self._reserialize_reply(msg_list)
156 msg = self._reserialize_reply(msg_list)
157 except:
157 except:
158 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
158 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
159 else:
159 else:
160 self.write_message(msg)
160 self.write_message(msg)
161
161
162
162
163 class IOPubHandler(ZMQStreamHandler):
163 class IOPubHandler(ZMQStreamHandler):
164
164
165 def initialize(self, *args, **kwargs):
165 def initialize(self, *args, **kwargs):
166 self._kernel_alive = True
166 self._kernel_alive = True
167 self._beating = False
167 self._beating = False
168 self.iopub_stream = None
168 self.iopub_stream = None
169 self.hb_stream = None
169 self.hb_stream = None
170
170
171 def open(self, kernel_id):
171 def open(self, kernel_id):
172 km = self.application.kernel_manager
172 km = self.application.kernel_manager
173 self.kernel_id = kernel_id
173 self.kernel_id = kernel_id
174 self.session = Session()
174 self.session = Session()
175 self.time_to_dead = km.time_to_dead
175 self.time_to_dead = km.time_to_dead
176 try:
176 try:
177 self.iopub_stream = km.create_iopub_stream(kernel_id)
177 self.iopub_stream = km.create_iopub_stream(kernel_id)
178 self.hb_stream = km.create_hb_stream(kernel_id)
178 self.hb_stream = km.create_hb_stream(kernel_id)
179 except web.HTTPError:
179 except web.HTTPError:
180 # WebSockets don't response to traditional error codes so we
180 # WebSockets don't response to traditional error codes so we
181 # close the connection.
181 # close the connection.
182 if not self.stream.closed():
182 if not self.stream.closed():
183 self.stream.close()
183 self.stream.close()
184 else:
184 else:
185 self.iopub_stream.on_recv(self._on_zmq_reply)
185 self.iopub_stream.on_recv(self._on_zmq_reply)
186 self.start_hb(self.kernel_died)
186 self.start_hb(self.kernel_died)
187
187
188 def on_close(self):
188 def on_close(self):
189 # This method can be called twice, once by self.kernel_died and once
189 # This method can be called twice, once by self.kernel_died and once
190 # from the WebSocket close event. If the WebSocket connection is
190 # from the WebSocket close event. If the WebSocket connection is
191 # closed before the ZMQ streams are setup, they could be None.
191 # closed before the ZMQ streams are setup, they could be None.
192 self.stop_hb()
192 self.stop_hb()
193 if self.iopub_stream is not None and not self.iopub_stream.closed():
193 if self.iopub_stream is not None and not self.iopub_stream.closed():
194 self.iopub_stream.on_recv(None)
194 self.iopub_stream.on_recv(None)
195 self.iopub_stream.close()
195 self.iopub_stream.close()
196 if self.hb_stream is not None and not self.hb_stream.closed():
196 if self.hb_stream is not None and not self.hb_stream.closed():
197 self.hb_stream.close()
197 self.hb_stream.close()
198
198
199 def start_hb(self, callback):
199 def start_hb(self, callback):
200 """Start the heartbeating and call the callback if the kernel dies."""
200 """Start the heartbeating and call the callback if the kernel dies."""
201 if not self._beating:
201 if not self._beating:
202 self._kernel_alive = True
202 self._kernel_alive = True
203
203
204 def ping_or_dead():
204 def ping_or_dead():
205 if self._kernel_alive:
205 if self._kernel_alive:
206 self._kernel_alive = False
206 self._kernel_alive = False
207 self.hb_stream.send(b'ping')
207 self.hb_stream.send(b'ping')
208 else:
208 else:
209 try:
209 try:
210 callback()
210 callback()
211 except:
211 except:
212 pass
212 pass
213 finally:
213 finally:
214 self._hb_periodic_callback.stop()
214 self._hb_periodic_callback.stop()
215
215
216 def beat_received(msg):
216 def beat_received(msg):
217 self._kernel_alive = True
217 self._kernel_alive = True
218
218
219 self.hb_stream.on_recv(beat_received)
219 self.hb_stream.on_recv(beat_received)
220 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
220 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
221 self._hb_periodic_callback.start()
221 self._hb_periodic_callback.start()
222 self._beating= True
222 self._beating= True
223
223
224 def stop_hb(self):
224 def stop_hb(self):
225 """Stop the heartbeating and cancel all related callbacks."""
225 """Stop the heartbeating and cancel all related callbacks."""
226 if self._beating:
226 if self._beating:
227 self._hb_periodic_callback.stop()
227 self._hb_periodic_callback.stop()
228 if not self.hb_stream.closed():
228 if not self.hb_stream.closed():
229 self.hb_stream.on_recv(None)
229 self.hb_stream.on_recv(None)
230
230
231 def kernel_died(self):
231 def kernel_died(self):
232 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
232 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
233 self.write_message(
233 self.write_message(
234 {'header': {'msg_type': 'status'},
234 {'header': {'msg_type': 'status'},
235 'parent_header': {},
235 'parent_header': {},
236 'content': {'execution_state':'dead'}
236 'content': {'execution_state':'dead'}
237 }
237 }
238 )
238 )
239 self.on_close()
239 self.on_close()
240
240
241
241
242 class ShellHandler(ZMQStreamHandler):
242 class ShellHandler(ZMQStreamHandler):
243
243
244 def initialize(self, *args, **kwargs):
244 def initialize(self, *args, **kwargs):
245 self.shell_stream = None
245 self.shell_stream = None
246
246
247 def open(self, kernel_id):
247 def open(self, kernel_id):
248 km = self.application.kernel_manager
248 km = self.application.kernel_manager
249 self.max_msg_size = km.max_msg_size
249 self.max_msg_size = km.max_msg_size
250 self.kernel_id = kernel_id
250 self.kernel_id = kernel_id
251 try:
251 try:
252 self.shell_stream = km.create_shell_stream(kernel_id)
252 self.shell_stream = km.create_shell_stream(kernel_id)
253 except web.HTTPError:
253 except web.HTTPError:
254 # WebSockets don't response to traditional error codes so we
254 # WebSockets don't response to traditional error codes so we
255 # close the connection.
255 # close the connection.
256 if not self.stream.closed():
256 if not self.stream.closed():
257 self.stream.close()
257 self.stream.close()
258 else:
258 else:
259 self.session = Session()
259 self.session = Session()
260 self.shell_stream.on_recv(self._on_zmq_reply)
260 self.shell_stream.on_recv(self._on_zmq_reply)
261
261
262 def on_message(self, msg):
262 def on_message(self, msg):
263 if len(msg) < self.max_msg_size:
263 if len(msg) < self.max_msg_size:
264 msg = jsonapi.loads(msg)
264 msg = jsonapi.loads(msg)
265 self.session.send(self.shell_stream, msg)
265 self.session.send(self.shell_stream, msg)
266
266
267 def on_close(self):
267 def on_close(self):
268 # Make sure the stream exists and is not already closed.
268 # Make sure the stream exists and is not already closed.
269 if self.shell_stream is not None and not self.shell_stream.closed():
269 if self.shell_stream is not None and not self.shell_stream.closed():
270 self.shell_stream.close()
270 self.shell_stream.close()
271
271
272
272
273 #-----------------------------------------------------------------------------
273 #-----------------------------------------------------------------------------
274 # Notebook web service handlers
274 # Notebook web service handlers
275 #-----------------------------------------------------------------------------
275 #-----------------------------------------------------------------------------
276
276
277 class NotebookRootHandler(web.RequestHandler):
277 class NotebookRootHandler(web.RequestHandler):
278
278
279 def get(self):
279 def get(self):
280 nbm = self.application.notebook_manager
280 nbm = self.application.notebook_manager
281 files = nbm.list_notebooks()
281 files = nbm.list_notebooks()
282 self.finish(jsonapi.dumps(files))
282 self.finish(jsonapi.dumps(files))
283
283
284 def post(self):
284 def post(self):
285 nbm = self.application.notebook_manager
285 nbm = self.application.notebook_manager
286 body = self.request.body.strip()
286 body = self.request.body.strip()
287 format = self.get_argument('format', default='json')
287 format = self.get_argument('format', default='json')
288 name = self.get_argument('name', default=None)
288 name = self.get_argument('name', default=None)
289 if body:
289 if body:
290 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
290 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
291 else:
291 else:
292 notebook_id = nbm.new_notebook()
292 notebook_id = nbm.new_notebook()
293 self.set_header('Location', '/'+notebook_id)
293 self.set_header('Location', '/'+notebook_id)
294 self.finish(jsonapi.dumps(notebook_id))
294 self.finish(jsonapi.dumps(notebook_id))
295
295
296
296
297 class NotebookHandler(web.RequestHandler):
297 class NotebookHandler(web.RequestHandler):
298
298
299 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
299 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
300
300
301 def get(self, notebook_id):
301 def get(self, notebook_id):
302 nbm = self.application.notebook_manager
302 nbm = self.application.notebook_manager
303 format = self.get_argument('format', default='json')
303 format = self.get_argument('format', default='json')
304 last_mod, name, data = nbm.get_notebook(notebook_id, format)
304 last_mod, name, data = nbm.get_notebook(notebook_id, format)
305 if format == u'json':
305 if format == u'json':
306 self.set_header('Content-Type', 'application/json')
306 self.set_header('Content-Type', 'application/json')
307 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
307 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
308 elif format == u'py':
308 elif format == u'py':
309 self.set_header('Content-Type', 'application/x-python')
309 self.set_header('Content-Type', 'application/x-python')
310 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
310 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
311 self.set_header('Last-Modified', last_mod)
311 self.set_header('Last-Modified', last_mod)
312 self.finish(data)
312 self.finish(data)
313
313
314 def put(self, notebook_id):
314 def put(self, notebook_id):
315 nbm = self.application.notebook_manager
315 nbm = self.application.notebook_manager
316 format = self.get_argument('format', default='json')
316 format = self.get_argument('format', default='json')
317 name = self.get_argument('name', default=None)
317 name = self.get_argument('name', default=None)
318 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
318 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
319 self.set_status(204)
319 self.set_status(204)
320 self.finish()
320 self.finish()
321
321
322 def delete(self, notebook_id):
322 def delete(self, notebook_id):
323 nbm = self.application.notebook_manager
323 nbm = self.application.notebook_manager
324 nbm.delete_notebook(notebook_id)
324 nbm.delete_notebook(notebook_id)
325 self.set_status(204)
325 self.set_status(204)
326 self.finish()
326 self.finish()
327
327
328 #-----------------------------------------------------------------------------
328 #-----------------------------------------------------------------------------
329 # RST web service handlers
329 # RST web service handlers
330 #-----------------------------------------------------------------------------
330 #-----------------------------------------------------------------------------
331
331
332
332
333 class RSTHandler(web.RequestHandler):
333 class RSTHandler(web.RequestHandler):
334
334
335 def post(self):
335 def post(self):
336 if publish_string is None:
336 if publish_string is None:
337 raise web.HTTPError(503, u'docutils not available')
337 raise web.HTTPError(503, u'docutils not available')
338 body = self.request.body.strip()
338 body = self.request.body.strip()
339 source = body
339 source = body
340 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
340 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
341 defaults = {'file_insertion_enabled': 0,
341 defaults = {'file_insertion_enabled': 0,
342 'raw_enabled': 0,
342 'raw_enabled': 0,
343 '_disable_config': 1,
343 '_disable_config': 1,
344 'stylesheet_path': 0
344 'stylesheet_path': 0
345 # 'template': template_path
345 # 'template': template_path
346 }
346 }
347 try:
347 try:
348 html = publish_string(source, writer_name='html',
348 html = publish_string(source, writer_name='html',
349 settings_overrides=defaults
349 settings_overrides=defaults
350 )
350 )
351 except:
351 except:
352 raise web.HTTPError(400, u'Invalid RST')
352 raise web.HTTPError(400, u'Invalid RST')
353 print html
353 print html
354 self.set_header('Content-Type', 'text/html')
354 self.set_header('Content-Type', 'text/html')
355 self.finish(html)
355 self.finish(html)
356
356
357
357
General Comments 0
You need to be logged in to leave comments. Login now