##// END OF EJS Templates
Adding kernel/notebook associations.
Brian E. Granger -
Show More
@@ -1,135 +1,160 b''
1 """Tornado handlers for the notebook."""
1 """Tornado handlers for the notebook."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 import json
7 import json
8 import logging
8 import logging
9 import urllib
9 import urllib
10
10
11 from tornado import web
11 from tornado import web
12 from tornado import websocket
12 from tornado import websocket
13
13
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Handlers
16 # Top-level handlers
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19
19
20 class NBBrowserHandler(web.RequestHandler):
20 class NBBrowserHandler(web.RequestHandler):
21 def get(self):
21 def get(self):
22 nbm = self.application.notebook_manager
22 nbm = self.application.notebook_manager
23 project = nbm.notebook_dir
23 project = nbm.notebook_dir
24 self.render('nbbrowser.html', project=project)
24 self.render('nbbrowser.html', project=project)
25
25
26
26
27 class NewHandler(web.RequestHandler):
27 class NewHandler(web.RequestHandler):
28 def get(self):
28 def get(self):
29 notebook_id = self.application.notebook_manager.new_notebook()
29 notebook_id = self.application.notebook_manager.new_notebook()
30 self.render('notebook.html', notebook_id=notebook_id)
30 self.render('notebook.html', notebook_id=notebook_id)
31
31
32
32
33 class NamedNotebookHandler(web.RequestHandler):
33 class NamedNotebookHandler(web.RequestHandler):
34 def get(self, notebook_id):
34 def get(self, notebook_id):
35 nbm = self.application.notebook_manager
35 nbm = self.application.notebook_manager
36 if not nbm.notebook_exists(notebook_id):
36 if not nbm.notebook_exists(notebook_id):
37 raise web.HTTPError(404)
37 raise web.HTTPError(404)
38 self.render('notebook.html', notebook_id=notebook_id)
38 self.render('notebook.html', notebook_id=notebook_id)
39
39
40
40
41 class KernelHandler(web.RequestHandler):
41 #-----------------------------------------------------------------------------
42 # Kernel handlers
43 #-----------------------------------------------------------------------------
44
45
46 class MainKernelHandler(web.RequestHandler):
42
47
43 def get(self):
48 def get(self):
44 self.finish(json.dumps(self.application.kernel_ids))
49 rkm = self.application.routing_kernel_manager
50 self.finish(json.dumps(rkm.kernel_ids))
45
51
46 def post(self):
52 def post(self):
47 kernel_id = self.application.start_kernel()
53 rkm = self.application.routing_kernel_manager
54 notebook_id = self.get_argument('notebook', default=None)
55 kernel_id = rkm.start_kernel(notebook_id)
48 self.set_header('Location', '/'+kernel_id)
56 self.set_header('Location', '/'+kernel_id)
49 self.finish(json.dumps(kernel_id))
57 self.finish(json.dumps(kernel_id))
50
58
51
59
60 class KernelHandler(web.RequestHandler):
61
62 SUPPORTED_METHODS = ('DELETE')
63
64 def delete(self, kernel_id):
65 rkm = self.application.routing_kernel_manager
66 self.kill_kernel(kernel_id)
67 self.set_status(204)
68 self.finish()
69
70
52 class KernelActionHandler(web.RequestHandler):
71 class KernelActionHandler(web.RequestHandler):
53
72
54 def post(self, kernel_id, action):
73 def post(self, kernel_id, action):
55 # TODO: figure out a better way of handling RPC style calls.
74 rkm = self.application.routing_kernel_manager
56 if action == 'interrupt':
75 if action == 'interrupt':
57 self.application.interrupt_kernel(kernel_id)
76 rkm.interrupt_kernel(kernel_id)
77 self.set_status(204)
58 if action == 'restart':
78 if action == 'restart':
59 new_kernel_id = self.application.restart_kernel(kernel_id)
79 new_kernel_id = rkm.restart_kernel(kernel_id)
60 self.write(json.dumps(new_kernel_id))
80 self.write(json.dumps(new_kernel_id))
61 self.finish()
81 self.finish()
62
82
63
83
64 class ZMQStreamHandler(websocket.WebSocketHandler):
84 class ZMQStreamHandler(websocket.WebSocketHandler):
65
85
66 def initialize(self, stream_name):
86 def initialize(self, stream_name):
67 self.stream_name = stream_name
87 self.stream_name = stream_name
68
88
69 def open(self, kernel_id):
89 def open(self, kernel_id):
70 self.router = self.application.get_router(kernel_id, self.stream_name)
90 rkm = self.application.routing_kernel_manager
91 self.router = rkm.get_router(kernel_id, self.stream_name)
71 self.client_id = self.router.register_client(self)
92 self.client_id = self.router.register_client(self)
72 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
93 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
73
94
74 def on_message(self, msg):
95 def on_message(self, msg):
75 self.router.forward_msg(self.client_id, msg)
96 self.router.forward_msg(self.client_id, msg)
76
97
77 def on_close(self):
98 def on_close(self):
78 self.router.unregister_client(self.client_id)
99 self.router.unregister_client(self.client_id)
79 logging.info("Connection closed: %s" % self.client_id)
100 logging.info("Connection closed: %s" % self.client_id)
80
101
81
102
103 #-----------------------------------------------------------------------------
104 # Notebook web service handlers
105 #-----------------------------------------------------------------------------
106
82 class NotebookRootHandler(web.RequestHandler):
107 class NotebookRootHandler(web.RequestHandler):
83
108
84 def get(self):
109 def get(self):
85 nbm = self.application.notebook_manager
110 nbm = self.application.notebook_manager
86 files = nbm.list_notebooks()
111 files = nbm.list_notebooks()
87 self.finish(json.dumps(files))
112 self.finish(json.dumps(files))
88
113
89 def post(self):
114 def post(self):
90 nbm = self.application.notebook_manager
115 nbm = self.application.notebook_manager
91 body = self.request.body.strip()
116 body = self.request.body.strip()
92 format = self.get_argument('format', default='json')
117 format = self.get_argument('format', default='json')
93 name = self.get_argument('name', default=None)
118 name = self.get_argument('name', default=None)
94 if body:
119 if body:
95 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
120 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
96 else:
121 else:
97 notebook_id = nbm.new_notebook()
122 notebook_id = nbm.new_notebook()
98 self.set_header('Location', '/'+notebook_id)
123 self.set_header('Location', '/'+notebook_id)
99 self.finish(json.dumps(notebook_id))
124 self.finish(json.dumps(notebook_id))
100
125
101
126
102 class NotebookHandler(web.RequestHandler):
127 class NotebookHandler(web.RequestHandler):
103
128
104 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
129 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
105
130
106 def get(self, notebook_id):
131 def get(self, notebook_id):
107 nbm = self.application.notebook_manager
132 nbm = self.application.notebook_manager
108 format = self.get_argument('format', default='json')
133 format = self.get_argument('format', default='json')
109 last_mod, name, data = nbm.get_notebook(notebook_id, format)
134 last_mod, name, data = nbm.get_notebook(notebook_id, format)
110 if format == u'json':
135 if format == u'json':
111 self.set_header('Content-Type', 'application/json')
136 self.set_header('Content-Type', 'application/json')
112 self.set_header('Content-Disposition','attachment; filename=%s.json' % name)
137 self.set_header('Content-Disposition','attachment; filename=%s.json' % name)
113 elif format == u'xml':
138 elif format == u'xml':
114 self.set_header('Content-Type', 'application/xml')
139 self.set_header('Content-Type', 'application/xml')
115 self.set_header('Content-Disposition','attachment; filename=%s.ipynb' % name)
140 self.set_header('Content-Disposition','attachment; filename=%s.ipynb' % name)
116 elif format == u'py':
141 elif format == u'py':
117 self.set_header('Content-Type', 'application/x-python')
142 self.set_header('Content-Type', 'application/x-python')
118 self.set_header('Content-Disposition','attachment; filename=%s.py' % name)
143 self.set_header('Content-Disposition','attachment; filename=%s.py' % name)
119 self.set_header('Last-Modified', last_mod)
144 self.set_header('Last-Modified', last_mod)
120 self.finish(data)
145 self.finish(data)
121
146
122 def put(self, notebook_id):
147 def put(self, notebook_id):
123 nbm = self.application.notebook_manager
148 nbm = self.application.notebook_manager
124 format = self.get_argument('format', default='json')
149 format = self.get_argument('format', default='json')
125 name = self.get_argument('name', default=None)
150 name = self.get_argument('name', default=None)
126 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
151 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
127 self.set_status(204)
152 self.set_status(204)
128 self.finish()
153 self.finish()
129
154
130 def delete(self, notebook_id):
155 def delete(self, notebook_id):
131 nbm = self.application.notebook_manager
156 nbm = self.application.notebook_manager
132 nbm.delete_notebook(notebook_id)
157 nbm.delete_notebook(notebook_id)
133 self.set_status(204)
158 self.set_status(204)
134 self.finish()
159 self.finish()
135
160
@@ -1,188 +1,306 b''
1 """A kernel manager for multiple kernels."""
1 """A kernel manager for multiple kernels."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
4 # Copyright (C) 2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import signal
14 import signal
15 import sys
15 import sys
16 import uuid
16 import uuid
17
17
18 import zmq
18 import zmq
19
19
20 from tornado import web
21
22 from .routers import IOPubStreamRouter, ShellStreamRouter
23
20 from IPython.config.configurable import LoggingConfigurable
24 from IPython.config.configurable import LoggingConfigurable
21 from IPython.zmq.ipkernel import launch_kernel
25 from IPython.zmq.ipkernel import launch_kernel
22 from IPython.utils.traitlets import Instance, Dict
26 from IPython.utils.traitlets import Instance, Dict, List, Unicode
23
27
24 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
25 # Classes
29 # Classes
26 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
27
31
28 class DuplicateKernelError(Exception):
32 class DuplicateKernelError(Exception):
29 pass
33 pass
30
34
31
35
32 class KernelManager(LoggingConfigurable):
36 class KernelManager(LoggingConfigurable):
33 """A class for managing multiple kernels."""
37 """A class for managing multiple kernels."""
34
38
35 context = Instance('zmq.Context')
39 context = Instance('zmq.Context')
36 def _context_default(self):
40 def _context_default(self):
37 return zmq.Context.instance()
41 return zmq.Context.instance()
38
42
39 _kernels = Dict()
43 _kernels = Dict()
40
44
41 @property
45 @property
42 def kernel_ids(self):
46 def kernel_ids(self):
43 """Return a list of the kernel ids of the active kernels."""
47 """Return a list of the kernel ids of the active kernels."""
44 return self._kernels.keys()
48 return self._kernels.keys()
45
49
46 def __len__(self):
50 def __len__(self):
47 """Return the number of running kernels."""
51 """Return the number of running kernels."""
48 return len(self.kernel_ids)
52 return len(self.kernel_ids)
49
53
50 def __contains__(self, kernel_id):
54 def __contains__(self, kernel_id):
51 if kernel_id in self.kernel_ids:
55 if kernel_id in self.kernel_ids:
52 return True
56 return True
53 else:
57 else:
54 return False
58 return False
55
59
56 def start_kernel(self, **kwargs):
60 def start_kernel(self, **kwargs):
57 """Start a new kernel."""
61 """Start a new kernel."""
58 kernel_id = str(uuid.uuid4())
62 kernel_id = unicode(uuid.uuid4())
59 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs)
63 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs)
60 # Store the information for contacting the kernel. This assumes the kernel is
64 # Store the information for contacting the kernel. This assumes the kernel is
61 # running on localhost.
65 # running on localhost.
62 d = dict(
66 d = dict(
63 process = process,
67 process = process,
64 stdin_port = stdin_port,
68 stdin_port = stdin_port,
65 iopub_port = iopub_port,
69 iopub_port = iopub_port,
66 shell_port = shell_port,
70 shell_port = shell_port,
67 hb_port = hb_port,
71 hb_port = hb_port,
68 ip = '127.0.0.1'
72 ip = '127.0.0.1'
69 )
73 )
70 self._kernels[kernel_id] = d
74 self._kernels[kernel_id] = d
71 return kernel_id
75 return kernel_id
72
76
73 def kill_kernel(self, kernel_id):
77 def kill_kernel(self, kernel_id):
74 """Kill a kernel by its kernel uuid.
78 """Kill a kernel by its kernel uuid.
75
79
76 Parameters
80 Parameters
77 ==========
81 ==========
78 kernel_id : uuid
82 kernel_id : uuid
79 The id of the kernel to kill.
83 The id of the kernel to kill.
80 """
84 """
81 kernel_process = self.get_kernel_process(kernel_id)
85 kernel_process = self.get_kernel_process(kernel_id)
82 if kernel_process is not None:
86 if kernel_process is not None:
83 # Attempt to kill the kernel.
87 # Attempt to kill the kernel.
84 try:
88 try:
85 kernel_process.kill()
89 kernel_process.kill()
86 except OSError, e:
90 except OSError, e:
87 # In Windows, we will get an Access Denied error if the process
91 # In Windows, we will get an Access Denied error if the process
88 # has already terminated. Ignore it.
92 # has already terminated. Ignore it.
89 if not (sys.platform == 'win32' and e.winerror == 5):
93 if not (sys.platform == 'win32' and e.winerror == 5):
90 raise
94 raise
91 del self._kernels[kernel_id]
95 del self._kernels[kernel_id]
92
96
93 def interrupt_kernel(self, kernel_id):
97 def interrupt_kernel(self, kernel_id):
94 """Interrupt (SIGINT) the kernel by its uuid.
98 """Interrupt (SIGINT) the kernel by its uuid.
95
99
96 Parameters
100 Parameters
97 ==========
101 ==========
98 kernel_id : uuid
102 kernel_id : uuid
99 The id of the kernel to interrupt.
103 The id of the kernel to interrupt.
100 """
104 """
101 kernel_process = self.get_kernel_process(kernel_id)
105 kernel_process = self.get_kernel_process(kernel_id)
102 if kernel_process is not None:
106 if kernel_process is not None:
103 if sys.platform == 'win32':
107 if sys.platform == 'win32':
104 from parentpoller import ParentPollerWindows as Poller
108 from parentpoller import ParentPollerWindows as Poller
105 Poller.send_interrupt(kernel_process.win32_interrupt_event)
109 Poller.send_interrupt(kernel_process.win32_interrupt_event)
106 else:
110 else:
107 kernel_process.send_signal(signal.SIGINT)
111 kernel_process.send_signal(signal.SIGINT)
108
112
109 def signal_kernel(self, kernel_id, signum):
113 def signal_kernel(self, kernel_id, signum):
110 """ Sends a signal to the kernel by its uuid.
114 """ Sends a signal to the kernel by its uuid.
111
115
112 Note that since only SIGTERM is supported on Windows, this function
116 Note that since only SIGTERM is supported on Windows, this function
113 is only useful on Unix systems.
117 is only useful on Unix systems.
114
118
115 Parameters
119 Parameters
116 ==========
120 ==========
117 kernel_id : uuid
121 kernel_id : uuid
118 The id of the kernel to signal.
122 The id of the kernel to signal.
119 """
123 """
120 kernel_process = self.get_kernel_process(kernel_id)
124 kernel_process = self.get_kernel_process(kernel_id)
121 if kernel_process is not None:
125 if kernel_process is not None:
122 kernel_process.send_signal(signum)
126 kernel_process.send_signal(signum)
123
127
124 def get_kernel_process(self, kernel_id):
128 def get_kernel_process(self, kernel_id):
125 """Get the process object for a kernel by its uuid.
129 """Get the process object for a kernel by its uuid.
126
130
127 Parameters
131 Parameters
128 ==========
132 ==========
129 kernel_id : uuid
133 kernel_id : uuid
130 The id of the kernel.
134 The id of the kernel.
131 """
135 """
132 d = self._kernels.get(kernel_id)
136 d = self._kernels.get(kernel_id)
133 if d is not None:
137 if d is not None:
134 return d['process']
138 return d['process']
135 else:
139 else:
136 raise KeyError("Kernel with id not found: %s" % kernel_id)
140 raise KeyError("Kernel with id not found: %s" % kernel_id)
137
141
138 def get_kernel_ports(self, kernel_id):
142 def get_kernel_ports(self, kernel_id):
139 """Return a dictionary of ports for a kernel.
143 """Return a dictionary of ports for a kernel.
140
144
141 Parameters
145 Parameters
142 ==========
146 ==========
143 kernel_id : uuid
147 kernel_id : uuid
144 The id of the kernel.
148 The id of the kernel.
145
149
146 Returns
150 Returns
147 =======
151 =======
148 port_dict : dict
152 port_dict : dict
149 A dict of key, value pairs where the keys are the names
153 A dict of key, value pairs where the keys are the names
150 (stdin_port,iopub_port,shell_port) and the values are the
154 (stdin_port,iopub_port,shell_port) and the values are the
151 integer port numbers for those channels.
155 integer port numbers for those channels.
152 """
156 """
153 d = self._kernels.get(kernel_id)
157 d = self._kernels.get(kernel_id)
154 if d is not None:
158 if d is not None:
155 dcopy = d.copy()
159 dcopy = d.copy()
156 dcopy.pop('process')
160 dcopy.pop('process')
157 dcopy.pop('ip')
161 dcopy.pop('ip')
158 return dcopy
162 return dcopy
159 else:
163 else:
160 raise KeyError("Kernel with id not found: %s" % kernel_id)
164 raise KeyError("Kernel with id not found: %s" % kernel_id)
161
165
162 def get_kernel_ip(self, kernel_id):
166 def get_kernel_ip(self, kernel_id):
163 """Return ip address for a kernel.
167 """Return ip address for a kernel.
164
168
165 Parameters
169 Parameters
166 ==========
170 ==========
167 kernel_id : uuid
171 kernel_id : uuid
168 The id of the kernel.
172 The id of the kernel.
169
173
170 Returns
174 Returns
171 =======
175 =======
172 ip : str
176 ip : str
173 The ip address of the kernel.
177 The ip address of the kernel.
174 """
178 """
175 d = self._kernels.get(kernel_id)
179 d = self._kernels.get(kernel_id)
176 if d is not None:
180 if d is not None:
177 return d['ip']
181 return d['ip']
178 else:
182 else:
179 raise KeyError("Kernel with id not found: %s" % kernel_id)
183 raise KeyError("Kernel with id not found: %s" % kernel_id)
180
184
181 def create_session_manager(self, kernel_id):
185 def create_session_manager(self, kernel_id):
182 """Create a new session manager for a kernel by its uuid."""
186 """Create a new session manager for a kernel by its uuid."""
183 from sessionmanager import SessionManager
187 from sessionmanager import SessionManager
184 return SessionManager(
188 return SessionManager(
185 kernel_id=kernel_id, kernel_manager=self,
189 kernel_id=kernel_id, kernel_manager=self,
186 config=self.config, context=self.context, log=self.log
190 config=self.config, context=self.context, log=self.log
187 )
191 )
188
192
193
194 class RoutingKernelManager(LoggingConfigurable):
195 """A KernelManager that handles WebSocket routing and HTTP error handling"""
196
197 kernel_argv = List(Unicode)
198 kernel_manager = Instance(KernelManager)
199
200 _routers = Dict()
201 _session_dict = Dict()
202 _notebook_mapping = Dict()
203
204 #-------------------------------------------------------------------------
205 # Methods for managing kernels and sessions
206 #-------------------------------------------------------------------------
207
208 @property
209 def kernel_ids(self):
210 return self.kernel_manager.kernel_ids
211
212 def notebook_for_kernel(self, kernel_id):
213 notebook_ids = [k for k, v in self._notebook_mapping.iteritems() if v == kernel_id]
214 if len(notebook_ids) == 1:
215 return notebook_ids[0]
216 else:
217 return None
218
219 def delete_mapping_for_kernel(self, kernel_id):
220 notebook_id = self.notebook_for_kernel(kernel_id)
221 if notebook_id is not None:
222 del self._notebook_mapping[notebook_id]
223
224 def start_kernel(self, notebook_id=None):
225 self.log.info
226 kernel_id = self._notebook_mapping.get(notebook_id)
227 if kernel_id is None:
228 kwargs = dict()
229 kwargs['extra_arguments'] = self.kernel_argv
230 kernel_id = self.kernel_manager.start_kernel(**kwargs)
231 if notebook_id is not None:
232 self._notebook_mapping[notebook_id] = kernel_id
233 self.log.info("Kernel started for notebook %s: %s" % (notebook_id,kernel_id))
234 self.log.debug("Kernel args: %r" % kwargs)
235 self.start_session_manager(kernel_id)
236 else:
237 self.log.info("Using existing kernel: %s" % kernel_id)
238 return kernel_id
239
240 def start_session_manager(self, kernel_id):
241 sm = self.kernel_manager.create_session_manager(kernel_id)
242 self._session_dict[kernel_id] = sm
243 iopub_stream = sm.get_iopub_stream()
244 shell_stream = sm.get_shell_stream()
245 iopub_router = IOPubStreamRouter(
246 zmq_stream=iopub_stream, session=sm.session, config=self.config
247 )
248 shell_router = ShellStreamRouter(
249 zmq_stream=shell_stream, session=sm.session, config=self.config
250 )
251 self._routers[(kernel_id, 'iopub')] = iopub_router
252 self._routers[(kernel_id, 'shell')] = shell_router
253
254 def kill_kernel(self, kernel_id):
255 if kernel_id not in self.kernel_manager:
256 raise web.HTTPError(404)
257 try:
258 sm = self._session_dict.pop(kernel_id)
259 except KeyError:
260 raise web.HTTPError(404)
261 sm.stop()
262 self.kernel_manager.kill_kernel(kernel_id)
263 self.delete_mapping_for_kernel(kernel_id)
264 self.log.info("Kernel killed: %s" % kernel_id)
265
266 def interrupt_kernel(self, kernel_id):
267 if kernel_id not in self.kernel_manager:
268 raise web.HTTPError(404)
269 self.kernel_manager.interrupt_kernel(kernel_id)
270 self.log.debug("Kernel interrupted: %s" % kernel_id)
271
272 def restart_kernel(self, kernel_id):
273 if kernel_id not in self.kernel_manager:
274 raise web.HTTPError(404)
275
276 # Get the notebook_id to preserve the kernel/notebook association
277 notebook_id = self.notebook_for_kernel(kernel_id)
278 # Create the new kernel first so we can move the clients over.
279 new_kernel_id = self.start_kernel()
280
281 # Copy the clients over to the new routers.
282 old_iopub_router = self.get_router(kernel_id, 'iopub')
283 old_shell_router = self.get_router(kernel_id, 'shell')
284 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
285 new_shell_router = self.get_router(new_kernel_id, 'shell')
286 new_iopub_router.copy_clients(old_iopub_router)
287 new_shell_router.copy_clients(old_shell_router)
288
289 # Now shutdown the old session and the kernel.
290 # TODO: This causes a hard crash in ZMQStream.close, which sets
291 # self.socket to None to hastily. We will need to fix this in PyZMQ
292 # itself. For now, we just leave the old kernel running :(
293 # Maybe this is fixed now, but nothing was changed really.
294 self.kill_kernel(kernel_id)
295
296 # Now save the new kernel/notebook association. We have to save it
297 # after the old kernel is killed as that will delete the mapping.
298 self._notebook_mapping[notebook_id] = kernel_id
299
300 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
301 return new_kernel_id
302
303 def get_router(self, kernel_id, stream_name):
304 router = self._routers[(kernel_id, stream_name)]
305 return router
306
@@ -1,270 +1,203 b''
1 """A tornado based IPython notebook server."""
1 """A tornado based IPython notebook server."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
4 # Copyright (C) 2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import logging
14 import logging
15 import os
15 import os
16 import signal
16 import signal
17 import sys
17 import sys
18
18
19 import zmq
19 import zmq
20
20
21 # Install the pyzmq ioloop. This has to be done before anything else from
21 # Install the pyzmq ioloop. This has to be done before anything else from
22 # tornado is imported.
22 # tornado is imported.
23 from zmq.eventloop import ioloop
23 from zmq.eventloop import ioloop
24 import tornado.ioloop
24 import tornado.ioloop
25 tornado.ioloop = ioloop
25 tornado.ioloop = ioloop
26
26
27 from tornado import httpserver
27 from tornado import httpserver
28 from tornado import web
28 from tornado import web
29
29
30 from .kernelmanager import KernelManager
30 from .kernelmanager import KernelManager, RoutingKernelManager
31 from .sessionmanager import SessionManager
31 from .sessionmanager import SessionManager
32 from .handlers import (
32 from .handlers import (
33 NBBrowserHandler, NewHandler, NamedNotebookHandler,
33 NBBrowserHandler, NewHandler, NamedNotebookHandler,
34 KernelHandler, KernelActionHandler, ZMQStreamHandler,
34 MainKernelHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
35 NotebookRootHandler, NotebookHandler
35 NotebookRootHandler, NotebookHandler
36 )
36 )
37 from .routers import IOPubStreamRouter, ShellStreamRouter
38 from .notebookmanager import NotebookManager
37 from .notebookmanager import NotebookManager
39
38
40 from IPython.core.application import BaseIPythonApplication
39 from IPython.core.application import BaseIPythonApplication
41 from IPython.core.profiledir import ProfileDir
40 from IPython.core.profiledir import ProfileDir
42 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
41 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
43 from IPython.zmq.session import Session
42 from IPython.zmq.session import Session
44 from IPython.zmq.zmqshell import ZMQInteractiveShell
43 from IPython.zmq.zmqshell import ZMQInteractiveShell
45 from IPython.zmq.ipkernel import (
44 from IPython.zmq.ipkernel import (
46 flags as ipkernel_flags,
45 flags as ipkernel_flags,
47 aliases as ipkernel_aliases,
46 aliases as ipkernel_aliases,
48 IPKernelApp
47 IPKernelApp
49 )
48 )
50 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
49 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
51
50
52 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
53 # Module globals
52 # Module globals
54 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
55
54
56 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
55 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
57 _kernel_action_regex = r"(?P<action>restart|interrupt)"
56 _kernel_action_regex = r"(?P<action>restart|interrupt)"
58 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
57 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
59
58
60 LOCALHOST = '127.0.0.1'
59 LOCALHOST = '127.0.0.1'
61
60
62 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
63 # The Tornado web application
62 # The Tornado web application
64 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
65
64
66 class NotebookWebApplication(web.Application):
65 class NotebookWebApplication(web.Application):
67
66
68 def __init__(self, kernel_manager, log, kernel_argv, config):
67 def __init__(self, routing_kernel_manager, notebook_manager, log):
69 handlers = [
68 handlers = [
70 (r"/", NBBrowserHandler),
69 (r"/", NBBrowserHandler),
71 (r"/new", NewHandler),
70 (r"/new", NewHandler),
72 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
71 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
73 (r"/kernels", KernelHandler),
72 (r"/kernels", MainKernelHandler),
73 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
74 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
74 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
75 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
75 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
76 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
76 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
77 (r"/notebooks", NotebookRootHandler),
77 (r"/notebooks", NotebookRootHandler),
78 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler)
78 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler)
79 ]
79 ]
80 settings = dict(
80 settings = dict(
81 template_path=os.path.join(os.path.dirname(__file__), "templates"),
81 template_path=os.path.join(os.path.dirname(__file__), "templates"),
82 static_path=os.path.join(os.path.dirname(__file__), "static"),
82 static_path=os.path.join(os.path.dirname(__file__), "static"),
83 )
83 )
84 web.Application.__init__(self, handlers, **settings)
84 web.Application.__init__(self, handlers, **settings)
85
85
86 self.kernel_manager = kernel_manager
86 self.routing_kernel_manager = routing_kernel_manager
87 self.log = log
87 self.log = log
88 self.kernel_argv = kernel_argv
88 self.notebook_manager = notebook_manager
89 self.config = config
90 self._routers = {}
91 self._session_dict = {}
92 self.notebook_manager = NotebookManager(config=self.config)
93
94 #-------------------------------------------------------------------------
95 # Methods for managing kernels and sessions
96 #-------------------------------------------------------------------------
97
98 @property
99 def kernel_ids(self):
100 return self.kernel_manager.kernel_ids
101
102 def start_kernel(self):
103 kwargs = dict()
104 kwargs['extra_arguments'] = self.kernel_argv
105 kernel_id = self.kernel_manager.start_kernel(**kwargs)
106 self.log.info("Kernel started: %s" % kernel_id)
107 self.log.debug("Kernel args: %r" % kwargs)
108 self.start_session_manager(kernel_id)
109 return kernel_id
110
111 def start_session_manager(self, kernel_id):
112 sm = self.kernel_manager.create_session_manager(kernel_id)
113 self._session_dict[kernel_id] = sm
114 iopub_stream = sm.get_iopub_stream()
115 shell_stream = sm.get_shell_stream()
116 iopub_router = IOPubStreamRouter(
117 zmq_stream=iopub_stream, session=sm.session, config=self.config
118 )
119 shell_router = ShellStreamRouter(
120 zmq_stream=shell_stream, session=sm.session, config=self.config
121 )
122 self._routers[(kernel_id, 'iopub')] = iopub_router
123 self._routers[(kernel_id, 'shell')] = shell_router
124
125 def kill_kernel(self, kernel_id):
126 sm = self._session_dict.pop(kernel_id)
127 sm.stop()
128 self.kernel_manager.kill_kernel(kernel_id)
129 self.log.info("Kernel killed: %s" % kernel_id)
130
131 def interrupt_kernel(self, kernel_id):
132 self.kernel_manager.interrupt_kernel(kernel_id)
133 self.log.debug("Kernel interrupted: %s" % kernel_id)
134
135 def restart_kernel(self, kernel_id):
136 # Create the new kernel first so we can move the clients over.
137 new_kernel_id = self.start_kernel()
138
139 # Copy the clients over to the new routers.
140 old_iopub_router = self.get_router(kernel_id, 'iopub')
141 old_shell_router = self.get_router(kernel_id, 'shell')
142 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
143 new_shell_router = self.get_router(new_kernel_id, 'shell')
144 new_iopub_router.copy_clients(old_iopub_router)
145 new_shell_router.copy_clients(old_shell_router)
146
147 # Now shutdown the old session and the kernel.
148 # TODO: This causes a hard crash in ZMQStream.close, which sets
149 # self.socket to None to hastily. We will need to fix this in PyZMQ
150 # itself. For now, we just leave the old kernel running :(
151 # Maybe this is fixed now, but nothing was changed really.
152 self.kill_kernel(kernel_id)
153
154 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
155 return new_kernel_id
156
157 def get_router(self, kernel_id, stream_name):
158 router = self._routers[(kernel_id, stream_name)]
159 return router
160
89
161
162
90
163 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
164 # Aliases and Flags
92 # Aliases and Flags
165 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
166
94
167 flags = dict(ipkernel_flags)
95 flags = dict(ipkernel_flags)
168
96
169 # the flags that are specific to the frontend
97 # the flags that are specific to the frontend
170 # these must be scrubbed before being passed to the kernel,
98 # these must be scrubbed before being passed to the kernel,
171 # or it will raise an error on unrecognized flags
99 # or it will raise an error on unrecognized flags
172 notebook_flags = []
100 notebook_flags = []
173
101
174 aliases = dict(ipkernel_aliases)
102 aliases = dict(ipkernel_aliases)
175
103
176 aliases.update(dict(
104 aliases.update(dict(
177 ip = 'IPythonNotebookApp.ip',
105 ip = 'IPythonNotebookApp.ip',
178 port = 'IPythonNotebookApp.port',
106 port = 'IPythonNotebookApp.port',
179 colors = 'ZMQInteractiveShell.colors',
107 colors = 'ZMQInteractiveShell.colors',
180 editor = 'RichIPythonWidget.editor',
108 editor = 'RichIPythonWidget.editor',
181 ))
109 ))
182
110
183 #-----------------------------------------------------------------------------
111 #-----------------------------------------------------------------------------
184 # IPythonNotebookApp
112 # IPythonNotebookApp
185 #-----------------------------------------------------------------------------
113 #-----------------------------------------------------------------------------
186
114
187 class IPythonNotebookApp(BaseIPythonApplication):
115 class IPythonNotebookApp(BaseIPythonApplication):
188 name = 'ipython-notebook'
116 name = 'ipython-notebook'
189 default_config_file_name='ipython_notebook_config.py'
117 default_config_file_name='ipython_notebook_config.py'
190
118
191 description = """
119 description = """
192 The IPython HTML Notebook.
120 The IPython HTML Notebook.
193
121
194 This launches a Tornado based HTML Notebook Server that serves up an
122 This launches a Tornado based HTML Notebook Server that serves up an
195 HTML5/Javascript Notebook client.
123 HTML5/Javascript Notebook client.
196 """
124 """
197
125
198 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
126 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
127 RoutingKernelManager, NotebookManager,
199 KernelManager, SessionManager, RichIPythonWidget]
128 KernelManager, SessionManager, RichIPythonWidget]
200 flags = Dict(flags)
129 flags = Dict(flags)
201 aliases = Dict(aliases)
130 aliases = Dict(aliases)
202
131
203 kernel_argv = List(Unicode)
132 kernel_argv = List(Unicode)
204
133
205 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
134 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
206 default_value=logging.INFO,
135 default_value=logging.INFO,
207 config=True,
136 config=True,
208 help="Set the log level by value or name.")
137 help="Set the log level by value or name.")
209
138
210 # connection info:
139 # connection info:
211 ip = Unicode(LOCALHOST, config=True,
140 ip = Unicode(LOCALHOST, config=True,
212 help="The IP address the notebook server will listen on."
141 help="The IP address the notebook server will listen on."
213 )
142 )
214
143
215 port = Int(8888, config=True,
144 port = Int(8888, config=True,
216 help="The port the notebook server will listen on."
145 help="The port the notebook server will listen on."
217 )
146 )
218
147
219 # the factory for creating a widget
148 # the factory for creating a widget
220 widget_factory = Any(RichIPythonWidget)
149 widget_factory = Any(RichIPythonWidget)
221
150
222 def parse_command_line(self, argv=None):
151 def parse_command_line(self, argv=None):
223 super(IPythonNotebookApp, self).parse_command_line(argv)
152 super(IPythonNotebookApp, self).parse_command_line(argv)
224 if argv is None:
153 if argv is None:
225 argv = sys.argv[1:]
154 argv = sys.argv[1:]
226
155
227 self.kernel_argv = list(argv) # copy
156 self.kernel_argv = list(argv) # copy
228 # kernel should inherit default config file from frontend
157 # kernel should inherit default config file from frontend
229 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
158 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
230 # scrub frontend-specific flags
159 # scrub frontend-specific flags
231 for a in argv:
160 for a in argv:
232 if a.startswith('-') and a.lstrip('-') in notebook_flags:
161 if a.startswith('-') and a.lstrip('-') in notebook_flags:
233 self.kernel_argv.remove(a)
162 self.kernel_argv.remove(a)
234
163
235 def init_kernel_manager(self):
164 def init_configurables(self):
236 # Don't let Qt or ZMQ swallow KeyboardInterupts.
165 # Don't let Qt or ZMQ swallow KeyboardInterupts.
237 signal.signal(signal.SIGINT, signal.SIG_DFL)
166 signal.signal(signal.SIGINT, signal.SIG_DFL)
238
167
239 # Create a KernelManager and start a kernel.
168 # Create a KernelManager and start a kernel.
240 self.kernel_manager = KernelManager(config=self.config, log=self.log)
169 self.kernel_manager = KernelManager(config=self.config, log=self.log)
170 self.routing_kernel_manager = RoutingKernelManager(config=self.config, log=self.log,
171 kernel_manager=self.kernel_manager, kernel_argv=self.kernel_argv
172 )
173 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
241
174
242 def init_logging(self):
175 def init_logging(self):
243 super(IPythonNotebookApp, self).init_logging()
176 super(IPythonNotebookApp, self).init_logging()
244 # This prevents double log messages because tornado use a root logger that
177 # This prevents double log messages because tornado use a root logger that
245 # self.log is a child of. The logging module dipatches log messages to a log
178 # self.log is a child of. The logging module dipatches log messages to a log
246 # and all of its ancenstors until propagate is set to False.
179 # and all of its ancenstors until propagate is set to False.
247 self.log.propagate = False
180 self.log.propagate = False
248
181
249 def initialize(self, argv=None):
182 def initialize(self, argv=None):
250 super(IPythonNotebookApp, self).initialize(argv)
183 super(IPythonNotebookApp, self).initialize(argv)
251 self.init_kernel_manager()
184 self.init_configurables()
252 self.web_app = NotebookWebApplication(
185 self.web_app = NotebookWebApplication(
253 self.kernel_manager, self.log, self.kernel_argv, self.config
186 self.routing_kernel_manager, self.notebook_manager, self.log
254 )
187 )
255 self.http_server = httpserver.HTTPServer(self.web_app)
188 self.http_server = httpserver.HTTPServer(self.web_app)
256 self.http_server.listen(self.port)
189 self.http_server.listen(self.port)
257
190
258 def start(self):
191 def start(self):
259 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
192 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
260 ioloop.IOLoop.instance().start()
193 ioloop.IOLoop.instance().start()
261
194
262 #-----------------------------------------------------------------------------
195 #-----------------------------------------------------------------------------
263 # Main entry point
196 # Main entry point
264 #-----------------------------------------------------------------------------
197 #-----------------------------------------------------------------------------
265
198
266 def launch_new_instance():
199 def launch_new_instance():
267 app = IPythonNotebookApp()
200 app = IPythonNotebookApp()
268 app.initialize()
201 app.initialize()
269 app.start()
202 app.start()
270
203
@@ -1,225 +1,225 b''
1 #-----------------------------------------------------------------------------
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2011 The IPython Development Team
2 # Copyright (C) 2011 The IPython Development Team
3 #
3 #
4 # Distributed under the terms of the BSD License. The full license is in
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING.txt, distributed as part of this software.
5 # the file COPYING.txt, distributed as part of this software.
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Imports
9 # Imports
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 import datetime
12 import datetime
13 import os
13 import os
14 import uuid
14 import uuid
15
15
16 from tornado import web
16 from tornado import web
17
17
18 from IPython.config.configurable import Configurable
18 from IPython.config.configurable import LoggingConfigurable
19 from IPython.nbformat import current
19 from IPython.nbformat import current
20 from IPython.utils.traitlets import Unicode, List, Dict
20 from IPython.utils.traitlets import Unicode, List, Dict
21
21
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class NotebookManager(Configurable):
28 class NotebookManager(LoggingConfigurable):
29
29
30 notebook_dir = Unicode(os.getcwd())
30 notebook_dir = Unicode(os.getcwd())
31 filename_ext = Unicode(u'.ipynb')
31 filename_ext = Unicode(u'.ipynb')
32 allowed_formats = List([u'json',u'xml',u'py'])
32 allowed_formats = List([u'json',u'xml',u'py'])
33
33
34 # Map notebook_ids to notebook names
34 # Map notebook_ids to notebook names
35 mapping = Dict()
35 mapping = Dict()
36 # Map notebook names to notebook_ids
36 # Map notebook names to notebook_ids
37 rev_mapping = Dict()
37 rev_mapping = Dict()
38
38
39 def list_notebooks(self):
39 def list_notebooks(self):
40 """List all notebooks in the notebook dir.
40 """List all notebooks in the notebook dir.
41
41
42 This returns a list of dicts of the form::
42 This returns a list of dicts of the form::
43
43
44 dict(notebook_id=notebook,name=name)
44 dict(notebook_id=notebook,name=name)
45 """
45 """
46 names = os.listdir(self.notebook_dir)
46 names = os.listdir(self.notebook_dir)
47 names = [name.split(u'.')[0] \
47 names = [name.split(u'.')[0] \
48 for name in names if name.endswith(self.filename_ext)]
48 for name in names if name.endswith(self.filename_ext)]
49 data = []
49 data = []
50 for name in names:
50 for name in names:
51 if name not in self.rev_mapping:
51 if name not in self.rev_mapping:
52 notebook_id = self.new_notebook_id(name)
52 notebook_id = self.new_notebook_id(name)
53 else:
53 else:
54 notebook_id = self.rev_mapping[name]
54 notebook_id = self.rev_mapping[name]
55 data.append(dict(notebook_id=notebook_id,name=name))
55 data.append(dict(notebook_id=notebook_id,name=name))
56 data = sorted(data, key=lambda item: item['name'])
56 data = sorted(data, key=lambda item: item['name'])
57 return data
57 return data
58
58
59 def new_notebook_id(self, name):
59 def new_notebook_id(self, name):
60 """Generate a new notebook_id for a name and store its mappings."""
60 """Generate a new notebook_id for a name and store its mappings."""
61 notebook_id = unicode(uuid.uuid4())
61 notebook_id = unicode(uuid.uuid4())
62 self.mapping[notebook_id] = name
62 self.mapping[notebook_id] = name
63 self.rev_mapping[name] = notebook_id
63 self.rev_mapping[name] = notebook_id
64 return notebook_id
64 return notebook_id
65
65
66 def delete_notebook_id(self, notebook_id):
66 def delete_notebook_id(self, notebook_id):
67 """Delete a notebook's id only. This doesn't delete the actual notebook."""
67 """Delete a notebook's id only. This doesn't delete the actual notebook."""
68 name = self.mapping[notebook_id]
68 name = self.mapping[notebook_id]
69 del self.mapping[notebook_id]
69 del self.mapping[notebook_id]
70 del self.rev_mapping[name]
70 del self.rev_mapping[name]
71
71
72 def notebook_exists(self, notebook_id):
72 def notebook_exists(self, notebook_id):
73 """Does a notebook exist?"""
73 """Does a notebook exist?"""
74 if notebook_id not in self.mapping:
74 if notebook_id not in self.mapping:
75 return False
75 return False
76 path = self.get_path_by_name(self.mapping[notebook_id])
76 path = self.get_path_by_name(self.mapping[notebook_id])
77 if not os.path.isfile(path):
77 if not os.path.isfile(path):
78 return False
78 return False
79 return True
79 return True
80
80
81 def find_path(self, notebook_id):
81 def find_path(self, notebook_id):
82 """Return a full path to a notebook given its notebook_id."""
82 """Return a full path to a notebook given its notebook_id."""
83 try:
83 try:
84 name = self.mapping[notebook_id]
84 name = self.mapping[notebook_id]
85 except KeyError:
85 except KeyError:
86 raise web.HTTPError(404)
86 raise web.HTTPError(404)
87 return self.get_path_by_name(name)
87 return self.get_path_by_name(name)
88
88
89 def get_path_by_name(self, name):
89 def get_path_by_name(self, name):
90 """Return a full path to a notebook given its name."""
90 """Return a full path to a notebook given its name."""
91 filename = name + self.filename_ext
91 filename = name + self.filename_ext
92 path = os.path.join(self.notebook_dir, filename)
92 path = os.path.join(self.notebook_dir, filename)
93 return path
93 return path
94
94
95 def get_notebook(self, notebook_id, format=u'json'):
95 def get_notebook(self, notebook_id, format=u'json'):
96 """Get the representation of a notebook in format by notebook_id."""
96 """Get the representation of a notebook in format by notebook_id."""
97 format = unicode(format)
97 format = unicode(format)
98 if format not in self.allowed_formats:
98 if format not in self.allowed_formats:
99 raise web.HTTPError(415)
99 raise web.HTTPError(415)
100 last_modified, nb = self.get_notebook_object(notebook_id)
100 last_modified, nb = self.get_notebook_object(notebook_id)
101 data = current.writes(nb, format)
101 data = current.writes(nb, format)
102 name = nb.get('name','notebook')
102 name = nb.get('name','notebook')
103 return last_modified, name, data
103 return last_modified, name, data
104
104
105 def get_notebook_object(self, notebook_id):
105 def get_notebook_object(self, notebook_id):
106 """Get the NotebookNode representation of a notebook by notebook_id."""
106 """Get the NotebookNode representation of a notebook by notebook_id."""
107 path = self.find_path(notebook_id)
107 path = self.find_path(notebook_id)
108 if not os.path.isfile(path):
108 if not os.path.isfile(path):
109 raise web.HTTPError(404)
109 raise web.HTTPError(404)
110 info = os.stat(path)
110 info = os.stat(path)
111 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
111 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
112 try:
112 try:
113 with open(path,'r') as f:
113 with open(path,'r') as f:
114 s = f.read()
114 s = f.read()
115 try:
115 try:
116 # v2 and later have xml in the .ipynb files.
116 # v2 and later have xml in the .ipynb files.
117 nb = current.reads(s, 'xml')
117 nb = current.reads(s, 'xml')
118 except:
118 except:
119 # v1 had json in the .ipynb files.
119 # v1 had json in the .ipynb files.
120 nb = current.reads(s, 'json')
120 nb = current.reads(s, 'json')
121 # v1 notebooks don't have a name field, so use the filename.
121 # v1 notebooks don't have a name field, so use the filename.
122 nb.name = os.path.split(path)[-1].split(u'.')[0]
122 nb.name = os.path.split(path)[-1].split(u'.')[0]
123 except:
123 except:
124 raise web.HTTPError(404)
124 raise web.HTTPError(404)
125 return last_modified, nb
125 return last_modified, nb
126
126
127 def save_new_notebook(self, data, name=None, format=u'json'):
127 def save_new_notebook(self, data, name=None, format=u'json'):
128 """Save a new notebook and return its notebook_id.
128 """Save a new notebook and return its notebook_id.
129
129
130 If a name is passed in, it overrides any values in the notebook data
130 If a name is passed in, it overrides any values in the notebook data
131 and the value in the data is updated to use that value.
131 and the value in the data is updated to use that value.
132 """
132 """
133 if format not in self.allowed_formats:
133 if format not in self.allowed_formats:
134 raise web.HTTPError(415)
134 raise web.HTTPError(415)
135
135
136 try:
136 try:
137 nb = current.reads(data, format)
137 nb = current.reads(data, format)
138 except:
138 except:
139 if format == u'xml':
139 if format == u'xml':
140 # v1 notebooks might come in with a format='xml' but be json.
140 # v1 notebooks might come in with a format='xml' but be json.
141 try:
141 try:
142 nb = current.reads(data, u'json')
142 nb = current.reads(data, u'json')
143 except:
143 except:
144 raise web.HTTPError(400)
144 raise web.HTTPError(400)
145 else:
145 else:
146 raise web.HTTPError(400)
146 raise web.HTTPError(400)
147
147
148 if name is None:
148 if name is None:
149 try:
149 try:
150 name = nb.name
150 name = nb.name
151 except AttributeError:
151 except AttributeError:
152 raise web.HTTPError(400)
152 raise web.HTTPError(400)
153 nb.name = name
153 nb.name = name
154
154
155 notebook_id = self.new_notebook_id(name)
155 notebook_id = self.new_notebook_id(name)
156 self.save_notebook_object(notebook_id, nb)
156 self.save_notebook_object(notebook_id, nb)
157 return notebook_id
157 return notebook_id
158
158
159 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
159 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
160 """Save an existing notebook by notebook_id."""
160 """Save an existing notebook by notebook_id."""
161 if format not in self.allowed_formats:
161 if format not in self.allowed_formats:
162 raise web.HTTPError(415)
162 raise web.HTTPError(415)
163
163
164 try:
164 try:
165 nb = current.reads(data, format)
165 nb = current.reads(data, format)
166 except:
166 except:
167 if format == u'xml':
167 if format == u'xml':
168 # v1 notebooks might come in with a format='xml' but be json.
168 # v1 notebooks might come in with a format='xml' but be json.
169 try:
169 try:
170 nb = current.reads(data, u'json')
170 nb = current.reads(data, u'json')
171 except:
171 except:
172 raise web.HTTPError(400)
172 raise web.HTTPError(400)
173 else:
173 else:
174 raise web.HTTPError(400)
174 raise web.HTTPError(400)
175
175
176 if name is not None:
176 if name is not None:
177 nb.name = name
177 nb.name = name
178 self.save_notebook_object(notebook_id, nb)
178 self.save_notebook_object(notebook_id, nb)
179
179
180 def save_notebook_object(self, notebook_id, nb):
180 def save_notebook_object(self, notebook_id, nb):
181 """Save an existing notebook object by notebook_id."""
181 """Save an existing notebook object by notebook_id."""
182 if notebook_id not in self.mapping:
182 if notebook_id not in self.mapping:
183 raise web.HTTPError(404)
183 raise web.HTTPError(404)
184 old_name = self.mapping[notebook_id]
184 old_name = self.mapping[notebook_id]
185 try:
185 try:
186 new_name = nb.name
186 new_name = nb.name
187 except AttributeError:
187 except AttributeError:
188 raise web.HTTPError(400)
188 raise web.HTTPError(400)
189 path = self.get_path_by_name(new_name)
189 path = self.get_path_by_name(new_name)
190 try:
190 try:
191 with open(path,'w') as f:
191 with open(path,'w') as f:
192 current.write(nb, f, u'xml')
192 current.write(nb, f, u'xml')
193 except:
193 except:
194 raise web.HTTPError(400)
194 raise web.HTTPError(400)
195 if old_name != new_name:
195 if old_name != new_name:
196 old_path = self.get_path_by_name(old_name)
196 old_path = self.get_path_by_name(old_name)
197 if os.path.isfile(old_path):
197 if os.path.isfile(old_path):
198 os.unlink(old_path)
198 os.unlink(old_path)
199 self.mapping[notebook_id] = new_name
199 self.mapping[notebook_id] = new_name
200 self.rev_mapping[new_name] = notebook_id
200 self.rev_mapping[new_name] = notebook_id
201
201
202 def delete_notebook(self, notebook_id):
202 def delete_notebook(self, notebook_id):
203 """Delete notebook by notebook_id."""
203 """Delete notebook by notebook_id."""
204 path = self.find_path(notebook_id)
204 path = self.find_path(notebook_id)
205 if not os.path.isfile(path):
205 if not os.path.isfile(path):
206 raise web.HTTPError(404)
206 raise web.HTTPError(404)
207 os.unlink(path)
207 os.unlink(path)
208 self.delete_notebook_id(notebook_id)
208 self.delete_notebook_id(notebook_id)
209
209
210 def new_notebook(self):
210 def new_notebook(self):
211 """Create a new notebook and returns its notebook_id."""
211 """Create a new notebook and returns its notebook_id."""
212 i = 0
212 i = 0
213 while True:
213 while True:
214 name = u'Untitled%i' % i
214 name = u'Untitled%i' % i
215 path = self.get_path_by_name(name)
215 path = self.get_path_by_name(name)
216 if not os.path.isfile(path):
216 if not os.path.isfile(path):
217 break
217 break
218 else:
218 else:
219 i = i+1
219 i = i+1
220 notebook_id = self.new_notebook_id(name)
220 notebook_id = self.new_notebook_id(name)
221 nb = current.new_notebook(name=name, id=notebook_id)
221 nb = current.new_notebook(name=name, id=notebook_id)
222 with open(path,'w') as f:
222 with open(path,'w') as f:
223 current.write(nb, f, u'xml')
223 current.write(nb, f, u'xml')
224 return notebook_id
224 return notebook_id
225
225
@@ -1,105 +1,106 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Kernel
3 // Kernel
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var Kernel = function () {
10 var Kernel = function () {
11 this.kernel_id = null;
11 this.kernel_id = null;
12 this.base_url = "/kernels";
12 this.base_url = "/kernels";
13 this.kernel_url = null;
13 this.kernel_url = null;
14 };
14 };
15
15
16
16
17 Kernel.prototype.get_msg = function (msg_type, content) {
17 Kernel.prototype.get_msg = function (msg_type, content) {
18 var msg = {
18 var msg = {
19 header : {
19 header : {
20 msg_id : utils.uuid(),
20 msg_id : utils.uuid(),
21 username : "username",
21 username : "username",
22 session: this.session_id,
22 session: this.session_id,
23 msg_type : msg_type
23 msg_type : msg_type
24 },
24 },
25 content : content,
25 content : content,
26 parent_header : {}
26 parent_header : {}
27 };
27 };
28 return msg;
28 return msg;
29 }
29 }
30
30
31 Kernel.prototype.start_kernel = function (callback) {
31 Kernel.prototype.start_kernel = function (notebook_id, callback) {
32 var that = this;
32 var that = this;
33 $.post(this.base_url,
33 var qs = $.param({notebook:notebook_id});
34 $.post(this.base_url + '?' + qs,
34 function (kernel_id) {
35 function (kernel_id) {
35 that._handle_start_kernel(kernel_id, callback);
36 that._handle_start_kernel(kernel_id, callback);
36 },
37 },
37 'json'
38 'json'
38 );
39 );
39 };
40 };
40
41
41
42
42 Kernel.prototype._handle_start_kernel = function (kernel_id, callback) {
43 Kernel.prototype._handle_start_kernel = function (kernel_id, callback) {
43 this.kernel_id = kernel_id;
44 this.kernel_id = kernel_id;
44 this.kernel_url = this.base_url + "/" + this.kernel_id;
45 this.kernel_url = this.base_url + "/" + this.kernel_id;
45 this._start_channels();
46 this._start_channels();
46 callback();
47 callback();
47 };
48 };
48
49
49
50
50 Kernel.prototype._start_channels = function () {
51 Kernel.prototype._start_channels = function () {
51 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
52 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
52 this.shell_channel = new WebSocket(ws_url + "/shell");
53 this.shell_channel = new WebSocket(ws_url + "/shell");
53 this.iopub_channel = new WebSocket(ws_url + "/iopub");
54 this.iopub_channel = new WebSocket(ws_url + "/iopub");
54 }
55 }
55
56
56
57
57 Kernel.prototype.execute = function (code) {
58 Kernel.prototype.execute = function (code) {
58 var content = {
59 var content = {
59 code : code,
60 code : code,
60 silent : false,
61 silent : false,
61 user_variables : [],
62 user_variables : [],
62 user_expressions : {}
63 user_expressions : {}
63 };
64 };
64 var msg = this.get_msg("execute_request", content);
65 var msg = this.get_msg("execute_request", content);
65 this.shell_channel.send(JSON.stringify(msg));
66 this.shell_channel.send(JSON.stringify(msg));
66 return msg.header.msg_id;
67 return msg.header.msg_id;
67 }
68 }
68
69
69
70
70 Kernel.prototype.complete = function (line, cursor_pos) {
71 Kernel.prototype.complete = function (line, cursor_pos) {
71 var content = {
72 var content = {
72 text : '',
73 text : '',
73 line : line,
74 line : line,
74 cursor_pos : cursor_pos
75 cursor_pos : cursor_pos
75 };
76 };
76 var msg = this.get_msg("complete_request", content);
77 var msg = this.get_msg("complete_request", content);
77 this.shell_channel.send(JSON.stringify(msg));
78 this.shell_channel.send(JSON.stringify(msg));
78 return msg.header.msg_id;
79 return msg.header.msg_id;
79 }
80 }
80
81
81
82
82 Kernel.prototype.interrupt = function () {
83 Kernel.prototype.interrupt = function () {
83 $.post(this.kernel_url + "/interrupt");
84 $.post(this.kernel_url + "/interrupt");
84 };
85 };
85
86
86
87
87 Kernel.prototype.restart = function () {
88 Kernel.prototype.restart = function () {
88 IPython.kernel_status_widget.status_restarting();
89 IPython.kernel_status_widget.status_restarting();
89 url = this.kernel_url + "/restart"
90 url = this.kernel_url + "/restart"
90 var that = this;
91 var that = this;
91 $.post(url, function (kernel_id) {
92 $.post(url, function (kernel_id) {
92 console.log("Kernel restarted: " + kernel_id);
93 console.log("Kernel restarted: " + kernel_id);
93 that.kernel_id = kernel_id;
94 that.kernel_id = kernel_id;
94 that.kernel_url = that.base_url + "/" + that.kernel_id;
95 that.kernel_url = that.base_url + "/" + that.kernel_id;
95 IPython.kernel_status_widget.status_idle();
96 IPython.kernel_status_widget.status_idle();
96 }, 'json');
97 }, 'json');
97 };
98 };
98
99
99
100
100 IPython.Kernel = Kernel;
101 IPython.Kernel = Kernel;
101
102
102 return IPython;
103 return IPython;
103
104
104 }(IPython));
105 }(IPython));
105
106
@@ -1,622 +1,623 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Notebook
3 // Notebook
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var Notebook = function (selector) {
10 var Notebook = function (selector) {
11 this.element = $(selector);
11 this.element = $(selector);
12 this.element.scroll();
12 this.element.scroll();
13 this.element.data("notebook", this);
13 this.element.data("notebook", this);
14 this.next_prompt_number = 1;
14 this.next_prompt_number = 1;
15 this.kernel = null;
15 this.kernel = null;
16 this.msg_cell_map = {};
16 this.msg_cell_map = {};
17 this.style();
17 this.style();
18 this.create_elements();
18 this.create_elements();
19 this.bind_events();
19 this.bind_events();
20 };
20 };
21
21
22
22
23 Notebook.prototype.style = function () {
23 Notebook.prototype.style = function () {
24 $('div#notebook').addClass('border-box-sizing');
24 $('div#notebook').addClass('border-box-sizing');
25 };
25 };
26
26
27
27
28 Notebook.prototype.create_elements = function () {
28 Notebook.prototype.create_elements = function () {
29 // We add this end_space div to the end of the notebook div to:
29 // We add this end_space div to the end of the notebook div to:
30 // i) provide a margin between the last cell and the end of the notebook
30 // i) provide a margin between the last cell and the end of the notebook
31 // ii) to prevent the div from scrolling up when the last cell is being
31 // ii) to prevent the div from scrolling up when the last cell is being
32 // edited, but is too low on the page, which browsers will do automatically.
32 // edited, but is too low on the page, which browsers will do automatically.
33 this.element.append($('<div class="end_space"></div>').height(50));
33 this.element.append($('<div class="end_space"></div>').height(50));
34 $('div#notebook').addClass('border-box-sizing');
34 $('div#notebook').addClass('border-box-sizing');
35 };
35 };
36
36
37
37
38 Notebook.prototype.bind_events = function () {
38 Notebook.prototype.bind_events = function () {
39 var that = this;
39 var that = this;
40 $(document).keydown(function (event) {
40 $(document).keydown(function (event) {
41 // console.log(event);
41 // console.log(event);
42 if (event.which === 38) {
42 if (event.which === 38) {
43 var cell = that.selected_cell();
43 var cell = that.selected_cell();
44 if (cell.at_top()) {
44 if (cell.at_top()) {
45 event.preventDefault();
45 event.preventDefault();
46 that.select_prev();
46 that.select_prev();
47 };
47 };
48 } else if (event.which === 40) {
48 } else if (event.which === 40) {
49 var cell = that.selected_cell();
49 var cell = that.selected_cell();
50 if (cell.at_bottom()) {
50 if (cell.at_bottom()) {
51 event.preventDefault();
51 event.preventDefault();
52 that.select_next();
52 that.select_next();
53 };
53 };
54 } else if (event.which === 13 && event.shiftKey) {
54 } else if (event.which === 13 && event.shiftKey) {
55 that.execute_selected_cell();
55 that.execute_selected_cell();
56 return false;
56 return false;
57 } else if (event.which === 13 && event.ctrlKey) {
57 } else if (event.which === 13 && event.ctrlKey) {
58 that.execute_selected_cell({terminal:true});
58 that.execute_selected_cell({terminal:true});
59 return false;
59 return false;
60 };
60 };
61 });
61 });
62
62
63 this.element.bind('collapse_pager', function () {
63 this.element.bind('collapse_pager', function () {
64 var app_height = $('div#main_app').height(); // content height
64 var app_height = $('div#main_app').height(); // content height
65 var splitter_height = $('div#pager_splitter').outerHeight(true);
65 var splitter_height = $('div#pager_splitter').outerHeight(true);
66 var new_height = app_height - splitter_height;
66 var new_height = app_height - splitter_height;
67 that.element.animate({height : new_height + 'px'}, 'fast');
67 that.element.animate({height : new_height + 'px'}, 'fast');
68 });
68 });
69
69
70 this.element.bind('expand_pager', function () {
70 this.element.bind('expand_pager', function () {
71 var app_height = $('div#main_app').height(); // content height
71 var app_height = $('div#main_app').height(); // content height
72 var splitter_height = $('div#pager_splitter').outerHeight(true);
72 var splitter_height = $('div#pager_splitter').outerHeight(true);
73 var pager_height = $('div#pager').outerHeight(true);
73 var pager_height = $('div#pager').outerHeight(true);
74 var new_height = app_height - pager_height - splitter_height;
74 var new_height = app_height - pager_height - splitter_height;
75 that.element.animate({height : new_height + 'px'}, 'fast');
75 that.element.animate({height : new_height + 'px'}, 'fast');
76 });
76 });
77
77
78 this.element.bind('collapse_left_panel', function () {
78 this.element.bind('collapse_left_panel', function () {
79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
80 var new_margin = splitter_width;
80 var new_margin = splitter_width;
81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
82 });
82 });
83
83
84 this.element.bind('expand_left_panel', function () {
84 this.element.bind('expand_left_panel', function () {
85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 var left_panel_width = IPython.left_panel.width;
86 var left_panel_width = IPython.left_panel.width;
87 var new_margin = splitter_width + left_panel_width;
87 var new_margin = splitter_width + left_panel_width;
88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
89 });
89 });
90 };
90 };
91
91
92
92
93 Notebook.prototype.scroll_to_bottom = function () {
93 Notebook.prototype.scroll_to_bottom = function () {
94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
95 };
95 };
96
96
97
97
98 Notebook.prototype.scroll_to_top = function () {
98 Notebook.prototype.scroll_to_top = function () {
99 this.element.animate({scrollTop:0}, 0);
99 this.element.animate({scrollTop:0}, 0);
100 };
100 };
101
101
102
102
103 // Cell indexing, retrieval, etc.
103 // Cell indexing, retrieval, etc.
104
104
105
105
106 Notebook.prototype.cell_elements = function () {
106 Notebook.prototype.cell_elements = function () {
107 return this.element.children("div.cell");
107 return this.element.children("div.cell");
108 }
108 }
109
109
110
110
111 Notebook.prototype.ncells = function (cell) {
111 Notebook.prototype.ncells = function (cell) {
112 return this.cell_elements().length;
112 return this.cell_elements().length;
113 }
113 }
114
114
115
115
116 // TODO: we are often calling cells as cells()[i], which we should optimize
116 // TODO: we are often calling cells as cells()[i], which we should optimize
117 // to cells(i) or a new method.
117 // to cells(i) or a new method.
118 Notebook.prototype.cells = function () {
118 Notebook.prototype.cells = function () {
119 return this.cell_elements().toArray().map(function (e) {
119 return this.cell_elements().toArray().map(function (e) {
120 return $(e).data("cell");
120 return $(e).data("cell");
121 });
121 });
122 }
122 }
123
123
124
124
125 Notebook.prototype.find_cell_index = function (cell) {
125 Notebook.prototype.find_cell_index = function (cell) {
126 var result = null;
126 var result = null;
127 this.cell_elements().filter(function (index) {
127 this.cell_elements().filter(function (index) {
128 if ($(this).data("cell") === cell) {
128 if ($(this).data("cell") === cell) {
129 result = index;
129 result = index;
130 };
130 };
131 });
131 });
132 return result;
132 return result;
133 };
133 };
134
134
135
135
136 Notebook.prototype.index_or_selected = function (index) {
136 Notebook.prototype.index_or_selected = function (index) {
137 return index || this.selected_index() || 0;
137 return index || this.selected_index() || 0;
138 }
138 }
139
139
140
140
141 Notebook.prototype.select = function (index) {
141 Notebook.prototype.select = function (index) {
142 if (index !== undefined && index >= 0 && index < this.ncells()) {
142 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 if (this.selected_index() !== null) {
143 if (this.selected_index() !== null) {
144 this.selected_cell().unselect();
144 this.selected_cell().unselect();
145 };
145 };
146 this.cells()[index].select();
146 this.cells()[index].select();
147 if (index === (this.ncells()-1)) {
147 if (index === (this.ncells()-1)) {
148 this.scroll_to_bottom();
148 this.scroll_to_bottom();
149 };
149 };
150 };
150 };
151 return this;
151 return this;
152 };
152 };
153
153
154
154
155 Notebook.prototype.select_next = function () {
155 Notebook.prototype.select_next = function () {
156 var index = this.selected_index();
156 var index = this.selected_index();
157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 this.select(index+1);
158 this.select(index+1);
159 };
159 };
160 return this;
160 return this;
161 };
161 };
162
162
163
163
164 Notebook.prototype.select_prev = function () {
164 Notebook.prototype.select_prev = function () {
165 var index = this.selected_index();
165 var index = this.selected_index();
166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 this.select(index-1);
167 this.select(index-1);
168 };
168 };
169 return this;
169 return this;
170 };
170 };
171
171
172
172
173 Notebook.prototype.selected_index = function () {
173 Notebook.prototype.selected_index = function () {
174 var result = null;
174 var result = null;
175 this.cell_elements().filter(function (index) {
175 this.cell_elements().filter(function (index) {
176 if ($(this).data("cell").selected === true) {
176 if ($(this).data("cell").selected === true) {
177 result = index;
177 result = index;
178 };
178 };
179 });
179 });
180 return result;
180 return result;
181 };
181 };
182
182
183
183
184 Notebook.prototype.cell_for_msg = function (msg_id) {
184 Notebook.prototype.cell_for_msg = function (msg_id) {
185 var cell_id = this.msg_cell_map[msg_id];
185 var cell_id = this.msg_cell_map[msg_id];
186 var result = null;
186 var result = null;
187 this.cell_elements().filter(function (index) {
187 this.cell_elements().filter(function (index) {
188 cell = $(this).data("cell");
188 cell = $(this).data("cell");
189 if (cell.cell_id === cell_id) {
189 if (cell.cell_id === cell_id) {
190 result = cell;
190 result = cell;
191 };
191 };
192 });
192 });
193 return result;
193 return result;
194 };
194 };
195
195
196
196
197 Notebook.prototype.selected_cell = function () {
197 Notebook.prototype.selected_cell = function () {
198 return this.cell_elements().eq(this.selected_index()).data("cell");
198 return this.cell_elements().eq(this.selected_index()).data("cell");
199 }
199 }
200
200
201
201
202 // Cell insertion, deletion and moving.
202 // Cell insertion, deletion and moving.
203
203
204
204
205 Notebook.prototype.delete_cell = function (index) {
205 Notebook.prototype.delete_cell = function (index) {
206 var i = index || this.selected_index();
206 var i = index || this.selected_index();
207 if (i !== null && i >= 0 && i < this.ncells()) {
207 if (i !== null && i >= 0 && i < this.ncells()) {
208 this.cell_elements().eq(i).remove();
208 this.cell_elements().eq(i).remove();
209 if (i === (this.ncells())) {
209 if (i === (this.ncells())) {
210 this.select(i-1);
210 this.select(i-1);
211 } else {
211 } else {
212 this.select(i);
212 this.select(i);
213 };
213 };
214 };
214 };
215 return this;
215 return this;
216 };
216 };
217
217
218
218
219 Notebook.prototype.append_cell = function (cell) {
219 Notebook.prototype.append_cell = function (cell) {
220 this.element.find('div.end_space').before(cell.element);
220 this.element.find('div.end_space').before(cell.element);
221 return this;
221 return this;
222 };
222 };
223
223
224
224
225 Notebook.prototype.insert_cell_after = function (cell, index) {
225 Notebook.prototype.insert_cell_after = function (cell, index) {
226 var ncells = this.ncells();
226 var ncells = this.ncells();
227 if (ncells === 0) {
227 if (ncells === 0) {
228 this.append_cell(cell);
228 this.append_cell(cell);
229 return this;
229 return this;
230 };
230 };
231 if (index >= 0 && index < ncells) {
231 if (index >= 0 && index < ncells) {
232 this.cell_elements().eq(index).after(cell.element);
232 this.cell_elements().eq(index).after(cell.element);
233 };
233 };
234 return this
234 return this
235 };
235 };
236
236
237
237
238 Notebook.prototype.insert_cell_before = function (cell, index) {
238 Notebook.prototype.insert_cell_before = function (cell, index) {
239 var ncells = this.ncells();
239 var ncells = this.ncells();
240 if (ncells === 0) {
240 if (ncells === 0) {
241 this.append_cell(cell);
241 this.append_cell(cell);
242 return this;
242 return this;
243 };
243 };
244 if (index >= 0 && index < ncells) {
244 if (index >= 0 && index < ncells) {
245 this.cell_elements().eq(index).before(cell.element);
245 this.cell_elements().eq(index).before(cell.element);
246 };
246 };
247 return this;
247 return this;
248 };
248 };
249
249
250
250
251 Notebook.prototype.move_cell_up = function (index) {
251 Notebook.prototype.move_cell_up = function (index) {
252 var i = index || this.selected_index();
252 var i = index || this.selected_index();
253 if (i !== null && i < this.ncells() && i > 0) {
253 if (i !== null && i < this.ncells() && i > 0) {
254 var pivot = this.cell_elements().eq(i-1);
254 var pivot = this.cell_elements().eq(i-1);
255 var tomove = this.cell_elements().eq(i);
255 var tomove = this.cell_elements().eq(i);
256 if (pivot !== null && tomove !== null) {
256 if (pivot !== null && tomove !== null) {
257 tomove.detach();
257 tomove.detach();
258 pivot.before(tomove);
258 pivot.before(tomove);
259 this.select(i-1);
259 this.select(i-1);
260 };
260 };
261 };
261 };
262 return this;
262 return this;
263 }
263 }
264
264
265
265
266 Notebook.prototype.move_cell_down = function (index) {
266 Notebook.prototype.move_cell_down = function (index) {
267 var i = index || this.selected_index();
267 var i = index || this.selected_index();
268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 var pivot = this.cell_elements().eq(i+1)
269 var pivot = this.cell_elements().eq(i+1)
270 var tomove = this.cell_elements().eq(i)
270 var tomove = this.cell_elements().eq(i)
271 if (pivot !== null && tomove !== null) {
271 if (pivot !== null && tomove !== null) {
272 tomove.detach();
272 tomove.detach();
273 pivot.after(tomove);
273 pivot.after(tomove);
274 this.select(i+1);
274 this.select(i+1);
275 };
275 };
276 };
276 };
277 return this;
277 return this;
278 }
278 }
279
279
280
280
281 Notebook.prototype.sort_cells = function () {
281 Notebook.prototype.sort_cells = function () {
282 var ncells = this.ncells();
282 var ncells = this.ncells();
283 var sindex = this.selected_index();
283 var sindex = this.selected_index();
284 var swapped;
284 var swapped;
285 do {
285 do {
286 swapped = false
286 swapped = false
287 for (var i=1; i<ncells; i++) {
287 for (var i=1; i<ncells; i++) {
288 current = this.cell_elements().eq(i).data("cell");
288 current = this.cell_elements().eq(i).data("cell");
289 previous = this.cell_elements().eq(i-1).data("cell");
289 previous = this.cell_elements().eq(i-1).data("cell");
290 if (previous.input_prompt_number > current.input_prompt_number) {
290 if (previous.input_prompt_number > current.input_prompt_number) {
291 this.move_cell_up(i);
291 this.move_cell_up(i);
292 swapped = true;
292 swapped = true;
293 };
293 };
294 };
294 };
295 } while (swapped);
295 } while (swapped);
296 this.select(sindex);
296 this.select(sindex);
297 return this;
297 return this;
298 };
298 };
299
299
300
300
301 Notebook.prototype.insert_code_cell_before = function (index) {
301 Notebook.prototype.insert_code_cell_before = function (index) {
302 // TODO: Bounds check for i
302 // TODO: Bounds check for i
303 var i = this.index_or_selected(index);
303 var i = this.index_or_selected(index);
304 var cell = new IPython.CodeCell(this);
304 var cell = new IPython.CodeCell(this);
305 cell.set_input_prompt();
305 cell.set_input_prompt();
306 this.insert_cell_before(cell, i);
306 this.insert_cell_before(cell, i);
307 this.select(this.find_cell_index(cell));
307 this.select(this.find_cell_index(cell));
308 return cell;
308 return cell;
309 }
309 }
310
310
311
311
312 Notebook.prototype.insert_code_cell_after = function (index) {
312 Notebook.prototype.insert_code_cell_after = function (index) {
313 // TODO: Bounds check for i
313 // TODO: Bounds check for i
314 var i = this.index_or_selected(index);
314 var i = this.index_or_selected(index);
315 var cell = new IPython.CodeCell(this);
315 var cell = new IPython.CodeCell(this);
316 cell.set_input_prompt();
316 cell.set_input_prompt();
317 this.insert_cell_after(cell, i);
317 this.insert_cell_after(cell, i);
318 this.select(this.find_cell_index(cell));
318 this.select(this.find_cell_index(cell));
319 return cell;
319 return cell;
320 }
320 }
321
321
322
322
323 Notebook.prototype.insert_text_cell_before = function (index) {
323 Notebook.prototype.insert_text_cell_before = function (index) {
324 // TODO: Bounds check for i
324 // TODO: Bounds check for i
325 var i = this.index_or_selected(index);
325 var i = this.index_or_selected(index);
326 var cell = new IPython.TextCell(this);
326 var cell = new IPython.TextCell(this);
327 cell.config_mathjax();
327 cell.config_mathjax();
328 this.insert_cell_before(cell, i);
328 this.insert_cell_before(cell, i);
329 this.select(this.find_cell_index(cell));
329 this.select(this.find_cell_index(cell));
330 return cell;
330 return cell;
331 }
331 }
332
332
333
333
334 Notebook.prototype.insert_text_cell_after = function (index) {
334 Notebook.prototype.insert_text_cell_after = function (index) {
335 // TODO: Bounds check for i
335 // TODO: Bounds check for i
336 var i = this.index_or_selected(index);
336 var i = this.index_or_selected(index);
337 var cell = new IPython.TextCell(this);
337 var cell = new IPython.TextCell(this);
338 cell.config_mathjax();
338 cell.config_mathjax();
339 this.insert_cell_after(cell, i);
339 this.insert_cell_after(cell, i);
340 this.select(this.find_cell_index(cell));
340 this.select(this.find_cell_index(cell));
341 return cell;
341 return cell;
342 }
342 }
343
343
344
344
345 Notebook.prototype.text_to_code = function (index) {
345 Notebook.prototype.text_to_code = function (index) {
346 // TODO: Bounds check for i
346 // TODO: Bounds check for i
347 var i = this.index_or_selected(index);
347 var i = this.index_or_selected(index);
348 var source_element = this.cell_elements().eq(i);
348 var source_element = this.cell_elements().eq(i);
349 var source_cell = source_element.data("cell");
349 var source_cell = source_element.data("cell");
350 if (source_cell instanceof IPython.TextCell) {
350 if (source_cell instanceof IPython.TextCell) {
351 this.insert_code_cell_after(i);
351 this.insert_code_cell_after(i);
352 var target_cell = this.cells()[i+1];
352 var target_cell = this.cells()[i+1];
353 target_cell.set_code(source_cell.get_text());
353 target_cell.set_code(source_cell.get_text());
354 source_element.remove();
354 source_element.remove();
355 };
355 };
356 };
356 };
357
357
358
358
359 Notebook.prototype.code_to_text = function (index) {
359 Notebook.prototype.code_to_text = function (index) {
360 // TODO: Bounds check for i
360 // TODO: Bounds check for i
361 var i = this.index_or_selected(index);
361 var i = this.index_or_selected(index);
362 var source_element = this.cell_elements().eq(i);
362 var source_element = this.cell_elements().eq(i);
363 var source_cell = source_element.data("cell");
363 var source_cell = source_element.data("cell");
364 if (source_cell instanceof IPython.CodeCell) {
364 if (source_cell instanceof IPython.CodeCell) {
365 this.insert_text_cell_after(i);
365 this.insert_text_cell_after(i);
366 var target_cell = this.cells()[i+1];
366 var target_cell = this.cells()[i+1];
367 var text = source_cell.get_code();
367 var text = source_cell.get_code();
368 if (text === "") {text = target_cell.placeholder;};
368 if (text === "") {text = target_cell.placeholder;};
369 target_cell.set_text(text);
369 target_cell.set_text(text);
370 source_element.remove();
370 source_element.remove();
371 target_cell.edit();
371 target_cell.edit();
372 };
372 };
373 };
373 };
374
374
375
375
376 // Cell collapsing
376 // Cell collapsing
377
377
378 Notebook.prototype.collapse = function (index) {
378 Notebook.prototype.collapse = function (index) {
379 var i = this.index_or_selected(index);
379 var i = this.index_or_selected(index);
380 this.cells()[i].collapse();
380 this.cells()[i].collapse();
381 };
381 };
382
382
383
383
384 Notebook.prototype.expand = function (index) {
384 Notebook.prototype.expand = function (index) {
385 var i = this.index_or_selected(index);
385 var i = this.index_or_selected(index);
386 this.cells()[i].expand();
386 this.cells()[i].expand();
387 };
387 };
388
388
389
389
390 // Kernel related things
390 // Kernel related things
391
391
392 Notebook.prototype.start_kernel = function () {
392 Notebook.prototype.start_kernel = function () {
393 this.kernel = new IPython.Kernel();
393 this.kernel = new IPython.Kernel();
394 this.kernel.start_kernel($.proxy(this.kernel_started, this));
394 var notebook_id = IPython.save_widget.get_notebook_id();
395 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
395 };
396 };
396
397
397
398
398 Notebook.prototype.handle_shell_reply = function (e) {
399 Notebook.prototype.handle_shell_reply = function (e) {
399 reply = $.parseJSON(e.data);
400 reply = $.parseJSON(e.data);
400 var header = reply.header;
401 var header = reply.header;
401 var content = reply.content;
402 var content = reply.content;
402 var msg_type = header.msg_type;
403 var msg_type = header.msg_type;
403 // console.log(reply);
404 // console.log(reply);
404 var cell = this.cell_for_msg(reply.parent_header.msg_id);
405 var cell = this.cell_for_msg(reply.parent_header.msg_id);
405 if (msg_type === "execute_reply") {
406 if (msg_type === "execute_reply") {
406 cell.set_input_prompt(content.execution_count);
407 cell.set_input_prompt(content.execution_count);
407 } else if (msg_type === "complete_reply") {
408 } else if (msg_type === "complete_reply") {
408 cell.finish_completing(content.matched_text, content.matches);
409 cell.finish_completing(content.matched_text, content.matches);
409 };
410 };
410 var payload = content.payload || [];
411 var payload = content.payload || [];
411 this.handle_payload(payload);
412 this.handle_payload(payload);
412 };
413 };
413
414
414
415
415 Notebook.prototype.handle_payload = function (payload) {
416 Notebook.prototype.handle_payload = function (payload) {
416 var l = payload.length;
417 var l = payload.length;
417 if (l > 0) {
418 if (l > 0) {
418 IPython.pager.clear();
419 IPython.pager.clear();
419 IPython.pager.expand();
420 IPython.pager.expand();
420 };
421 };
421 for (var i=0; i<l; i++) {
422 for (var i=0; i<l; i++) {
422 IPython.pager.append_text(payload[i].text);
423 IPython.pager.append_text(payload[i].text);
423 };
424 };
424 };
425 };
425
426
426
427
427 Notebook.prototype.handle_iopub_reply = function (e) {
428 Notebook.prototype.handle_iopub_reply = function (e) {
428 reply = $.parseJSON(e.data);
429 reply = $.parseJSON(e.data);
429 var content = reply.content;
430 var content = reply.content;
430 // console.log(reply);
431 // console.log(reply);
431 var msg_type = reply.header.msg_type;
432 var msg_type = reply.header.msg_type;
432 var cell = this.cell_for_msg(reply.parent_header.msg_id);
433 var cell = this.cell_for_msg(reply.parent_header.msg_id);
433 if (msg_type === "stream") {
434 if (msg_type === "stream") {
434 cell.expand();
435 cell.expand();
435 cell.append_stream(content.data + "\n");
436 cell.append_stream(content.data + "\n");
436 } else if (msg_type === "display_data") {
437 } else if (msg_type === "display_data") {
437 cell.expand();
438 cell.expand();
438 cell.append_display_data(content.data);
439 cell.append_display_data(content.data);
439 } else if (msg_type === "pyout") {
440 } else if (msg_type === "pyout") {
440 cell.expand();
441 cell.expand();
441 cell.append_pyout(content.data, content.execution_count)
442 cell.append_pyout(content.data, content.execution_count)
442 } else if (msg_type === "pyerr") {
443 } else if (msg_type === "pyerr") {
443 cell.expand();
444 cell.expand();
444 cell.append_pyerr(content.ename, content.evalue, content.traceback);
445 cell.append_pyerr(content.ename, content.evalue, content.traceback);
445 } else if (msg_type === "status") {
446 } else if (msg_type === "status") {
446 if (content.execution_state === "busy") {
447 if (content.execution_state === "busy") {
447 IPython.kernel_status_widget.status_busy();
448 IPython.kernel_status_widget.status_busy();
448 } else if (content.execution_state === "idle") {
449 } else if (content.execution_state === "idle") {
449 IPython.kernel_status_widget.status_idle();
450 IPython.kernel_status_widget.status_idle();
450 };
451 };
451 }
452 }
452 };
453 };
453
454
454
455
455 Notebook.prototype.kernel_started = function () {
456 Notebook.prototype.kernel_started = function () {
456 console.log("Kernel started: ", this.kernel.kernel_id);
457 console.log("Kernel started: ", this.kernel.kernel_id);
457 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
458 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
458 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
459 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
459 };
460 };
460
461
461
462
462 Notebook.prototype.execute_selected_cell = function (options) {
463 Notebook.prototype.execute_selected_cell = function (options) {
463 // add_new: should a new cell be added if we are at the end of the nb
464 // add_new: should a new cell be added if we are at the end of the nb
464 // terminal: execute in terminal mode, which stays in the current cell
465 // terminal: execute in terminal mode, which stays in the current cell
465 default_options = {terminal: false, add_new: true}
466 default_options = {terminal: false, add_new: true}
466 $.extend(default_options, options)
467 $.extend(default_options, options)
467 var that = this;
468 var that = this;
468 var cell = that.selected_cell();
469 var cell = that.selected_cell();
469 var cell_index = that.find_cell_index(cell);
470 var cell_index = that.find_cell_index(cell);
470 if (cell instanceof IPython.CodeCell) {
471 if (cell instanceof IPython.CodeCell) {
471 cell.clear_output();
472 cell.clear_output();
472 var code = cell.get_code();
473 var code = cell.get_code();
473 var msg_id = that.kernel.execute(cell.get_code());
474 var msg_id = that.kernel.execute(cell.get_code());
474 that.msg_cell_map[msg_id] = cell.cell_id;
475 that.msg_cell_map[msg_id] = cell.cell_id;
475 } else if (cell instanceof IPython.TextCell) {
476 } else if (cell instanceof IPython.TextCell) {
476 cell.render();
477 cell.render();
477 }
478 }
478 if (default_options.terminal) {
479 if (default_options.terminal) {
479 cell.clear_input();
480 cell.clear_input();
480 } else {
481 } else {
481 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
482 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
482 that.insert_code_cell_after();
483 that.insert_code_cell_after();
483 // If we are adding a new cell at the end, scroll down to show it.
484 // If we are adding a new cell at the end, scroll down to show it.
484 that.scroll_to_bottom();
485 that.scroll_to_bottom();
485 } else {
486 } else {
486 that.select(cell_index+1);
487 that.select(cell_index+1);
487 };
488 };
488 };
489 };
489 };
490 };
490
491
491
492
492 Notebook.prototype.execute_all_cells = function () {
493 Notebook.prototype.execute_all_cells = function () {
493 var ncells = this.ncells();
494 var ncells = this.ncells();
494 for (var i=0; i<ncells; i++) {
495 for (var i=0; i<ncells; i++) {
495 this.select(i);
496 this.select(i);
496 this.execute_selected_cell({add_new:false});
497 this.execute_selected_cell({add_new:false});
497 };
498 };
498 this.scroll_to_bottom();
499 this.scroll_to_bottom();
499 };
500 };
500
501
501
502
502 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
503 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
503 var msg_id = this.kernel.complete(line, cursor_pos);
504 var msg_id = this.kernel.complete(line, cursor_pos);
504 this.msg_cell_map[msg_id] = cell.cell_id;
505 this.msg_cell_map[msg_id] = cell.cell_id;
505 };
506 };
506
507
507 // Persistance and loading
508 // Persistance and loading
508
509
509
510
510 Notebook.prototype.fromJSON = function (data) {
511 Notebook.prototype.fromJSON = function (data) {
511 var ncells = this.ncells();
512 var ncells = this.ncells();
512 for (var i=0; i<ncells; i++) {
513 for (var i=0; i<ncells; i++) {
513 // Always delete cell 0 as they get renumbered as they are deleted.
514 // Always delete cell 0 as they get renumbered as they are deleted.
514 this.delete_cell(0);
515 this.delete_cell(0);
515 };
516 };
516 // Only handle 1 worksheet for now.
517 // Only handle 1 worksheet for now.
517 var worksheet = data.worksheets[0];
518 var worksheet = data.worksheets[0];
518 if (worksheet !== undefined) {
519 if (worksheet !== undefined) {
519 var new_cells = worksheet.cells;
520 var new_cells = worksheet.cells;
520 ncells = new_cells.length;
521 ncells = new_cells.length;
521 var cell_data = null;
522 var cell_data = null;
522 var new_cell = null;
523 var new_cell = null;
523 for (var i=0; i<ncells; i++) {
524 for (var i=0; i<ncells; i++) {
524 cell_data = new_cells[i];
525 cell_data = new_cells[i];
525 if (cell_data.cell_type == 'code') {
526 if (cell_data.cell_type == 'code') {
526 new_cell = this.insert_code_cell_after();
527 new_cell = this.insert_code_cell_after();
527 new_cell.fromJSON(cell_data);
528 new_cell.fromJSON(cell_data);
528 } else if (cell_data.cell_type === 'text') {
529 } else if (cell_data.cell_type === 'text') {
529 new_cell = this.insert_text_cell_after();
530 new_cell = this.insert_text_cell_after();
530 new_cell.fromJSON(cell_data);
531 new_cell.fromJSON(cell_data);
531 };
532 };
532 };
533 };
533 };
534 };
534 };
535 };
535
536
536
537
537 Notebook.prototype.toJSON = function () {
538 Notebook.prototype.toJSON = function () {
538 var cells = this.cells();
539 var cells = this.cells();
539 var ncells = cells.length;
540 var ncells = cells.length;
540 cell_array = new Array(ncells);
541 cell_array = new Array(ncells);
541 for (var i=0; i<ncells; i++) {
542 for (var i=0; i<ncells; i++) {
542 cell_array[i] = cells[i].toJSON();
543 cell_array[i] = cells[i].toJSON();
543 };
544 };
544 data = {
545 data = {
545 // Only handle 1 worksheet for now.
546 // Only handle 1 worksheet for now.
546 worksheets : [{cells:cell_array}]
547 worksheets : [{cells:cell_array}]
547 }
548 }
548 return data
549 return data
549 };
550 };
550
551
551 Notebook.prototype.save_notebook = function () {
552 Notebook.prototype.save_notebook = function () {
552 if (IPython.save_widget.test_notebook_name()) {
553 if (IPython.save_widget.test_notebook_name()) {
553 var notebook_id = IPython.save_widget.get_notebook_id();
554 var notebook_id = IPython.save_widget.get_notebook_id();
554 var nbname = IPython.save_widget.get_notebook_name();
555 var nbname = IPython.save_widget.get_notebook_name();
555 // We may want to move the name/id/nbformat logic inside toJSON?
556 // We may want to move the name/id/nbformat logic inside toJSON?
556 var data = this.toJSON();
557 var data = this.toJSON();
557 data.name = nbname;
558 data.name = nbname;
558 data.nbformat = 2;
559 data.nbformat = 2;
559 data.id = notebook_id
560 data.id = notebook_id
560 // We do the call with settings so we can set cache to false.
561 // We do the call with settings so we can set cache to false.
561 var settings = {
562 var settings = {
562 processData : false,
563 processData : false,
563 cache : false,
564 cache : false,
564 type : "PUT",
565 type : "PUT",
565 data : JSON.stringify(data),
566 data : JSON.stringify(data),
566 headers : {'Content-Type': 'application/json'},
567 headers : {'Content-Type': 'application/json'},
567 success : $.proxy(this.notebook_saved,this)
568 success : $.proxy(this.notebook_saved,this)
568 };
569 };
569 IPython.save_widget.status_saving();
570 IPython.save_widget.status_saving();
570 $.ajax("/notebooks/" + notebook_id, settings);
571 $.ajax("/notebooks/" + notebook_id, settings);
571 };
572 };
572 };
573 };
573
574
574
575
575 Notebook.prototype.notebook_saved = function (data, status, xhr) {
576 Notebook.prototype.notebook_saved = function (data, status, xhr) {
576 IPython.save_widget.status_save();
577 IPython.save_widget.status_save();
577 }
578 }
578
579
579
580
580 Notebook.prototype.load_notebook = function (callback) {
581 Notebook.prototype.load_notebook = function (callback) {
581 var that = this;
582 var that = this;
582 var notebook_id = IPython.save_widget.get_notebook_id();
583 var notebook_id = IPython.save_widget.get_notebook_id();
583 // We do the call with settings so we can set cache to false.
584 // We do the call with settings so we can set cache to false.
584 var settings = {
585 var settings = {
585 processData : false,
586 processData : false,
586 cache : false,
587 cache : false,
587 type : "GET",
588 type : "GET",
588 dataType : "json",
589 dataType : "json",
589 success : function (data, status, xhr) {
590 success : function (data, status, xhr) {
590 that.notebook_loaded(data, status, xhr);
591 that.notebook_loaded(data, status, xhr);
591 if (callback !== undefined) {
592 if (callback !== undefined) {
592 callback();
593 callback();
593 };
594 };
594 }
595 }
595 };
596 };
596 IPython.save_widget.status_loading();
597 IPython.save_widget.status_loading();
597 $.ajax("/notebooks/" + notebook_id, settings);
598 $.ajax("/notebooks/" + notebook_id, settings);
598 }
599 }
599
600
600
601
601 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
602 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
602 this.fromJSON(data);
603 this.fromJSON(data);
603 if (this.ncells() === 0) {
604 if (this.ncells() === 0) {
604 this.insert_code_cell_after();
605 this.insert_code_cell_after();
605 };
606 };
606 IPython.save_widget.status_save();
607 IPython.save_widget.status_save();
607 IPython.save_widget.set_notebook_name(data.name);
608 IPython.save_widget.set_notebook_name(data.name);
608 this.start_kernel();
609 this.start_kernel();
609 // fromJSON always selects the last cell inserted. We need to wait
610 // fromJSON always selects the last cell inserted. We need to wait
610 // until that is done before scrolling to the top.
611 // until that is done before scrolling to the top.
611 setTimeout(function () {
612 setTimeout(function () {
612 IPython.notebook.select(0);
613 IPython.notebook.select(0);
613 IPython.notebook.scroll_to_top();
614 IPython.notebook.scroll_to_top();
614 }, 50);
615 }, 50);
615 };
616 };
616
617
617 IPython.Notebook = Notebook;
618 IPython.Notebook = Notebook;
618
619
619 return IPython;
620 return IPython;
620
621
621 }(IPython));
622 }(IPython));
622
623
General Comments 0
You need to be logged in to leave comments. Login now