##// END OF EJS Templates
update Jsonschema version
Matthias BUSSONNIER -
Show More
@@ -0,0 +1,3 b''
1 _jsonschema from commit :
2 3ddd80543bd6da56eeea84b2f364febaeadf31b9
3
This diff has been collapsed as it changes many lines, (595 lines changed) Show them Hide them
@@ -1,12 +1,11 b''
1 """
1 """
2 An implementation of JSON Schema for Python
2 An implementation of JSON Schema for Python
3
3
4 The main functionality is provided by the :class:`Validator` class, with the
4 The main functionality is provided by the validator classes for each of the
5 :function:`validate` function being the most common way to quickly create a
5 supported JSON Schema versions.
6 :class:`Validator` object and validate an instance with a given schema.
7
6
8 The :class:`Validator` class generally attempts to be as strict as possible
7 Most commonly, the :function:`validate` function is the quickest way to simply
9 under the JSON Schema specification. See its docstring for details.
8 validate a given instance under a schema, and will create a validator for you.
10
9
11 """
10 """
12
11
@@ -20,131 +19,33 b' import sys'
20 import warnings
19 import warnings
21
20
22
21
22 __version__ = "0.7"
23
24 FLOAT_TOLERANCE = 10 ** -15
23 PY3 = sys.version_info[0] >= 3
25 PY3 = sys.version_info[0] >= 3
24
26
25 if PY3:
27 if PY3:
26 basestring = unicode = str
28 basestring = unicode = str
27 iteritems = operator.methodcaller("items")
29 iteritems = operator.methodcaller("items")
30 from urllib.parse import unquote
28 else:
31 else:
29 from itertools import izip as zip
32 from itertools import izip as zip
30 iteritems = operator.methodcaller("iteritems")
33 iteritems = operator.methodcaller("iteritems")
34 from urllib import unquote
31
35
32
36
33 def _uniq(container):
37 class UnknownType(Exception):
34 """
38 """
35 Check if all of a container's elements are unique.
39 An unknown type was given.
36
37 Successively tries first to rely that the elements are hashable, then
38 falls back on them being sortable, and finally falls back on brute
39 force.
40
40
41 """
41 """
42
42
43 try:
44 return len(set(container)) == len(container)
45 except TypeError:
46 try:
47 sort = sorted(container)
48 sliced = itertools.islice(container, 1, None)
49 for i, j in zip(container, sliced):
50 if i == j:
51 return False
52 except (NotImplementedError, TypeError):
53 seen = []
54 for e in container:
55 if e in seen:
56 return False
57 seen.append(e)
58 return True
59
60
61 __version__ = "0.5"
62
63
64 DRAFT_3 = {
65 "$schema" : "http://json-schema.org/draft-03/schema#",
66 "id" : "http://json-schema.org/draft-03/schema#",
67 "type" : "object",
68
43
69 "properties" : {
44 class InvalidRef(Exception):
70 "type" : {
45 """
71 "type" : ["string", "array"],
46 An invalid reference was given.
72 "items" : {"type" : ["string", {"$ref" : "#"}]},
73 "uniqueItems" : True,
74 "default" : "any"
75 },
76 "properties" : {
77 "type" : "object",
78 "additionalProperties" : {"$ref" : "#", "type": "object"},
79 "default" : {}
80 },
81 "patternProperties" : {
82 "type" : "object",
83 "additionalProperties" : {"$ref" : "#"},
84 "default" : {}
85 },
86 "additionalProperties" : {
87 "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
88 },
89 "items" : {
90 "type" : [{"$ref" : "#"}, "array"],
91 "items" : {"$ref" : "#"},
92 "default" : {}
93 },
94 "additionalItems" : {
95 "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
96 },
97 "required" : {"type" : "boolean", "default" : False},
98 "dependencies" : {
99 "type" : ["string", "array", "object"],
100 "additionalProperties" : {
101 "type" : ["string", "array", {"$ref" : "#"}],
102 "items" : {"type" : "string"}
103 },
104 "default" : {}
105 },
106 "minimum" : {"type" : "number"},
107 "maximum" : {"type" : "number"},
108 "exclusiveMinimum" : {"type" : "boolean", "default" : False},
109 "exclusiveMaximum" : {"type" : "boolean", "default" : False},
110 "minItems" : {"type" : "integer", "minimum" : 0, "default" : 0},
111 "maxItems" : {"type" : "integer", "minimum" : 0},
112 "uniqueItems" : {"type" : "boolean", "default" : False},
113 "pattern" : {"type" : "string", "format" : "regex"},
114 "minLength" : {"type" : "integer", "minimum" : 0, "default" : 0},
115 "maxLength" : {"type" : "integer"},
116 "enum" : {"type" : "array", "minItems" : 1, "uniqueItems" : True},
117 "default" : {"type" : "any"},
118 "title" : {"type" : "string"},
119 "description" : {"type" : "string"},
120 "format" : {"type" : "string"},
121 "maxDecimal" : {"type" : "number", "minimum" : 0},
122 "divisibleBy" : {
123 "type" : "number",
124 "minimum" : 0,
125 "exclusiveMinimum" : True,
126 "default" : 1
127 },
128 "disallow" : {
129 "type" : ["string", "array"],
130 "items" : {"type" : ["string", {"$ref" : "#"}]},
131 "uniqueItems" : True
132 },
133 "extends" : {
134 "type" : [{"$ref" : "#"}, "array"],
135 "items" : {"$ref" : "#"},
136 "default" : {}
137 },
138 "id" : {"type" : "string", "format" : "uri"},
139 "$ref" : {"type" : "string", "format" : "uri"},
140 "$schema" : {"type" : "string", "format" : "uri"},
141 },
142 "dependencies" : {
143 "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum"
144 },
145 }
146
47
147 EPSILON = 10 ** -15
48 """
148
49
149
50
150 class SchemaError(Exception):
51 class SchemaError(Exception):
@@ -188,9 +89,9 b' class ValidationError(Exception):'
188 self.path = []
89 self.path = []
189
90
190
91
191 class Validator(object):
92 class Draft3Validator(object):
192 """
93 """
193 A JSON Schema validator.
94 A validator for JSON Schema draft 3.
194
95
195 """
96 """
196
97
@@ -199,23 +100,12 b' class Validator(object):'
199 "number" : (int, float), "object" : dict, "string" : basestring,
100 "number" : (int, float), "object" : dict, "string" : basestring,
200 }
101 }
201
102
202 def __init__(
103 def __init__(self, schema, types=()):
203 self, version=DRAFT_3, unknown_type="skip",
204 unknown_property="skip", types=(),
205 ):
206 """
104 """
207 Initialize a Validator.
105 Initialize a validator.
208
209 ``version`` specifies which version of the JSON Schema specification to
210 validate with. Currently only draft-03 is supported (and is the
211 default).
212
106
213 ``unknown_type`` and ``unknown_property`` control what to do when an
107 ``schema`` should be a *valid* JSON Schema object already converted to
214 unknown type (resp. property) is encountered. By default, the
108 a native Python object (typically a dict via ``json.load``).
215 metaschema is respected (which e.g. for draft 3 allows a schema to have
216 additional properties), but if for some reason you want to modify this
217 behavior, you can do so without needing to modify the metaschema by
218 passing ``"error"`` or ``"warn"`` to these arguments.
219
109
220 ``types`` is a mapping (or iterable of 2-tuples) containing additional
110 ``types`` is a mapping (or iterable of 2-tuples) containing additional
221 types or alternate types to verify via the 'type' property. For
111 types or alternate types to verify via the 'type' property. For
@@ -223,100 +113,76 b' class Validator(object):'
223 ``int`` and ``float``. To override this behavior (e.g. for also
113 ``int`` and ``float``. To override this behavior (e.g. for also
224 allowing ``decimal.Decimal``), pass ``types={"number" : (int, float,
114 allowing ``decimal.Decimal``), pass ``types={"number" : (int, float,
225 decimal.Decimal)} *including* the default types if so desired, which
115 decimal.Decimal)} *including* the default types if so desired, which
226 are fairly obvious but can be accessed via ``Validator.DEFAULT_TYPES``
116 are fairly obvious but can be accessed via the ``DEFAULT_TYPES``
227 if necessary.
117 attribute on this class if necessary.
228
118
229 """
119 """
230
120
231 self._unknown_type = unknown_type
232 self._unknown_property = unknown_property
233 self._version = version
234
235 self._types = dict(self.DEFAULT_TYPES)
121 self._types = dict(self.DEFAULT_TYPES)
236 self._types.update(types)
122 self._types.update(types)
237 self._types["any"] = tuple(self._types.values())
123 self._types["any"] = tuple(self._types.values())
238
124
125 self.schema = schema
126
239 def is_type(self, instance, type):
127 def is_type(self, instance, type):
240 """
128 """
241 Check if an ``instance`` is of the provided ``type``.
129 Check if an ``instance`` is of the provided (JSON Schema) ``type``.
242
130
243 """
131 """
244
132
245 py_type = self._types.get(type)
133 if type not in self._types:
246
134 raise UnknownType(type)
247 if py_type is None:
135 type = self._types[type]
248 return self.schema_error(
249 self._unknown_type, "%r is not a known type" % (type,)
250 )
251
136
252 # the only thing we're careful about here is evading bool inheriting
137 # bool inherits from int, so ensure bools aren't reported as integers
253 # from int, so let's be even dirtier than usual
138 if isinstance(instance, bool):
139 type = _flatten(type)
140 if int in type and bool not in type:
141 return False
142 return isinstance(instance, type)
254
143
255 elif (
144 def is_valid(self, instance, _schema=None):
256 # it's not a bool, so no worries
145 """
257 not isinstance(instance, bool) or
146 Check if the ``instance`` is valid under the current schema.
258
147
259 # it is a bool, but we're checking for a bool, so no worries
148 Returns a bool indicating whether validation succeeded.
260 (
261 py_type is bool or
262 isinstance(py_type, tuple) and bool in py_type
263 )
264
149
265 ):
150 """
266 return isinstance(instance, py_type)
267
151
268 def schema_error(self, level, msg):
152 error = next(self.iter_errors(instance, _schema), None)
269 if level == "skip":
153 return error is None
270 return
271 elif level == "warn":
272 warnings.warn(msg)
273 else:
274 raise SchemaError(msg)
275
154
276 def is_valid(self, instance, schema, meta_validate=True):
155 @classmethod
156 def check_schema(cls, schema):
277 """
157 """
278 Check if the ``instance`` is valid under the ``schema``.
158 Validate a ``schema`` against the meta-schema to see if it is valid.
279
280 Returns a bool indicating whether validation succeeded.
281
159
282 """
160 """
283
161
284 error = next(self.iter_errors(instance, schema, meta_validate), None)
162 for error in cls(cls.META_SCHEMA).iter_errors(schema):
285 return error is None
163 s = SchemaError(error.message)
164 s.path = error.path
165 s.validator = error.validator
166 # I think we're safer raising these always, not yielding them
167 raise s
286
168
287 def iter_errors(self, instance, schema, meta_validate=True):
169 def iter_errors(self, instance, _schema=None):
288 """
170 """
289 Lazily yield each of the errors in the given ``instance``.
171 Lazily yield each of the errors in the given ``instance``.
290
172
291 If you are unsure whether your schema itself is valid,
292 ``meta_validate`` will first validate that the schema is valid before
293 attempting to validate the instance. ``meta_validate`` is ``True`` by
294 default, since setting it to ``False`` can lead to confusing error
295 messages with an invalid schema. If you're sure your schema is in fact
296 valid, or don't care, feel free to set this to ``False``. The meta
297 validation will be done using the appropriate ``version``.
298
299 """
173 """
300
174
301 if meta_validate:
175 if _schema is None:
302 for error in self.iter_errors(
176 _schema = self.schema
303 schema, self._version, meta_validate=False
177
304 ):
178 for k, v in iteritems(_schema):
305 s = SchemaError(error.message)
306 s.path = error.path
307 s.validator = error.validator
308 # I think we're safer raising these always, not yielding them
309 raise s
310
311 for k, v in iteritems(schema):
312 validator = getattr(self, "validate_%s" % (k.lstrip("$"),), None)
179 validator = getattr(self, "validate_%s" % (k.lstrip("$"),), None)
313
180
314 if validator is None:
181 if validator is None:
315 errors = self.unknown_property(k, instance, schema)
182 continue
316 else:
317 errors = validator(v, instance, schema)
318
183
319 for error in errors or ():
184 errors = validator(v, instance, _schema) or ()
185 for error in errors:
320 # if the validator hasn't already been set (due to recursion)
186 # if the validator hasn't already been set (due to recursion)
321 # make sure to set it
187 # make sure to set it
322 error.validator = error.validator or k
188 error.validator = error.validator or k
@@ -331,12 +197,6 b' class Validator(object):'
331 for error in self.iter_errors(*args, **kwargs):
197 for error in self.iter_errors(*args, **kwargs):
332 raise error
198 raise error
333
199
334 def unknown_property(self, property, instance, schema):
335 self.schema_error(
336 self._unknown_property,
337 "%r is not a known schema property" % (property,)
338 )
339
340 def validate_type(self, types, instance, schema):
200 def validate_type(self, types, instance, schema):
341 types = _list(types)
201 types = _list(types)
342
202
@@ -357,9 +217,7 b' class Validator(object):'
357 )):
217 )):
358 return
218 return
359 else:
219 else:
360 yield ValidationError(
220 yield ValidationError(_types_msg(instance, types))
361 "%r is not of type %r" % (instance, _delist(types))
362 )
363
221
364 def validate_properties(self, properties, instance, schema):
222 def validate_properties(self, properties, instance, schema):
365 if not self.is_type(instance, "object"):
223 if not self.is_type(instance, "object"):
@@ -367,22 +225,7 b' class Validator(object):'
367
225
368 for property, subschema in iteritems(properties):
226 for property, subschema in iteritems(properties):
369 if property in instance:
227 if property in instance:
370 dependencies = _list(subschema.get("dependencies", []))
228 for error in self.iter_errors(instance[property], subschema):
371 if self.is_type(dependencies, "object"):
372 for error in self.iter_errors(
373 instance, dependencies, meta_validate=False
374 ):
375 yield error
376 else:
377 for dependency in dependencies:
378 if dependency not in instance:
379 yield ValidationError(
380 "%r is a dependency of %r" % (dependency, property)
381 )
382
383 for error in self.iter_errors(
384 instance[property], subschema, meta_validate=False
385 ):
386 error.path.append(property)
229 error.path.append(property)
387 yield error
230 yield error
388 elif subschema.get("required", False):
231 elif subschema.get("required", False):
@@ -397,59 +240,71 b' class Validator(object):'
397 for pattern, subschema in iteritems(patternProperties):
240 for pattern, subschema in iteritems(patternProperties):
398 for k, v in iteritems(instance):
241 for k, v in iteritems(instance):
399 if re.match(pattern, k):
242 if re.match(pattern, k):
400 for error in self.iter_errors(
243 for error in self.iter_errors(v, subschema):
401 v, subschema, meta_validate=False
402 ):
403 yield error
244 yield error
404
245
405 def validate_additionalProperties(self, aP, instance, schema):
246 def validate_additionalProperties(self, aP, instance, schema):
406 if not self.is_type(instance, "object"):
247 if not self.is_type(instance, "object"):
407 return
248 return
408
249
409 # no viewkeys in <2.7, and pypy seems to fail on vk - vk anyhow, so...
250 extras = set(_find_additional_properties(instance, schema))
410 extras = set(instance) - set(schema.get("properties", {}))
411
251
412 if self.is_type(aP, "object"):
252 if self.is_type(aP, "object"):
413 for extra in extras:
253 for extra in extras:
414 for error in self.iter_errors(
254 for error in self.iter_errors(instance[extra], aP):
415 instance[extra], aP, meta_validate=False
416 ):
417 yield error
255 yield error
418 elif not aP and extras:
256 elif not aP and extras:
419 error = "Additional properties are not allowed (%s %s unexpected)"
257 error = "Additional properties are not allowed (%s %s unexpected)"
420 yield ValidationError(error % _extras_msg(extras))
258 yield ValidationError(error % _extras_msg(extras))
421
259
260 def validate_dependencies(self, dependencies, instance, schema):
261 if not self.is_type(instance, "object"):
262 return
263
264 for property, dependency in iteritems(dependencies):
265 if property not in instance:
266 continue
267
268 if self.is_type(dependency, "object"):
269 for error in self.iter_errors(instance, dependency):
270 yield error
271 else:
272 dependencies = _list(dependency)
273 for dependency in dependencies:
274 if dependency not in instance:
275 yield ValidationError(
276 "%r is a dependency of %r" % (dependency, property)
277 )
278
422 def validate_items(self, items, instance, schema):
279 def validate_items(self, items, instance, schema):
423 if not self.is_type(instance, "array"):
280 if not self.is_type(instance, "array"):
424 return
281 return
425
282
426 if self.is_type(items, "object"):
283 if self.is_type(items, "object"):
427 for index, item in enumerate(instance):
284 for index, item in enumerate(instance):
428 for error in self.iter_errors(
285 for error in self.iter_errors(item, items):
429 item, items, meta_validate=False
430 ):
431 error.path.append(index)
286 error.path.append(index)
432 yield error
287 yield error
433 else:
288 else:
434 for (index, item), subschema in zip(enumerate(instance), items):
289 for (index, item), subschema in zip(enumerate(instance), items):
435 for error in self.iter_errors(
290 for error in self.iter_errors(item, subschema):
436 item, subschema, meta_validate=False
437 ):
438 error.path.append(index)
291 error.path.append(index)
439 yield error
292 yield error
440
293
441 def validate_additionalItems(self, aI, instance, schema):
294 def validate_additionalItems(self, aI, instance, schema):
442 if not self.is_type(instance, "array"):
295 if not self.is_type(instance, "array"):
443 return
296 return
297 if not self.is_type(schema.get("items"), "array"):
298 return
444
299
445 if self.is_type(aI, "object"):
300 if self.is_type(aI, "object"):
446 for item in instance[len(schema):]:
301 for item in instance[len(schema):]:
447 for error in self.iter_errors(item, aI, meta_validate=False):
302 for error in self.iter_errors(item, aI):
448 yield error
303 yield error
449 elif not aI and len(instance) > len(schema.get("items", [])):
304 elif not aI and len(instance) > len(schema.get("items", [])):
450 error = "Additional items are not allowed (%s %s unexpected)"
305 error = "Additional items are not allowed (%s %s unexpected)"
451 yield ValidationError(
306 yield ValidationError(
452 error % _extras_msg(instance[len(schema) - 1:])
307 error % _extras_msg(instance[len(schema.get("items", [])):])
453 )
308 )
454
309
455 def validate_minimum(self, minimum, instance, schema):
310 def validate_minimum(self, minimum, instance, schema):
@@ -520,7 +375,7 b' class Validator(object):'
520
375
521 if isinstance(dB, float):
376 if isinstance(dB, float):
522 mod = instance % dB
377 mod = instance % dB
523 failed = (mod > EPSILON) and (dB - mod) > EPSILON
378 failed = (mod > FLOAT_TOLERANCE) and (dB - mod) > FLOAT_TOLERANCE
524 else:
379 else:
525 failed = instance % dB
380 failed = instance % dB
526
381
@@ -538,20 +393,119 b' class Validator(object):'
538 if self.is_type(extends, "object"):
393 if self.is_type(extends, "object"):
539 extends = [extends]
394 extends = [extends]
540 for subschema in extends:
395 for subschema in extends:
541 for error in self.iter_errors(
396 for error in self.iter_errors(instance, subschema):
542 instance, subschema, meta_validate=False
543 ):
544 yield error
397 yield error
545
398
399 def validate_ref(self, ref, instance, schema):
400 if ref != "#" and not ref.startswith("#/"):
401 warnings.warn("jsonschema only supports json-pointer $refs")
402 return
403
404 resolved = resolve_json_pointer(self.schema, ref)
405 for error in self.iter_errors(instance, resolved):
406 yield error
407
408
409 Draft3Validator.META_SCHEMA = {
410 "$schema" : "http://json-schema.org/draft-03/schema#",
411 "id" : "http://json-schema.org/draft-03/schema#",
412 "type" : "object",
413
414 "properties" : {
415 "type" : {
416 "type" : ["string", "array"],
417 "items" : {"type" : ["string", {"$ref" : "#"}]},
418 "uniqueItems" : True,
419 "default" : "any"
420 },
421 "properties" : {
422 "type" : "object",
423 "additionalProperties" : {"$ref" : "#", "type": "object"},
424 "default" : {}
425 },
426 "patternProperties" : {
427 "type" : "object",
428 "additionalProperties" : {"$ref" : "#"},
429 "default" : {}
430 },
431 "additionalProperties" : {
432 "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
433 },
434 "items" : {
435 "type" : [{"$ref" : "#"}, "array"],
436 "items" : {"$ref" : "#"},
437 "default" : {}
438 },
439 "additionalItems" : {
440 "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
441 },
442 "required" : {"type" : "boolean", "default" : False},
443 "dependencies" : {
444 "type" : ["string", "array", "object"],
445 "additionalProperties" : {
446 "type" : ["string", "array", {"$ref" : "#"}],
447 "items" : {"type" : "string"}
448 },
449 "default" : {}
450 },
451 "minimum" : {"type" : "number"},
452 "maximum" : {"type" : "number"},
453 "exclusiveMinimum" : {"type" : "boolean", "default" : False},
454 "exclusiveMaximum" : {"type" : "boolean", "default" : False},
455 "minItems" : {"type" : "integer", "minimum" : 0, "default" : 0},
456 "maxItems" : {"type" : "integer", "minimum" : 0},
457 "uniqueItems" : {"type" : "boolean", "default" : False},
458 "pattern" : {"type" : "string", "format" : "regex"},
459 "minLength" : {"type" : "integer", "minimum" : 0, "default" : 0},
460 "maxLength" : {"type" : "integer"},
461 "enum" : {"type" : "array", "minItems" : 1, "uniqueItems" : True},
462 "default" : {"type" : "any"},
463 "title" : {"type" : "string"},
464 "description" : {"type" : "string"},
465 "format" : {"type" : "string"},
466 "maxDecimal" : {"type" : "number", "minimum" : 0},
467 "divisibleBy" : {
468 "type" : "number",
469 "minimum" : 0,
470 "exclusiveMinimum" : True,
471 "default" : 1
472 },
473 "disallow" : {
474 "type" : ["string", "array"],
475 "items" : {"type" : ["string", {"$ref" : "#"}]},
476 "uniqueItems" : True
477 },
478 "extends" : {
479 "type" : [{"$ref" : "#"}, "array"],
480 "items" : {"$ref" : "#"},
481 "default" : {}
482 },
483 "id" : {"type" : "string", "format" : "uri"},
484 "$ref" : {"type" : "string", "format" : "uri"},
485 "$schema" : {"type" : "string", "format" : "uri"},
486 },
487 "dependencies" : {
488 "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum"
489 },
490 }
491
546
492
547 for no_op in [ # handled in:
493 class Validator(Draft3Validator):
548 "dependencies", "required", # properties
494 """
549 "exclusiveMinimum", "exclusiveMaximum", # min*/max*
495 Deprecated: Use :class:`Draft3Validator` instead.
550 "default", "description", "format", "id", # no validation needed
496
551 "links", "name", "title",
497 """
552 "ref", "schema", # not yet supported
498
553 ]:
499 def __init__(
554 setattr(Validator, "validate_" + no_op, lambda *args, **kwargs : None)
500 self, version=None, unknown_type="skip", unknown_property="skip",
501 *args, **kwargs
502 ):
503 super(Validator, self).__init__({}, *args, **kwargs)
504 warnings.warn(
505 "Validator is deprecated and will be removed. "
506 "Use Draft3Validator instead.",
507 DeprecationWarning, stacklevel=2,
508 )
555
509
556
510
557 class ErrorTree(object):
511 class ErrorTree(object):
@@ -590,6 +544,51 b' class ErrorTree(object):'
590 return "<%s (%s errors)>" % (self.__class__.__name__, len(self))
544 return "<%s (%s errors)>" % (self.__class__.__name__, len(self))
591
545
592
546
547 def resolve_json_pointer(schema, ref):
548 """
549 Resolve a local reference ``ref`` within the given root ``schema``.
550
551 ``ref`` should be a local ref whose ``#`` is still present.
552
553 """
554
555 if ref == "#":
556 return schema
557
558 parts = ref.lstrip("#/").split("/")
559
560 parts = map(unquote, parts)
561 parts = [part.replace('~1', '/').replace('~0', '~') for part in parts]
562
563 try:
564 for part in parts:
565 schema = schema[part]
566 except KeyError:
567 raise InvalidRef("Unresolvable json-pointer %r" % ref)
568 else:
569 return schema
570
571
572 def _find_additional_properties(instance, schema):
573 """
574 Return the set of additional properties for the given ``instance``.
575
576 Weeds out properties that should have been validated by ``properties`` and
577 / or ``patternProperties``.
578
579 Assumes ``instance`` is dict-like already.
580
581 """
582
583 properties = schema.get("properties", {})
584 patterns = "|".join(schema.get("patternProperties", {}))
585 for property in instance:
586 if property not in properties:
587 if patterns and re.search(patterns, property):
588 continue
589 yield property
590
591
593 def _extras_msg(extras):
592 def _extras_msg(extras):
594 """
593 """
595 Create an error message for extra items or properties.
594 Create an error message for extra items or properties.
@@ -603,6 +602,49 b' def _extras_msg(extras):'
603 return ", ".join(repr(extra) for extra in extras), verb
602 return ", ".join(repr(extra) for extra in extras), verb
604
603
605
604
605 def _types_msg(instance, types):
606 """
607 Create an error message for a failure to match the given types.
608
609 If the ``instance`` is an object and contains a ``name`` property, it will
610 be considered to be a description of that object and used as its type.
611
612 Otherwise the message is simply the reprs of the given ``types``.
613
614 """
615
616 reprs = []
617 for type in types:
618 try:
619 reprs.append(repr(type["name"]))
620 except Exception:
621 reprs.append(repr(type))
622 return "%r is not of type %s" % (instance, ", ".join(reprs))
623
624
625 def _flatten(suitable_for_isinstance):
626 """
627 isinstance() can accept a bunch of really annoying different types:
628 * a single type
629 * a tuple of types
630 * an arbitrary nested tree of tuples
631
632 Return a flattened tuple of the given argument.
633
634 """
635
636 types = set()
637
638 if not isinstance(suitable_for_isinstance, tuple):
639 suitable_for_isinstance = (suitable_for_isinstance,)
640 for thing in suitable_for_isinstance:
641 if isinstance(thing, tuple):
642 types.update(_flatten(thing))
643 else:
644 types.add(thing)
645 return tuple(types)
646
647
606 def _list(thing):
648 def _list(thing):
607 """
649 """
608 Wrap ``thing`` in a list if it's a single str.
650 Wrap ``thing`` in a list if it's a single str.
@@ -633,21 +675,60 b' def _delist(thing):'
633 return thing
675 return thing
634
676
635
677
636 def validate(
678 def _uniq(container):
637 instance, schema, meta_validate=True, cls=Validator, *args, **kwargs
679 """
638 ):
680 Check if all of a container's elements are unique.
681
682 Successively tries first to rely that the elements are hashable, then
683 falls back on them being sortable, and finally falls back on brute
684 force.
685
686 """
687
688 try:
689 return len(set(container)) == len(container)
690 except TypeError:
691 try:
692 sort = sorted(container)
693 sliced = itertools.islice(container, 1, None)
694 for i, j in zip(container, sliced):
695 if i == j:
696 return False
697 except (NotImplementedError, TypeError):
698 seen = []
699 for e in container:
700 if e in seen:
701 return False
702 seen.append(e)
703 return True
704
705
706 def validate(instance, schema, cls=Draft3Validator, *args, **kwargs):
639 """
707 """
640 Validate an ``instance`` under the given ``schema``.
708 Validate an ``instance`` under the given ``schema``.
641
709
642 By default, the :class:`Validator` class from this module is used to
710 First verifies that the provided schema is itself valid, since not doing so
643 perform the validation. To use another validator, pass it into the ``cls``
711 can lead to less obvious failures when validating. If you know it is or
644 argument.
712 don't care, use ``YourValidator(schema).validate(instance)`` directly
713 instead (e.g. ``Draft3Validator``).
645
714
646 Any other provided positional and keyword arguments will be provided to the
715 ``cls`` is a validator class that will be used to validate the instance.
647 ``cls``. See the :class:`Validator` class' docstring for details on the
716 By default this is a draft 3 validator. Any other provided positional and
648 arguments it accepts.
717 keyword arguments will be provided to this class when constructing a
718 validator.
649
719
650 """
720 """
651
721
652 validator = cls(*args, **kwargs)
722
653 validator.validate(instance, schema, meta_validate=meta_validate)
723 meta_validate = kwargs.pop("meta_validate", None)
724
725 if meta_validate is not None:
726 warnings.warn(
727 "meta_validate is deprecated and will be removed. If you do not "
728 "want to validate a schema, use Draft3Validator.validate instead.",
729 DeprecationWarning, stacklevel=2,
730 )
731
732 if meta_validate is not False: # yes this is needed since True was default
733 cls.check_schema(schema)
734 cls(schema, *args, **kwargs).validate(instance)
@@ -1,19 +1,19 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
2 # -*- coding: utf8 -*-
3
3
4 from IPython.external.jsonschema import Validator, validate, ValidationError
4 from IPython.external.jsonschema import Draft3Validator, validate, ValidationError
5 import IPython.external.jsonpointer as jsonpointer
5 import IPython.external.jsonpointer as jsonpointer
6 import argparse
6 import argparse
7 import traceback
7 import traceback
8 import json
8 import json
9
9
10 v = Validator();
11 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
10 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
12 v3schema = resolve_ref(json.load(open(schema,'r')))
11 v3schema = resolve_ref(json.load(open(schema,'r')))
13 if key :
12 if key :
14 v3schema = jsonpointer.resolve_pointer(v3schema,key)
13 v3schema = jsonpointer.resolve_pointer(v3schema,key)
15 errors = 0
14 errors = 0
16 for error in v.iter_errors(nbjson, v3schema):
15 v = Draft3Validator(v3schema);
16 for error in v.iter_errors(nbjson):
17 errors = errors + 1
17 errors = errors + 1
18 if verbose:
18 if verbose:
19 print(error)
19 print(error)
General Comments 0
You need to be logged in to leave comments. Login now