##// END OF EJS Templates
log, don’t raise on validation failure in v4.convert
MinRK -
Show More
@@ -1,200 +1,208 b''
1 """Code for converting notebooks to and from v3."""
1 """Code for converting notebooks to and from v3."""
2
2
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 import json
6 import json
7
7
8 from .nbbase import (
8 from .nbbase import (
9 nbformat, nbformat_minor,
9 nbformat, nbformat_minor,
10 NotebookNode,
10 NotebookNode,
11 )
11 )
12
12
13 from IPython.nbformat import v3
13 from IPython.nbformat import v3
14 from IPython.utils.log import get_logger
14
15
15
16
16 def upgrade(nb, from_version=3, from_minor=0):
17 def upgrade(nb, from_version=3, from_minor=0):
17 """Convert a notebook to v4.
18 """Convert a notebook to v4.
18
19
19 Parameters
20 Parameters
20 ----------
21 ----------
21 nb : NotebookNode
22 nb : NotebookNode
22 The Python representation of the notebook to convert.
23 The Python representation of the notebook to convert.
23 from_version : int
24 from_version : int
24 The original version of the notebook to convert.
25 The original version of the notebook to convert.
25 from_minor : int
26 from_minor : int
26 The original minor version of the notebook to convert (only relevant for v >= 3).
27 The original minor version of the notebook to convert (only relevant for v >= 3).
27 """
28 """
28 from IPython.nbformat.current import validate
29 from IPython.nbformat.current import validate, ValidationError
29 if from_version == 3:
30 if from_version == 3:
30 # Validate the notebook before conversion
31 # Validate the notebook before conversion
32 try:
31 validate(nb, version=from_version)
33 validate(nb, version=from_version)
34 except ValidationError as e:
35 get_logger().error("Notebook JSON is not valid v%i: %s", from_version, e)
32
36
33 # Mark the original nbformat so consumers know it has been converted.
37 # Mark the original nbformat so consumers know it has been converted.
38 nb.pop('orig_nbformat', None)
34 nb.metadata.orig_nbformat = 3
39 nb.metadata.orig_nbformat = 3
35 # Mark the new format
40 # Mark the new format
36 nb.nbformat = nbformat
41 nb.nbformat = nbformat
37 nb.nbformat_minor = nbformat_minor
42 nb.nbformat_minor = nbformat_minor
38
43
39 # remove worksheet(s)
44 # remove worksheet(s)
40 nb['cells'] = cells = []
45 nb['cells'] = cells = []
41 # In the unlikely event of multiple worksheets,
46 # In the unlikely event of multiple worksheets,
42 # they will be flattened
47 # they will be flattened
43 for ws in nb.pop('worksheets', []):
48 for ws in nb.pop('worksheets', []):
44 # upgrade each cell
49 # upgrade each cell
45 for cell in ws['cells']:
50 for cell in ws['cells']:
46 cells.append(upgrade_cell(cell))
51 cells.append(upgrade_cell(cell))
47 # upgrade metadata
52 # upgrade metadata
48 nb.metadata.pop('name', '')
53 nb.metadata.pop('name', '')
49 # Validate the converted notebook before returning it
54 # Validate the converted notebook before returning it
55 try:
50 validate(nb, version=nbformat)
56 validate(nb, version=nbformat)
57 except ValidationError as e:
58 get_logger().error("Notebook JSON is not valid v%i: %s", nbformat, e)
51 return nb
59 return nb
52 elif from_version == 4:
60 elif from_version == 4:
53 # nothing to do
61 # nothing to do
54 if from_minor != nbformat_minor:
62 if from_minor != nbformat_minor:
55 nb.metadata.orig_nbformat_minor = from_minor
63 nb.metadata.orig_nbformat_minor = from_minor
56 nb.nbformat_minor = nbformat_minor
64 nb.nbformat_minor = nbformat_minor
57
65
58 return nb
66 return nb
59 else:
67 else:
60 raise ValueError('Cannot convert a notebook directly from v%s to v4. ' \
68 raise ValueError('Cannot convert a notebook directly from v%s to v4. ' \
61 'Try using the IPython.nbformat.convert module.' % from_version)
69 'Try using the IPython.nbformat.convert module.' % from_version)
62
70
63 def upgrade_cell(cell):
71 def upgrade_cell(cell):
64 """upgrade a cell from v3 to v4
72 """upgrade a cell from v3 to v4
65
73
66 code cell:
74 code cell:
67 - remove language metadata
75 - remove language metadata
68 - cell.input -> cell.source
76 - cell.input -> cell.source
69 - update outputs
77 - update outputs
70 """
78 """
71 cell.setdefault('metadata', NotebookNode())
79 cell.setdefault('metadata', NotebookNode())
72 if cell.cell_type == 'code':
80 if cell.cell_type == 'code':
73 cell.pop('language', '')
81 cell.pop('language', '')
74 cell.metadata.collapsed = cell.pop('collapsed')
82 cell.metadata.collapsed = cell.pop('collapsed')
75 cell.source = cell.pop('input')
83 cell.source = cell.pop('input')
76 cell.setdefault('prompt_number', None)
84 cell.setdefault('prompt_number', None)
77 cell.outputs = upgrade_outputs(cell.outputs)
85 cell.outputs = upgrade_outputs(cell.outputs)
78 elif cell.cell_type == 'html':
86 elif cell.cell_type == 'html':
79 # Technically, this exists. It will never happen in practice.
87 # Technically, this exists. It will never happen in practice.
80 cell.cell_type = 'markdown'
88 cell.cell_type = 'markdown'
81 return cell
89 return cell
82
90
83 def downgrade_cell(cell):
91 def downgrade_cell(cell):
84 if cell.cell_type == 'code':
92 if cell.cell_type == 'code':
85 cell.language = 'python'
93 cell.language = 'python'
86 cell.input = cell.pop('source', '')
94 cell.input = cell.pop('source', '')
87 cell.collapsed = cell.metadata.pop('collapsed', False)
95 cell.collapsed = cell.metadata.pop('collapsed', False)
88 cell.outputs = downgrade_outputs(cell.outputs)
96 cell.outputs = downgrade_outputs(cell.outputs)
89 return cell
97 return cell
90
98
91 _mime_map = {
99 _mime_map = {
92 "text" : "text/plain",
100 "text" : "text/plain",
93 "html" : "text/html",
101 "html" : "text/html",
94 "svg" : "image/svg+xml",
102 "svg" : "image/svg+xml",
95 "png" : "image/png",
103 "png" : "image/png",
96 "jpeg" : "image/jpeg",
104 "jpeg" : "image/jpeg",
97 "latex" : "text/latex",
105 "latex" : "text/latex",
98 "json" : "application/json",
106 "json" : "application/json",
99 "javascript" : "application/javascript",
107 "javascript" : "application/javascript",
100 };
108 };
101
109
102 def to_mime_key(d):
110 def to_mime_key(d):
103 """convert dict with v3 aliases to plain mime-type keys"""
111 """convert dict with v3 aliases to plain mime-type keys"""
104 for alias, mime in _mime_map.items():
112 for alias, mime in _mime_map.items():
105 if alias in d:
113 if alias in d:
106 d[mime] = d.pop(alias)
114 d[mime] = d.pop(alias)
107 return d
115 return d
108
116
109 def from_mime_key(d):
117 def from_mime_key(d):
110 """convert dict with mime-type keys to v3 aliases"""
118 """convert dict with mime-type keys to v3 aliases"""
111 for alias, mime in _mime_map.items():
119 for alias, mime in _mime_map.items():
112 if mime in d:
120 if mime in d:
113 d[alias] = d.pop(mime)
121 d[alias] = d.pop(mime)
114 return d
122 return d
115
123
116 def upgrade_output(output):
124 def upgrade_output(output):
117 """upgrade a single code cell output from v3 to v4
125 """upgrade a single code cell output from v3 to v4
118
126
119 - pyout -> execute_result
127 - pyout -> execute_result
120 - pyerr -> error
128 - pyerr -> error
121 - mime-type keys
129 - mime-type keys
122 - stream.stream -> stream.name
130 - stream.stream -> stream.name
123 """
131 """
124 output.setdefault('metadata', NotebookNode())
132 output.setdefault('metadata', NotebookNode())
125 if output['output_type'] in {'pyout', 'display_data'}:
133 if output['output_type'] in {'pyout', 'display_data'}:
126 if output['output_type'] == 'pyout':
134 if output['output_type'] == 'pyout':
127 output['output_type'] = 'execute_result'
135 output['output_type'] = 'execute_result'
128 to_mime_key(output)
136 to_mime_key(output)
129 to_mime_key(output.metadata)
137 to_mime_key(output.metadata)
130 if 'application/json' in output:
138 if 'application/json' in output:
131 output['application/json'] = json.loads(output['application/json'])
139 output['application/json'] = json.loads(output['application/json'])
132 # promote ascii bytes (from v2) to unicode
140 # promote ascii bytes (from v2) to unicode
133 for key in ('image/png', 'image/jpeg'):
141 for key in ('image/png', 'image/jpeg'):
134 if key in output and isinstance(output[key], bytes):
142 if key in output and isinstance(output[key], bytes):
135 output[key] = output[key].decode('ascii')
143 output[key] = output[key].decode('ascii')
136 elif output['output_type'] == 'pyerr':
144 elif output['output_type'] == 'pyerr':
137 output['output_type'] = 'error'
145 output['output_type'] = 'error'
138 elif output['output_type'] == 'stream':
146 elif output['output_type'] == 'stream':
139 output['name'] = output.pop('stream')
147 output['name'] = output.pop('stream')
140 return output
148 return output
141
149
142 def downgrade_output(output):
150 def downgrade_output(output):
143 """downgrade a single code cell output to v3 from v4
151 """downgrade a single code cell output to v3 from v4
144
152
145 - pyout <- execute_result
153 - pyout <- execute_result
146 - pyerr <- error
154 - pyerr <- error
147 - un-mime-type keys
155 - un-mime-type keys
148 - stream.stream <- stream.name
156 - stream.stream <- stream.name
149 """
157 """
150 if output['output_type'] == 'execute_result':
158 if output['output_type'] == 'execute_result':
151 output['output_type'] = 'pyout'
159 output['output_type'] = 'pyout'
152 if 'application/json' in output:
160 if 'application/json' in output:
153 output['application/json'] = json.dumps(output['application/json'])
161 output['application/json'] = json.dumps(output['application/json'])
154 from_mime_key(output)
162 from_mime_key(output)
155 from_mime_key(output.get('metadata', {}))
163 from_mime_key(output.get('metadata', {}))
156 elif output['output_type'] == 'error':
164 elif output['output_type'] == 'error':
157 output['output_type'] = 'pyerr'
165 output['output_type'] = 'pyerr'
158 elif output['output_type'] == 'display_data':
166 elif output['output_type'] == 'display_data':
159 if 'application/json' in output:
167 if 'application/json' in output:
160 output['application/json'] = json.dumps(output['application/json'])
168 output['application/json'] = json.dumps(output['application/json'])
161 from_mime_key(output)
169 from_mime_key(output)
162 from_mime_key(output.get('metadata', {}))
170 from_mime_key(output.get('metadata', {}))
163 elif output['output_type'] == 'stream':
171 elif output['output_type'] == 'stream':
164 output['stream'] = output.pop('name')
172 output['stream'] = output.pop('name')
165 output.pop('metadata')
173 output.pop('metadata')
166 return output
174 return output
167
175
168 def upgrade_outputs(outputs):
176 def upgrade_outputs(outputs):
169 """upgrade outputs of a code cell from v3 to v4"""
177 """upgrade outputs of a code cell from v3 to v4"""
170 return [upgrade_output(op) for op in outputs]
178 return [upgrade_output(op) for op in outputs]
171
179
172 def downgrade_outputs(outputs):
180 def downgrade_outputs(outputs):
173 """downgrade outputs of a code cell to v3 from v4"""
181 """downgrade outputs of a code cell to v3 from v4"""
174 return [downgrade_output(op) for op in outputs]
182 return [downgrade_output(op) for op in outputs]
175
183
176 def downgrade(nb):
184 def downgrade(nb):
177 """Convert a v4 notebook to v3.
185 """Convert a v4 notebook to v3.
178
186
179 Parameters
187 Parameters
180 ----------
188 ----------
181 nb : NotebookNode
189 nb : NotebookNode
182 The Python representation of the notebook to convert.
190 The Python representation of the notebook to convert.
183 """
191 """
184 from IPython.nbformat.current import validate
192 from IPython.nbformat.current import validate
185 # Validate the notebook before conversion
193 # Validate the notebook before conversion
186 validate(nb, version=nbformat)
194 validate(nb, version=nbformat)
187
195
188 if nb.nbformat != 4:
196 if nb.nbformat != 4:
189 return nb
197 return nb
190 nb.nbformat = v3.nbformat
198 nb.nbformat = v3.nbformat
191 nb.nbformat_minor = v3.nbformat_minor
199 nb.nbformat_minor = v3.nbformat_minor
192 cells = [ downgrade_cell(cell) for cell in nb.pop('cells') ]
200 cells = [ downgrade_cell(cell) for cell in nb.pop('cells') ]
193 nb.worksheets = [v3.new_worksheet(cells=cells)]
201 nb.worksheets = [v3.new_worksheet(cells=cells)]
194 nb.metadata.setdefault('name', '')
202 nb.metadata.setdefault('name', '')
195 nb.metadata.pop('orig_nbformat', None)
203 nb.metadata.pop('orig_nbformat', None)
196 nb.metadata.pop('orig_nbformat_minor', None)
204 nb.metadata.pop('orig_nbformat_minor', None)
197
205
198 # Validate the converted notebook before returning it
206 # Validate the converted notebook before returning it
199 validate(nb, version=v3.nbformat)
207 validate(nb, version=v3.nbformat)
200 return nb
208 return nb
General Comments 0
You need to be logged in to leave comments. Login now