Show More
@@ -114,6 +114,7 b' class NbconvertPostHandler(IPythonHandler):' | |||||
114 | exporter = get_exporter(format, config=self.config) |
|
114 | exporter = get_exporter(format, config=self.config) | |
115 |
|
115 | |||
116 | model = self.get_json_body() |
|
116 | model = self.get_json_body() | |
|
117 | name = model.get('name', 'notebook.ipynb') | |||
117 | nbnode = to_notebook_json(model['content']) |
|
118 | nbnode = to_notebook_json(model['content']) | |
118 |
|
119 | |||
119 | try: |
|
120 | try: | |
@@ -121,7 +122,7 b' class NbconvertPostHandler(IPythonHandler):' | |||||
121 | except Exception as e: |
|
122 | except Exception as e: | |
122 | raise web.HTTPError(500, "nbconvert failed: %s" % e) |
|
123 | raise web.HTTPError(500, "nbconvert failed: %s" % e) | |
123 |
|
124 | |||
124 |
if respond_zip(self, |
|
125 | if respond_zip(self, name, output, resources): | |
125 | return |
|
126 | return | |
126 |
|
127 | |||
127 | # MIME type |
|
128 | # MIME type |
@@ -10,7 +10,7 b' import requests' | |||||
10 |
|
10 | |||
11 | from IPython.html.utils import url_path_join |
|
11 | from IPython.html.utils import url_path_join | |
12 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error |
|
12 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error | |
13 |
from IPython.nbformat.current import (new_notebook, write, |
|
13 | from IPython.nbformat.current import (new_notebook, write, | |
14 | new_heading_cell, new_code_cell, |
|
14 | new_heading_cell, new_code_cell, | |
15 | new_output) |
|
15 | new_output) | |
16 |
|
16 | |||
@@ -43,7 +43,8 b' class NbconvertAPI(object):' | |||||
43 |
|
43 | |||
44 | png_green_pixel = base64.encodestring(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00' |
|
44 | png_green_pixel = base64.encodestring(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00' | |
45 | b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT' |
|
45 | b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT' | |
46 |
b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82' |
|
46 | b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82' | |
|
47 | ).decode('ascii') | |||
47 |
|
48 | |||
48 | class APITest(NotebookTestBase): |
|
49 | class APITest(NotebookTestBase): | |
49 | def setUp(self): |
|
50 | def setUp(self): | |
@@ -52,19 +53,20 b' class APITest(NotebookTestBase):' | |||||
52 | if not os.path.isdir(pjoin(nbdir, 'foo')): |
|
53 | if not os.path.isdir(pjoin(nbdir, 'foo')): | |
53 | os.mkdir(pjoin(nbdir, 'foo')) |
|
54 | os.mkdir(pjoin(nbdir, 'foo')) | |
54 |
|
55 | |||
55 |
nb = new_notebook( |
|
56 | nb = new_notebook() | |
56 |
|
57 | |||
57 | ws = new_worksheet() |
|
58 | nb.cells.append(new_heading_cell(u'Created by test ³')) | |
58 | nb.worksheets = [ws] |
|
59 | cc1 = new_code_cell(source=u'print(2*6)') | |
59 | ws.cells.append(new_heading_cell(u'Created by test ³')) |
|
60 | cc1.outputs.append(new_output(output_type="stream", data=u'12')) | |
60 | cc1 = new_code_cell(input=u'print(2*6)') |
|
61 | cc1.outputs.append(new_output(output_type="execute_result", | |
61 | cc1.outputs.append(new_output(output_text=u'12', output_type='stream')) |
|
62 | mime_bundle={'image/png' : png_green_pixel}, | |
62 | cc1.outputs.append(new_output(output_png=png_green_pixel, output_type='pyout')) |
|
63 | prompt_number=1, | |
63 | ws.cells.append(cc1) |
|
64 | )) | |
|
65 | nb.cells.append(cc1) | |||
64 |
|
66 | |||
65 | with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w', |
|
67 | with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w', | |
66 | encoding='utf-8') as f: |
|
68 | encoding='utf-8') as f: | |
67 |
write(nb, f |
|
69 | write(nb, f) | |
68 |
|
70 | |||
69 | self.nbconvert_api = NbconvertAPI(self.base_url()) |
|
71 | self.nbconvert_api = NbconvertAPI(self.base_url()) | |
70 |
|
72 |
@@ -234,8 +234,7 b' class ContentsManager(LoggingConfigurable):' | |||||
234 | model = {} |
|
234 | model = {} | |
235 | if 'content' not in model and model.get('type', None) != 'directory': |
|
235 | if 'content' not in model and model.get('type', None) != 'directory': | |
236 | if ext == '.ipynb': |
|
236 | if ext == '.ipynb': | |
237 |
m |
|
237 | model['content'] = current.new_notebook() | |
238 | model['content'] = current.new_notebook(metadata=metadata) |
|
|||
239 | model['type'] = 'notebook' |
|
238 | model['type'] = 'notebook' | |
240 | model['format'] = 'json' |
|
239 | model['format'] = 'json' | |
241 | else: |
|
240 | else: |
@@ -15,7 +15,7 b' import requests' | |||||
15 | from IPython.html.utils import url_path_join, url_escape |
|
15 | from IPython.html.utils import url_path_join, url_escape | |
16 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error |
|
16 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error | |
17 | from IPython.nbformat import current |
|
17 | from IPython.nbformat import current | |
18 |
from IPython.nbformat.current import (new_notebook, write, read, |
|
18 | from IPython.nbformat.current import (new_notebook, write, read, | |
19 | new_heading_cell, to_notebook_json) |
|
19 | new_heading_cell, to_notebook_json) | |
20 | from IPython.nbformat import v2 |
|
20 | from IPython.nbformat import v2 | |
21 | from IPython.utils import py3compat |
|
21 | from IPython.utils import py3compat | |
@@ -142,7 +142,7 b' class APITest(NotebookTestBase):' | |||||
142 | # create a notebook |
|
142 | # create a notebook | |
143 | with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w', |
|
143 | with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w', | |
144 | encoding='utf-8') as f: |
|
144 | encoding='utf-8') as f: | |
145 |
nb = new_notebook( |
|
145 | nb = new_notebook() | |
146 | write(nb, f, format='ipynb') |
|
146 | write(nb, f, format='ipynb') | |
147 |
|
147 | |||
148 | # create a text file |
|
148 | # create a text file | |
@@ -286,14 +286,14 b' class APITest(NotebookTestBase):' | |||||
286 | self.assertEqual(model['content'], '') |
|
286 | self.assertEqual(model['content'], '') | |
287 |
|
287 | |||
288 | def test_upload_untitled(self): |
|
288 | def test_upload_untitled(self): | |
289 |
nb = new_notebook( |
|
289 | nb = new_notebook() | |
290 | nbmodel = {'content': nb, 'type': 'notebook'} |
|
290 | nbmodel = {'content': nb, 'type': 'notebook'} | |
291 | resp = self.api.upload_untitled(path=u'å b', |
|
291 | resp = self.api.upload_untitled(path=u'å b', | |
292 | body=json.dumps(nbmodel)) |
|
292 | body=json.dumps(nbmodel)) | |
293 | self._check_created(resp, 'Untitled0.ipynb', u'å b') |
|
293 | self._check_created(resp, 'Untitled0.ipynb', u'å b') | |
294 |
|
294 | |||
295 | def test_upload(self): |
|
295 | def test_upload(self): | |
296 |
nb = new_notebook( |
|
296 | nb = new_notebook() | |
297 | nbmodel = {'content': nb, 'type': 'notebook'} |
|
297 | nbmodel = {'content': nb, 'type': 'notebook'} | |
298 | resp = self.api.upload(u'Upload tést.ipynb', path=u'å b', |
|
298 | resp = self.api.upload(u'Upload tést.ipynb', path=u'å b', | |
299 | body=json.dumps(nbmodel)) |
|
299 | body=json.dumps(nbmodel)) | |
@@ -355,7 +355,6 b' class APITest(NotebookTestBase):' | |||||
355 | resp = self.api.read(u'Upload tést.ipynb', u'å b') |
|
355 | resp = self.api.read(u'Upload tést.ipynb', u'å b') | |
356 | data = resp.json() |
|
356 | data = resp.json() | |
357 | self.assertEqual(data['content']['nbformat'], current.nbformat) |
|
357 | self.assertEqual(data['content']['nbformat'], current.nbformat) | |
358 | self.assertEqual(data['content']['orig_nbformat'], 2) |
|
|||
359 |
|
358 | |||
360 | def test_copy_untitled(self): |
|
359 | def test_copy_untitled(self): | |
361 | resp = self.api.copy_untitled(u'ç d.ipynb', path=u'å b') |
|
360 | resp = self.api.copy_untitled(u'ç d.ipynb', path=u'å b') | |
@@ -416,9 +415,7 b' class APITest(NotebookTestBase):' | |||||
416 | resp = self.api.read('a.ipynb', 'foo') |
|
415 | resp = self.api.read('a.ipynb', 'foo') | |
417 | nbcontent = json.loads(resp.text)['content'] |
|
416 | nbcontent = json.loads(resp.text)['content'] | |
418 | nb = to_notebook_json(nbcontent) |
|
417 | nb = to_notebook_json(nbcontent) | |
419 | ws = new_worksheet() |
|
418 | nb.cells.append(new_heading_cell(u'Created by test ³')) | |
420 | nb.worksheets = [ws] |
|
|||
421 | ws.cells.append(new_heading_cell(u'Created by test ³')) |
|
|||
422 |
|
419 | |||
423 | nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'} |
|
420 | nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'} | |
424 | resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) |
|
421 | resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) | |
@@ -426,11 +423,11 b' class APITest(NotebookTestBase):' | |||||
426 | nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb') |
|
423 | nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb') | |
427 | with io.open(nbfile, 'r', encoding='utf-8') as f: |
|
424 | with io.open(nbfile, 'r', encoding='utf-8') as f: | |
428 | newnb = read(f, format='ipynb') |
|
425 | newnb = read(f, format='ipynb') | |
429 |
self.assertEqual(newnb |
|
426 | self.assertEqual(newnb.cells[0].source, | |
430 | u'Created by test ³') |
|
427 | u'Created by test ³') | |
431 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] |
|
428 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] | |
432 | newnb = to_notebook_json(nbcontent) |
|
429 | newnb = to_notebook_json(nbcontent) | |
433 |
self.assertEqual(newnb |
|
430 | self.assertEqual(newnb.cells[0].source, | |
434 | u'Created by test ³') |
|
431 | u'Created by test ³') | |
435 |
|
432 | |||
436 | # Save and rename |
|
433 | # Save and rename | |
@@ -455,10 +452,8 b' class APITest(NotebookTestBase):' | |||||
455 | # Modify it |
|
452 | # Modify it | |
456 | nbcontent = json.loads(resp.text)['content'] |
|
453 | nbcontent = json.loads(resp.text)['content'] | |
457 | nb = to_notebook_json(nbcontent) |
|
454 | nb = to_notebook_json(nbcontent) | |
458 | ws = new_worksheet() |
|
|||
459 | nb.worksheets = [ws] |
|
|||
460 | hcell = new_heading_cell('Created by test') |
|
455 | hcell = new_heading_cell('Created by test') | |
461 |
|
|
456 | nb.cells.append(hcell) | |
462 | # Save |
|
457 | # Save | |
463 | nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'} |
|
458 | nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'} | |
464 | resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) |
|
459 | resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) | |
@@ -469,14 +464,14 b' class APITest(NotebookTestBase):' | |||||
469 |
|
464 | |||
470 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] |
|
465 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] | |
471 | nb = to_notebook_json(nbcontent) |
|
466 | nb = to_notebook_json(nbcontent) | |
472 |
self.assertEqual(nb |
|
467 | self.assertEqual(nb.cells[0].source, 'Created by test') | |
473 |
|
468 | |||
474 | # Restore cp1 |
|
469 | # Restore cp1 | |
475 | r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id']) |
|
470 | r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id']) | |
476 | self.assertEqual(r.status_code, 204) |
|
471 | self.assertEqual(r.status_code, 204) | |
477 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] |
|
472 | nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] | |
478 | nb = to_notebook_json(nbcontent) |
|
473 | nb = to_notebook_json(nbcontent) | |
479 |
self.assertEqual(nb. |
|
474 | self.assertEqual(nb.cells, []) | |
480 |
|
475 | |||
481 | # Delete cp1 |
|
476 | # Delete cp1 | |
482 | r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id']) |
|
477 | r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id']) |
@@ -95,11 +95,9 b' class TestContentsManager(TestCase):' | |||||
95 | return os_path |
|
95 | return os_path | |
96 |
|
96 | |||
97 | def add_code_cell(self, nb): |
|
97 | def add_code_cell(self, nb): | |
98 |
output = current.new_output("display_data", |
|
98 | output = current.new_output("display_data", {'application/javascript': "alert('hi');"}) | |
99 | cell = current.new_code_cell("print('hi')", outputs=[output]) |
|
99 | cell = current.new_code_cell("print('hi')", outputs=[output]) | |
100 | if not nb.worksheets: |
|
100 | nb.cells.append(cell) | |
101 | nb.worksheets.append(current.new_worksheet()) |
|
|||
102 | nb.worksheets[0].cells.append(cell) |
|
|||
103 |
|
101 | |||
104 | def new_notebook(self): |
|
102 | def new_notebook(self): | |
105 | cm = self.contents_manager |
|
103 | cm = self.contents_manager | |
@@ -309,13 +307,13 b' class TestContentsManager(TestCase):' | |||||
309 | nb, name, path = self.new_notebook() |
|
307 | nb, name, path = self.new_notebook() | |
310 |
|
308 | |||
311 | cm.mark_trusted_cells(nb, name, path) |
|
309 | cm.mark_trusted_cells(nb, name, path) | |
312 |
for cell in nb. |
|
310 | for cell in nb.cells: | |
313 | if cell.cell_type == 'code': |
|
311 | if cell.cell_type == 'code': | |
314 | assert not cell.metadata.trusted |
|
312 | assert not cell.metadata.trusted | |
315 |
|
313 | |||
316 | cm.trust_notebook(name, path) |
|
314 | cm.trust_notebook(name, path) | |
317 | nb = cm.get_model(name, path)['content'] |
|
315 | nb = cm.get_model(name, path)['content'] | |
318 |
for cell in nb. |
|
316 | for cell in nb.cells: | |
319 | if cell.cell_type == 'code': |
|
317 | if cell.cell_type == 'code': | |
320 | assert cell.metadata.trusted |
|
318 | assert cell.metadata.trusted | |
321 |
|
319 |
@@ -62,7 +62,7 b' class SessionAPITest(NotebookTestBase):' | |||||
62 |
|
62 | |||
63 | with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w', |
|
63 | with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w', | |
64 | encoding='utf-8') as f: |
|
64 | encoding='utf-8') as f: | |
65 |
nb = new_notebook( |
|
65 | nb = new_notebook() | |
66 | write(nb, f, format='ipynb') |
|
66 | write(nb, f, format='ipynb') | |
67 |
|
67 | |||
68 | self.sess_api = SessionAPI(self.base_url()) |
|
68 | self.sess_api = SessionAPI(self.base_url()) |
@@ -382,13 +382,11 b' define([' | |||||
382 |
|
382 | |||
383 |
|
383 | |||
384 | CodeCell.prototype.collapse_output = function () { |
|
384 | CodeCell.prototype.collapse_output = function () { | |
385 | this.collapsed = true; |
|
|||
386 | this.output_area.collapse(); |
|
385 | this.output_area.collapse(); | |
387 | }; |
|
386 | }; | |
388 |
|
387 | |||
389 |
|
388 | |||
390 | CodeCell.prototype.expand_output = function () { |
|
389 | CodeCell.prototype.expand_output = function () { | |
391 | this.collapsed = false; |
|
|||
392 | this.output_area.expand(); |
|
390 | this.output_area.expand(); | |
393 | this.output_area.unscroll_area(); |
|
391 | this.output_area.unscroll_area(); | |
394 | }; |
|
392 | }; | |
@@ -399,7 +397,6 b' define([' | |||||
399 | }; |
|
397 | }; | |
400 |
|
398 | |||
401 | CodeCell.prototype.toggle_output = function () { |
|
399 | CodeCell.prototype.toggle_output = function () { | |
402 | this.collapsed = Boolean(1 - this.collapsed); |
|
|||
403 | this.output_area.toggle_output(); |
|
400 | this.output_area.toggle_output(); | |
404 | }; |
|
401 | }; | |
405 |
|
402 | |||
@@ -467,22 +464,18 b' define([' | |||||
467 | CodeCell.prototype.fromJSON = function (data) { |
|
464 | CodeCell.prototype.fromJSON = function (data) { | |
468 | Cell.prototype.fromJSON.apply(this, arguments); |
|
465 | Cell.prototype.fromJSON.apply(this, arguments); | |
469 | if (data.cell_type === 'code') { |
|
466 | if (data.cell_type === 'code') { | |
470 |
if (data. |
|
467 | if (data.source !== undefined) { | |
471 |
this.set_text(data. |
|
468 | this.set_text(data.source); | |
472 | // make this value the starting point, so that we can only undo |
|
469 | // make this value the starting point, so that we can only undo | |
473 | // to this state, instead of a blank cell |
|
470 | // to this state, instead of a blank cell | |
474 | this.code_mirror.clearHistory(); |
|
471 | this.code_mirror.clearHistory(); | |
475 | this.auto_highlight(); |
|
472 | this.auto_highlight(); | |
476 | } |
|
473 | } | |
477 | if (data.prompt_number !== undefined) { |
|
|||
478 |
|
|
474 | this.set_input_prompt(data.prompt_number); | |
479 | } else { |
|
|||
480 | this.set_input_prompt(); |
|
|||
481 | } |
|
|||
482 | this.output_area.trusted = data.metadata.trusted || false; |
|
475 | this.output_area.trusted = data.metadata.trusted || false; | |
483 | this.output_area.fromJSON(data.outputs); |
|
476 | this.output_area.fromJSON(data.outputs); | |
484 | if (data.collapsed !== undefined) { |
|
477 | if (data.metadata.collapsed !== undefined) { | |
485 | if (data.collapsed) { |
|
478 | if (data.metadata.collapsed) { | |
486 | this.collapse_output(); |
|
479 | this.collapse_output(); | |
487 | } else { |
|
480 | } else { | |
488 | this.expand_output(); |
|
481 | this.expand_output(); | |
@@ -494,16 +487,17 b' define([' | |||||
494 |
|
487 | |||
495 | CodeCell.prototype.toJSON = function () { |
|
488 | CodeCell.prototype.toJSON = function () { | |
496 | var data = Cell.prototype.toJSON.apply(this); |
|
489 | var data = Cell.prototype.toJSON.apply(this); | |
497 |
data. |
|
490 | data.source = this.get_text(); | |
498 | // is finite protect against undefined and '*' value |
|
491 | // is finite protect against undefined and '*' value | |
499 | if (isFinite(this.input_prompt_number)) { |
|
492 | if (isFinite(this.input_prompt_number)) { | |
500 | data.prompt_number = this.input_prompt_number; |
|
493 | data.prompt_number = this.input_prompt_number; | |
|
494 | } else { | |||
|
495 | data.prompt_number = null; | |||
501 | } |
|
496 | } | |
502 | var outputs = this.output_area.toJSON(); |
|
497 | var outputs = this.output_area.toJSON(); | |
503 | data.outputs = outputs; |
|
498 | data.outputs = outputs; | |
504 | data.language = 'python'; |
|
|||
505 | data.metadata.trusted = this.output_area.trusted; |
|
499 | data.metadata.trusted = this.output_area.trusted; | |
506 | data.collapsed = this.output_area.collapsed; |
|
500 | data.metadata.collapsed = this.output_area.collapsed; | |
507 | return data; |
|
501 | return data; | |
508 | }; |
|
502 | }; | |
509 |
|
503 |
@@ -121,10 +121,8 b' define([' | |||||
121 | this.autosave_timer = null; |
|
121 | this.autosave_timer = null; | |
122 | // autosave *at most* every two minutes |
|
122 | // autosave *at most* every two minutes | |
123 | this.minimum_autosave_interval = 120000; |
|
123 | this.minimum_autosave_interval = 120000; | |
124 | // single worksheet for now |
|
|||
125 | this.worksheet_metadata = {}; |
|
|||
126 | this.notebook_name_blacklist_re = /[\/\\:]/; |
|
124 | this.notebook_name_blacklist_re = /[\/\\:]/; | |
127 |
this.nbformat = |
|
125 | this.nbformat = 4; // Increment this when changing the nbformat | |
128 | this.nbformat_minor = 0; // Increment this when changing the nbformat |
|
126 | this.nbformat_minor = 0; // Increment this when changing the nbformat | |
129 | this.codemirror_mode = 'ipython'; |
|
127 | this.codemirror_mode = 'ipython'; | |
130 | this.create_elements(); |
|
128 | this.create_elements(); | |
@@ -1785,8 +1783,6 b' define([' | |||||
1785 | /** |
|
1783 | /** | |
1786 | * Load a notebook from JSON (.ipynb). |
|
1784 | * Load a notebook from JSON (.ipynb). | |
1787 | * |
|
1785 | * | |
1788 | * This currently handles one worksheet: others are deleted. |
|
|||
1789 | * |
|
|||
1790 | * @method fromJSON |
|
1786 | * @method fromJSON | |
1791 | * @param {Object} data JSON representation of a notebook |
|
1787 | * @param {Object} data JSON representation of a notebook | |
1792 | */ |
|
1788 | */ | |
@@ -1818,50 +1814,22 b' define([' | |||||
1818 | this.set_codemirror_mode(cm_mode); |
|
1814 | this.set_codemirror_mode(cm_mode); | |
1819 | } |
|
1815 | } | |
1820 |
|
1816 | |||
1821 | // Only handle 1 worksheet for now. |
|
1817 | var new_cells = content.cells; | |
1822 | var worksheet = content.worksheets[0]; |
|
|||
1823 | if (worksheet !== undefined) { |
|
|||
1824 | if (worksheet.metadata) { |
|
|||
1825 | this.worksheet_metadata = worksheet.metadata; |
|
|||
1826 | } |
|
|||
1827 | var new_cells = worksheet.cells; |
|
|||
1828 |
|
|
1818 | ncells = new_cells.length; | |
1829 |
|
|
1819 | var cell_data = null; | |
1830 |
|
|
1820 | var new_cell = null; | |
1831 |
|
|
1821 | for (i=0; i<ncells; i++) { | |
1832 |
|
|
1822 | cell_data = new_cells[i]; | |
1833 | // VERSIONHACK: plaintext -> raw |
|
|||
1834 | // handle never-released plaintext name for raw cells |
|
|||
1835 | if (cell_data.cell_type === 'plaintext'){ |
|
|||
1836 | cell_data.cell_type = 'raw'; |
|
|||
1837 | } |
|
|||
1838 |
|
||||
1839 |
|
|
1823 | new_cell = this.insert_cell_at_index(cell_data.cell_type, i); | |
1840 |
|
|
1824 | new_cell.fromJSON(cell_data); | |
1841 |
|
|
1825 | if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) { | |
1842 |
|
|
1826 | trusted = false; | |
1843 |
|
|
1827 | } | |
1844 |
|
|
1828 | } | |
1845 | } |
|
|||
1846 | if (trusted !== this.trusted) { |
|
1829 | if (trusted !== this.trusted) { | |
1847 | this.trusted = trusted; |
|
1830 | this.trusted = trusted; | |
1848 | this.events.trigger("trust_changed.Notebook", trusted); |
|
1831 | this.events.trigger("trust_changed.Notebook", trusted); | |
1849 | } |
|
1832 | } | |
1850 | if (content.worksheets.length > 1) { |
|
|||
1851 | dialog.modal({ |
|
|||
1852 | notebook: this, |
|
|||
1853 | keyboard_manager: this.keyboard_manager, |
|
|||
1854 | title : "Multiple worksheets", |
|
|||
1855 | body : "This notebook has " + data.worksheets.length + " worksheets, " + |
|
|||
1856 | "but this version of IPython can only handle the first. " + |
|
|||
1857 | "If you save this notebook, worksheets after the first will be lost.", |
|
|||
1858 | buttons : { |
|
|||
1859 | OK : { |
|
|||
1860 | class : "btn-danger" |
|
|||
1861 | } |
|
|||
1862 | } |
|
|||
1863 | }); |
|
|||
1864 | } |
|
|||
1865 | }; |
|
1833 | }; | |
1866 |
|
1834 | |||
1867 | /** |
|
1835 | /** | |
@@ -1871,6 +1839,10 b' define([' | |||||
1871 | * @return {Object} A JSON-friendly representation of this notebook. |
|
1839 | * @return {Object} A JSON-friendly representation of this notebook. | |
1872 | */ |
|
1840 | */ | |
1873 | Notebook.prototype.toJSON = function () { |
|
1841 | Notebook.prototype.toJSON = function () { | |
|
1842 | // remove the conversion indicator, which only belongs in-memory | |||
|
1843 | delete this.metadata.orig_nbformat; | |||
|
1844 | delete this.metadata.orig_nbformat_minor; | |||
|
1845 | ||||
1874 | var cells = this.get_cells(); |
|
1846 | var cells = this.get_cells(); | |
1875 | var ncells = cells.length; |
|
1847 | var ncells = cells.length; | |
1876 | var cell_array = new Array(ncells); |
|
1848 | var cell_array = new Array(ncells); | |
@@ -1883,11 +1855,7 b' define([' | |||||
1883 | cell_array[i] = cell.toJSON(); |
|
1855 | cell_array[i] = cell.toJSON(); | |
1884 | } |
|
1856 | } | |
1885 | var data = { |
|
1857 | var data = { | |
1886 | // Only handle 1 worksheet for now. |
|
|||
1887 | worksheets : [{ |
|
|||
1888 |
|
|
1858 | cells: cell_array, | |
1889 | metadata: this.worksheet_metadata |
|
|||
1890 | }], |
|
|||
1891 | metadata : this.metadata |
|
1859 | metadata : this.metadata | |
1892 | }; |
|
1860 | }; | |
1893 | if (trusted != this.trusted) { |
|
1861 | if (trusted != this.trusted) { | |
@@ -2337,10 +2305,13 b' define([' | |||||
2337 | } |
|
2305 | } | |
2338 | this.set_dirty(false); |
|
2306 | this.set_dirty(false); | |
2339 | this.scroll_to_top(); |
|
2307 | this.scroll_to_top(); | |
2340 | if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) { |
|
2308 | var nbmodel = data.content; | |
|
2309 | var orig_nbformat = nbmodel.metadata.orig_nbformat; | |||
|
2310 | var orig_nbformat_minor = nbmodel.metadata.orig_nbformat_minor; | |||
|
2311 | if (orig_nbformat !== undefined && nbmodel.nbformat !== orig_nbformat) { | |||
2341 | var msg = "This notebook has been converted from an older " + |
|
2312 | var msg = "This notebook has been converted from an older " + | |
2342 |
"notebook format (v"+ |
|
2313 | "notebook format (v"+orig_nbformat+") to the current notebook " + | |
2343 |
"format (v"+ |
|
2314 | "format (v"+nbmodel.nbformat+"). The next time you save this notebook, the " + | |
2344 | "newer notebook format will be used and older versions of IPython " + |
|
2315 | "newer notebook format will be used and older versions of IPython " + | |
2345 | "may not be able to read it. To keep the older version, close the " + |
|
2316 | "may not be able to read it. To keep the older version, close the " + | |
2346 | "notebook without saving it."; |
|
2317 | "notebook without saving it."; | |
@@ -2355,10 +2326,10 b' define([' | |||||
2355 | } |
|
2326 | } | |
2356 | } |
|
2327 | } | |
2357 | }); |
|
2328 | }); | |
2358 |
} else if ( |
|
2329 | } else if (orig_nbformat_minor !== undefined && nbmodel.nbformat_minor !== orig_nbformat_minor) { | |
2359 | var that = this; |
|
2330 | var that = this; | |
2360 |
var orig_vs = 'v' + |
|
2331 | var orig_vs = 'v' + nbmodel.nbformat + '.' + orig_nbformat_minor; | |
2361 |
var this_vs = 'v' + |
|
2332 | var this_vs = 'v' + nbmodel.nbformat + '.' + this.nbformat_minor; | |
2362 | var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " + |
|
2333 | var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " + | |
2363 | this_vs + ". You can still work with this notebook, but some features " + |
|
2334 | this_vs + ". You can still work with this notebook, but some features " + | |
2364 | "introduced in later notebook versions may not be available."; |
|
2335 | "introduced in later notebook versions may not be available."; |
@@ -211,7 +211,7 b' define([' | |||||
211 | var content = msg.content; |
|
211 | var content = msg.content; | |
212 | if (msg_type === "stream") { |
|
212 | if (msg_type === "stream") { | |
213 | json.text = content.text; |
|
213 | json.text = content.text; | |
214 |
json. |
|
214 | json.name = content.name; | |
215 | } else if (msg_type === "display_data") { |
|
215 | } else if (msg_type === "display_data") { | |
216 | json = content.data; |
|
216 | json = content.data; | |
217 | json.output_type = msg_type; |
|
217 | json.output_type = msg_type; | |
@@ -234,6 +234,7 b' define([' | |||||
234 |
|
234 | |||
235 |
|
235 | |||
236 | OutputArea.prototype.rename_keys = function (data, key_map) { |
|
236 | OutputArea.prototype.rename_keys = function (data, key_map) { | |
|
237 | // TODO: This is now unused, should it be removed? | |||
237 | var remapped = {}; |
|
238 | var remapped = {}; | |
238 | for (var key in data) { |
|
239 | for (var key in data) { | |
239 | var new_key = key_map[key] || key; |
|
240 | var new_key = key_map[key] || key; | |
@@ -260,7 +261,10 b' define([' | |||||
260 | // TODO: right now everything is a string, but JSON really shouldn't be. |
|
261 | // TODO: right now everything is a string, but JSON really shouldn't be. | |
261 | // nbformat 4 will fix that. |
|
262 | // nbformat 4 will fix that. | |
262 | $.map(OutputArea.output_types, function(key){ |
|
263 | $.map(OutputArea.output_types, function(key){ | |
263 | if (json[key] !== undefined && typeof json[key] !== 'string') { |
|
264 | if (key !== 'application/json' && | |
|
265 | json[key] !== undefined && | |||
|
266 | typeof json[key] !== 'string' | |||
|
267 | ) { | |||
264 | console.log("Invalid type for " + key, json[key]); |
|
268 | console.log("Invalid type for " + key, json[key]); | |
265 | delete json[key]; |
|
269 | delete json[key]; | |
266 | } |
|
270 | } | |
@@ -449,23 +453,18 b' define([' | |||||
449 |
|
453 | |||
450 |
|
454 | |||
451 | OutputArea.prototype.append_stream = function (json) { |
|
455 | OutputArea.prototype.append_stream = function (json) { | |
452 | // temporary fix: if stream undefined (json file written prior to this patch), |
|
456 | var text = json.data; | |
453 | // default to most likely stdout: |
|
457 | var subclass = "output_"+json.name; | |
454 | if (json.stream === undefined){ |
|
|||
455 | json.stream = 'stdout'; |
|
|||
456 | } |
|
|||
457 | var text = json.text; |
|
|||
458 | var subclass = "output_"+json.stream; |
|
|||
459 | if (this.outputs.length > 0){ |
|
458 | if (this.outputs.length > 0){ | |
460 | // have at least one output to consider |
|
459 | // have at least one output to consider | |
461 | var last = this.outputs[this.outputs.length-1]; |
|
460 | var last = this.outputs[this.outputs.length-1]; | |
462 |
if (last.output_type == 'stream' && json. |
|
461 | if (last.output_type == 'stream' && json.name == last.name){ | |
463 | // latest output was in the same stream, |
|
462 | // latest output was in the same stream, | |
464 | // so append directly into its pre tag |
|
463 | // so append directly into its pre tag | |
465 | // escape ANSI & HTML specials: |
|
464 | // escape ANSI & HTML specials: | |
466 |
last. |
|
465 | last.data = utils.fixCarriageReturn(last.data + json.data); | |
467 | var pre = this.element.find('div.'+subclass).last().find('pre'); |
|
466 | var pre = this.element.find('div.'+subclass).last().find('pre'); | |
468 |
var html = utils.fixConsole(last. |
|
467 | var html = utils.fixConsole(last.data); | |
469 | // The only user content injected with this HTML call is |
|
468 | // The only user content injected with this HTML call is | |
470 | // escaped by the fixConsole() method. |
|
469 | // escaped by the fixConsole() method. | |
471 | pre.html(html); |
|
470 | pre.html(html); | |
@@ -852,70 +851,33 b' define([' | |||||
852 |
|
851 | |||
853 | // JSON serialization |
|
852 | // JSON serialization | |
854 |
|
853 | |||
855 | OutputArea.prototype.fromJSON = function (outputs) { |
|
854 | OutputArea.prototype.fromJSON = function (outputs, metadata) { | |
856 | var len = outputs.length; |
|
855 | var len = outputs.length; | |
857 | var data; |
|
856 | metadata = metadata || {}; | |
858 |
|
857 | |||
859 | for (var i=0; i<len; i++) { |
|
858 | for (var i=0; i<len; i++) { | |
860 |
|
|
859 | this.append_output(outputs[i]); | |
861 | var msg_type = data.output_type; |
|
|||
862 | if (msg_type == "pyout") { |
|
|||
863 | // pyout message has been renamed to execute_result, |
|
|||
864 | // but the nbformat has not been updated, |
|
|||
865 | // so transform back to pyout for json. |
|
|||
866 | msg_type = data.output_type = "execute_result"; |
|
|||
867 | } else if (msg_type == "pyerr") { |
|
|||
868 | // pyerr message has been renamed to error, |
|
|||
869 | // but the nbformat has not been updated, |
|
|||
870 | // so transform back to pyerr for json. |
|
|||
871 | msg_type = data.output_type = "error"; |
|
|||
872 |
|
|
860 | } | |
873 | if (msg_type === "display_data" || msg_type === "execute_result") { |
|
861 | ||
874 | // convert short keys to mime keys |
|
862 | if (metadata.collapsed !== undefined) { | |
875 | // TODO: remove mapping of short keys when we update to nbformat 4 |
|
863 | this.collapsed = metadata.collapsed; | |
876 | data = this.rename_keys(data, OutputArea.mime_map_r); |
|
864 | if (metadata.collapsed) { | |
877 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r); |
|
865 | this.collapse_output(); | |
878 | // msg spec JSON is an object, nbformat v3 JSON is a JSON string |
|
|||
879 | if (data["application/json"] !== undefined && typeof data["application/json"] === 'string') { |
|
|||
880 | data["application/json"] = JSON.parse(data["application/json"]); |
|
|||
881 |
|
|
866 | } | |
882 |
|
|
867 | } | |
883 |
|
868 | if (metadata.autoscroll !== undefined) { | ||
884 |
this. |
|
869 | this.collapsed = metadata.collapsed; | |
|
870 | if (metadata.collapsed) { | |||
|
871 | this.collapse_output(); | |||
|
872 | } else { | |||
|
873 | this.expand_output(); | |||
|
874 | } | |||
885 | } |
|
875 | } | |
886 | }; |
|
876 | }; | |
887 |
|
877 | |||
888 |
|
878 | |||
889 | OutputArea.prototype.toJSON = function () { |
|
879 | OutputArea.prototype.toJSON = function () { | |
890 |
|
|
880 | return this.outputs; | |
891 | var len = this.outputs.length; |
|
|||
892 | var data; |
|
|||
893 | for (var i=0; i<len; i++) { |
|
|||
894 | data = this.outputs[i]; |
|
|||
895 | var msg_type = data.output_type; |
|
|||
896 | if (msg_type === "display_data" || msg_type === "execute_result") { |
|
|||
897 | // convert mime keys to short keys |
|
|||
898 | data = this.rename_keys(data, OutputArea.mime_map); |
|
|||
899 | data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map); |
|
|||
900 | // msg spec JSON is an object, nbformat v3 JSON is a JSON string |
|
|||
901 | if (data.json !== undefined && typeof data.json !== 'string') { |
|
|||
902 | data.json = JSON.stringify(data.json); |
|
|||
903 | } |
|
|||
904 | } |
|
|||
905 | if (msg_type == "execute_result") { |
|
|||
906 | // pyout message has been renamed to execute_result, |
|
|||
907 | // but the nbformat has not been updated, |
|
|||
908 | // so transform back to pyout for json. |
|
|||
909 | data.output_type = "pyout"; |
|
|||
910 | } else if (msg_type == "error") { |
|
|||
911 | // pyerr message has been renamed to error, |
|
|||
912 | // but the nbformat has not been updated, |
|
|||
913 | // so transform back to pyerr for json. |
|
|||
914 | data.output_type = "pyerr"; |
|
|||
915 | } |
|
|||
916 | outputs[i] = data; |
|
|||
917 | } |
|
|||
918 | return outputs; |
|
|||
919 | }; |
|
881 | }; | |
920 |
|
882 | |||
921 | /** |
|
883 | /** | |
@@ -948,29 +910,6 b' define([' | |||||
948 | OutputArea.minimum_scroll_threshold = 20; |
|
910 | OutputArea.minimum_scroll_threshold = 20; | |
949 |
|
911 | |||
950 |
|
912 | |||
951 |
|
||||
952 | OutputArea.mime_map = { |
|
|||
953 | "text/plain" : "text", |
|
|||
954 | "text/html" : "html", |
|
|||
955 | "image/svg+xml" : "svg", |
|
|||
956 | "image/png" : "png", |
|
|||
957 | "image/jpeg" : "jpeg", |
|
|||
958 | "text/latex" : "latex", |
|
|||
959 | "application/json" : "json", |
|
|||
960 | "application/javascript" : "javascript", |
|
|||
961 | }; |
|
|||
962 |
|
||||
963 | OutputArea.mime_map_r = { |
|
|||
964 | "text" : "text/plain", |
|
|||
965 | "html" : "text/html", |
|
|||
966 | "svg" : "image/svg+xml", |
|
|||
967 | "png" : "image/png", |
|
|||
968 | "jpeg" : "image/jpeg", |
|
|||
969 | "latex" : "text/latex", |
|
|||
970 | "json" : "application/json", |
|
|||
971 | "javascript" : "application/javascript", |
|
|||
972 | }; |
|
|||
973 |
|
||||
974 | OutputArea.display_order = [ |
|
913 | OutputArea.display_order = [ | |
975 | 'application/javascript', |
|
914 | 'application/javascript', | |
976 | 'text/html', |
|
915 | 'text/html', |
@@ -29,7 +29,7 b' casper.notebook_test(function () {' | |||||
29 | var ex = expected[i]; |
|
29 | var ex = expected[i]; | |
30 | this.test.assertEquals(r.output_type, ex.output_type, "output " + i); |
|
30 | this.test.assertEquals(r.output_type, ex.output_type, "output " + i); | |
31 | if (r.output_type === 'stream') { |
|
31 | if (r.output_type === 'stream') { | |
32 |
this.test.assertEquals(r. |
|
32 | this.test.assertEquals(r.name, ex.name, "stream " + i); | |
33 | this.test.assertEquals(r.text, ex.text, "content " + i); |
|
33 | this.test.assertEquals(r.text, ex.text, "content " + i); | |
34 | } |
|
34 | } | |
35 | } |
|
35 | } | |
@@ -57,7 +57,7 b' casper.notebook_test(function () {' | |||||
57 | "print(3)" |
|
57 | "print(3)" | |
58 | ].join("\n"), [{ |
|
58 | ].join("\n"), [{ | |
59 | output_type: "stream", |
|
59 | output_type: "stream", | |
60 |
|
|
60 | name: "stdout", | |
61 | text: "1\n2\n3\n" |
|
61 | text: "1\n2\n3\n" | |
62 | }] |
|
62 | }] | |
63 | ); |
|
63 | ); | |
@@ -69,11 +69,11 b' casper.notebook_test(function () {' | |||||
69 | "print(3, file=sys.stderr)" |
|
69 | "print(3, file=sys.stderr)" | |
70 | ].join("\n"), [{ |
|
70 | ].join("\n"), [{ | |
71 | output_type: "stream", |
|
71 | output_type: "stream", | |
72 |
|
|
72 | name: "stdout", | |
73 | text: "1\n2\n" |
|
73 | text: "1\n2\n" | |
74 | },{ |
|
74 | },{ | |
75 | output_type: "stream", |
|
75 | output_type: "stream", | |
76 |
|
|
76 | name: "stderr", | |
77 | text: "3\n" |
|
77 | text: "3\n" | |
78 | }] |
|
78 | }] | |
79 | ); |
|
79 | ); | |
@@ -85,13 +85,13 b' casper.notebook_test(function () {' | |||||
85 | "print(3)" |
|
85 | "print(3)" | |
86 | ].join("\n"), [{ |
|
86 | ].join("\n"), [{ | |
87 | output_type: "stream", |
|
87 | output_type: "stream", | |
88 |
|
|
88 | name: "stdout", | |
89 | text: "1\n" |
|
89 | text: "1\n" | |
90 | },{ |
|
90 | },{ | |
91 | output_type: "display_data", |
|
91 | output_type: "display_data", | |
92 | },{ |
|
92 | },{ | |
93 | output_type: "stream", |
|
93 | output_type: "stream", | |
94 |
|
|
94 | name: "stdout", | |
95 | text: "3\n" |
|
95 | text: "3\n" | |
96 | }] |
|
96 | }] | |
97 | ); |
|
97 | ); |
@@ -24,8 +24,10 b' var svg = "\\"<svg width=\'1cm\' height=\'1cm\' viewBox=\'0 0 1000 500\'><defs><style>r' | |||||
24 | // name, and that fromJSON also gets its long mimetype name |
|
24 | // name, and that fromJSON also gets its long mimetype name | |
25 | function assert_has(short_name, json, result, result2) { |
|
25 | function assert_has(short_name, json, result, result2) { | |
26 | long_name = mime[short_name]; |
|
26 | long_name = mime[short_name]; | |
27 |
this.test.assert |
|
27 | this.test.assertFalse(json[0].hasOwnProperty(short_name), | |
28 |
|
|
28 | "toJSON() representation doesn't use " + short_name); | |
|
29 | this.test.assertTrue(json[0].hasOwnProperty(long_name), | |||
|
30 | 'toJSON() representation uses ' + long_name); | |||
29 | this.test.assertTrue(result.hasOwnProperty(long_name), |
|
31 | this.test.assertTrue(result.hasOwnProperty(long_name), | |
30 | 'toJSON() original embedded JSON keeps ' + long_name); |
|
32 | 'toJSON() original embedded JSON keeps ' + long_name); | |
31 | this.test.assertTrue(result2.hasOwnProperty(long_name), |
|
33 | this.test.assertTrue(result2.hasOwnProperty(long_name), | |
@@ -34,8 +36,8 b' function assert_has(short_name, json, result, result2) {' | |||||
34 |
|
36 | |||
35 | // helper function for checkout that the first two cells have a particular |
|
37 | // helper function for checkout that the first two cells have a particular | |
36 | // output_type (either 'execute_result' or 'display_data'), and checks the to/fromJSON |
|
38 | // output_type (either 'execute_result' or 'display_data'), and checks the to/fromJSON | |
37 |
// for a set of mimetype keys, |
|
39 | // for a set of mimetype keys, ensuring the old short names ('javascript', 'text', | |
38 | // 'png', etc). |
|
40 | // 'png', etc) are not used. | |
39 | function check_output_area(output_type, keys) { |
|
41 | function check_output_area(output_type, keys) { | |
40 | this.wait_for_output(0); |
|
42 | this.wait_for_output(0); | |
41 | json = this.evaluate(function() { |
|
43 | json = this.evaluate(function() { |
@@ -10,8 +10,8 b' pjoin = os.path.join' | |||||
10 | import requests |
|
10 | import requests | |
11 | import json |
|
11 | import json | |
12 |
|
12 | |||
13 |
from IPython.nbformat.current import (new_notebook, write, |
|
13 | from IPython.nbformat.current import (new_notebook, write, | |
14 |
new_ |
|
14 | new_markdown_cell, new_code_cell, | |
15 | new_output) |
|
15 | new_output) | |
16 |
|
16 | |||
17 | from IPython.html.utils import url_path_join |
|
17 | from IPython.html.utils import url_path_join | |
@@ -62,18 +62,18 b' class FilesTest(NotebookTestBase):' | |||||
62 | nbdir = self.notebook_dir.name |
|
62 | nbdir = self.notebook_dir.name | |
63 | base = self.base_url() |
|
63 | base = self.base_url() | |
64 |
|
64 | |||
65 |
nb = new_notebook( |
|
65 | nb = new_notebook( | |
66 |
|
66 | cells=[ | ||
67 | ws = new_worksheet() |
|
67 | new_markdown_cell(u'Created by test ³'), | |
68 | nb.worksheets = [ws] |
|
68 | new_code_cell("print(2*6)", outputs=[ | |
69 | ws.cells.append(new_heading_cell(u'Created by test ³')) |
|
69 | new_output("stream", text="12"), | |
70 | cc1 = new_code_cell(input=u'print(2*6)') |
|
70 | ]) | |
71 | cc1.outputs.append(new_output(output_text=u'12', output_type='stream')) |
|
71 | ] | |
72 | ws.cells.append(cc1) |
|
72 | ) | |
73 |
|
73 | |||
74 | with io.open(pjoin(nbdir, 'testnb.ipynb'), 'w', |
|
74 | with io.open(pjoin(nbdir, 'testnb.ipynb'), 'w', | |
75 | encoding='utf-8') as f: |
|
75 | encoding='utf-8') as f: | |
76 |
write(nb, f |
|
76 | write(nb, f) | |
77 |
|
77 | |||
78 | with io.open(pjoin(nbdir, 'test.bin'), 'wb') as f: |
|
78 | with io.open(pjoin(nbdir, 'test.bin'), 'wb') as f: | |
79 | f.write(b'\xff' + os.urandom(5)) |
|
79 | f.write(b'\xff' + os.urandom(5)) |
General Comments 0
You need to be logged in to leave comments.
Login now