Show More
@@ -0,0 +1,363 b'' | |||||
|
1 | { | |||
|
2 | "$schema": "http://json-schema.org/draft-04/schema#", | |||
|
3 | "description": "IPython Notebook v3.0 JSON schema.", | |||
|
4 | "type": "object", | |||
|
5 | "additionalProperties": false, | |||
|
6 | "required": ["metadata", "nbformat_minor", "nbformat", "worksheets"], | |||
|
7 | "properties": { | |||
|
8 | "metadata": { | |||
|
9 | "description": "Notebook root-level metadata.", | |||
|
10 | "type": "object", | |||
|
11 | "additionalProperties": true, | |||
|
12 | "properties": { | |||
|
13 | "kernel_info": { | |||
|
14 | "description": "Kernel information.", | |||
|
15 | "type": "object", | |||
|
16 | "required": ["name", "language"], | |||
|
17 | "properties": { | |||
|
18 | "name": { | |||
|
19 | "description": "Name of the kernel specification.", | |||
|
20 | "type": "string" | |||
|
21 | }, | |||
|
22 | "language": { | |||
|
23 | "description": "The programming language which this kernel runs.", | |||
|
24 | "type": "string" | |||
|
25 | }, | |||
|
26 | "codemirror_mode": { | |||
|
27 | "description": "The codemirror mode to use for code in this language.", | |||
|
28 | "type": "string" | |||
|
29 | } | |||
|
30 | } | |||
|
31 | }, | |||
|
32 | "signature": { | |||
|
33 | "description": "Hash of the notebook.", | |||
|
34 | "type": "string" | |||
|
35 | } | |||
|
36 | } | |||
|
37 | }, | |||
|
38 | "nbformat_minor": { | |||
|
39 | "description": "Notebook format (minor number). Incremented for backward compatible changes to the notebook format.", | |||
|
40 | "type": "integer", | |||
|
41 | "minimum": 0 | |||
|
42 | }, | |||
|
43 | "nbformat": { | |||
|
44 | "description": "Notebook format (major number). Incremented between backwards incompatible changes to the notebook format.", | |||
|
45 | "type": "integer", | |||
|
46 | "minimum": 3, | |||
|
47 | "maximum": 3 | |||
|
48 | }, | |||
|
49 | "orig_nbformat": { | |||
|
50 | "description": "Original notebook format (major number) before converting the notebook between versions.", | |||
|
51 | "type": "integer", | |||
|
52 | "minimum": 1 | |||
|
53 | }, | |||
|
54 | "worksheets" : { | |||
|
55 | "description": "Array of worksheets", | |||
|
56 | "type": "array", | |||
|
57 | "items": {"$ref": "#/definitions/worksheet"} | |||
|
58 | } | |||
|
59 | }, | |||
|
60 | ||||
|
61 | "definitions": { | |||
|
62 | "worksheet": { | |||
|
63 | "additionalProperties": false, | |||
|
64 | "required" : ["cells"], | |||
|
65 | "properties":{ | |||
|
66 | "cells": { | |||
|
67 | "description": "Array of cells of the current notebook.", | |||
|
68 | "type": "array", | |||
|
69 | "items": { | |||
|
70 | "type": "object", | |||
|
71 | "oneOf": [ | |||
|
72 | {"$ref": "#/definitions/raw_cell"}, | |||
|
73 | {"$ref": "#/definitions/markdown_cell"}, | |||
|
74 | {"$ref": "#/definitions/heading_cell"}, | |||
|
75 | {"$ref": "#/definitions/code_cell"} | |||
|
76 | ] | |||
|
77 | } | |||
|
78 | }, | |||
|
79 | "metadata": { | |||
|
80 | "type": "object", | |||
|
81 | "description": "metadata of the current worksheet" | |||
|
82 | } | |||
|
83 | } | |||
|
84 | }, | |||
|
85 | "raw_cell": { | |||
|
86 | "description": "Notebook raw nbconvert cell.", | |||
|
87 | "type": "object", | |||
|
88 | "additionalProperties": false, | |||
|
89 | "required": ["cell_type", "source"], | |||
|
90 | "properties": { | |||
|
91 | "cell_type": { | |||
|
92 | "description": "String identifying the type of cell.", | |||
|
93 | "enum": ["raw"] | |||
|
94 | }, | |||
|
95 | "metadata": { | |||
|
96 | "description": "Cell-level metadata.", | |||
|
97 | "type": "object", | |||
|
98 | "additionalProperties": true, | |||
|
99 | "properties": { | |||
|
100 | "format": { | |||
|
101 | "description": "Raw cell metadata format for nbconvert.", | |||
|
102 | "type": "string" | |||
|
103 | }, | |||
|
104 | "name": {"$ref": "#/definitions/misc/metadata_name"}, | |||
|
105 | "tags": {"$ref": "#/definitions/misc/metadata_tags"} | |||
|
106 | } | |||
|
107 | }, | |||
|
108 | "source": {"$ref": "#/definitions/misc/source"} | |||
|
109 | } | |||
|
110 | }, | |||
|
111 | ||||
|
112 | "markdown_cell": { | |||
|
113 | "description": "Notebook markdown cell.", | |||
|
114 | "type": "object", | |||
|
115 | "additionalProperties": false, | |||
|
116 | "required": ["cell_type", "source"], | |||
|
117 | "properties": { | |||
|
118 | "cell_type": { | |||
|
119 | "description": "String identifying the type of cell.", | |||
|
120 | "enum": ["markdown"] | |||
|
121 | }, | |||
|
122 | "metadata": { | |||
|
123 | "description": "Cell-level metadata.", | |||
|
124 | "type": "object", | |||
|
125 | "properties": { | |||
|
126 | "name": {"$ref": "#/definitions/misc/metadata_name"}, | |||
|
127 | "tags": {"$ref": "#/definitions/misc/metadata_tags"} | |||
|
128 | }, | |||
|
129 | "additionalProperties": true | |||
|
130 | }, | |||
|
131 | "source": {"$ref": "#/definitions/misc/source"} | |||
|
132 | } | |||
|
133 | }, | |||
|
134 | ||||
|
135 | "heading_cell": { | |||
|
136 | "description": "Notebook heading cell.", | |||
|
137 | "type": "object", | |||
|
138 | "additionalProperties": false, | |||
|
139 | "required": ["cell_type", "source", "level"], | |||
|
140 | "properties": { | |||
|
141 | "cell_type": { | |||
|
142 | "description": "String identifying the type of cell.", | |||
|
143 | "enum": ["heading"] | |||
|
144 | }, | |||
|
145 | "metadata": { | |||
|
146 | "description": "Cell-level metadata.", | |||
|
147 | "type": "object", | |||
|
148 | "additionalProperties": true | |||
|
149 | }, | |||
|
150 | "source": {"$ref": "#/definitions/misc/source"}, | |||
|
151 | "level": { | |||
|
152 | "description": "Level of heading cells.", | |||
|
153 | "type": "integer", | |||
|
154 | "minimum": 1 | |||
|
155 | } | |||
|
156 | } | |||
|
157 | }, | |||
|
158 | ||||
|
159 | "code_cell": { | |||
|
160 | "description": "Notebook code cell.", | |||
|
161 | "type": "object", | |||
|
162 | "additionalProperties": false, | |||
|
163 | "required": ["cell_type", "input", "outputs", "collapsed", "language"], | |||
|
164 | "properties": { | |||
|
165 | "cell_type": { | |||
|
166 | "description": "String identifying the type of cell.", | |||
|
167 | "enum": ["code"] | |||
|
168 | }, | |||
|
169 | "language": { | |||
|
170 | "description": "The cell's language (always Python)", | |||
|
171 | "type": "string" | |||
|
172 | }, | |||
|
173 | "collapsed": { | |||
|
174 | "description": "Whether the cell is collapsed/expanded.", | |||
|
175 | "type": "boolean" | |||
|
176 | }, | |||
|
177 | "metadata": { | |||
|
178 | "description": "Cell-level metadata.", | |||
|
179 | "type": "object", | |||
|
180 | "additionalProperties": true | |||
|
181 | }, | |||
|
182 | "input": {"$ref": "#/definitions/misc/source"}, | |||
|
183 | "outputs": { | |||
|
184 | "description": "Execution, display, or stream outputs.", | |||
|
185 | "type": "array", | |||
|
186 | "items": {"$ref": "#/definitions/output"} | |||
|
187 | }, | |||
|
188 | "prompt_number": { | |||
|
189 | "description": "The code cell's prompt number. Will be null if the cell has not been run.", | |||
|
190 | "type": ["integer", "null"], | |||
|
191 | "minimum": 0 | |||
|
192 | } | |||
|
193 | } | |||
|
194 | }, | |||
|
195 | "output": { | |||
|
196 | "type": "object", | |||
|
197 | "oneOf": [ | |||
|
198 | {"$ref": "#/definitions/pyout"}, | |||
|
199 | {"$ref": "#/definitions/display_data"}, | |||
|
200 | {"$ref": "#/definitions/stream"}, | |||
|
201 | {"$ref": "#/definitions/pyerr"} | |||
|
202 | ] | |||
|
203 | }, | |||
|
204 | "pyout": { | |||
|
205 | "description": "Result of executing a code cell.", | |||
|
206 | "type": "object", | |||
|
207 | "additionalProperties": false, | |||
|
208 | "required": ["output_type", "prompt_number"], | |||
|
209 | "properties": { | |||
|
210 | "output_type": { | |||
|
211 | "description": "Type of cell output.", | |||
|
212 | "enum": ["pyout"] | |||
|
213 | }, | |||
|
214 | "prompt_number": { | |||
|
215 | "description": "A result's prompt number.", | |||
|
216 | "type": ["integer"], | |||
|
217 | "minimum": 0 | |||
|
218 | }, | |||
|
219 | "text": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
220 | "latex": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
221 | "png": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
222 | "jpeg": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
223 | "svg": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
224 | "html": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
225 | "javascript": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
226 | "json": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
227 | "pdf": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
228 | "metadata": {"$ref": "#/definitions/misc/output_metadata"} | |||
|
229 | }, | |||
|
230 | "patternProperties": { | |||
|
231 | "^[a-zA-Z0-9]+/[a-zA-Z0-9\\-\\+\\.]+$": { | |||
|
232 | "description": "mimetype output (e.g. text/plain), represented as either an array of strings or a string.", | |||
|
233 | "$ref": "#/definitions/misc/multiline_string" | |||
|
234 | } | |||
|
235 | } | |||
|
236 | }, | |||
|
237 | ||||
|
238 | "display_data": { | |||
|
239 | "description": "Data displayed as a result of code cell execution.", | |||
|
240 | "type": "object", | |||
|
241 | "additionalProperties": false, | |||
|
242 | "required": ["output_type"], | |||
|
243 | "properties": { | |||
|
244 | "output_type": { | |||
|
245 | "description": "Type of cell output.", | |||
|
246 | "enum": ["display_data"] | |||
|
247 | }, | |||
|
248 | "text": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
249 | "latex": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
250 | "png": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
251 | "jpeg": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
252 | "svg": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
253 | "html": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
254 | "javascript": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
255 | "json": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
256 | "pdf": {"$ref": "#/definitions/misc/multiline_string"}, | |||
|
257 | "metadata": {"$ref": "#/definitions/misc/output_metadata"} | |||
|
258 | }, | |||
|
259 | "patternProperties": { | |||
|
260 | "[a-zA-Z0-9]+/[a-zA-Z0-9\\-\\+\\.]+$": { | |||
|
261 | "description": "mimetype output (e.g. text/plain), represented as either an array of strings or a string.", | |||
|
262 | "$ref": "#/definitions/misc/multiline_string" | |||
|
263 | } | |||
|
264 | } | |||
|
265 | }, | |||
|
266 | ||||
|
267 | "stream": { | |||
|
268 | "description": "Stream output from a code cell.", | |||
|
269 | "type": "object", | |||
|
270 | "additionalProperties": false, | |||
|
271 | "required": ["output_type", "stream", "text"], | |||
|
272 | "properties": { | |||
|
273 | "output_type": { | |||
|
274 | "description": "Type of cell output.", | |||
|
275 | "enum": ["stream"] | |||
|
276 | }, | |||
|
277 | "stream": { | |||
|
278 | "description": "The stream type/destination.", | |||
|
279 | "type": "string" | |||
|
280 | }, | |||
|
281 | "text": { | |||
|
282 | "description": "The stream's text output, represented as an array of strings.", | |||
|
283 | "$ref": "#/definitions/misc/multiline_string" | |||
|
284 | } | |||
|
285 | } | |||
|
286 | }, | |||
|
287 | ||||
|
288 | "pyerr": { | |||
|
289 | "description": "Output of an error that occurred during code cell execution.", | |||
|
290 | "type": "object", | |||
|
291 | "additionalProperties": false, | |||
|
292 | "required": ["output_type", "ename", "evalue", "traceback"], | |||
|
293 | "properties": { | |||
|
294 | "output_type": { | |||
|
295 | "description": "Type of cell output.", | |||
|
296 | "enum": ["pyerr"] | |||
|
297 | }, | |||
|
298 | "metadata": {"$ref": "#/definitions/misc/output_metadata"}, | |||
|
299 | "ename": { | |||
|
300 | "description": "The name of the error.", | |||
|
301 | "type": "string" | |||
|
302 | }, | |||
|
303 | "evalue": { | |||
|
304 | "description": "The value, or message, of the error.", | |||
|
305 | "type": "string" | |||
|
306 | }, | |||
|
307 | "traceback": { | |||
|
308 | "description": "The error's traceback, represented as an array of strings.", | |||
|
309 | "type": "array", | |||
|
310 | "items": {"type": "string"} | |||
|
311 | } | |||
|
312 | } | |||
|
313 | }, | |||
|
314 | ||||
|
315 | "misc": { | |||
|
316 | "metadata_name": { | |||
|
317 | "description": "The cell's name. If present, must be a non-empty string.", | |||
|
318 | "type": "string", | |||
|
319 | "pattern": "^.+$" | |||
|
320 | }, | |||
|
321 | "metadata_tags": { | |||
|
322 | "description": "The cell's tags. Tags must be unique, and must not contain commas.", | |||
|
323 | "type": "array", | |||
|
324 | "uniqueItems": true, | |||
|
325 | "items": { | |||
|
326 | "type": "string", | |||
|
327 | "pattern": "^[^,]+$" | |||
|
328 | } | |||
|
329 | }, | |||
|
330 | "source": { | |||
|
331 | "description": "Contents of the cell, represented as an array of lines.", | |||
|
332 | "$ref": "#/definitions/misc/multiline_string" | |||
|
333 | }, | |||
|
334 | "prompt_number": { | |||
|
335 | "description": "The code cell's prompt number. Will be null if the cell has not been run.", | |||
|
336 | "type": ["integer", "null"], | |||
|
337 | "minimum": 0 | |||
|
338 | }, | |||
|
339 | "mimetype": { | |||
|
340 | "patternProperties": { | |||
|
341 | "^[a-zA-Z0-9\\-\\+]+/[a-zA-Z0-9\\-\\+]+": { | |||
|
342 | "description": "The cell's mimetype output (e.g. text/plain), represented as either an array of strings or a string.", | |||
|
343 | "$ref": "#/definitions/misc/multiline_string" | |||
|
344 | } | |||
|
345 | } | |||
|
346 | }, | |||
|
347 | "output_metadata": { | |||
|
348 | "description": "Cell output metadata.", | |||
|
349 | "type": "object", | |||
|
350 | "additionalProperties": true | |||
|
351 | }, | |||
|
352 | "multiline_string": { | |||
|
353 | "oneOf" : [ | |||
|
354 | {"type": "string"}, | |||
|
355 | { | |||
|
356 | "type": "array", | |||
|
357 | "items": {"type": "string"} | |||
|
358 | } | |||
|
359 | ] | |||
|
360 | } | |||
|
361 | } | |||
|
362 | } | |||
|
363 | } |
@@ -1,5 +1,8 b'' | |||||
1 | """The official API for working with notebooks in the current format version.""" |
|
1 | """The official API for working with notebooks in the current format version.""" | |
2 |
|
2 | |||
|
3 | # Copyright (c) IPython Development Team. | |||
|
4 | # Distributed under the terms of the Modified BSD License. | |||
|
5 | ||||
3 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
4 |
|
7 | |||
5 | import re |
|
8 | import re | |
@@ -17,7 +20,7 b' from IPython.nbformat import v3 as _v_latest' | |||||
17 | from .reader import reads as reader_reads |
|
20 | from .reader import reads as reader_reads | |
18 | from .reader import versions |
|
21 | from .reader import versions | |
19 | from .convert import convert |
|
22 | from .convert import convert | |
20 | from .validator import validate |
|
23 | from .validator import validate, ValidationError | |
21 |
|
24 | |||
22 | from IPython.utils.log import get_logger |
|
25 | from IPython.utils.log import get_logger | |
23 |
|
26 | |||
@@ -60,11 +63,10 b' def reads_json(nbjson, **kwargs):' | |||||
60 | """ |
|
63 | """ | |
61 | nb = reader_reads(nbjson, **kwargs) |
|
64 | nb = reader_reads(nbjson, **kwargs) | |
62 | nb_current = convert(nb, current_nbformat) |
|
65 | nb_current = convert(nb, current_nbformat) | |
63 | errors = validate(nb_current) |
|
66 | try: | |
64 | if errors: |
|
67 | validate(nb_current) | |
65 | get_logger().error( |
|
68 | except ValidationError as e: | |
66 | "Notebook JSON is invalid (%d errors detected during read)", |
|
69 | get_logger().error("Notebook JSON is invalid: %s", e) | |
67 | len(errors)) |
|
|||
68 | return nb_current |
|
70 | return nb_current | |
69 |
|
71 | |||
70 |
|
72 | |||
@@ -73,11 +75,10 b' def writes_json(nb, **kwargs):' | |||||
73 | any JSON format errors are detected. |
|
75 | any JSON format errors are detected. | |
74 |
|
76 | |||
75 | """ |
|
77 | """ | |
76 | errors = validate(nb) |
|
78 | try: | |
77 | if errors: |
|
79 | validate(nb) | |
78 | get_logger().error( |
|
80 | except ValidationError as e: | |
79 | "Notebook JSON is invalid (%d errors detected during write)", |
|
81 | get_logger().error("Notebook JSON is invalid: %s", e) | |
80 | len(errors)) |
|
|||
81 | nbjson = versions[current_nbformat].writes_json(nb, **kwargs) |
|
82 | nbjson = versions[current_nbformat].writes_json(nb, **kwargs) | |
82 | return nbjson |
|
83 | return nbjson | |
83 |
|
84 |
@@ -1,23 +1,14 b'' | |||||
1 | """ |
|
1 | """Test nbformat.validator""" | |
2 | Contains tests class for validator.py |
|
|||
3 | """ |
|
|||
4 | #----------------------------------------------------------------------------- |
|
|||
5 | # Copyright (C) 2014 The IPython Development Team |
|
|||
6 | # |
|
|||
7 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
8 | # the file COPYING, distributed as part of this software. |
|
|||
9 | #----------------------------------------------------------------------------- |
|
|||
10 |
|
2 | |||
11 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
12 | # Imports |
|
4 | # Distributed under the terms of the Modified BSD License. | |
13 | #----------------------------------------------------------------------------- |
|
|||
14 |
|
5 | |||
15 | import os |
|
6 | import os | |
16 |
|
7 | |||
17 | from .base import TestsBase |
|
8 | from .base import TestsBase | |
18 |
from jsonschema import |
|
9 | from jsonschema import ValidationError | |
19 | from ..current import read |
|
10 | from ..current import read | |
20 |
from ..validator import |
|
11 | from ..validator import isvalid, validate | |
21 |
|
12 | |||
22 |
|
13 | |||
23 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
@@ -26,22 +17,18 b' from ..validator import schema_path, isvalid, validate, resolve_ref' | |||||
26 |
|
17 | |||
27 | class TestValidator(TestsBase): |
|
18 | class TestValidator(TestsBase): | |
28 |
|
19 | |||
29 | def test_schema_path(self): |
|
|||
30 | """Test that the schema path exists""" |
|
|||
31 | self.assertEqual(os.path.exists(schema_path), True) |
|
|||
32 |
|
||||
33 | def test_nb2(self): |
|
20 | def test_nb2(self): | |
34 | """Test that a v2 notebook converted to v3 passes validation""" |
|
21 | """Test that a v2 notebook converted to v3 passes validation""" | |
35 | with self.fopen(u'test2.ipynb', u'r') as f: |
|
22 | with self.fopen(u'test2.ipynb', u'r') as f: | |
36 | nb = read(f, u'json') |
|
23 | nb = read(f, u'json') | |
37 |
|
|
24 | validate(nb) | |
38 | self.assertEqual(isvalid(nb), True) |
|
25 | self.assertEqual(isvalid(nb), True) | |
39 |
|
26 | |||
40 | def test_nb3(self): |
|
27 | def test_nb3(self): | |
41 | """Test that a v3 notebook passes validation""" |
|
28 | """Test that a v3 notebook passes validation""" | |
42 | with self.fopen(u'test3.ipynb', u'r') as f: |
|
29 | with self.fopen(u'test3.ipynb', u'r') as f: | |
43 | nb = read(f, u'json') |
|
30 | nb = read(f, u'json') | |
44 |
|
|
31 | validate(nb) | |
45 | self.assertEqual(isvalid(nb), True) |
|
32 | self.assertEqual(isvalid(nb), True) | |
46 |
|
33 | |||
47 | def test_invalid(self): |
|
34 | def test_invalid(self): | |
@@ -52,22 +39,7 b' class TestValidator(TestsBase):' | |||||
52 | # - one cell has an invalid level |
|
39 | # - one cell has an invalid level | |
53 | with self.fopen(u'invalid.ipynb', u'r') as f: |
|
40 | with self.fopen(u'invalid.ipynb', u'r') as f: | |
54 | nb = read(f, u'json') |
|
41 | nb = read(f, u'json') | |
55 |
self.assert |
|
42 | with self.assertRaises(ValidationError): | |
|
43 | validate(nb) | |||
56 | self.assertEqual(isvalid(nb), False) |
|
44 | self.assertEqual(isvalid(nb), False) | |
57 |
|
45 | |||
58 | def test_resolve_ref(self): |
|
|||
59 | """Test that references are correctly resolved""" |
|
|||
60 | # make sure it resolves the ref correctly |
|
|||
61 | json = {"abc": "def", "ghi": {"$ref": "/abc"}} |
|
|||
62 | resolved = resolve_ref(json) |
|
|||
63 | self.assertEqual(resolved, {"abc": "def", "ghi": "def"}) |
|
|||
64 |
|
||||
65 | # make sure it throws an error if the ref is not by itself |
|
|||
66 | json = {"abc": "def", "ghi": {"$ref": "/abc", "foo": "bar"}} |
|
|||
67 | with self.assertRaises(SchemaError): |
|
|||
68 | resolved = resolve_ref(json) |
|
|||
69 |
|
||||
70 | # make sure it can handle json with no reference |
|
|||
71 | json = {"abc": "def"} |
|
|||
72 | resolved = resolve_ref(json) |
|
|||
73 | self.assertEqual(resolved, json) |
|
@@ -22,7 +22,7 b' from IPython.utils.py3compat import cast_unicode, unicode_type' | |||||
22 | # Change this when incrementing the nbformat version |
|
22 | # Change this when incrementing the nbformat version | |
23 | nbformat = 3 |
|
23 | nbformat = 3 | |
24 | nbformat_minor = 0 |
|
24 | nbformat_minor = 0 | |
25 |
nbformat_schema = ' |
|
25 | nbformat_schema = 'nbformat.v3.schema.json' | |
26 |
|
26 | |||
27 | class NotebookNode(Struct): |
|
27 | class NotebookNode(Struct): | |
28 | pass |
|
28 | pass |
@@ -1,112 +1,72 b'' | |||||
|
1 | # Copyright (c) IPython Development Team. | |||
|
2 | # Distributed under the terms of the Modified BSD License. | |||
|
3 | ||||
1 | from __future__ import print_function |
|
4 | from __future__ import print_function | |
2 | import json |
|
5 | import json | |
3 | import os |
|
6 | import os | |
4 |
|
7 | |||
5 | try: |
|
8 | try: | |
6 |
from jsonschema import |
|
9 | from jsonschema import ValidationError | |
7 |
from jsonschema import Draft |
|
10 | from jsonschema import Draft4Validator as Validator | |
8 | except ImportError as e: |
|
11 | except ImportError as e: | |
9 | verbose_msg = """ |
|
12 | verbose_msg = """ | |
10 |
|
13 | |||
11 |
IPython depends on the jsonschema package: |
|
14 | IPython notebook format depends on the jsonschema package: | |
12 |
|
15 | |||
13 | Please install it first. |
|
16 | https://pypi.python.org/pypi/jsonschema | |
14 | """ |
|
|||
15 | raise ImportError(str(e) + verbose_msg) |
|
|||
16 |
|
||||
17 | try: |
|
|||
18 | import jsonpointer as jsonpointer |
|
|||
19 | except ImportError as e: |
|
|||
20 | verbose_msg = """ |
|
|||
21 |
|
||||
22 | IPython depends on the jsonpointer package: https://pypi.python.org/pypi/jsonpointer |
|
|||
23 |
|
17 | |||
24 | Please install it first. |
|
18 | Please install it first. | |
25 | """ |
|
19 | """ | |
26 | raise ImportError(str(e) + verbose_msg) |
|
20 | raise ImportError(str(e) + verbose_msg) | |
27 |
|
21 | |||
28 |
from IPython.utils. |
|
22 | from IPython.utils.importstring import import_item | |
|
23 | ||||
29 |
|
24 | |||
|
25 | validators = {} | |||
30 |
|
26 | |||
31 | from .current import nbformat, nbformat_schema |
|
27 | def get_validator(version=None): | |
32 | schema_path = os.path.join( |
|
28 | """Load the JSON schema into a Validator""" | |
33 | os.path.dirname(__file__), "v%d" % nbformat, nbformat_schema) |
|
29 | if version is None: | |
|
30 | from .current import nbformat as version | |||
34 |
|
31 | |||
|
32 | if version not in validators: | |||
|
33 | v = import_item("IPython.nbformat.v%s" % version) | |||
|
34 | schema_path = os.path.join(os.path.dirname(v.__file__), v.nbformat_schema) | |||
|
35 | with open(schema_path) as f: | |||
|
36 | schema_json = json.load(f) | |||
|
37 | validators[version] = Validator(schema_json) | |||
|
38 | return validators[version] | |||
35 |
|
39 | |||
36 | def isvalid(nbjson): |
|
40 | def isvalid(nbjson, ref=None, version=None): | |
37 | """Checks whether the given notebook JSON conforms to the current |
|
41 | """Checks whether the given notebook JSON conforms to the current | |
38 | notebook format schema. Returns True if the JSON is valid, and |
|
42 | notebook format schema. Returns True if the JSON is valid, and | |
39 | False otherwise. |
|
43 | False otherwise. | |
40 |
|
44 | |||
41 | To see the individual errors that were encountered, please use the |
|
45 | To see the individual errors that were encountered, please use the | |
42 | `validate` function instead. |
|
46 | `validate` function instead. | |
43 |
|
||||
44 | """ |
|
47 | """ | |
45 |
|
48 | try: | ||
46 |
|
|
49 | validate(nbjson, ref, version) | |
47 | return errors == [] |
|
50 | except ValidationError: | |
|
51 | return False | |||
|
52 | else: | |||
|
53 | return True | |||
48 |
|
54 | |||
49 |
|
55 | |||
50 | def validate(nbjson): |
|
56 | def validate(nbjson, ref=None, version=None): | |
51 | """Checks whether the given notebook JSON conforms to the current |
|
57 | """Checks whether the given notebook JSON conforms to the current | |
52 |
notebook format schema |
|
58 | notebook format schema. | |
53 |
|
||||
54 | """ |
|
|||
55 |
|
||||
56 | # load the schema file |
|
|||
57 | with open(schema_path, 'r') as fh: |
|
|||
58 | schema_json = json.load(fh) |
|
|||
59 |
|
||||
60 | # resolve internal references |
|
|||
61 | schema = resolve_ref(schema_json) |
|
|||
62 | schema = jsonpointer.resolve_pointer(schema, '/notebook') |
|
|||
63 |
|
||||
64 | # count how many errors there are |
|
|||
65 | v = Validator(schema) |
|
|||
66 | errors = list(v.iter_errors(nbjson)) |
|
|||
67 | return errors |
|
|||
68 |
|
||||
69 |
|
||||
70 | def resolve_ref(json, schema=None): |
|
|||
71 | """Resolve internal references within the given JSON. This essentially |
|
|||
72 | means that dictionaries of this form: |
|
|||
73 |
|
||||
74 | {"$ref": "/somepointer"} |
|
|||
75 |
|
||||
76 | will be replaced with the resolved reference to `/somepointer`. |
|
|||
77 | This only supports local reference to the same JSON file. |
|
|||
78 |
|
59 | |||
|
60 | Raises ValidationError if not valid. | |||
79 | """ |
|
61 | """ | |
|
62 | if version is None: | |||
|
63 | from .current import nbformat | |||
|
64 | version = nbjson.get('nbformat', nbformat) | |||
80 |
|
65 | |||
81 | if not schema: |
|
66 | validator = get_validator(version) | |
82 | schema = json |
|
|||
83 |
|
||||
84 | # if it's a list, resolve references for each item in the list |
|
|||
85 | if type(json) is list: |
|
|||
86 | resolved = [] |
|
|||
87 | for item in json: |
|
|||
88 | resolved.append(resolve_ref(item, schema=schema)) |
|
|||
89 |
|
||||
90 | # if it's a dictionary, resolve references for each item in the |
|
|||
91 | # dictionary |
|
|||
92 | elif type(json) is dict: |
|
|||
93 | resolved = {} |
|
|||
94 | for key, ref in iteritems(json): |
|
|||
95 |
|
||||
96 | # if the key is equal to $ref, then replace the entire |
|
|||
97 | # dictionary with the resolved value |
|
|||
98 | if key == '$ref': |
|
|||
99 | if len(json) != 1: |
|
|||
100 | raise SchemaError( |
|
|||
101 | "objects containing a $ref should only have one item") |
|
|||
102 | pointer = jsonpointer.resolve_pointer(schema, ref) |
|
|||
103 | resolved = resolve_ref(pointer, schema=schema) |
|
|||
104 |
|
||||
105 | else: |
|
|||
106 | resolved[key] = resolve_ref(ref, schema=schema) |
|
|||
107 |
|
67 | |||
108 | # otherwise it's a normal object, so just return it |
|
68 | if ref: | |
|
69 | return validator.validate(nbjson, {'$ref' : '#/definitions/%s' % ref}) | |||
109 | else: |
|
70 | else: | |
110 | resolved = json |
|
71 | return validator.validate(nbjson) | |
111 |
|
72 | |||
112 | return resolved |
|
@@ -218,7 +218,7 b' def all_js_groups():' | |||||
218 | class JSController(TestController): |
|
218 | class JSController(TestController): | |
219 | """Run CasperJS tests """ |
|
219 | """Run CasperJS tests """ | |
220 | requirements = ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3', |
|
220 | requirements = ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3', | |
221 |
'jsonschema' |
|
221 | 'jsonschema'] | |
222 | display_slimer_output = False |
|
222 | display_slimer_output = False | |
223 |
|
223 | |||
224 | def __init__(self, section, xunit=True, engine='phantomjs'): |
|
224 | def __init__(self, section, xunit=True, engine='phantomjs'): |
@@ -275,7 +275,7 b' extras_require = dict(' | |||||
275 | doc = ['Sphinx>=1.1', 'numpydoc'], |
|
275 | doc = ['Sphinx>=1.1', 'numpydoc'], | |
276 | test = ['nose>=0.10.1'], |
|
276 | test = ['nose>=0.10.1'], | |
277 | terminal = [], |
|
277 | terminal = [], | |
278 |
nbformat = ['jsonschema>=2.0' |
|
278 | nbformat = ['jsonschema>=2.0'], | |
279 | notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3.1'], |
|
279 | notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3.1'], | |
280 | nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1'] |
|
280 | nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1'] | |
281 | ) |
|
281 | ) |
@@ -194,7 +194,10 b' def find_package_data():' | |||||
194 | 'preprocessors/tests/files/*.*', |
|
194 | 'preprocessors/tests/files/*.*', | |
195 | ], |
|
195 | ], | |
196 | 'IPython.nbconvert.filters' : ['marked.js'], |
|
196 | 'IPython.nbconvert.filters' : ['marked.js'], | |
197 |
'IPython.nbformat' : [ |
|
197 | 'IPython.nbformat' : [ | |
|
198 | 'tests/*.ipynb', | |||
|
199 | 'v3/nbformat.v3.schema.json', | |||
|
200 | ] | |||
198 | } |
|
201 | } | |
199 |
|
202 | |||
200 | return package_data |
|
203 | return package_data |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now