##// END OF EJS Templates
Adding tracebacks, evalue and etype to the nbformat and notebook.
Brian E. Granger -
Show More
@@ -1,212 +1,195 b''
1 1 """Tornado handlers for the notebook."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6
7 7 import json
8 8 import logging
9 9 import os
10 10 import urllib
11 11
12 12 from tornado import web
13 13 from tornado import websocket
14 14
15 15 try:
16 16 from docutils.core import publish_string
17 17 except ImportError:
18 18 publish_string = None
19 19
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Top-level handlers
23 23 #-----------------------------------------------------------------------------
24 24
25 25
26 26 class NBBrowserHandler(web.RequestHandler):
27 27 def get(self):
28 28 nbm = self.application.notebook_manager
29 29 project = nbm.notebook_dir
30 30 self.render('nbbrowser.html', project=project)
31 31
32 32
33 33 class NewHandler(web.RequestHandler):
34 34 def get(self):
35 35 notebook_id = self.application.notebook_manager.new_notebook()
36 36 self.render('notebook.html', notebook_id=notebook_id)
37 37
38 38
39 39 class NamedNotebookHandler(web.RequestHandler):
40 40 def get(self, notebook_id):
41 41 nbm = self.application.notebook_manager
42 42 if not nbm.notebook_exists(notebook_id):
43 43 raise web.HTTPError(404)
44 44 self.render('notebook.html', notebook_id=notebook_id)
45 45
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Kernel handlers
49 49 #-----------------------------------------------------------------------------
50 50
51 51
52 52 class MainKernelHandler(web.RequestHandler):
53 53
54 54 def get(self):
55 55 rkm = self.application.routing_kernel_manager
56 56 self.finish(json.dumps(rkm.kernel_ids))
57 57
58 58 def post(self):
59 59 rkm = self.application.routing_kernel_manager
60 60 notebook_id = self.get_argument('notebook', default=None)
61 61 kernel_id = rkm.start_kernel(notebook_id)
62 62 self.set_header('Location', '/'+kernel_id)
63 63 self.finish(json.dumps(kernel_id))
64 64
65 65
66 66 class KernelHandler(web.RequestHandler):
67 67
68 68 SUPPORTED_METHODS = ('DELETE')
69 69
70 70 def delete(self, kernel_id):
71 71 rkm = self.application.routing_kernel_manager
72 72 rkm.kill_kernel(kernel_id)
73 73 self.set_status(204)
74 74 self.finish()
75 75
76 76
77 77 class KernelActionHandler(web.RequestHandler):
78 78
79 79 def post(self, kernel_id, action):
80 80 rkm = self.application.routing_kernel_manager
81 81 if action == 'interrupt':
82 82 rkm.interrupt_kernel(kernel_id)
83 83 self.set_status(204)
84 84 if action == 'restart':
85 85 new_kernel_id = rkm.restart_kernel(kernel_id)
86 86 self.write(json.dumps(new_kernel_id))
87 87 self.finish()
88 88
89 89
90 90 class ZMQStreamHandler(websocket.WebSocketHandler):
91 91
92 92 def initialize(self, stream_name):
93 93 self.stream_name = stream_name
94 94
95 95 def open(self, kernel_id):
96 96 rkm = self.application.routing_kernel_manager
97 97 self.router = rkm.get_router(kernel_id, self.stream_name)
98 98 self.client_id = self.router.register_client(self)
99 99
100 100 def on_message(self, msg):
101 101 self.router.forward_msg(self.client_id, msg)
102 102
103 103 def on_close(self):
104 104 self.router.unregister_client(self.client_id)
105 105
106 106
107 107 #-----------------------------------------------------------------------------
108 108 # Notebook web service handlers
109 109 #-----------------------------------------------------------------------------
110 110
111 111 class NotebookRootHandler(web.RequestHandler):
112 112
113 113 def get(self):
114 114 nbm = self.application.notebook_manager
115 115 files = nbm.list_notebooks()
116 116 self.finish(json.dumps(files))
117 117
118 118 def post(self):
119 119 nbm = self.application.notebook_manager
120 120 body = self.request.body.strip()
121 121 format = self.get_argument('format', default='json')
122 122 name = self.get_argument('name', default=None)
123 123 if body:
124 124 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
125 125 else:
126 126 notebook_id = nbm.new_notebook()
127 127 self.set_header('Location', '/'+notebook_id)
128 128 self.finish(json.dumps(notebook_id))
129 129
130 130
131 131 class NotebookHandler(web.RequestHandler):
132 132
133 133 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
134 134
135 135 def get(self, notebook_id):
136 136 nbm = self.application.notebook_manager
137 137 format = self.get_argument('format', default='json')
138 138 last_mod, name, data = nbm.get_notebook(notebook_id, format)
139 139 if format == u'json':
140 140 self.set_header('Content-Type', 'application/json')
141 141 self.set_header('Content-Disposition','attachment; filename=%s.json' % name)
142 142 elif format == u'xml':
143 143 self.set_header('Content-Type', 'application/xml')
144 144 self.set_header('Content-Disposition','attachment; filename=%s.ipynb' % name)
145 145 elif format == u'py':
146 146 self.set_header('Content-Type', 'application/x-python')
147 147 self.set_header('Content-Disposition','attachment; filename=%s.py' % name)
148 148 self.set_header('Last-Modified', last_mod)
149 149 self.finish(data)
150 150
151 151 def put(self, notebook_id):
152 152 nbm = self.application.notebook_manager
153 153 format = self.get_argument('format', default='json')
154 154 name = self.get_argument('name', default=None)
155 155 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
156 156 self.set_status(204)
157 157 self.finish()
158 158
159 159 def delete(self, notebook_id):
160 160 nbm = self.application.notebook_manager
161 161 nbm.delete_notebook(notebook_id)
162 162 self.set_status(204)
163 163 self.finish()
164 164
165 165 #-----------------------------------------------------------------------------
166 166 # RST web service handlers
167 167 #-----------------------------------------------------------------------------
168 168
169 _rst_header = """========
170 Heading1
171 ========
172
173 Heading2
174 ========
175
176 Heading3
177 --------
178
179 Heading4
180 ^^^^^^^^
181
182 """
183 169
184 170 class RSTHandler(web.RequestHandler):
185 171
186 172 def post(self):
187 173 if publish_string is None:
188 174 raise web.HTTPError(503)
189 175 body = self.request.body.strip()
190 source = _rst_header + body
191 template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
176 source = body
177 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
192 178 print template_path
193 179 defaults = {'file_insertion_enabled': 0,
194 180 'raw_enabled': 0,
195 181 '_disable_config': 1,
196 182 'stylesheet_path': 0,
197 'initial_header_level': 3,
198 'template': template_path
183 # 'template': template_path
199 184 }
200 185 try:
201 186 html = publish_string(source, writer_name='html',
202 187 settings_overrides=defaults
203 188 )
204 189 except:
205 190 raise web.HTTPError(400)
206 191 print html
207 # html = '\n'.join(html.split('\n')[7:-3])
208 # print html
209 192 self.set_header('Content-Type', 'text/html')
210 193 self.finish(html)
211 194
212 195
@@ -1,421 +1,421 b''
1 1
2 2 //============================================================================
3 3 // CodeCell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var CodeCell = function (notebook) {
11 11 this.code_mirror = null;
12 12 this.input_prompt_number = ' ';
13 13 this.is_completing = false;
14 14 this.completion_cursor = null;
15 15 this.outputs = [];
16 16 this.collapsed = false;
17 17 IPython.Cell.apply(this, arguments);
18 18 };
19 19
20 20
21 21 CodeCell.prototype = new IPython.Cell();
22 22
23 23
24 24 CodeCell.prototype.create_element = function () {
25 25 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
26 26 var input = $('<div></div>').addClass('input hbox');
27 27 input.append($('<div/>').addClass('prompt input_prompt'));
28 28 var input_area = $('<div/>').addClass('input_area box-flex1');
29 29 this.code_mirror = CodeMirror(input_area.get(0), {
30 30 indentUnit : 4,
31 31 mode: 'python',
32 32 theme: 'ipython',
33 33 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
34 34 });
35 35 input.append(input_area);
36 36 var output = $('<div></div>').addClass('output vbox');
37 37 cell.append(input).append(output);
38 38 this.element = cell;
39 39 this.collapse()
40 40 };
41 41
42 42
43 43 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
44 44 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
45 45 // is used to provide custom key handling. Its return value is used to determine
46 46 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
47 47 if (event.keyCode === 13 && event.shiftKey) {
48 48 // Always ignore shift-enter in CodeMirror as we handle it.
49 49 return true;
50 50 } else if (event.keyCode === 9) {
51 51 var cur = editor.getCursor();
52 52 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
53 53 if (pre_cursor === "") {
54 54 // Don't autocomplete if the part of the line before the cursor is empty.
55 55 // In this case, let CodeMirror handle indentation.
56 56 return false;
57 57 } else {
58 58 // Autocomplete the current line.
59 59 event.stop();
60 60 var line = editor.getLine(cur.line);
61 61 this.is_completing = true;
62 62 this.completion_cursor = cur;
63 63 IPython.notebook.complete_cell(this, line, cur.ch);
64 64 return true;
65 65 }
66 66 } else if (event.keyCode === 8 && event.type == 'keydown') {
67 67 // If backspace and the line ends with 4 spaces, remove them.
68 68 var cur = editor.getCursor();
69 69 var line = editor.getLine(cur.line);
70 70 var ending = line.slice(-4);
71 71 if (ending === ' ') {
72 72 editor.replaceRange('',
73 73 {line: cur.line, ch: cur.ch-4},
74 74 {line: cur.line, ch: cur.ch}
75 75 );
76 76 event.stop();
77 77 return true;
78 78 } else {
79 79 return false;
80 80 };
81 81 } else {
82 82 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
83 83 this.is_completing = false;
84 84 this.completion_cursor = null;
85 85 }
86 86 return false;
87 87 };
88 88 };
89 89
90 90
91 91 CodeCell.prototype.finish_completing = function (matched_text, matches) {
92 92 if (!this.is_completing || matches.length === 0) {return;}
93 93 // console.log("Got matches", matched_text, matches);
94 94
95 95 var that = this;
96 96 var cur = this.completion_cursor;
97 97 var complete = $('<div/>').addClass('completions');
98 98 var select = $('<select/>').attr('multiple','true');
99 99 for (var i=0; i<matches.length; ++i) {
100 100 select.append($('<option/>').text(matches[i]));
101 101 }
102 102 select.children().first().attr('selected','true');
103 103 select.attr('size',Math.min(10,matches.length));
104 104 var pos = this.code_mirror.cursorCoords();
105 105 complete.css('left',pos.x+'px');
106 106 complete.css('top',pos.yBot+'px');
107 107 complete.append(select);
108 108
109 109 $('body').append(complete);
110 110 var done = false;
111 111
112 112 var insert = function (selected_text) {
113 113 that.code_mirror.replaceRange(
114 114 selected_text,
115 115 {line: cur.line, ch: (cur.ch-matched_text.length)},
116 116 {line: cur.line, ch: cur.ch}
117 117 );
118 118 };
119 119
120 120 var close = function () {
121 121 if (done) return;
122 122 done = true;
123 123 complete.remove();
124 124 that.is_completing = false;
125 125 that.completion_cursor = null;
126 126 };
127 127
128 128 var pick = function () {
129 129 insert(select.val()[0]);
130 130 close();
131 131 setTimeout(function(){that.code_mirror.focus();}, 50);
132 132 };
133 133
134 134 select.blur(close);
135 135 select.keydown(function (event) {
136 136 var code = event.which;
137 137 if (code === 13 || code === 32) {
138 138 // Pressing SPACE or ENTER will cause a pick
139 139 event.stopPropagation();
140 140 event.preventDefault();
141 141 pick();
142 142 } else if (code === 38 || code === 40) {
143 143 // We don't want the document keydown handler to handle UP/DOWN,
144 144 // but we want the default action.
145 145 event.stopPropagation();
146 146 } else {
147 147 // All other key presses simple exit completion.
148 148 event.stopPropagation();
149 149 event.preventDefault();
150 150 close();
151 151 that.code_mirror.focus();
152 152 }
153 153 });
154 154 // Double click also causes a pick.
155 155 select.dblclick(pick);
156 156 select.focus();
157 157 };
158 158
159 159
160 160 CodeCell.prototype.select = function () {
161 161 IPython.Cell.prototype.select.apply(this);
162 162 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
163 163 // not causing the cursor to blink if the editor is empty initially.
164 164 // While this seems to fix the issue, this should be fixed
165 165 // in CodeMirror proper.
166 166 var s = this.code_mirror.getValue();
167 167 if (s === '') this.code_mirror.setValue('.');
168 168 this.code_mirror.focus();
169 169 if (s === '') this.code_mirror.setValue('');
170 170 };
171 171
172 172
173 173 CodeCell.prototype.append_output = function (json) {
174 174 this.expand();
175 175 if (json.output_type === 'pyout') {
176 176 this.append_pyout(json);
177 177 } else if (json.output_type === 'pyerr') {
178 178 this.append_pyerr(json);
179 179 } else if (json.output_type === 'display_data') {
180 180 this.append_display_data(json);
181 181 } else if (json.output_type === 'stream') {
182 182 this.append_stream(json);
183 183 };
184 184 this.outputs.push(json);
185 185 };
186 186
187 187
188 188 CodeCell.prototype.append_pyout = function (json) {
189 189 n = json.prompt_number || ' ';
190 190 var toinsert = $("<div/>").addClass("output_pyout hbox");
191 191 toinsert.append($('<div/>').
192 192 addClass('prompt output_prompt').
193 193 html('Out[' + n + ']:')
194 194 );
195 195 this.append_mime_type(json, toinsert).addClass('output_area');
196 196 toinsert.children().last().addClass("box_flex1 pyout_area");
197 197 this.element.find("div.output").append(toinsert);
198 198 // If we just output latex, typeset it.
199 199 if (json.latex !== undefined) {
200 200 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
201 201 };
202 202 };
203 203
204 204
205 205 CodeCell.prototype.append_pyerr = function (json) {
206 206 var tb = json.traceback;
207 if (tb !== undefined) {
207 if (tb !== undefined && tb.length > 0) {
208 208 var s = '';
209 209 var len = tb.length;
210 210 for (var i=0; i<len; i++) {
211 211 s = s + tb[i] + '\n';
212 212 }
213 213 s = s + '\n';
214 214 this.append_text(s).addClass('output_area');
215 215 };
216 216 };
217 217
218 218
219 219 CodeCell.prototype.append_stream = function (json) {
220 220 this.append_text(json.text).addClass('output_area');
221 221 };
222 222
223 223
224 224 CodeCell.prototype.append_display_data = function (json) {
225 225 this.append_mime_type(json).addClass('output_area');
226 226 };
227 227
228 228
229 229 CodeCell.prototype.append_mime_type = function (json, element) {
230 230 if (json.html !== undefined) {
231 231 this.append_html(json.html, element);
232 232 } else if (json.latex !== undefined) {
233 233 this.append_latex(json.latex, element);
234 234 // If it is undefined, then we just appended to div.output, which
235 235 // makes the latex visible and we can typeset it. The typesetting
236 236 // has to be done after the latex is on the page.
237 237 if (element === undefined) {
238 238 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
239 239 };
240 240 } else if (json.svg !== undefined) {
241 241 this.append_svg(json.svg, element);
242 242 } else if (json.png !== undefined) {
243 243 this.append_png(json.png, element);
244 244 } else if (json.jpeg !== undefined) {
245 245 this.append_jpeg(json.jpeg, element);
246 246 } else if (json.text !== undefined) {
247 247 this.append_text(json.text, element);
248 248 };
249 249 return element;
250 250 };
251 251
252 252
253 253 CodeCell.prototype.append_html = function (html, element) {
254 254 element = element || this.element.find("div.output");
255 255 var toinsert = $("<div/>").addClass("output_html");
256 256 toinsert.append(html);
257 257 element.append(toinsert);
258 258 return element;
259 259 }
260 260
261 261
262 262 CodeCell.prototype.append_text = function (data, element) {
263 263 element = element || this.element.find("div.output");
264 264 var toinsert = $("<div/>").addClass("output_stream");
265 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
265 toinsert.append($("<pre/>").html(data));
266 266 element.append(toinsert);
267 267 return element;
268 268 };
269 269
270 270
271 271 CodeCell.prototype.append_svg = function (svg, element) {
272 272 element = element || this.element.find("div.output");
273 273 var toinsert = $("<div/>").addClass("output_svg");
274 274 toinsert.append(svg);
275 275 element.append(toinsert);
276 276 return element;
277 277 };
278 278
279 279
280 280 CodeCell.prototype.append_png = function (png, element) {
281 281 element = element || this.element.find("div.output");
282 282 var toinsert = $("<div/>").addClass("output_png");
283 283 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
284 284 element.append(toinsert);
285 285 return element;
286 286 };
287 287
288 288
289 289 CodeCell.prototype.append_jpeg = function (jpeg, element) {
290 290 element = element || this.element.find("div.output");
291 291 var toinsert = $("<div/>").addClass("output_jpeg");
292 292 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
293 293 element.append(toinsert);
294 294 return element;
295 295 };
296 296
297 297
298 298 CodeCell.prototype.append_latex = function (latex, element) {
299 299 // This method cannot do the typesetting because the latex first has to
300 300 // be on the page.
301 301 element = element || this.element.find("div.output");
302 302 var toinsert = $("<div/>").addClass("output_latex");
303 303 toinsert.append(latex);
304 304 element.append(toinsert);
305 305 return element;
306 306 }
307 307
308 308
309 309 CodeCell.prototype.clear_output = function () {
310 310 this.element.find("div.output").html("");
311 311 this.outputs = [];
312 312 };
313 313
314 314
315 315 CodeCell.prototype.clear_input = function () {
316 316 this.code_mirror.setValue('');
317 317 };
318 318
319 319
320 320 CodeCell.prototype.collapse = function () {
321 321 if (!this.collapsed) {
322 322 this.element.find('div.output').hide();
323 323 this.collapsed = true;
324 324 };
325 325 };
326 326
327 327
328 328 CodeCell.prototype.expand = function () {
329 329 if (this.collapsed) {
330 330 this.element.find('div.output').show();
331 331 this.collapsed = false;
332 332 };
333 333 };
334 334
335 335
336 336 CodeCell.prototype.set_input_prompt = function (number) {
337 337 var n = number || ' ';
338 338 this.input_prompt_number = n
339 339 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
340 340 };
341 341
342 342
343 343 CodeCell.prototype.get_code = function () {
344 344 return this.code_mirror.getValue();
345 345 };
346 346
347 347
348 348 CodeCell.prototype.set_code = function (code) {
349 349 return this.code_mirror.setValue(code);
350 350 };
351 351
352 352
353 353 CodeCell.prototype.at_top = function () {
354 354 var cursor = this.code_mirror.getCursor();
355 355 if (cursor.line === 0) {
356 356 return true;
357 357 } else {
358 358 return false;
359 359 }
360 360 };
361 361
362 362
363 363 CodeCell.prototype.at_bottom = function () {
364 364 var cursor = this.code_mirror.getCursor();
365 365 if (cursor.line === (this.code_mirror.lineCount()-1)) {
366 366 return true;
367 367 } else {
368 368 return false;
369 369 }
370 370 };
371 371
372 372
373 373 CodeCell.prototype.fromJSON = function (data) {
374 374 // console.log('Import from JSON:', data);
375 375 if (data.cell_type === 'code') {
376 376 if (data.input !== undefined) {
377 377 this.set_code(data.input);
378 378 }
379 379 if (data.prompt_number !== undefined) {
380 380 this.set_input_prompt(data.prompt_number);
381 381 } else {
382 382 this.set_input_prompt();
383 383 };
384 384 var len = data.outputs.length;
385 385 for (var i=0; i<len; i++) {
386 386 this.append_output(data.outputs[i]);
387 387 };
388 388 if (data.collapsed !== undefined) {
389 389 if (data.collapsed) {
390 390 this.collapse();
391 391 };
392 392 };
393 393 };
394 394 };
395 395
396 396
397 397 CodeCell.prototype.toJSON = function () {
398 398 var data = {};
399 399 data.input = this.get_code();
400 400 data.cell_type = 'code';
401 401 if (this.input_prompt_number !== ' ') {
402 402 data.prompt_number = this.input_prompt_number
403 403 };
404 404 var outputs = [];
405 405 var len = this.outputs.length;
406 406 for (var i=0; i<len; i++) {
407 407 outputs[i] = this.outputs[i];
408 408 };
409 409 data.outputs = outputs;
410 410 data.language = 'python';
411 411 data.collapsed = this.collapsed;
412 412 // console.log('Export to JSON:',data);
413 413 return data;
414 414 };
415 415
416 416
417 417 IPython.CodeCell = CodeCell;
418 418
419 419 return IPython;
420 420 }(IPython));
421 421
@@ -1,737 +1,740 b''
1 1
2 2 //============================================================================
3 3 // Notebook
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var Notebook = function (selector) {
11 11 this.element = $(selector);
12 12 this.element.scroll();
13 13 this.element.data("notebook", this);
14 14 this.next_prompt_number = 1;
15 15 this.kernel = null;
16 16 this.msg_cell_map = {};
17 17 this.style();
18 18 this.create_elements();
19 19 this.bind_events();
20 20 };
21 21
22 22
23 23 Notebook.prototype.style = function () {
24 24 $('div#notebook').addClass('border-box-sizing');
25 25 };
26 26
27 27
28 28 Notebook.prototype.create_elements = function () {
29 29 // We add this end_space div to the end of the notebook div to:
30 30 // i) provide a margin between the last cell and the end of the notebook
31 31 // ii) to prevent the div from scrolling up when the last cell is being
32 32 // edited, but is too low on the page, which browsers will do automatically.
33 33 this.element.append($('<div class="end_space"></div>').height(50));
34 34 $('div#notebook').addClass('border-box-sizing');
35 35 };
36 36
37 37
38 38 Notebook.prototype.bind_events = function () {
39 39 var that = this;
40 40 $(document).keydown(function (event) {
41 41 // console.log(event);
42 42 if (event.which === 38) {
43 43 var cell = that.selected_cell();
44 44 if (cell.at_top()) {
45 45 event.preventDefault();
46 46 that.select_prev();
47 47 };
48 48 } else if (event.which === 40) {
49 49 var cell = that.selected_cell();
50 50 if (cell.at_bottom()) {
51 51 event.preventDefault();
52 52 that.select_next();
53 53 };
54 54 } else if (event.which === 13 && event.shiftKey) {
55 55 that.execute_selected_cell();
56 56 return false;
57 57 } else if (event.which === 13 && event.ctrlKey) {
58 58 that.execute_selected_cell({terminal:true});
59 59 return false;
60 60 };
61 61 });
62 62
63 63 this.element.bind('collapse_pager', function () {
64 64 var app_height = $('div#main_app').height(); // content height
65 65 var splitter_height = $('div#pager_splitter').outerHeight(true);
66 66 var new_height = app_height - splitter_height;
67 67 that.element.animate({height : new_height + 'px'}, 'fast');
68 68 });
69 69
70 70 this.element.bind('expand_pager', function () {
71 71 var app_height = $('div#main_app').height(); // content height
72 72 var splitter_height = $('div#pager_splitter').outerHeight(true);
73 73 var pager_height = $('div#pager').outerHeight(true);
74 74 var new_height = app_height - pager_height - splitter_height;
75 75 that.element.animate({height : new_height + 'px'}, 'fast');
76 76 });
77 77
78 78 this.element.bind('collapse_left_panel', function () {
79 79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
80 80 var new_margin = splitter_width;
81 81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
82 82 });
83 83
84 84 this.element.bind('expand_left_panel', function () {
85 85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 86 var left_panel_width = IPython.left_panel.width;
87 87 var new_margin = splitter_width + left_panel_width;
88 88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
89 89 });
90 90 };
91 91
92 92
93 93 Notebook.prototype.scroll_to_bottom = function () {
94 94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
95 95 };
96 96
97 97
98 98 Notebook.prototype.scroll_to_top = function () {
99 99 this.element.animate({scrollTop:0}, 0);
100 100 };
101 101
102 102
103 103 // Cell indexing, retrieval, etc.
104 104
105 105
106 106 Notebook.prototype.cell_elements = function () {
107 107 return this.element.children("div.cell");
108 108 }
109 109
110 110
111 111 Notebook.prototype.ncells = function (cell) {
112 112 return this.cell_elements().length;
113 113 }
114 114
115 115
116 116 // TODO: we are often calling cells as cells()[i], which we should optimize
117 117 // to cells(i) or a new method.
118 118 Notebook.prototype.cells = function () {
119 119 return this.cell_elements().toArray().map(function (e) {
120 120 return $(e).data("cell");
121 121 });
122 122 }
123 123
124 124
125 125 Notebook.prototype.find_cell_index = function (cell) {
126 126 var result = null;
127 127 this.cell_elements().filter(function (index) {
128 128 if ($(this).data("cell") === cell) {
129 129 result = index;
130 130 };
131 131 });
132 132 return result;
133 133 };
134 134
135 135
136 136 Notebook.prototype.index_or_selected = function (index) {
137 137 return index || this.selected_index() || 0;
138 138 }
139 139
140 140
141 141 Notebook.prototype.select = function (index) {
142 142 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 143 if (this.selected_index() !== null) {
144 144 this.selected_cell().unselect();
145 145 };
146 146 this.cells()[index].select();
147 147 if (index === (this.ncells()-1)) {
148 148 this.scroll_to_bottom();
149 149 };
150 150 };
151 151 return this;
152 152 };
153 153
154 154
155 155 Notebook.prototype.select_next = function () {
156 156 var index = this.selected_index();
157 157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 158 this.select(index+1);
159 159 };
160 160 return this;
161 161 };
162 162
163 163
164 164 Notebook.prototype.select_prev = function () {
165 165 var index = this.selected_index();
166 166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 167 this.select(index-1);
168 168 };
169 169 return this;
170 170 };
171 171
172 172
173 173 Notebook.prototype.selected_index = function () {
174 174 var result = null;
175 175 this.cell_elements().filter(function (index) {
176 176 if ($(this).data("cell").selected === true) {
177 177 result = index;
178 178 };
179 179 });
180 180 return result;
181 181 };
182 182
183 183
184 184 Notebook.prototype.cell_for_msg = function (msg_id) {
185 185 var cell_id = this.msg_cell_map[msg_id];
186 186 var result = null;
187 187 this.cell_elements().filter(function (index) {
188 188 cell = $(this).data("cell");
189 189 if (cell.cell_id === cell_id) {
190 190 result = cell;
191 191 };
192 192 });
193 193 return result;
194 194 };
195 195
196 196
197 197 Notebook.prototype.selected_cell = function () {
198 198 return this.cell_elements().eq(this.selected_index()).data("cell");
199 199 }
200 200
201 201
202 202 // Cell insertion, deletion and moving.
203 203
204 204
205 205 Notebook.prototype.delete_cell = function (index) {
206 206 var i = index || this.selected_index();
207 207 if (i !== null && i >= 0 && i < this.ncells()) {
208 208 this.cell_elements().eq(i).remove();
209 209 if (i === (this.ncells())) {
210 210 this.select(i-1);
211 211 } else {
212 212 this.select(i);
213 213 };
214 214 };
215 215 return this;
216 216 };
217 217
218 218
219 219 Notebook.prototype.append_cell = function (cell) {
220 220 this.element.find('div.end_space').before(cell.element);
221 221 return this;
222 222 };
223 223
224 224
225 225 Notebook.prototype.insert_cell_after = function (cell, index) {
226 226 var ncells = this.ncells();
227 227 if (ncells === 0) {
228 228 this.append_cell(cell);
229 229 return this;
230 230 };
231 231 if (index >= 0 && index < ncells) {
232 232 this.cell_elements().eq(index).after(cell.element);
233 233 };
234 234 return this
235 235 };
236 236
237 237
238 238 Notebook.prototype.insert_cell_before = function (cell, index) {
239 239 var ncells = this.ncells();
240 240 if (ncells === 0) {
241 241 this.append_cell(cell);
242 242 return this;
243 243 };
244 244 if (index >= 0 && index < ncells) {
245 245 this.cell_elements().eq(index).before(cell.element);
246 246 };
247 247 return this;
248 248 };
249 249
250 250
251 251 Notebook.prototype.move_cell_up = function (index) {
252 252 var i = index || this.selected_index();
253 253 if (i !== null && i < this.ncells() && i > 0) {
254 254 var pivot = this.cell_elements().eq(i-1);
255 255 var tomove = this.cell_elements().eq(i);
256 256 if (pivot !== null && tomove !== null) {
257 257 tomove.detach();
258 258 pivot.before(tomove);
259 259 this.select(i-1);
260 260 };
261 261 };
262 262 return this;
263 263 }
264 264
265 265
266 266 Notebook.prototype.move_cell_down = function (index) {
267 267 var i = index || this.selected_index();
268 268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 269 var pivot = this.cell_elements().eq(i+1)
270 270 var tomove = this.cell_elements().eq(i)
271 271 if (pivot !== null && tomove !== null) {
272 272 tomove.detach();
273 273 pivot.after(tomove);
274 274 this.select(i+1);
275 275 };
276 276 };
277 277 return this;
278 278 }
279 279
280 280
281 281 Notebook.prototype.sort_cells = function () {
282 282 var ncells = this.ncells();
283 283 var sindex = this.selected_index();
284 284 var swapped;
285 285 do {
286 286 swapped = false
287 287 for (var i=1; i<ncells; i++) {
288 288 current = this.cell_elements().eq(i).data("cell");
289 289 previous = this.cell_elements().eq(i-1).data("cell");
290 290 if (previous.input_prompt_number > current.input_prompt_number) {
291 291 this.move_cell_up(i);
292 292 swapped = true;
293 293 };
294 294 };
295 295 } while (swapped);
296 296 this.select(sindex);
297 297 return this;
298 298 };
299 299
300 300
301 301 Notebook.prototype.insert_code_cell_before = function (index) {
302 302 // TODO: Bounds check for i
303 303 var i = this.index_or_selected(index);
304 304 var cell = new IPython.CodeCell(this);
305 305 cell.set_input_prompt();
306 306 this.insert_cell_before(cell, i);
307 307 this.select(this.find_cell_index(cell));
308 308 return cell;
309 309 }
310 310
311 311
312 312 Notebook.prototype.insert_code_cell_after = function (index) {
313 313 // TODO: Bounds check for i
314 314 var i = this.index_or_selected(index);
315 315 var cell = new IPython.CodeCell(this);
316 316 cell.set_input_prompt();
317 317 this.insert_cell_after(cell, i);
318 318 this.select(this.find_cell_index(cell));
319 319 return cell;
320 320 }
321 321
322 322
323 323 Notebook.prototype.insert_html_cell_before = function (index) {
324 324 // TODO: Bounds check for i
325 325 var i = this.index_or_selected(index);
326 326 var cell = new IPython.HTMLCell(this);
327 327 cell.config_mathjax();
328 328 this.insert_cell_before(cell, i);
329 329 this.select(this.find_cell_index(cell));
330 330 return cell;
331 331 }
332 332
333 333
334 334 Notebook.prototype.insert_html_cell_after = function (index) {
335 335 // TODO: Bounds check for i
336 336 var i = this.index_or_selected(index);
337 337 var cell = new IPython.HTMLCell(this);
338 338 cell.config_mathjax();
339 339 this.insert_cell_after(cell, i);
340 340 this.select(this.find_cell_index(cell));
341 341 return cell;
342 342 }
343 343
344 344
345 345 Notebook.prototype.insert_markdown_cell_before = function (index) {
346 346 // TODO: Bounds check for i
347 347 var i = this.index_or_selected(index);
348 348 var cell = new IPython.MarkdownCell(this);
349 349 cell.config_mathjax();
350 350 this.insert_cell_before(cell, i);
351 351 this.select(this.find_cell_index(cell));
352 352 return cell;
353 353 }
354 354
355 355
356 356 Notebook.prototype.insert_markdown_cell_after = function (index) {
357 357 // TODO: Bounds check for i
358 358 var i = this.index_or_selected(index);
359 359 var cell = new IPython.MarkdownCell(this);
360 360 cell.config_mathjax();
361 361 this.insert_cell_after(cell, i);
362 362 this.select(this.find_cell_index(cell));
363 363 return cell;
364 364 }
365 365
366 366
367 367 Notebook.prototype.to_code = function (index) {
368 368 // TODO: Bounds check for i
369 369 var i = this.index_or_selected(index);
370 370 var source_element = this.cell_elements().eq(i);
371 371 var source_cell = source_element.data("cell");
372 372 if (source_cell instanceof IPython.HTMLCell ||
373 373 source_cell instanceof IPython.MarkdownCell) {
374 374 this.insert_code_cell_after(i);
375 375 var target_cell = this.cells()[i+1];
376 376 target_cell.set_code(source_cell.get_source());
377 377 source_element.remove();
378 378 };
379 379 };
380 380
381 381
382 382 Notebook.prototype.to_markdown = function (index) {
383 383 // TODO: Bounds check for i
384 384 var i = this.index_or_selected(index);
385 385 var source_element = this.cell_elements().eq(i);
386 386 var source_cell = source_element.data("cell");
387 387 var target_cell = null;
388 388 if (source_cell instanceof IPython.CodeCell) {
389 389 this.insert_markdown_cell_after(i);
390 390 var target_cell = this.cells()[i+1];
391 391 var text = source_cell.get_code();
392 392 } else if (source_cell instanceof IPython.HTMLCell) {
393 393 this.insert_markdown_cell_after(i);
394 394 var target_cell = this.cells()[i+1];
395 395 var text = source_cell.get_source();
396 396 if (text === source_cell.placeholder) {
397 397 text = target_cell.placeholder;
398 398 }
399 399 }
400 400 if (target_cell !== null) {
401 401 if (text === "") {text = target_cell.placeholder;};
402 402 target_cell.set_source(text);
403 403 source_element.remove();
404 404 target_cell.edit();
405 405 }
406 406 };
407 407
408 408
409 409 Notebook.prototype.to_html = function (index) {
410 410 // TODO: Bounds check for i
411 411 var i = this.index_or_selected(index);
412 412 var source_element = this.cell_elements().eq(i);
413 413 var source_cell = source_element.data("cell");
414 414 var target_cell = null;
415 415 if (source_cell instanceof IPython.CodeCell) {
416 416 this.insert_html_cell_after(i);
417 417 var target_cell = this.cells()[i+1];
418 418 var text = source_cell.get_code();
419 419 } else if (source_cell instanceof IPython.MarkdownCell) {
420 420 this.insert_html_cell_after(i);
421 421 var target_cell = this.cells()[i+1];
422 422 var text = source_cell.get_source();
423 423 if (text === source_cell.placeholder) {
424 424 text = target_cell.placeholder;
425 425 }
426 426 }
427 427 if (target_cell !== null) {
428 428 if (text === "") {text = target_cell.placeholder;};
429 429 target_cell.set_source(text);
430 430 source_element.remove();
431 431 target_cell.edit();
432 432 }
433 433 };
434 434
435 435
436 436 // Cell collapsing
437 437
438 438 Notebook.prototype.collapse = function (index) {
439 439 var i = this.index_or_selected(index);
440 440 this.cells()[i].collapse();
441 441 };
442 442
443 443
444 444 Notebook.prototype.expand = function (index) {
445 445 var i = this.index_or_selected(index);
446 446 this.cells()[i].expand();
447 447 };
448 448
449 449
450 450 Notebook.prototype.set_autoindent = function (state) {
451 451 var cells = this.cells();
452 452 len = cells.length;
453 453 for (var i=0; i<len; i++) {
454 454 cells[i].set_autoindent(state)
455 455 };
456 456 };
457 457
458 458 // Kernel related things
459 459
460 460 Notebook.prototype.start_kernel = function () {
461 461 this.kernel = new IPython.Kernel();
462 462 var notebook_id = IPython.save_widget.get_notebook_id();
463 463 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
464 464 };
465 465
466 466
467 467 Notebook.prototype.handle_shell_reply = function (e) {
468 468 reply = $.parseJSON(e.data);
469 469 var header = reply.header;
470 470 var content = reply.content;
471 471 var msg_type = header.msg_type;
472 472 // console.log(reply);
473 473 var cell = this.cell_for_msg(reply.parent_header.msg_id);
474 474 if (msg_type === "execute_reply") {
475 475 cell.set_input_prompt(content.execution_count);
476 476 } else if (msg_type === "complete_reply") {
477 477 cell.finish_completing(content.matched_text, content.matches);
478 478 };
479 479 var payload = content.payload || [];
480 480 this.handle_payload(cell, payload);
481 481 };
482 482
483 483
484 484 Notebook.prototype.handle_payload = function (cell, payload) {
485 485 var l = payload.length;
486 486 for (var i=0; i<l; i++) {
487 487 if (payload[i].source === 'IPython.zmq.page.page') {
488 488 IPython.pager.clear();
489 489 IPython.pager.expand();
490 490 IPython.pager.append_text(payload[i].text);
491 491 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
492 492 var index = this.find_cell_index(cell);
493 493 var new_cell = this.insert_code_cell_after(index);
494 494 new_cell.set_code(payload[i].text);
495 495 }
496 496 };
497 497 };
498 498
499 499
500 500 Notebook.prototype.handle_iopub_reply = function (e) {
501 501 reply = $.parseJSON(e.data);
502 502 var content = reply.content;
503 503 // console.log(reply);
504 504 var msg_type = reply.header.msg_type;
505 505 var cell = this.cell_for_msg(reply.parent_header.msg_id);
506 506 var output_types = ['stream','display_data','pyout','pyerr'];
507 507 if (output_types.indexOf(msg_type) >= 0) {
508 508 this.handle_output(cell, msg_type, content);
509 509 } else if (msg_type === "status") {
510 510 if (content.execution_state === "busy") {
511 511 IPython.kernel_status_widget.status_busy();
512 512 } else if (content.execution_state === "idle") {
513 513 IPython.kernel_status_widget.status_idle();
514 514 };
515 515 }
516 516 };
517 517
518 518
519 519 Notebook.prototype.handle_output = function (cell, msg_type, content) {
520 520 var json = {};
521 521 json.output_type = msg_type;
522 522 if (msg_type === "stream") {
523 json.text = content.data + '\n';
523 json.text = utils.fixConsole(content.data + '\n');
524 524 } else if (msg_type === "display_data") {
525 525 json = this.convert_mime_types(json, content.data);
526 526 } else if (msg_type === "pyout") {
527 527 json.prompt_number = content.execution_count;
528 528 json = this.convert_mime_types(json, content.data);
529 529 } else if (msg_type === "pyerr") {
530 530 json.ename = content.ename;
531 531 json.evalue = content.evalue;
532 json.traceback = content.traceback;
532 var traceback = [];
533 for (var i=0; i<content.traceback.length; i++) {
534 traceback.push(utils.fixConsole(content.traceback[i]));
535 }
536 json.traceback = traceback;
533 537 };
534 538 cell.append_output(json);
535 539 };
536 540
537 541
538 542 Notebook.prototype.convert_mime_types = function (json, data) {
539 543 if (data['text/plain'] !== undefined) {
540 json.text = data['text/plain'];
544 json.text = utils.fixConsole(data['text/plain']);
541 545 };
542 546 if (data['text/html'] !== undefined) {
543 547 json.html = data['text/html'];
544 548 };
545 549 if (data['image/svg+xml'] !== undefined) {
546 550 json.svg = data['image/svg+xml'];
547 551 };
548 552 if (data['image/png'] !== undefined) {
549 553 json.png = data['image/png'];
550 554 };
551 555 if (data['image/jpeg'] !== undefined) {
552 556 json.jpeg = data['image/jpeg'];
553 557 };
554 558 if (data['text/latex'] !== undefined) {
555 559 json.latex = data['text/latex'];
556 560 };
557 561 if (data['application/json'] !== undefined) {
558 562 json.json = data['application/json'];
559 563 };
560 564 if (data['application/javascript'] !== undefined) {
561 565 json.javascript = data['application/javascript'];
562 566 }
563 567 return json;
564 568 };
565 569
566 570 Notebook.prototype.kernel_started = function () {
567 571 console.log("Kernel started: ", this.kernel.kernel_id);
568 572 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
569 573 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
570 574 };
571 575
572 576
573 577 Notebook.prototype.execute_selected_cell = function (options) {
574 578 // add_new: should a new cell be added if we are at the end of the nb
575 579 // terminal: execute in terminal mode, which stays in the current cell
576 580 default_options = {terminal: false, add_new: true}
577 581 $.extend(default_options, options)
578 582 var that = this;
579 583 var cell = that.selected_cell();
580 584 var cell_index = that.find_cell_index(cell);
581 585 if (cell instanceof IPython.CodeCell) {
582 586 cell.clear_output();
583 587 var code = cell.get_code();
584 588 var msg_id = that.kernel.execute(cell.get_code());
585 589 that.msg_cell_map[msg_id] = cell.cell_id;
586 590 } else if (cell instanceof IPython.HTMLCell) {
587 591 cell.render();
588 592 }
589 593 if (default_options.terminal) {
590 594 cell.clear_input();
591 595 } else {
592 596 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
593 597 that.insert_code_cell_after();
594 598 // If we are adding a new cell at the end, scroll down to show it.
595 599 that.scroll_to_bottom();
596 600 } else {
597 601 that.select(cell_index+1);
598 602 };
599 603 };
600 604 };
601 605
602 606
603 607 Notebook.prototype.execute_all_cells = function () {
604 608 var ncells = this.ncells();
605 609 for (var i=0; i<ncells; i++) {
606 610 this.select(i);
607 611 this.execute_selected_cell({add_new:false});
608 612 };
609 613 this.scroll_to_bottom();
610 614 };
611 615
612 616
613 617 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
614 618 var msg_id = this.kernel.complete(line, cursor_pos);
615 619 this.msg_cell_map[msg_id] = cell.cell_id;
616 620 };
617 621
618 622 // Persistance and loading
619 623
620 624
621 625 Notebook.prototype.fromJSON = function (data) {
622 626 var ncells = this.ncells();
623 627 for (var i=0; i<ncells; i++) {
624 628 // Always delete cell 0 as they get renumbered as they are deleted.
625 629 this.delete_cell(0);
626 630 };
627 631 // Only handle 1 worksheet for now.
628 632 var worksheet = data.worksheets[0];
629 633 if (worksheet !== undefined) {
630 634 var new_cells = worksheet.cells;
631 635 ncells = new_cells.length;
632 636 var cell_data = null;
633 637 var new_cell = null;
634 638 for (var i=0; i<ncells; i++) {
635 639 cell_data = new_cells[i];
636 640 if (cell_data.cell_type == 'code') {
637 641 new_cell = this.insert_code_cell_after();
638 642 new_cell.fromJSON(cell_data);
639 643 } else if (cell_data.cell_type === 'html') {
640 644 new_cell = this.insert_html_cell_after();
641 645 new_cell.fromJSON(cell_data);
642 646 } else if (cell_data.cell_type === 'markdown') {
643 647 new_cell = this.insert_markdown_cell_after();
644 648 new_cell.fromJSON(cell_data);
645 649 };
646 650 };
647 651 };
648 652 };
649 653
650 654
651 655 Notebook.prototype.toJSON = function () {
652 656 var cells = this.cells();
653 657 var ncells = cells.length;
654 658 cell_array = new Array(ncells);
655 659 for (var i=0; i<ncells; i++) {
656 660 cell_array[i] = cells[i].toJSON();
657 661 };
658 662 data = {
659 663 // Only handle 1 worksheet for now.
660 664 worksheets : [{cells:cell_array}]
661 665 }
662 666 return data
663 667 };
664 668
665 669 Notebook.prototype.save_notebook = function () {
666 670 if (IPython.save_widget.test_notebook_name()) {
667 671 var notebook_id = IPython.save_widget.get_notebook_id();
668 672 var nbname = IPython.save_widget.get_notebook_name();
669 673 // We may want to move the name/id/nbformat logic inside toJSON?
670 674 var data = this.toJSON();
671 675 data.name = nbname;
672 676 data.nbformat = 2;
673 677 data.id = notebook_id
674 678 // We do the call with settings so we can set cache to false.
675 679 var settings = {
676 680 processData : false,
677 681 cache : false,
678 682 type : "PUT",
679 683 data : JSON.stringify(data),
680 684 headers : {'Content-Type': 'application/json'},
681 685 success : $.proxy(this.notebook_saved,this)
682 686 };
683 687 IPython.save_widget.status_saving();
684 688 $.ajax("/notebooks/" + notebook_id, settings);
685 689 };
686 690 };
687 691
688 692
689 693 Notebook.prototype.notebook_saved = function (data, status, xhr) {
690 694 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
691 // IPython.save_widget.status_save();
692 695 }
693 696
694 697
695 698 Notebook.prototype.load_notebook = function (callback) {
696 699 var that = this;
697 700 var notebook_id = IPython.save_widget.get_notebook_id();
698 701 // We do the call with settings so we can set cache to false.
699 702 var settings = {
700 703 processData : false,
701 704 cache : false,
702 705 type : "GET",
703 706 dataType : "json",
704 707 success : function (data, status, xhr) {
705 708 that.notebook_loaded(data, status, xhr);
706 709 if (callback !== undefined) {
707 710 callback();
708 711 };
709 712 }
710 713 };
711 714 IPython.save_widget.status_loading();
712 715 $.ajax("/notebooks/" + notebook_id, settings);
713 716 }
714 717
715 718
716 719 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
717 720 this.fromJSON(data);
718 721 if (this.ncells() === 0) {
719 722 this.insert_code_cell_after();
720 723 };
721 724 IPython.save_widget.status_save();
722 725 IPython.save_widget.set_notebook_name(data.name);
723 726 this.start_kernel();
724 727 // fromJSON always selects the last cell inserted. We need to wait
725 728 // until that is done before scrolling to the top.
726 729 setTimeout(function () {
727 730 IPython.notebook.select(0);
728 731 IPython.notebook.scroll_to_top();
729 732 }, 50);
730 733 };
731 734
732 735 IPython.Notebook = Notebook;
733 736
734 737 return IPython;
735 738
736 739 }(IPython));
737 740
@@ -1,122 +1,136 b''
1 1 """The basic dict based notebook format."""
2 2
3 3 import pprint
4 4 import uuid
5 5
6 6 from IPython.utils.ipstruct import Struct
7 7
8 8
9 9 class NotebookNode(Struct):
10 10 pass
11 11
12 12
13 13 def from_dict(d):
14 14 if isinstance(d, dict):
15 15 newd = NotebookNode()
16 16 for k,v in d.items():
17 17 newd[k] = from_dict(v)
18 18 return newd
19 19 elif isinstance(d, (tuple, list)):
20 20 return [from_dict(i) for i in d]
21 21 else:
22 22 return d
23 23
24 24
25 25 def new_output(output_type=None, output_text=None, output_png=None,
26 26 output_html=None, output_svg=None, output_latex=None, output_json=None,
27 output_javascript=None, output_jpeg=None, prompt_number=None):
27 output_javascript=None, output_jpeg=None, prompt_number=None,
28 etype=None, evalue=None, traceback=None):
28 29 """Create a new code cell with input and output"""
29 30 output = NotebookNode()
30 31 if output_type is not None:
31 32 output.output_type = unicode(output_type)
32 if output_text is not None:
33 output.text = unicode(output_text)
34 if output_png is not None:
35 output.png = bytes(output_png)
36 if output_jpeg is not None:
37 output.jpeg = bytes(output_jpeg)
38 if output_html is not None:
39 output.html = unicode(output_html)
40 if output_svg is not None:
41 output.svg = unicode(output_svg)
42 if output_latex is not None:
43 output.latex = unicode(output_latex)
44 if output_json is not None:
45 output.json = unicode(output_json)
46 if output_javascript is not None:
47 output.javascript = unicode(output_javascript)
48 if prompt_number is not None:
49 output.prompt_number = int(prompt_number)
33
34 if output_type != 'pyerr':
35 if output_text is not None:
36 output.text = unicode(output_text)
37 if output_png is not None:
38 output.png = bytes(output_png)
39 if output_jpeg is not None:
40 output.jpeg = bytes(output_jpeg)
41 if output_html is not None:
42 output.html = unicode(output_html)
43 if output_svg is not None:
44 output.svg = unicode(output_svg)
45 if output_latex is not None:
46 output.latex = unicode(output_latex)
47 if output_json is not None:
48 output.json = unicode(output_json)
49 if output_javascript is not None:
50 output.javascript = unicode(output_javascript)
51
52 if output_type == u'pyout':
53 if prompt_number is not None:
54 output.prompt_number = int(prompt_number)
55
56 if output_type == u'pyerr':
57 if etype is not None:
58 output.etype = unicode(etype)
59 if evalue is not None:
60 output.evalue = unicode(evalue)
61 if traceback is not None:
62 output.traceback = [unicode(frame) for frame in list(traceback)]
63
50 64 return output
51 65
52 66
53 67 def new_code_cell(input=None, prompt_number=None, outputs=None,
54 68 language=u'python', collapsed=False):
55 69 """Create a new code cell with input and output"""
56 70 cell = NotebookNode()
57 71 cell.cell_type = u'code'
58 72 if language is not None:
59 73 cell.language = unicode(language)
60 74 if input is not None:
61 75 cell.input = unicode(input)
62 76 if prompt_number is not None:
63 77 cell.prompt_number = int(prompt_number)
64 78 if outputs is None:
65 79 cell.outputs = []
66 80 else:
67 81 cell.outputs = outputs
68 82 if collapsed is not None:
69 83 cell.collapsed = bool(collapsed)
70 84
71 85 return cell
72 86
73 87 def new_text_cell(cell_type, source=None, rendered=None):
74 88 """Create a new text cell."""
75 89 cell = NotebookNode()
76 90 if source is not None:
77 91 cell.source = unicode(source)
78 92 if rendered is not None:
79 93 cell.rendered = unicode(rendered)
80 94 cell.cell_type = cell_type
81 95 return cell
82 96
83 97
84 98 def new_worksheet(name=None, cells=None):
85 99 """Create a worksheet by name with with a list of cells."""
86 100 ws = NotebookNode()
87 101 if name is not None:
88 102 ws.name = unicode(name)
89 103 if cells is None:
90 104 ws.cells = []
91 105 else:
92 106 ws.cells = list(cells)
93 107 return ws
94 108
95 109
96 110 def new_notebook(name=None, id=None, worksheets=None, author=None, email=None,
97 111 created=None, saved=None, license=None):
98 112 """Create a notebook by name, id and a list of worksheets."""
99 113 nb = NotebookNode()
100 114 nb.nbformat = 2
101 115 if name is not None:
102 116 nb.name = unicode(name)
103 117 if id is None:
104 118 nb.id = unicode(uuid.uuid4())
105 119 else:
106 120 nb.id = unicode(id)
107 121 if worksheets is None:
108 122 nb.worksheets = []
109 123 else:
110 124 nb.worksheets = list(worksheets)
111 125 if author is not None:
112 126 nb.author = unicode(author)
113 127 if email is not None:
114 128 nb.email = unicode(email)
115 129 if created is not None:
116 130 nb.created = unicode(created)
117 131 if saved is not None:
118 132 nb.saved = unicode(saved)
119 133 if license is not None:
120 134 nb.license = unicode(license)
121 135 return nb
122 136
@@ -1,210 +1,228 b''
1 1 """Read and write notebook files as XML."""
2 2
3 3 from base64 import encodestring, decodestring
4 4 from xml.etree import ElementTree as ET
5 5
6 6 from .rwbase import NotebookReader, NotebookWriter
7 7 from .nbbase import (
8 8 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
9 9 )
10 10
11 11 def indent(elem, level=0):
12 12 i = "\n" + level*" "
13 13 if len(elem):
14 14 if not elem.text or not elem.text.strip():
15 15 elem.text = i + " "
16 16 if not elem.tail or not elem.tail.strip():
17 17 elem.tail = i
18 18 for elem in elem:
19 19 indent(elem, level+1)
20 20 if not elem.tail or not elem.tail.strip():
21 21 elem.tail = i
22 22 else:
23 23 if level and (not elem.tail or not elem.tail.strip()):
24 24 elem.tail = i
25 25
26 26
27 27 def _get_text(e, tag):
28 28 sub_e = e.find(tag)
29 29 if sub_e is None:
30 30 return None
31 31 else:
32 32 return sub_e.text
33 33
34 34
35 35 def _set_text(nbnode, attr, parent, tag):
36 36 if attr in nbnode:
37 37 e = ET.SubElement(parent, tag)
38 38 e.text = nbnode[attr]
39 39
40 40
41 41 def _get_int(e, tag):
42 42 sub_e = e.find(tag)
43 43 if sub_e is None:
44 44 return None
45 45 else:
46 46 return int(sub_e.text)
47 47
48 48
49 49 def _set_int(nbnode, attr, parent, tag):
50 50 if attr in nbnode:
51 51 e = ET.SubElement(parent, tag)
52 52 e.text = unicode(nbnode[attr])
53 53
54 54
55 55 def _get_bool(e, tag):
56 56 sub_e = e.find(tag)
57 57 if sub_e is None:
58 58 return None
59 59 else:
60 60 return bool(int(sub_e.text))
61 61
62 62
63 63 def _set_bool(nbnode, attr, parent, tag):
64 64 if attr in nbnode:
65 65 e = ET.SubElement(parent, tag)
66 66 if nbnode[attr]:
67 67 e.text = u'1'
68 68 else:
69 69 e.text = u'0'
70 70
71 71
72 72 def _get_binary(e, tag):
73 73 sub_e = e.find(tag)
74 74 if sub_e is None:
75 75 return None
76 76 else:
77 77 return decodestring(sub_e.text)
78 78
79 79
80 80 def _set_binary(nbnode, attr, parent, tag):
81 81 if attr in nbnode:
82 82 e = ET.SubElement(parent, tag)
83 83 e.text = encodestring(nbnode[attr])
84 84
85 85
86 86 class XMLReader(NotebookReader):
87 87
88 88 def reads(self, s, **kwargs):
89 89 root = ET.fromstring(s)
90 90 return self.to_notebook(root, **kwargs)
91 91
92 92 def to_notebook(self, root, **kwargs):
93 nbname = _get_text(root,'name')
94 nbid = _get_text(root,'id')
95 nbauthor = _get_text(root,'author')
96 nbemail = _get_text(root,'email')
97 nblicense = _get_text(root,'license')
98 nbcreated = _get_text(root,'created')
99 nbsaved = _get_text(root,'saved')
93 nbname = _get_text(root,u'name')
94 nbid = _get_text(root,u'id')
95 nbauthor = _get_text(root,u'author')
96 nbemail = _get_text(root,u'email')
97 nblicense = _get_text(root,u'license')
98 nbcreated = _get_text(root,u'created')
99 nbsaved = _get_text(root,u'saved')
100 100
101 101 worksheets = []
102 for ws_e in root.find('worksheets').getiterator('worksheet'):
103 wsname = _get_text(ws_e,'name')
102 for ws_e in root.find(u'worksheets').getiterator(u'worksheet'):
103 wsname = _get_text(ws_e,u'name')
104 104 cells = []
105 for cell_e in ws_e.find('cells').getiterator():
106 if cell_e.tag == 'codecell':
107 input = _get_text(cell_e,'input')
108 prompt_number = _get_int(cell_e,'prompt_number')
109 collapsed = _get_bool(cell_e,'collapsed')
110 language = _get_text(cell_e,'language')
105 for cell_e in ws_e.find(u'cells').getiterator():
106 if cell_e.tag == u'codecell':
107 input = _get_text(cell_e,u'input')
108 prompt_number = _get_int(cell_e,u'prompt_number')
109 collapsed = _get_bool(cell_e,u'collapsed')
110 language = _get_text(cell_e,u'language')
111 111 outputs = []
112 for output_e in cell_e.find('outputs').getiterator('output'):
113 out_prompt_number = _get_int(output_e,'prompt_number')
114 output_type = _get_text(output_e,'output_type')
115 output_text = _get_text(output_e,'text')
116 output_png = _get_binary(output_e,'png')
117 output_jpeg = _get_binary(output_e,'jpeg')
118 output_svg = _get_text(output_e,'svg')
119 output_html = _get_text(output_e,'html')
120 output_latex = _get_text(output_e,'latex')
121 output_json = _get_text(output_e,'json')
122 output_javascript = _get_text(output_e,'javascript')
112 for output_e in cell_e.find(u'outputs').getiterator(u'output'):
113 output_type = _get_text(output_e,u'output_type')
114 output_text = _get_text(output_e,u'text')
115 output_png = _get_binary(output_e,u'png')
116 output_jpeg = _get_binary(output_e,u'jpeg')
117 output_svg = _get_text(output_e,u'svg')
118 output_html = _get_text(output_e,u'html')
119 output_latex = _get_text(output_e,u'latex')
120 output_json = _get_text(output_e,u'json')
121 output_javascript = _get_text(output_e,u'javascript')
122
123 out_prompt_number = _get_int(output_e,u'prompt_number')
124 etype = _get_text(output_e,u'etype')
125 evalue = _get_text(output_e,u'evalue')
126 traceback = []
127 traceback_e = output_e.find(u'traceback')
128 if traceback_e is not None:
129 for frame_e in traceback_e.getiterator(u'frame'):
130 traceback.append(frame_e.text)
131 if len(traceback) == 0:
132 traceback = None
123 133 output = new_output(output_type=output_type,output_png=output_png,
124 134 output_text=output_text, output_svg=output_svg,
125 135 output_html=output_html, output_latex=output_latex,
126 136 output_json=output_json, output_javascript=output_javascript,
127 output_jpeg=output_jpeg, prompt_number=out_prompt_number
137 output_jpeg=output_jpeg, prompt_number=out_prompt_number,
138 etype=etype, evalue=evalue, traceback=traceback
128 139 )
129 140 outputs.append(output)
130 141 cc = new_code_cell(input=input,prompt_number=prompt_number,
131 142 language=language,outputs=outputs,collapsed=collapsed)
132 143 cells.append(cc)
133 if cell_e.tag == 'htmlcell':
134 source = _get_text(cell_e,'source')
135 rendered = _get_text(cell_e,'rendered')
144 if cell_e.tag == u'htmlcell':
145 source = _get_text(cell_e,u'source')
146 rendered = _get_text(cell_e,u'rendered')
136 147 cells.append(new_text_cell(u'html', source=source, rendered=rendered))
137 if cell_e.tag == 'markdowncell':
138 source = _get_text(cell_e,'source')
139 rendered = _get_text(cell_e,'rendered')
148 if cell_e.tag == u'markdowncell':
149 source = _get_text(cell_e,u'source')
150 rendered = _get_text(cell_e,u'rendered')
140 151 cells.append(new_text_cell(u'markdown', source=source, rendered=rendered))
141 152 ws = new_worksheet(name=wsname,cells=cells)
142 153 worksheets.append(ws)
143 154
144 155 nb = new_notebook(name=nbname,id=nbid,worksheets=worksheets,author=nbauthor,
145 156 email=nbemail,license=nblicense,saved=nbsaved,created=nbcreated)
146 157 return nb
147 158
148 159
149 160 class XMLWriter(NotebookWriter):
150 161
151 162 def writes(self, nb, **kwargs):
152 nb_e = ET.Element('notebook')
153 _set_text(nb,'name',nb_e,'name')
154 _set_text(nb,'id',nb_e,'id')
155 _set_text(nb,'author',nb_e,'author')
156 _set_text(nb,'email',nb_e,'email')
157 _set_text(nb,'license',nb_e,'license')
158 _set_text(nb,'created',nb_e,'created')
159 _set_text(nb,'saved',nb_e,'saved')
160 _set_int(nb,'nbformat',nb_e,'nbformat')
161 wss_e = ET.SubElement(nb_e,'worksheets')
163 nb_e = ET.Element(u'notebook')
164 _set_text(nb,u'name',nb_e,u'name')
165 _set_text(nb,u'id',nb_e,u'id')
166 _set_text(nb,u'author',nb_e,u'author')
167 _set_text(nb,u'email',nb_e,u'email')
168 _set_text(nb,u'license',nb_e,u'license')
169 _set_text(nb,u'created',nb_e,u'created')
170 _set_text(nb,u'saved',nb_e,u'saved')
171 _set_int(nb,u'nbformat',nb_e,u'nbformat')
172 wss_e = ET.SubElement(nb_e,u'worksheets')
162 173 for ws in nb.worksheets:
163 ws_e = ET.SubElement(wss_e, 'worksheet')
164 _set_text(ws,'name',ws_e,'name')
165 cells_e = ET.SubElement(ws_e,'cells')
174 ws_e = ET.SubElement(wss_e, u'worksheet')
175 _set_text(ws,u'name',ws_e,u'name')
176 cells_e = ET.SubElement(ws_e,u'cells')
166 177 for cell in ws.cells:
167 178 cell_type = cell.cell_type
168 if cell_type == 'code':
169 cell_e = ET.SubElement(cells_e, 'codecell')
170 _set_text(cell,'input',cell_e,'input')
171 _set_text(cell,'language',cell_e,'language')
172 _set_int(cell,'prompt_number',cell_e,'prompt_number')
173 _set_bool(cell,'collapsed',cell_e,'collapsed')
174 outputs_e = ET.SubElement(cell_e, 'outputs')
179 if cell_type == u'code':
180 cell_e = ET.SubElement(cells_e, u'codecell')
181 _set_text(cell,u'input',cell_e,u'input')
182 _set_text(cell,u'language',cell_e,u'language')
183 _set_int(cell,u'prompt_number',cell_e,u'prompt_number')
184 _set_bool(cell,u'collapsed',cell_e,u'collapsed')
185 outputs_e = ET.SubElement(cell_e, u'outputs')
175 186 for output in cell.outputs:
176 output_e = ET.SubElement(outputs_e, 'output')
177 _set_int(output,'prompt_number',output_e,'prompt_number')
178 _set_text(output,'output_type',output_e,'output_type')
179 _set_text(output,'text',output_e,'text')
180 _set_binary(output,'png',output_e,'png')
181 _set_binary(output,'jpeg',output_e,'jpeg')
182 _set_text(output,'html',output_e,'html')
183 _set_text(output,'svg',output_e,'svg')
184 _set_text(output,'latex',output_e,'latex')
185 _set_text(output,'json',output_e,'json')
186 _set_text(output,'javascript',output_e,'javascript')
187 elif cell_type == 'html':
188 cell_e = ET.SubElement(cells_e, 'htmlcell')
189 _set_text(cell,'source',cell_e,'source')
190 _set_text(cell,'rendered',cell_e,'rendered')
191 elif cell_type == 'markdown':
192 cell_e = ET.SubElement(cells_e, 'markdowncell')
193 _set_text(cell,'source',cell_e,'source')
194 _set_text(cell,'rendered',cell_e,'rendered')
187 output_e = ET.SubElement(outputs_e, u'output')
188 _set_text(output,u'output_type',output_e,u'output_type')
189 _set_text(output,u'text',output_e,u'text')
190 _set_binary(output,u'png',output_e,u'png')
191 _set_binary(output,u'jpeg',output_e,u'jpeg')
192 _set_text(output,u'html',output_e,u'html')
193 _set_text(output,u'svg',output_e,u'svg')
194 _set_text(output,u'latex',output_e,u'latex')
195 _set_text(output,u'json',output_e,u'json')
196 _set_text(output,u'javascript',output_e,u'javascript')
197 _set_int(output,u'prompt_number',output_e,u'prompt_number')
198 _set_text(output,u'etype',output_e,u'etype')
199 _set_text(output,u'evalue',output_e,u'evalue')
200 if u'traceback' in output:
201 tb_e = ET.SubElement(output_e, u'traceback')
202 for frame in output.traceback:
203 frame_e = ET.SubElement(tb_e, u'frame')
204 frame_e.text = frame
205 elif cell_type == u'html':
206 cell_e = ET.SubElement(cells_e, u'htmlcell')
207 _set_text(cell,u'source',cell_e,u'source')
208 _set_text(cell,u'rendered',cell_e,u'rendered')
209 elif cell_type == u'markdown':
210 cell_e = ET.SubElement(cells_e, u'markdowncell')
211 _set_text(cell,u'source',cell_e,u'source')
212 _set_text(cell,u'rendered',cell_e,u'rendered')
195 213
196 214 indent(nb_e)
197 215 txt = ET.tostring(nb_e, encoding="utf-8")
198 216 txt = '<?xml version="1.0" encoding="utf-8"?>\n' + txt
199 217 return txt
200 218
201 219
202 220 _reader = XMLReader()
203 221 _writer = XMLWriter()
204 222
205 223 reads = _reader.reads
206 224 read = _reader.read
207 225 to_notebook = _reader.to_notebook
208 226 write = _writer.write
209 227 writes = _writer.writes
210 228
@@ -1,98 +1,102 b''
1 1 from ..nbbase import (
2 2 NotebookNode,
3 3 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
4 4 )
5 5
6 6
7 7
8 8 ws = new_worksheet(name='worksheet1')
9 9
10 10 ws.cells.append(new_text_cell(
11 11 u'html',
12 12 source='Some NumPy Examples',
13 13 rendered='Some NumPy Examples'
14 14 ))
15 15
16 16
17 17 ws.cells.append(new_code_cell(
18 18 input='import numpy',
19 19 prompt_number=1,
20 20 collapsed=False
21 21 ))
22 22
23 23 ws.cells.append(new_text_cell(
24 24 u'markdown',
25 25 source='A random array',
26 26 rendered='A random array'
27 27 ))
28 28
29 29 ws.cells.append(new_code_cell(
30 30 input='a = numpy.random.rand(100)',
31 31 prompt_number=2,
32 32 collapsed=True
33 33 ))
34 34
35 35 ws.cells.append(new_code_cell(
36 36 input='print a',
37 37 prompt_number=3,
38 38 collapsed=False,
39 39 outputs=[new_output(
40 40 output_type=u'pyout',
41 41 output_text=u'<array a>',
42 42 output_html=u'The HTML rep',
43 43 output_latex=u'$a$',
44 44 output_png=b'data',
45 45 output_jpeg=b'data',
46 46 output_svg=u'<svg>',
47 47 output_json=u'json data',
48 48 output_javascript=u'var i=0;',
49 49 prompt_number=3
50 50 ),new_output(
51 51 output_type=u'display_data',
52 52 output_text=u'<array a>',
53 53 output_html=u'The HTML rep',
54 54 output_latex=u'$a$',
55 55 output_png=b'data',
56 56 output_jpeg=b'data',
57 57 output_svg=u'<svg>',
58 58 output_json=u'json data',
59 output_javascript=u'var i=0;',
60 prompt_number=4
59 output_javascript=u'var i=0;'
60 ),new_output(
61 output_type=u'pyerr',
62 etype=u'NameError',
63 evalue=u'NameError was here',
64 traceback=[u'frame 0', u'frame 1', u'frame 2']
61 65 )]
62 66 ))
63 67
64 68 nb0 = new_notebook(
65 69 name='nb0',
66 70 worksheets=[ws, new_worksheet(name='worksheet2')],
67 71 author='Bart Simpson',
68 email='bsimple@fox.com',
72 email='bsimpson@fox.com',
69 73 saved='ISO8601_goes_here',
70 74 created='ISO8601_goes_here',
71 75 license='BSD'
72 76 )
73 77
74 78 nb0_py = """# <nbformat>2</nbformat>
75 79
76 80 # <htmlcell>
77 81
78 82 # Some NumPy Examples
79 83
80 84 # <codecell>
81 85
82 86 import numpy
83 87
84 88 # <markdowncell>
85 89
86 90 # A random array
87 91
88 92 # <codecell>
89 93
90 94 a = numpy.random.rand(100)
91 95
92 96 # <codecell>
93 97
94 98 print a
95 99
96 100 """
97 101
98 102
@@ -1,82 +1,90 b''
1 1 from unittest import TestCase
2 2
3 3 from ..nbbase import (
4 4 NotebookNode,
5 5 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
6 6 )
7 7
8 8 class TestCell(TestCase):
9 9
10 10 def test_empty_code_cell(self):
11 11 cc = new_code_cell()
12 12 self.assertEquals(cc.cell_type,'code')
13 13 self.assertEquals('input' not in cc, True)
14 14 self.assertEquals('prompt_number' not in cc, True)
15 15 self.assertEquals(cc.outputs, [])
16 16 self.assertEquals(cc.collapsed, False)
17 17
18 18 def test_code_cell(self):
19 19 cc = new_code_cell(input='a=10', prompt_number=0, collapsed=True)
20 20 cc.outputs = [new_output(output_type='pyout',
21 21 output_svg='foo',output_text='10',prompt_number=0)]
22 22 self.assertEquals(cc.input, u'a=10')
23 23 self.assertEquals(cc.prompt_number, 0)
24 24 self.assertEquals(cc.language, u'python')
25 25 self.assertEquals(cc.outputs[0].svg, u'foo')
26 26 self.assertEquals(cc.outputs[0].text, u'10')
27 27 self.assertEquals(cc.outputs[0].prompt_number, 0)
28 28 self.assertEquals(cc.collapsed, True)
29 29
30 def test_pyerr(self):
31 o = new_output(output_type=u'pyerr', etype=u'NameError',
32 evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2']
33 )
34 self.assertEquals(o.output_type, u'pyerr')
35 self.assertEquals(o.etype, u'NameError')
36 self.assertEquals(o.evalue, u'Name not found')
37 self.assertEquals(o.traceback, [u'frame 0', u'frame 1', u'frame 2'])
30 38
31 39 def test_empty_html_cell(self):
32 40 tc = new_text_cell(u'html')
33 41 self.assertEquals(tc.cell_type, u'html')
34 42 self.assertEquals('source' not in tc, True)
35 43 self.assertEquals('rendered' not in tc, True)
36 44
37 45 def test_html_cell(self):
38 46 tc = new_text_cell(u'html', 'hi', 'hi')
39 47 self.assertEquals(tc.source, u'hi')
40 48 self.assertEquals(tc.rendered, u'hi')
41 49
42 50 def test_empty_markdown_cell(self):
43 51 tc = new_text_cell(u'markdown')
44 52 self.assertEquals(tc.cell_type, u'markdown')
45 53 self.assertEquals('source' not in tc, True)
46 54 self.assertEquals('rendered' not in tc, True)
47 55
48 56 def test_markdown_cell(self):
49 57 tc = new_text_cell(u'markdown', 'hi', 'hi')
50 58 self.assertEquals(tc.source, u'hi')
51 59 self.assertEquals(tc.rendered, u'hi')
52 60
53 61
54 62 class TestWorksheet(TestCase):
55 63
56 64 def test_empty_worksheet(self):
57 65 ws = new_worksheet()
58 66 self.assertEquals(ws.cells,[])
59 67 self.assertEquals('name' not in ws, True)
60 68
61 69 def test_worksheet(self):
62 70 cells = [new_code_cell(), new_text_cell(u'html')]
63 71 ws = new_worksheet(cells=cells,name='foo')
64 72 self.assertEquals(ws.cells,cells)
65 73 self.assertEquals(ws.name,u'foo')
66 74
67 75 class TestNotebook(TestCase):
68 76
69 77 def test_empty_notebook(self):
70 78 nb = new_notebook()
71 79 self.assertEquals('id' in nb, True)
72 80 self.assertEquals(nb.worksheets, [])
73 81 self.assertEquals('name' not in nb, True)
74 82 self.assertEquals(nb.nbformat,2)
75 83
76 84 def test_notebook(self):
77 85 worksheets = [new_worksheet(),new_worksheet()]
78 86 nb = new_notebook(name='foo',worksheets=worksheets)
79 87 self.assertEquals(nb.name,u'foo')
80 88 self.assertEquals(nb.worksheets,worksheets)
81 89 self.assertEquals(nb.nbformat,2)
82 90
General Comments 0
You need to be logged in to leave comments. Login now