Show More
@@ -57,7 +57,7 b' casper.notebook_test(function () {' | |||||
57 | this.trigger_keydown('b'); // new cell below |
|
57 | this.trigger_keydown('b'); // new cell below | |
58 | this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell below code cell'); |
|
58 | this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell below code cell'); | |
59 | this.trigger_keydown('a'); // new cell above |
|
59 | this.trigger_keydown('a'); // new cell above | |
60 |
this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'a; inserts a code cell |
|
60 | this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'a; inserts a code cell above code cell'); | |
61 | }); |
|
61 | }); | |
62 |
|
62 | |||
63 | this.thenEvaluate(function() { |
|
63 | this.thenEvaluate(function() { | |
@@ -72,6 +72,6 b' casper.notebook_test(function () {' | |||||
72 | this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'a; inserts a raw cell above raw cell'); |
|
72 | this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'a; inserts a raw cell above raw cell'); | |
73 | this.trigger_keydown('y'); // switch it to code for the next test |
|
73 | this.trigger_keydown('y'); // switch it to code for the next test | |
74 | this.trigger_keydown('b'); // new cell below |
|
74 | this.trigger_keydown('b'); // new cell below | |
75 |
this.test.assertEquals(this.get_cell(3).cell_type, 'raw', 'b; inserts a raw cell |
|
75 | this.test.assertEquals(this.get_cell(3).cell_type, 'raw', 'b; inserts a raw cell below raw cell'); | |
76 | }); |
|
76 | }); | |
77 | }); |
|
77 | }); |
@@ -72,14 +72,9 b' class ExtractOutputPreprocessor(Preprocessor):' | |||||
72 | else: |
|
72 | else: | |
73 | data = data.encode("UTF-8") |
|
73 | data = data.encode("UTF-8") | |
74 |
|
74 | |||
75 | # Build an output name |
|
75 | ext = guess_extension(mime_type) | |
76 | # filthy hack while we have some mimetype output, and some not |
|
76 | if ext is None: | |
77 |
|
|
77 | ext = '.' + mime_type.rsplit('/')[-1] | |
78 | ext = guess_extension(mime_type) |
|
|||
79 | if ext is None: |
|
|||
80 | ext = '.' + mime_type.rsplit('/')[-1] |
|
|||
81 | else: |
|
|||
82 | ext = '.' + mime_type |
|
|||
83 |
|
78 | |||
84 | filename = self.output_filename_template.format( |
|
79 | filename = self.output_filename_template.format( | |
85 | unique_key=unique_key, |
|
80 | unique_key=unique_key, | |
@@ -87,10 +82,10 b' class ExtractOutputPreprocessor(Preprocessor):' | |||||
87 | index=index, |
|
82 | index=index, | |
88 | extension=ext) |
|
83 | extension=ext) | |
89 |
|
84 | |||
90 |
#On the cell, make the figure available via |
|
85 | # On the cell, make the figure available via | |
91 | # cell.outputs[i].metadata.filenames['mime/type'] |
|
86 | # cell.outputs[i].metadata.filenames['mime/type'] | |
92 |
# |
|
87 | # where | |
93 | # cell.outputs[i].data['mime/type' contains the data |
|
88 | # cell.outputs[i].data['mime/type'] contains the data | |
94 | if output_files_dir is not None: |
|
89 | if output_files_dir is not None: | |
95 | filename = os.path.join(output_files_dir, filename) |
|
90 | filename = os.path.join(output_files_dir, filename) | |
96 | out.metadata.setdefault('filenames', {}) |
|
91 | out.metadata.setdefault('filenames', {}) |
@@ -1,17 +1,9 b'' | |||||
1 | """Module containing a preprocessor that converts outputs in the notebook from |
|
1 | """Module containing a preprocessor that converts outputs in the notebook from | |
2 | one format to another. |
|
2 | one format to another. | |
3 | """ |
|
3 | """ | |
4 | #----------------------------------------------------------------------------- |
|
|||
5 | # Copyright (c) 2013, the IPython Development Team. |
|
|||
6 | # |
|
|||
7 | # Distributed under the terms of the Modified BSD License. |
|
|||
8 | # |
|
|||
9 | # The full license is in the file COPYING.txt, distributed with this software. |
|
|||
10 | #----------------------------------------------------------------------------- |
|
|||
11 |
|
4 | |||
12 | #----------------------------------------------------------------------------- |
|
5 | # Copyright (c) IPython Development Team. | |
13 | # Imports |
|
6 | # Distributed under the terms of the Modified BSD License. | |
14 | #----------------------------------------------------------------------------- |
|
|||
15 |
|
7 | |||
16 | import base64 |
|
8 | import base64 | |
17 | import io |
|
9 | import io | |
@@ -25,15 +17,8 b' from IPython.utils.traitlets import Unicode' | |||||
25 | from .convertfigures import ConvertFiguresPreprocessor |
|
17 | from .convertfigures import ConvertFiguresPreprocessor | |
26 |
|
18 | |||
27 |
|
19 | |||
28 | #----------------------------------------------------------------------------- |
|
|||
29 | # Constants |
|
|||
30 | #----------------------------------------------------------------------------- |
|
|||
31 |
|
||||
32 | INKSCAPE_APP = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape' |
|
20 | INKSCAPE_APP = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape' | |
33 |
|
21 | |||
34 | #----------------------------------------------------------------------------- |
|
|||
35 | # Classes |
|
|||
36 | #----------------------------------------------------------------------------- |
|
|||
37 |
|
22 | |||
38 | class SVG2PDFPreprocessor(ConvertFiguresPreprocessor): |
|
23 | class SVG2PDFPreprocessor(ConvertFiguresPreprocessor): | |
39 | """ |
|
24 | """ | |
@@ -41,7 +26,7 b' class SVG2PDFPreprocessor(ConvertFiguresPreprocessor):' | |||||
41 | """ |
|
26 | """ | |
42 |
|
27 | |||
43 | def _from_format_default(self): |
|
28 | def _from_format_default(self): | |
44 | return 'svg' |
|
29 | return 'image/svg+xml' | |
45 | def _to_format_default(self): |
|
30 | def _to_format_default(self): | |
46 | return 'application/pdf' |
|
31 | return 'application/pdf' | |
47 |
|
32 |
@@ -16,16 +16,16 b' class PreprocessorTestsBase(TestsBase):' | |||||
16 | def build_notebook(self): |
|
16 | def build_notebook(self): | |
17 | """Build a notebook in memory for use with preprocessor tests""" |
|
17 | """Build a notebook in memory for use with preprocessor tests""" | |
18 |
|
18 | |||
19 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="a"), |
|
19 | outputs = [ | |
20 | nbformat.new_output(output_type="display_data", data={'text/plain': 'b'}), |
|
20 | nbformat.new_output("stream", name="stdout", text="a"), | |
21 | nbformat.new_output(output_type="stream", name="stdout", text="c"), |
|
21 | nbformat.new_output("display_data", data={'text/plain': 'b'}), | |
22 |
|
|
22 | nbformat.new_output("stream", name="stdout", text="c"), | |
23 |
|
|
23 | nbformat.new_output("stream", name="stdout", text="d"), | |
24 |
|
|
24 | nbformat.new_output("stream", name="stderr", text="e"), | |
25 | nbformat.new_output(output_type="display_data", data={'image/png': 'Zw=='})] # g |
|
25 | nbformat.new_output("stream", name="stderr", text="f"), | |
26 |
|
|
26 | nbformat.new_output("display_data", data={'image/png': 'Zw=='}), # g | |
27 |
|
|
27 | nbformat.new_output("display_data", data={'application/pdf': 'aA=='}), # h | |
28 | outputs.append(out) |
|
28 | ] | |
29 |
|
29 | |||
30 | cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs), |
|
30 | cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs), | |
31 | nbformat.new_markdown_cell(source="$ e $")] |
|
31 | nbformat.new_markdown_cell(source="$ e $")] |
@@ -47,18 +47,18 b' class TestExecute(PreprocessorTestsBase):' | |||||
47 | def assert_notebooks_equal(self, expected, actual): |
|
47 | def assert_notebooks_equal(self, expected, actual): | |
48 | expected_cells = expected['cells'] |
|
48 | expected_cells = expected['cells'] | |
49 | actual_cells = actual['cells'] |
|
49 | actual_cells = actual['cells'] | |
50 |
assert |
|
50 | self.assertEqual(len(expected_cells), len(actual_cells)) | |
51 |
|
51 | |||
52 | for expected_cell, actual_cell in zip(expected_cells, actual_cells): |
|
52 | for expected_cell, actual_cell in zip(expected_cells, actual_cells): | |
53 | expected_outputs = expected_cell.get('outputs', []) |
|
53 | expected_outputs = expected_cell.get('outputs', []) | |
54 | actual_outputs = actual_cell.get('outputs', []) |
|
54 | actual_outputs = actual_cell.get('outputs', []) | |
55 | normalized_expected_outputs = list(map(self.normalize_output, expected_outputs)) |
|
55 | normalized_expected_outputs = list(map(self.normalize_output, expected_outputs)) | |
56 | normalized_actual_outputs = list(map(self.normalize_output, actual_outputs)) |
|
56 | normalized_actual_outputs = list(map(self.normalize_output, actual_outputs)) | |
57 |
assert |
|
57 | self.assertEqual(normalized_expected_outputs, normalized_actual_outputs) | |
58 |
|
58 | |||
59 | expected_execution_count = expected_cell.get('execution_count', None) |
|
59 | expected_execution_count = expected_cell.get('execution_count', None) | |
60 | actual_execution_count = actual_cell.get('execution_count', None) |
|
60 | actual_execution_count = actual_cell.get('execution_count', None) | |
61 |
assert |
|
61 | self.assertEqual(expected_execution_count, actual_execution_count) | |
62 |
|
62 | |||
63 |
|
63 | |||
64 | def build_preprocessor(self): |
|
64 | def build_preprocessor(self): |
@@ -10,7 +10,6 b' from ..extractoutput import ExtractOutputPreprocessor' | |||||
10 | class TestExtractOutput(PreprocessorTestsBase): |
|
10 | class TestExtractOutput(PreprocessorTestsBase): | |
11 | """Contains test functions for extractoutput.py""" |
|
11 | """Contains test functions for extractoutput.py""" | |
12 |
|
12 | |||
13 |
|
||||
14 | def build_preprocessor(self): |
|
13 | def build_preprocessor(self): | |
15 | """Make an instance of a preprocessor""" |
|
14 | """Make an instance of a preprocessor""" | |
16 | preprocessor = ExtractOutputPreprocessor() |
|
15 | preprocessor = ExtractOutputPreprocessor() | |
@@ -18,11 +17,9 b' class TestExtractOutput(PreprocessorTestsBase):' | |||||
18 | preprocessor.enabled = True |
|
17 | preprocessor.enabled = True | |
19 | return preprocessor |
|
18 | return preprocessor | |
20 |
|
19 | |||
21 |
|
||||
22 | def test_constructor(self): |
|
20 | def test_constructor(self): | |
23 | """Can a ExtractOutputPreprocessor be constructed?""" |
|
21 | """Can a ExtractOutputPreprocessor be constructed?""" | |
24 | self.build_preprocessor() |
|
22 | self.build_preprocessor() | |
25 |
|
||||
26 |
|
23 | |||
27 | def test_output(self): |
|
24 | def test_output(self): | |
28 | """Test the output of the ExtractOutputPreprocessor""" |
|
25 | """Test the output of the ExtractOutputPreprocessor""" | |
@@ -32,30 +29,30 b' class TestExtractOutput(PreprocessorTestsBase):' | |||||
32 | nb, res = preprocessor(nb, res) |
|
29 | nb, res = preprocessor(nb, res) | |
33 | # Check if text was extracted. |
|
30 | # Check if text was extracted. | |
34 | output = nb.cells[0].outputs[1] |
|
31 | output = nb.cells[0].outputs[1] | |
35 |
assert |
|
32 | self.assertIn('filenames', output.metadata) | |
36 |
assert |
|
33 | self.assertIn('text/plain', output.metadata.filenames) | |
37 | text_filename = output.metadata.filenames['text/plain'] |
|
34 | text_filename = output.metadata.filenames['text/plain'] | |
38 |
|
35 | |||
39 | # Check if png was extracted. |
|
36 | # Check if png was extracted. | |
40 | output = nb.cells[0].outputs[6] |
|
37 | output = nb.cells[0].outputs[6] | |
41 |
assert |
|
38 | self.assertIn('filenames', output.metadata) | |
42 |
assert |
|
39 | self.assertIn('image/png', output.metadata.filenames) | |
43 | png_filename = output.metadata.filenames['image/png'] |
|
40 | png_filename = output.metadata.filenames['image/png'] | |
44 |
|
41 | |||
45 | # Check that pdf was extracted |
|
42 | # Check that pdf was extracted | |
46 | output = nb.cells[0].outputs[7] |
|
43 | output = nb.cells[0].outputs[7] | |
47 |
assert |
|
44 | self.assertIn('filenames', output.metadata) | |
48 |
assert |
|
45 | self.assertIn('application/pdf', output.metadata.filenames) | |
49 | pdf_filename = output.metadata.filenames['application/pdf'] |
|
46 | pdf_filename = output.metadata.filenames['application/pdf'] | |
50 |
|
47 | |||
51 | # Verify text output |
|
48 | # Verify text output | |
52 |
assert |
|
49 | self.assertIn(text_filename, res['outputs']) | |
53 | self.assertEqual(res['outputs'][text_filename], b'b') |
|
50 | self.assertEqual(res['outputs'][text_filename], b'b') | |
54 |
|
51 | |||
55 | # Verify png output |
|
52 | # Verify png output | |
56 |
assert |
|
53 | self.assertIn(png_filename, res['outputs']) | |
57 | self.assertEqual(res['outputs'][png_filename], b'g') |
|
54 | self.assertEqual(res['outputs'][png_filename], b'g') | |
58 |
|
55 | |||
59 | # Verify pdf output |
|
56 | # Verify pdf output | |
60 |
assert |
|
57 | self.assertIn(pdf_filename, res['outputs']) | |
61 | self.assertEqual(res['outputs'][pdf_filename], b'h') |
|
58 | self.assertEqual(res['outputs'][pdf_filename], b'h') |
@@ -39,10 +39,10 b' class Testsvg2pdf(PreprocessorTestsBase):' | |||||
39 | </svg>""" |
|
39 | </svg>""" | |
40 |
|
40 | |||
41 | def build_notebook(self): |
|
41 | def build_notebook(self): | |
42 |
"""Build a reveal slides notebook in memory for use with tests. |
|
42 | """Build a reveal slides notebook in memory for use with tests. | |
43 | Overrides base in PreprocessorTestsBase""" |
|
43 | Overrides base in PreprocessorTestsBase""" | |
44 |
|
44 | |||
45 | outputs = [nbformat.new_output(output_type="svg", output_svg=self.simple_svg)] |
|
45 | outputs = [nbformat.new_output(output_type="image/svg+xml", output_svg=self.simple_svg)] | |
46 |
|
46 | |||
47 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} |
|
47 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} | |
48 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} |
|
48 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} | |
@@ -71,4 +71,4 b' class Testsvg2pdf(PreprocessorTestsBase):' | |||||
71 | res = self.build_resources() |
|
71 | res = self.build_resources() | |
72 | preprocessor = self.build_preprocessor() |
|
72 | preprocessor = self.build_preprocessor() | |
73 | nb, res = preprocessor(nb, res) |
|
73 | nb, res = preprocessor(nb, res) | |
74 |
assert |
|
74 | self.assertIn('application/pdf', nb.cells[0].outputs[0]) |
@@ -44,8 +44,7 b' def convert(nb, to_version):' | |||||
44 | # Convert and make sure version changed during conversion. |
|
44 | # Convert and make sure version changed during conversion. | |
45 | converted = convert_function(nb) |
|
45 | converted = convert_function(nb) | |
46 | if converted.get('nbformat', 1) == version: |
|
46 | if converted.get('nbformat', 1) == version: | |
47 |
raise ValueError(" |
|
47 | raise ValueError("Failed to convert notebook from v%d to v%d." % (version, step_version)) | |
48 | "failed silently." % (version, step_version)) |
|
|||
49 |
|
48 | |||
50 | # Recursively convert until target version is reached. |
|
49 | # Recursively convert until target version is reached. | |
51 | return convert(converted, to_version) |
|
50 | return convert(converted, to_version) |
@@ -180,9 +180,6 b' class NotebookNotary(LoggingConfigurable):' | |||||
180 |
|
180 | |||
181 | This function is the inverse of check_cells |
|
181 | This function is the inverse of check_cells | |
182 | """ |
|
182 | """ | |
183 | if not nb['cells']: |
|
|||
184 | # nothing to mark if there are no cells |
|
|||
185 | return |
|
|||
186 | for cell in nb['cells']: |
|
183 | for cell in nb['cells']: | |
187 | if cell['cell_type'] == 'code': |
|
184 | if cell['cell_type'] == 'code': | |
188 | cell['metadata']['trusted'] = trusted |
|
185 | cell['metadata']['trusted'] = trusted | |
@@ -224,8 +221,6 b' class NotebookNotary(LoggingConfigurable):' | |||||
224 |
|
221 | |||
225 | This function is the inverse of mark_cells. |
|
222 | This function is the inverse of mark_cells. | |
226 | """ |
|
223 | """ | |
227 | if not nb['cells']: |
|
|||
228 | return True |
|
|||
229 | trusted = True |
|
224 | trusted = True | |
230 | for cell in nb['cells']: |
|
225 | for cell in nb['cells']: | |
231 | if cell['cell_type'] != 'code': |
|
226 | if cell['cell_type'] != 'code': |
@@ -28,10 +28,7 b' class NotebookNode(Struct):' | |||||
28 |
|
28 | |||
29 | def from_dict(d): |
|
29 | def from_dict(d): | |
30 | if isinstance(d, dict): |
|
30 | if isinstance(d, dict): | |
31 | newd = NotebookNode() |
|
31 | return NotebookNode({k:from_dict(v) for k,v in d.items()}) | |
32 | for k,v in d.items(): |
|
|||
33 | newd[k] = from_dict(v) |
|
|||
34 | return newd |
|
|||
35 | elif isinstance(d, (tuple, list)): |
|
32 | elif isinstance(d, (tuple, list)): | |
36 | return [from_dict(i) for i in d] |
|
33 | return [from_dict(i) for i in d] | |
37 | else: |
|
34 | else: | |
@@ -41,17 +38,19 b' def from_dict(d):' | |||||
41 | def new_output(output_type, data=None, **kwargs): |
|
38 | def new_output(output_type, data=None, **kwargs): | |
42 | """Create a new output, to go in the ``cell.outputs`` list of a code cell.""" |
|
39 | """Create a new output, to go in the ``cell.outputs`` list of a code cell.""" | |
43 | output = NotebookNode(output_type=output_type) |
|
40 | output = NotebookNode(output_type=output_type) | |
44 | output.update(from_dict(kwargs)) |
|
|||
45 | if data is not None: |
|
|||
46 | output.data = from_dict(data) |
|
|||
47 |
|
41 | |||
48 | # populate defaults: |
|
42 | # populate defaults: | |
49 | if output_type == 'stream': |
|
43 | if output_type == 'stream': | |
50 |
output. |
|
44 | output.name = u'stdout' | |
51 |
output. |
|
45 | output.text = u'' | |
52 | elif output_type in {'execute_result', 'display_data'}: |
|
46 | elif output_type in {'execute_result', 'display_data'}: | |
53 |
output. |
|
47 | output.metadata = NotebookNode() | |
54 |
output. |
|
48 | output.data = NotebookNode() | |
|
49 | # load from args: | |||
|
50 | output.update(from_dict(kwargs)) | |||
|
51 | if data is not None: | |||
|
52 | output.data = from_dict(data) | |||
|
53 | # validate | |||
55 | validate(output, output_type) |
|
54 | validate(output, output_type) | |
56 | return output |
|
55 | return output | |
57 |
|
56 | |||
@@ -79,7 +78,6 b' def output_from_msg(msg):' | |||||
79 | data=content['data'], |
|
78 | data=content['data'], | |
80 | execution_count=content['execution_count'], |
|
79 | execution_count=content['execution_count'], | |
81 | ) |
|
80 | ) | |
82 |
|
||||
83 | elif msg_type == 'stream': |
|
81 | elif msg_type == 'stream': | |
84 | return new_output(output_type=msg_type, |
|
82 | return new_output(output_type=msg_type, | |
85 | name=content['name'], |
|
83 | name=content['name'], | |
@@ -102,40 +100,50 b' def output_from_msg(msg):' | |||||
102 |
|
100 | |||
103 | def new_code_cell(source='', **kwargs): |
|
101 | def new_code_cell(source='', **kwargs): | |
104 | """Create a new code cell""" |
|
102 | """Create a new code cell""" | |
105 |
cell = NotebookNode( |
|
103 | cell = NotebookNode( | |
|
104 | cell_type='code', | |||
|
105 | metadata=NotebookNode(), | |||
|
106 | execution_count=None, | |||
|
107 | source=source, | |||
|
108 | outputs=[], | |||
|
109 | ) | |||
106 | cell.update(from_dict(kwargs)) |
|
110 | cell.update(from_dict(kwargs)) | |
107 | cell.setdefault('metadata', NotebookNode()) |
|
|||
108 | cell.setdefault('source', '') |
|
|||
109 | cell.setdefault('execution_count', None) |
|
|||
110 | cell.setdefault('outputs', []) |
|
|||
111 |
|
111 | |||
112 | validate(cell, 'code_cell') |
|
112 | validate(cell, 'code_cell') | |
113 | return cell |
|
113 | return cell | |
114 |
|
114 | |||
115 | def new_markdown_cell(source='', **kwargs): |
|
115 | def new_markdown_cell(source='', **kwargs): | |
116 | """Create a new markdown cell""" |
|
116 | """Create a new markdown cell""" | |
117 | cell = NotebookNode(cell_type='markdown', source=source) |
|
117 | cell = NotebookNode( | |
|
118 | cell_type='markdown', | |||
|
119 | source=source, | |||
|
120 | metadata=NotebookNode(), | |||
|
121 | ) | |||
118 | cell.update(from_dict(kwargs)) |
|
122 | cell.update(from_dict(kwargs)) | |
119 | cell.setdefault('metadata', NotebookNode()) |
|
|||
120 |
|
123 | |||
121 | validate(cell, 'markdown_cell') |
|
124 | validate(cell, 'markdown_cell') | |
122 | return cell |
|
125 | return cell | |
123 |
|
126 | |||
124 | def new_raw_cell(source='', **kwargs): |
|
127 | def new_raw_cell(source='', **kwargs): | |
125 | """Create a new raw cell""" |
|
128 | """Create a new raw cell""" | |
126 |
cell = NotebookNode( |
|
129 | cell = NotebookNode( | |
|
130 | cell_type='raw', | |||
|
131 | source=source, | |||
|
132 | metadata=NotebookNode(), | |||
|
133 | ) | |||
127 | cell.update(from_dict(kwargs)) |
|
134 | cell.update(from_dict(kwargs)) | |
128 | cell.setdefault('metadata', NotebookNode()) |
|
|||
129 |
|
135 | |||
130 | validate(cell, 'raw_cell') |
|
136 | validate(cell, 'raw_cell') | |
131 | return cell |
|
137 | return cell | |
132 |
|
138 | |||
133 | def new_notebook(**kwargs): |
|
139 | def new_notebook(**kwargs): | |
134 | """Create a new notebook""" |
|
140 | """Create a new notebook""" | |
135 | nb = from_dict(kwargs) |
|
141 | nb = NotebookNode( | |
136 | nb.nbformat = nbformat |
|
142 | nbformat=nbformat, | |
137 | nb.nbformat_minor = nbformat_minor |
|
143 | nbformat_minor=nbformat_minor, | |
138 | nb.setdefault('cells', []) |
|
144 | metadata=NotebookNode(), | |
139 | nb.setdefault('metadata', NotebookNode()) |
|
145 | cells=[], | |
|
146 | ) | |||
|
147 | nb.update(from_dict(kwargs)) | |||
140 | validate(nb) |
|
148 | validate(nb) | |
141 | return nb |
|
149 | return nb |
@@ -3,32 +3,7 b'' | |||||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | from IPython.utils import py3compat |
|
6 | from IPython.utils.py3compat import string_types, cast_unicode_py2 | |
7 | from IPython.utils.py3compat import unicode_type, string_types |
|
|||
8 |
|
||||
9 | # output keys that are likely to have multiline values |
|
|||
10 | _multiline_outputs = { |
|
|||
11 | 'text/plain', |
|
|||
12 | 'text/html', |
|
|||
13 | 'image/svg+xml', |
|
|||
14 | 'text/latex', |
|
|||
15 | 'application/javascript', |
|
|||
16 | } |
|
|||
17 |
|
||||
18 |
|
||||
19 | # FIXME: workaround for old splitlines() |
|
|||
20 | def _join_lines(lines): |
|
|||
21 | """join lines that have been written by splitlines() |
|
|||
22 |
|
||||
23 | Has logic to protect against `splitlines()`, which |
|
|||
24 | should have been `splitlines(True)` |
|
|||
25 | """ |
|
|||
26 | if lines and lines[0].endswith(('\n', '\r')): |
|
|||
27 | # created by splitlines(True) |
|
|||
28 | return u''.join(lines) |
|
|||
29 | else: |
|
|||
30 | # created by splitlines() |
|
|||
31 | return u'\n'.join(lines) |
|
|||
32 |
|
7 | |||
33 |
|
8 | |||
34 | def rejoin_lines(nb): |
|
9 | def rejoin_lines(nb): | |
@@ -43,17 +18,17 b' def rejoin_lines(nb):' | |||||
43 | """ |
|
18 | """ | |
44 | for cell in nb.cells: |
|
19 | for cell in nb.cells: | |
45 | if 'source' in cell and isinstance(cell.source, list): |
|
20 | if 'source' in cell and isinstance(cell.source, list): | |
46 |
cell.source = |
|
21 | cell.source = ''.join(cell.source) | |
47 | if cell.get('cell_type', None) == 'code': |
|
22 | if cell.get('cell_type', None) == 'code': | |
48 | for output in cell.get('outputs', []): |
|
23 | for output in cell.get('outputs', []): | |
49 | output_type = output.get('output_type', '') |
|
24 | output_type = output.get('output_type', '') | |
50 | if output_type in {'execute_result', 'display_data'}: |
|
25 | if output_type in {'execute_result', 'display_data'}: | |
51 | for key, value in output.get('data', {}).items(): |
|
26 | for key, value in output.get('data', {}).items(): | |
52 | if key != 'application/json' and isinstance(value, list): |
|
27 | if key != 'application/json' and isinstance(value, list): | |
53 |
output.data[key] = |
|
28 | output.data[key] = ''.join(value) | |
54 | elif output_type: |
|
29 | elif output_type: | |
55 | if isinstance(output.get('text', ''), list): |
|
30 | if isinstance(output.get('text', ''), list): | |
56 |
output.text = |
|
31 | output.text = ''.join(output.text) | |
57 | return nb |
|
32 | return nb | |
58 |
|
33 | |||
59 |
|
34 | |||
@@ -103,9 +78,7 b' class NotebookReader(object):' | |||||
103 |
|
78 | |||
104 | def read(self, fp, **kwargs): |
|
79 | def read(self, fp, **kwargs): | |
105 | """Read a notebook from a file like object""" |
|
80 | """Read a notebook from a file like object""" | |
106 | nbs = fp.read() |
|
81 | nbs = cast_unicode_py2(fp.read()) | |
107 | if not py3compat.PY3 and not isinstance(nbs, unicode_type): |
|
|||
108 | nbs = py3compat.str_to_unicode(nbs) |
|
|||
109 | return self.reads(nbs, **kwargs) |
|
82 | return self.reads(nbs, **kwargs) | |
110 |
|
83 | |||
111 |
|
84 | |||
@@ -118,8 +91,5 b' class NotebookWriter(object):' | |||||
118 |
|
91 | |||
119 | def write(self, nb, fp, **kwargs): |
|
92 | def write(self, nb, fp, **kwargs): | |
120 | """Write a notebook to a file like object""" |
|
93 | """Write a notebook to a file like object""" | |
121 | nbs = self.writes(nb,**kwargs) |
|
94 | nbs = cast_unicode_py2(self.writes(nb, **kwargs)) | |
122 | if not py3compat.PY3 and not isinstance(nbs, unicode_type): |
|
|||
123 | # this branch is likely only taken for JSON on Python 2 |
|
|||
124 | nbs = py3compat.str_to_unicode(nbs) |
|
|||
125 | return fp.write(nbs) |
|
95 | return fp.write(nbs) |
@@ -49,6 +49,14 b' At the highest level, a Jupyter notebook is a dictionary with a few keys:' | |||||
49 | ], |
|
49 | ], | |
50 | } |
|
50 | } | |
51 |
|
51 | |||
|
52 | Some fields, such as code input and text output, are characteristically multi-line strings. | |||
|
53 | When these fields are written to disk, they **may** be written as a list of strings, | |||
|
54 | which should be joined with ``''`` when reading back into memory. | |||
|
55 | In programmatic APIs for working with notebooks (Python, Javascript), | |||
|
56 | these are always re-joined into the original multi-line string. | |||
|
57 | If you intend to work with notebook files directly, | |||
|
58 | you must allow multi-line string fields to be either a string or list of strings. | |||
|
59 | ||||
52 |
|
60 | |||
53 | Cell Types |
|
61 | Cell Types | |
54 | ========== |
|
62 | ========== | |
@@ -82,7 +90,7 b' as defined in `GitHub-flavored markdown`_, and implemented in marked_.' | |||||
82 | "source" : ["some *markdown*"], |
|
90 | "source" : ["some *markdown*"], | |
83 | } |
|
91 | } | |
84 |
|
92 | |||
85 | .. versionchanged:: 4.0 |
|
93 | .. versionchanged:: nbformat 4.0 | |
86 |
|
94 | |||
87 | Heading cells have been removed, in favor of simple headings in markdown. |
|
95 | Heading cells have been removed, in favor of simple headings in markdown. | |
88 |
|
96 | |||
@@ -91,8 +99,8 b' Code cells' | |||||
91 | ---------- |
|
99 | ---------- | |
92 |
|
100 | |||
93 | Code cells are the primary content of Jupyter notebooks. |
|
101 | Code cells are the primary content of Jupyter notebooks. | |
94 |
They contain source code in |
|
102 | They contain source code in the language of the document's associated kernel, | |
95 | and a list of outputs associated with executing. |
|
103 | and a list of outputs associated with executing that code. | |
96 | They also have an execution_count, which must be an integer or ``null``. |
|
104 | They also have an execution_count, which must be an integer or ``null``. | |
97 |
|
105 | |||
98 | .. sourcecode:: python |
|
106 | .. sourcecode:: python | |
@@ -112,11 +120,11 b' They also have an execution_count, which must be an integer or ``null``.' | |||||
112 | }], |
|
120 | }], | |
113 | } |
|
121 | } | |
114 |
|
122 | |||
115 | .. versionchanged:: 4.0 |
|
123 | .. versionchanged:: nbformat 4.0 | |
116 |
|
124 | |||
117 | ``input`` was renamed to ``source``, for consistency among cell types. |
|
125 | ``input`` was renamed to ``source``, for consistency among cell types. | |
118 |
|
126 | |||
119 | .. versionchanged:: 4.0 |
|
127 | .. versionchanged:: nbformat 4.0 | |
120 |
|
128 | |||
121 | ``prompt_number`` renamed to ``execution_count`` |
|
129 | ``prompt_number`` renamed to ``execution_count`` | |
122 |
|
130 | |||
@@ -141,7 +149,7 b' stream output' | |||||
141 | "data" : ["multiline stream text"], |
|
149 | "data" : ["multiline stream text"], | |
142 | } |
|
150 | } | |
143 |
|
151 | |||
144 | .. versionchanged:: 4.0 |
|
152 | .. versionchanged:: nbformat 4.0 | |
145 |
|
153 | |||
146 | The keys ``stream`` and ``text`` were changed to ``name`` and ``data`` to match |
|
154 | The keys ``stream`` and ``text`` were changed to ``name`` and ``data`` to match | |
147 | the stream message specification. |
|
155 | the stream message specification. | |
@@ -176,11 +184,11 b' The metadata of these messages may be keyed by mime-type as well.' | |||||
176 | } |
|
184 | } | |
177 |
|
185 | |||
178 |
|
186 | |||
179 | .. versionchanged:: 4.0 |
|
187 | .. versionchanged:: nbformat 4.0 | |
180 |
|
188 | |||
181 | ``application/json`` output is no longer double-serialized into a string. |
|
189 | ``application/json`` output is no longer double-serialized into a string. | |
182 |
|
190 | |||
183 | .. versionchanged:: 4.0 |
|
191 | .. versionchanged:: nbformat 4.0 | |
184 |
|
192 | |||
185 | mime-types are used for keys, instead of a combination of short names (``text``) |
|
193 | mime-types are used for keys, instead of a combination of short names (``text``) | |
186 | and mime-types, and are stored in a ``data`` key, rather than the top-level. |
|
194 | and mime-types, and are stored in a ``data`` key, rather than the top-level. | |
@@ -193,13 +201,13 b' execute_result' | |||||
193 | Results of executing a cell (as created by ``displayhook`` in Python) |
|
201 | Results of executing a cell (as created by ``displayhook`` in Python) | |
194 | are stored in ``execute_result`` outputs. |
|
202 | are stored in ``execute_result`` outputs. | |
195 | `execute_result` outputs are identical to ``display_data``, |
|
203 | `execute_result` outputs are identical to ``display_data``, | |
196 |
adding only a `` |
|
204 | adding only a ``execution_count`` field, which must be an integer. | |
197 |
|
205 | |||
198 | .. sourcecode:: python |
|
206 | .. sourcecode:: python | |
199 |
|
207 | |||
200 | { |
|
208 | { | |
201 | "output_type" : "execute_result", |
|
209 | "output_type" : "execute_result", | |
202 |
"execut |
|
210 | "execution_count": 42, | |
203 | "data" : { |
|
211 | "data" : { | |
204 | "text/plain" : ["multiline text data"], |
|
212 | "text/plain" : ["multiline text data"], | |
205 | "image/png": ["base64-encoded-png-data"], |
|
213 | "image/png": ["base64-encoded-png-data"], | |
@@ -216,11 +224,11 b' adding only a ``prompt_number`` field, which must be an integer.' | |||||
216 | }, |
|
224 | }, | |
217 | } |
|
225 | } | |
218 |
|
226 | |||
219 | .. versionchanged:: 4.0 |
|
227 | .. versionchanged:: nbformat 4.0 | |
220 |
|
228 | |||
221 | ``pyout`` renamed to ``execute_result`` |
|
229 | ``pyout`` renamed to ``execute_result`` | |
222 |
|
230 | |||
223 | .. versionchanged:: 4.0 |
|
231 | .. versionchanged:: nbformat 4.0 | |
224 |
|
232 | |||
225 | ``prompt_number`` renamed to ``execution_count`` |
|
233 | ``prompt_number`` renamed to ``execution_count`` | |
226 |
|
234 | |||
@@ -241,7 +249,7 b' Failed execution may show a traceback' | |||||
241 | 'traceback' : list, |
|
249 | 'traceback' : list, | |
242 | } |
|
250 | } | |
243 |
|
251 | |||
244 | .. versionchanged:: 4.0 |
|
252 | .. versionchanged:: nbformat 4.0 | |
245 |
|
253 | |||
246 | ``pyerr`` renamed to ``error`` |
|
254 | ``pyerr`` renamed to ``error`` | |
247 |
|
255 | |||
@@ -295,8 +303,8 b' The following metadata keys are defined at the notebook level:' | |||||
295 | =========== =============== ============== |
|
303 | =========== =============== ============== | |
296 | Key Value Interpretation |
|
304 | Key Value Interpretation | |
297 | =========== =============== ============== |
|
305 | =========== =============== ============== | |
298 |
kernelspec dict A |
|
306 | kernelspec dict A :ref:`kernel specification <kernelspecs>` | |
299 | signature str A hashed signature of the notebook |
|
307 | signature str A hashed :ref:`signature <notebook_security>` of the notebook | |
300 | =========== =============== ============== |
|
308 | =========== =============== ============== | |
301 |
|
309 | |||
302 |
|
310 |
General Comments 0
You need to be logged in to leave comments.
Login now