jsonschema.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471
  1. """
  2. An implementation of JSON Schema for Python
  3. The main functionality is provided by the validator classes for each of the
  4. supported JSON Schema versions.
  5. Most commonly, :func:`validate` is the quickest way to simply validate a given
  6. instance under a schema, and will create a validator for you.
  7. """
  8. from __future__ import division, unicode_literals
  9. import contextlib
  10. import datetime
  11. import itertools
  12. import collections
  13. import json
  14. import numbers
  15. import operator
  16. import pprint
  17. import re
  18. import socket
  19. import sys
  20. import textwrap
  21. try:
  22. from collections import MutableMapping
  23. except ImportError:
  24. from collections.abc import MutableMapping
  25. try:
  26. import requests
  27. except ImportError:
  28. requests = None
  29. __version__ = "1.3.0"
  30. PY3 = sys.version_info[0] >= 3
  31. if PY3:
  32. from urllib import parse as urlparse
  33. from urllib.parse import unquote
  34. from urllib.request import urlopen
  35. basestring = unicode = str
  36. long = int
  37. iteritems = operator.methodcaller("items")
  38. else:
  39. from itertools import izip as zip
  40. from urllib import unquote
  41. from urllib2 import urlopen
  42. import urlparse
  43. iteritems = operator.methodcaller("iteritems")
  44. FLOAT_TOLERANCE = 10 ** -15
  45. validators = {}
  46. class _Unset(object):
  47. """
  48. An as-of-yet unset attribute.
  49. """
  50. def __repr__(self):
  51. return "<unset>"
  52. _unset = _Unset()
  53. class _Error(Exception):
  54. def __init__(
  55. self, message, validator=_unset, path=(), cause=None, context=(),
  56. validator_value=_unset, instance=_unset, schema=_unset, schema_path=(),
  57. ):
  58. self.message = message
  59. self.path = collections.deque(path)
  60. self.schema_path = collections.deque(schema_path)
  61. self.context = list(context)
  62. self.cause = self.__cause__ = cause
  63. self.validator = validator
  64. self.validator_value = validator_value
  65. self.instance = instance
  66. self.schema = schema
  67. @classmethod
  68. def create_from(cls, other):
  69. return cls(
  70. message=other.message,
  71. cause=other.cause,
  72. context=other.context,
  73. path=other.path,
  74. schema_path=other.schema_path,
  75. validator=other.validator,
  76. validator_value=other.validator_value,
  77. instance=other.instance,
  78. schema=other.schema,
  79. )
  80. def _set(self, **kwargs):
  81. for k, v in iteritems(kwargs):
  82. if getattr(self, k) is _unset:
  83. setattr(self, k, v)
  84. def __repr__(self):
  85. return "<%s: %r>" % (self.__class__.__name__, self.message)
  86. def __str__(self):
  87. return unicode(self).encode("utf-8")
  88. def __unicode__(self):
  89. if _unset in (
  90. self.validator, self.validator_value, self.instance, self.schema,
  91. ):
  92. return self.message
  93. path = _format_as_index(self.path)
  94. schema_path = _format_as_index(list(self.schema_path)[:-1])
  95. pschema = pprint.pformat(self.schema, width=72)
  96. pinstance = pprint.pformat(self.instance, width=72)
  97. return self.message + textwrap.dedent("""
  98. Failed validating %r in schema%s:
  99. %s
  100. On instance%s:
  101. %s
  102. """.rstrip()
  103. ) % (
  104. self.validator,
  105. schema_path,
  106. _indent(pschema),
  107. path,
  108. _indent(pinstance),
  109. )
  110. if PY3:
  111. __str__ = __unicode__
  112. class FormatError(Exception):
  113. def __init__(self, message, cause=None):
  114. super(FormatError, self).__init__(message, cause)
  115. self.message = message
  116. self.cause = self.__cause__ = cause
  117. def __str__(self):
  118. return self.message.encode("utf-8")
  119. def __unicode__(self):
  120. return self.message
  121. if PY3:
  122. __str__ = __unicode__
  123. class SchemaError(_Error): pass
  124. class ValidationError(_Error): pass
  125. class RefResolutionError(Exception): pass
  126. class UnknownType(Exception): pass
  127. class _URIDict(MutableMapping):
  128. """
  129. Dictionary which uses normalized URIs as keys.
  130. """
  131. def normalize(self, uri):
  132. return urlparse.urlsplit(uri).geturl()
  133. def __init__(self, *args, **kwargs):
  134. self.store = dict()
  135. self.store.update(*args, **kwargs)
  136. def __getitem__(self, uri):
  137. return self.store[self.normalize(uri)]
  138. def __setitem__(self, uri, value):
  139. self.store[self.normalize(uri)] = value
  140. def __delitem__(self, uri):
  141. del self.store[self.normalize(uri)]
  142. def __iter__(self):
  143. return iter(self.store)
  144. def __len__(self):
  145. return len(self.store)
  146. def __repr__(self):
  147. return repr(self.store)
  148. meta_schemas = _URIDict()
  149. def validates(version):
  150. """
  151. Register the decorated validator for a ``version`` of the specification.
  152. Registered validators and their meta schemas will be considered when
  153. parsing ``$schema`` properties' URIs.
  154. :argument str version: an identifier to use as the version's name
  155. :returns: a class decorator to decorate the validator with the version
  156. """
  157. def _validates(cls):
  158. validators[version] = cls
  159. if "id" in cls.META_SCHEMA:
  160. meta_schemas[cls.META_SCHEMA["id"]] = cls
  161. return cls
  162. return _validates
  163. class ValidatorMixin(object):
  164. """
  165. Concrete implementation of :class:`IValidator`.
  166. Provides default implementations of each method. Validation of schema
  167. properties is dispatched to ``validate_property`` methods. E.g., to
  168. implement a validator for a ``maximum`` property, create a
  169. ``validate_maximum`` method. Validator methods should yield zero or more
  170. :exc:`ValidationError``\s to signal failed validation.
  171. """
  172. DEFAULT_TYPES = {
  173. "array" : list, "boolean" : bool, "integer" : (int, long),
  174. "null" : type(None), "number" : numbers.Number, "object" : dict,
  175. "string" : basestring,
  176. }
  177. def __init__(self, schema, types=(), resolver=None, format_checker=None):
  178. self._types = dict(self.DEFAULT_TYPES)
  179. self._types.update(types)
  180. if resolver is None:
  181. resolver = RefResolver.from_schema(schema)
  182. self.resolver = resolver
  183. self.format_checker = format_checker
  184. self.schema = schema
  185. def is_type(self, instance, type):
  186. if type not in self._types:
  187. raise UnknownType(type)
  188. pytypes = self._types[type]
  189. # bool inherits from int, so ensure bools aren't reported as integers
  190. if isinstance(instance, bool):
  191. pytypes = _flatten(pytypes)
  192. num = any(issubclass(pytype, numbers.Number) for pytype in pytypes)
  193. if num and bool not in pytypes:
  194. return False
  195. return isinstance(instance, pytypes)
  196. def is_valid(self, instance, _schema=None):
  197. error = next(self.iter_errors(instance, _schema), None)
  198. return error is None
  199. @classmethod
  200. def check_schema(cls, schema):
  201. for error in cls(cls.META_SCHEMA).iter_errors(schema):
  202. raise SchemaError.create_from(error)
  203. def iter_errors(self, instance, _schema=None):
  204. if _schema is None:
  205. _schema = self.schema
  206. with self.resolver.in_scope(_schema.get("id", "")):
  207. ref = _schema.get("$ref")
  208. if ref is not None:
  209. validators = [("$ref", ref)]
  210. else:
  211. validators = iteritems(_schema)
  212. for k, v in validators:
  213. validator_attr = "validate_%s" % (k.lstrip("$"),)
  214. validator = getattr(self, validator_attr, None)
  215. if validator is None:
  216. continue
  217. errors = validator(v, instance, _schema) or ()
  218. for error in errors:
  219. # set details if they weren't already set by the called fn
  220. error._set(
  221. validator=k,
  222. validator_value=v,
  223. instance=instance,
  224. schema=_schema,
  225. )
  226. if k != "$ref":
  227. error.schema_path.appendleft(k)
  228. yield error
  229. def descend(self, instance, schema, path=None, schema_path=None):
  230. for error in self.iter_errors(instance, schema):
  231. if path is not None:
  232. error.path.appendleft(path)
  233. if schema_path is not None:
  234. error.schema_path.appendleft(schema_path)
  235. yield error
  236. def validate(self, *args, **kwargs):
  237. for error in self.iter_errors(*args, **kwargs):
  238. raise error
  239. class _Draft34CommonMixin(object):
  240. """
  241. Contains the validator methods common to both JSON schema drafts.
  242. """
  243. def validate_patternProperties(self, patternProperties, instance, schema):
  244. if not self.is_type(instance, "object"):
  245. return
  246. for pattern, subschema in iteritems(patternProperties):
  247. for k, v in iteritems(instance):
  248. if re.search(pattern, k):
  249. for error in self.descend(
  250. v, subschema, path=k, schema_path=pattern
  251. ):
  252. yield error
  253. def validate_additionalProperties(self, aP, instance, schema):
  254. if not self.is_type(instance, "object"):
  255. return
  256. extras = set(_find_additional_properties(instance, schema))
  257. if self.is_type(aP, "object"):
  258. for extra in extras:
  259. for error in self.descend(instance[extra], aP, path=extra):
  260. yield error
  261. elif not aP and extras:
  262. error = "Additional properties are not allowed (%s %s unexpected)"
  263. yield ValidationError(error % _extras_msg(extras))
  264. def validate_items(self, items, instance, schema):
  265. if not self.is_type(instance, "array"):
  266. return
  267. if self.is_type(items, "object"):
  268. for index, item in enumerate(instance):
  269. for error in self.descend(item, items, path=index):
  270. yield error
  271. else:
  272. for (index, item), subschema in zip(enumerate(instance), items):
  273. for error in self.descend(
  274. item, subschema, path=index, schema_path=index
  275. ):
  276. yield error
  277. def validate_additionalItems(self, aI, instance, schema):
  278. if (
  279. not self.is_type(instance, "array") or
  280. self.is_type(schema.get("items", {}), "object")
  281. ):
  282. return
  283. if self.is_type(aI, "object"):
  284. for index, item in enumerate(
  285. instance[len(schema.get("items", [])):]):
  286. for error in self.descend(item, aI, path=index):
  287. yield error
  288. elif not aI and len(instance) > len(schema.get("items", [])):
  289. error = "Additional items are not allowed (%s %s unexpected)"
  290. yield ValidationError(
  291. error % _extras_msg(instance[len(schema.get("items", [])):])
  292. )
  293. def validate_minimum(self, minimum, instance, schema):
  294. if not self.is_type(instance, "number"):
  295. return
  296. instance = float(instance)
  297. if schema.get("exclusiveMinimum", False):
  298. failed = instance <= minimum
  299. cmp = "less than or equal to"
  300. else:
  301. failed = instance < minimum
  302. cmp = "less than"
  303. if failed:
  304. yield ValidationError(
  305. "%r is %s the minimum of %r" % (instance, cmp, minimum)
  306. )
  307. def validate_maximum(self, maximum, instance, schema):
  308. if not self.is_type(instance, "number"):
  309. return
  310. instance = float(instance)
  311. if schema.get("exclusiveMaximum", False):
  312. failed = instance >= maximum
  313. cmp = "greater than or equal to"
  314. else:
  315. failed = instance > maximum
  316. cmp = "greater than"
  317. if failed:
  318. yield ValidationError(
  319. "%r is %s the maximum of %r" % (instance, cmp, maximum)
  320. )
  321. def _validate_multipleOf(self, dB, instance, schema):
  322. if not self.is_type(instance, "number"):
  323. return
  324. if isinstance(dB, float):
  325. mod = instance % dB
  326. failed = (mod > FLOAT_TOLERANCE) and (dB - mod) > FLOAT_TOLERANCE
  327. else:
  328. failed = instance % dB
  329. if failed:
  330. yield ValidationError(
  331. "%r is not a multiple of %r" % (instance, dB)
  332. )
  333. def validate_minItems(self, mI, instance, schema):
  334. if self.is_type(instance, "array") and len(instance) < mI:
  335. yield ValidationError("%r is too short" % (instance,))
  336. def validate_maxItems(self, mI, instance, schema):
  337. if self.is_type(instance, "array") and len(instance) > mI:
  338. yield ValidationError("%r is too long" % (instance,))
  339. def validate_uniqueItems(self, uI, instance, schema):
  340. if uI and self.is_type(instance, "array") and not _uniq(instance):
  341. yield ValidationError("%r has non-unique elements" % instance)
  342. def validate_pattern(self, patrn, instance, schema):
  343. if self.is_type(instance, "string") and not re.search(patrn, instance):
  344. yield ValidationError("%r does not match %r" % (instance, patrn))
  345. def validate_format(self, format, instance, schema):
  346. if (
  347. self.format_checker is not None and
  348. self.is_type(instance, "string")
  349. ):
  350. try:
  351. self.format_checker.check(instance, format)
  352. except FormatError as error:
  353. yield ValidationError(error.message, cause=error.cause)
  354. def validate_minLength(self, mL, instance, schema):
  355. if self.is_type(instance, "string") and len(instance) < mL:
  356. yield ValidationError("%r is too short" % (instance,))
  357. def validate_maxLength(self, mL, instance, schema):
  358. if self.is_type(instance, "string") and len(instance) > mL:
  359. yield ValidationError("%r is too long" % (instance,))
  360. def validate_dependencies(self, dependencies, instance, schema):
  361. if not self.is_type(instance, "object"):
  362. return
  363. for property, dependency in iteritems(dependencies):
  364. if property not in instance:
  365. continue
  366. if self.is_type(dependency, "object"):
  367. for error in self.descend(
  368. instance, dependency, schema_path=property
  369. ):
  370. yield error
  371. else:
  372. dependencies = _list(dependency)
  373. for depend in dependencies:
  374. if depend not in instance:
  375. yield ValidationError(
  376. "%r is a dependency of %r" % (depend, property)
  377. )
  378. def validate_enum(self, enums, instance, schema):
  379. if instance not in enums:
  380. yield ValidationError("%r is not one of %r" % (instance, enums))
  381. def validate_ref(self, ref, instance, schema):
  382. with self.resolver.resolving(ref) as resolved:
  383. for error in self.descend(instance, resolved):
  384. yield error
  385. @validates("draft3")
  386. class Draft3Validator(ValidatorMixin, _Draft34CommonMixin, object):
  387. """
  388. A validator for JSON Schema draft 3.
  389. """
  390. def validate_type(self, types, instance, schema):
  391. types = _list(types)
  392. all_errors = []
  393. for index, type in enumerate(types):
  394. if type == "any":
  395. return
  396. if self.is_type(type, "object"):
  397. errors = list(self.descend(instance, type, schema_path=index))
  398. if not errors:
  399. return
  400. all_errors.extend(errors)
  401. elif self.is_type(type, "string"):
  402. if self.is_type(instance, type):
  403. return
  404. else:
  405. yield ValidationError(
  406. _types_msg(instance, types), context=all_errors,
  407. )
  408. def validate_properties(self, properties, instance, schema):
  409. if not self.is_type(instance, "object"):
  410. return
  411. for property, subschema in iteritems(properties):
  412. if property in instance:
  413. for error in self.descend(
  414. instance[property],
  415. subschema,
  416. path=property,
  417. schema_path=property,
  418. ):
  419. yield error
  420. elif subschema.get("required", False):
  421. error = ValidationError("%r is a required property" % property)
  422. error._set(
  423. validator="required",
  424. validator_value=subschema["required"],
  425. instance=instance,
  426. schema=schema,
  427. )
  428. error.path.appendleft(property)
  429. error.schema_path.extend([property, "required"])
  430. yield error
  431. def validate_disallow(self, disallow, instance, schema):
  432. for disallowed in _list(disallow):
  433. if self.is_valid(instance, {"type" : [disallowed]}):
  434. yield ValidationError(
  435. "%r is disallowed for %r" % (disallowed, instance)
  436. )
  437. def validate_extends(self, extends, instance, schema):
  438. if self.is_type(extends, "object"):
  439. for error in self.descend(instance, extends):
  440. yield error
  441. return
  442. for index, subschema in enumerate(extends):
  443. for error in self.descend(instance, subschema, schema_path=index):
  444. yield error
  445. validate_divisibleBy = _Draft34CommonMixin._validate_multipleOf
  446. META_SCHEMA = {
  447. "$schema" : "http://json-schema.org/draft-03/schema#",
  448. "id" : "http://json-schema.org/draft-03/schema#",
  449. "type" : "object",
  450. "properties" : {
  451. "type" : {
  452. "type" : ["string", "array"],
  453. "items" : {"type" : ["string", {"$ref" : "#"}]},
  454. "uniqueItems" : True,
  455. "default" : "any"
  456. },
  457. "properties" : {
  458. "type" : "object",
  459. "additionalProperties" : {"$ref" : "#", "type": "object"},
  460. "default" : {}
  461. },
  462. "patternProperties" : {
  463. "type" : "object",
  464. "additionalProperties" : {"$ref" : "#"},
  465. "default" : {}
  466. },
  467. "additionalProperties" : {
  468. "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
  469. },
  470. "items" : {
  471. "type" : [{"$ref" : "#"}, "array"],
  472. "items" : {"$ref" : "#"},
  473. "default" : {}
  474. },
  475. "additionalItems" : {
  476. "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
  477. },
  478. "required" : {"type" : "boolean", "default" : False},
  479. "dependencies" : {
  480. "type" : ["string", "array", "object"],
  481. "additionalProperties" : {
  482. "type" : ["string", "array", {"$ref" : "#"}],
  483. "items" : {"type" : "string"}
  484. },
  485. "default" : {}
  486. },
  487. "minimum" : {"type" : "number"},
  488. "maximum" : {"type" : "number"},
  489. "exclusiveMinimum" : {"type" : "boolean", "default" : False},
  490. "exclusiveMaximum" : {"type" : "boolean", "default" : False},
  491. "minItems" : {"type" : "integer", "minimum" : 0, "default" : 0},
  492. "maxItems" : {"type" : "integer", "minimum" : 0},
  493. "uniqueItems" : {"type" : "boolean", "default" : False},
  494. "pattern" : {"type" : "string", "format" : "regex"},
  495. "minLength" : {"type" : "integer", "minimum" : 0, "default" : 0},
  496. "maxLength" : {"type" : "integer"},
  497. "enum" : {"type" : "array", "minItems" : 1, "uniqueItems" : True},
  498. "default" : {"type" : "any"},
  499. "title" : {"type" : "string"},
  500. "description" : {"type" : "string"},
  501. "format" : {"type" : "string"},
  502. "maxDecimal" : {"type" : "number", "minimum" : 0},
  503. "divisibleBy" : {
  504. "type" : "number",
  505. "minimum" : 0,
  506. "exclusiveMinimum" : True,
  507. "default" : 1
  508. },
  509. "disallow" : {
  510. "type" : ["string", "array"],
  511. "items" : {"type" : ["string", {"$ref" : "#"}]},
  512. "uniqueItems" : True
  513. },
  514. "extends" : {
  515. "type" : [{"$ref" : "#"}, "array"],
  516. "items" : {"$ref" : "#"},
  517. "default" : {}
  518. },
  519. "id" : {"type" : "string", "format" : "uri"},
  520. "$ref" : {"type" : "string", "format" : "uri"},
  521. "$schema" : {"type" : "string", "format" : "uri"},
  522. },
  523. "dependencies" : {
  524. "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum"
  525. },
  526. }
  527. @validates("draft4")
  528. class Draft4Validator(ValidatorMixin, _Draft34CommonMixin, object):
  529. """
  530. A validator for JSON Schema draft 4.
  531. """
  532. def validate_type(self, types, instance, schema):
  533. types = _list(types)
  534. if not any(self.is_type(instance, type) for type in types):
  535. yield ValidationError(_types_msg(instance, types))
  536. def validate_properties(self, properties, instance, schema):
  537. if not self.is_type(instance, "object"):
  538. return
  539. for property, subschema in iteritems(properties):
  540. if property in instance:
  541. for error in self.descend(
  542. instance[property],
  543. subschema,
  544. path=property,
  545. schema_path=property,
  546. ):
  547. yield error
  548. def validate_required(self, required, instance, schema):
  549. if not self.is_type(instance, "object"):
  550. return
  551. for property in required:
  552. if property not in instance:
  553. yield ValidationError("%r is a required property" % property)
  554. def validate_minProperties(self, mP, instance, schema):
  555. if self.is_type(instance, "object") and len(instance) < mP:
  556. yield ValidationError("%r is too short" % (instance,))
  557. def validate_maxProperties(self, mP, instance, schema):
  558. if not self.is_type(instance, "object"):
  559. return
  560. if self.is_type(instance, "object") and len(instance) > mP:
  561. yield ValidationError("%r is too short" % (instance,))
  562. def validate_allOf(self, allOf, instance, schema):
  563. for index, subschema in enumerate(allOf):
  564. for error in self.descend(instance, subschema, schema_path=index):
  565. yield error
  566. def validate_oneOf(self, oneOf, instance, schema):
  567. first_valid = ""
  568. subschemas = enumerate(oneOf)
  569. all_errors = []
  570. for index, subschema in subschemas:
  571. errors = list(self.descend(instance, subschema, schema_path=index))
  572. if not errors:
  573. first_valid = subschema
  574. break
  575. all_errors.extend(errors)
  576. else:
  577. yield ValidationError(
  578. "%r is not valid under any of the given schemas" % (instance,),
  579. context=all_errors,
  580. )
  581. more_valid = [s for i, s in subschemas if self.is_valid(instance, s)]
  582. if more_valid:
  583. more_valid.append(first_valid)
  584. reprs = ", ".join(repr(schema) for schema in more_valid)
  585. yield ValidationError(
  586. "%r is valid under each of %s" % (instance, reprs)
  587. )
  588. def validate_anyOf(self, anyOf, instance, schema):
  589. all_errors = []
  590. for index, subschema in enumerate(anyOf):
  591. errors = list(self.descend(instance, subschema, schema_path=index))
  592. if not errors:
  593. break
  594. all_errors.extend(errors)
  595. else:
  596. yield ValidationError(
  597. "%r is not valid under any of the given schemas" % (instance,),
  598. context=all_errors,
  599. )
  600. def validate_not(self, not_schema, instance, schema):
  601. if self.is_valid(instance, not_schema):
  602. yield ValidationError(
  603. "%r is not allowed for %r" % (not_schema, instance)
  604. )
  605. validate_multipleOf = _Draft34CommonMixin._validate_multipleOf
  606. META_SCHEMA = {
  607. "id": "http://json-schema.org/draft-04/schema#",
  608. "$schema": "http://json-schema.org/draft-04/schema#",
  609. "description": "Core schema meta-schema",
  610. "definitions": {
  611. "schemaArray": {
  612. "type": "array",
  613. "minItems": 1,
  614. "items": {"$ref": "#"}
  615. },
  616. "positiveInteger": {
  617. "type": "integer",
  618. "minimum": 0
  619. },
  620. "positiveIntegerDefault0": {
  621. "allOf": [
  622. {"$ref": "#/definitions/positiveInteger"}, {"default": 0}
  623. ]
  624. },
  625. "simpleTypes": {
  626. "enum": [
  627. "array",
  628. "boolean",
  629. "integer",
  630. "null",
  631. "number",
  632. "object",
  633. "string",
  634. ]
  635. },
  636. "stringArray": {
  637. "type": "array",
  638. "items": {"type": "string"},
  639. "minItems": 1,
  640. "uniqueItems": True
  641. }
  642. },
  643. "type": "object",
  644. "properties": {
  645. "id": {
  646. "type": "string",
  647. "format": "uri"
  648. },
  649. "$schema": {
  650. "type": "string",
  651. "format": "uri"
  652. },
  653. "title": {
  654. "type": "string"
  655. },
  656. "description": {
  657. "type": "string"
  658. },
  659. "default": {},
  660. "multipleOf": {
  661. "type": "number",
  662. "minimum": 0,
  663. "exclusiveMinimum": True
  664. },
  665. "maximum": {
  666. "type": "number"
  667. },
  668. "exclusiveMaximum": {
  669. "type": "boolean",
  670. "default": False
  671. },
  672. "minimum": {
  673. "type": "number"
  674. },
  675. "exclusiveMinimum": {
  676. "type": "boolean",
  677. "default": False
  678. },
  679. "maxLength": {"$ref": "#/definitions/positiveInteger"},
  680. "minLength": {"$ref": "#/definitions/positiveIntegerDefault0"},
  681. "pattern": {
  682. "type": "string",
  683. "format": "regex"
  684. },
  685. "additionalItems": {
  686. "anyOf": [
  687. {"type": "boolean"},
  688. {"$ref": "#"}
  689. ],
  690. "default": {}
  691. },
  692. "items": {
  693. "anyOf": [
  694. {"$ref": "#"},
  695. {"$ref": "#/definitions/schemaArray"}
  696. ],
  697. "default": {}
  698. },
  699. "maxItems": {"$ref": "#/definitions/positiveInteger"},
  700. "minItems": {"$ref": "#/definitions/positiveIntegerDefault0"},
  701. "uniqueItems": {
  702. "type": "boolean",
  703. "default": False
  704. },
  705. "maxProperties": {"$ref": "#/definitions/positiveInteger"},
  706. "minProperties": {"$ref": "#/definitions/positiveIntegerDefault0"},
  707. "required": {"$ref": "#/definitions/stringArray"},
  708. "additionalProperties": {
  709. "anyOf": [
  710. {"type": "boolean"},
  711. {"$ref": "#"}
  712. ],
  713. "default": {}
  714. },
  715. "definitions": {
  716. "type": "object",
  717. "additionalProperties": {"$ref": "#"},
  718. "default": {}
  719. },
  720. "properties": {
  721. "type": "object",
  722. "additionalProperties": {"$ref": "#"},
  723. "default": {}
  724. },
  725. "patternProperties": {
  726. "type": "object",
  727. "additionalProperties": {"$ref": "#"},
  728. "default": {}
  729. },
  730. "dependencies": {
  731. "type": "object",
  732. "additionalProperties": {
  733. "anyOf": [
  734. {"$ref": "#"},
  735. {"$ref": "#/definitions/stringArray"}
  736. ]
  737. }
  738. },
  739. "enum": {
  740. "type": "array",
  741. "minItems": 1,
  742. "uniqueItems": True
  743. },
  744. "type": {
  745. "anyOf": [
  746. {"$ref": "#/definitions/simpleTypes"},
  747. {
  748. "type": "array",
  749. "items": {"$ref": "#/definitions/simpleTypes"},
  750. "minItems": 1,
  751. "uniqueItems": True
  752. }
  753. ]
  754. },
  755. "allOf": {"$ref": "#/definitions/schemaArray"},
  756. "anyOf": {"$ref": "#/definitions/schemaArray"},
  757. "oneOf": {"$ref": "#/definitions/schemaArray"},
  758. "not": {"$ref": "#"}
  759. },
  760. "dependencies": {
  761. "exclusiveMaximum": ["maximum"],
  762. "exclusiveMinimum": ["minimum"]
  763. },
  764. "default": {}
  765. }
  766. class FormatChecker(object):
  767. """
  768. A ``format`` property checker.
  769. JSON Schema does not mandate that the ``format`` property actually do any
  770. validation. If validation is desired however, instances of this class can
  771. be hooked into validators to enable format validation.
  772. :class:`FormatChecker` objects always return ``True`` when asked about
  773. formats that they do not know how to validate.
  774. To check a custom format using a function that takes an instance and
  775. returns a ``bool``, use the :meth:`FormatChecker.checks` or
  776. :meth:`FormatChecker.cls_checks` decorators.
  777. :argument iterable formats: the known formats to validate. This argument
  778. can be used to limit which formats will be used
  779. during validation.
  780. >>> checker = FormatChecker(formats=("date-time", "regex"))
  781. """
  782. checkers = {}
  783. def __init__(self, formats=None):
  784. if formats is None:
  785. self.checkers = self.checkers.copy()
  786. else:
  787. self.checkers = dict((k, self.checkers[k]) for k in formats)
  788. def checks(self, format, raises=()):
  789. """
  790. Register a decorated function as validating a new format.
  791. :argument str format: the format that the decorated function will check
  792. :argument Exception raises: the exception(s) raised by the decorated
  793. function when an invalid instance is found. The exception object
  794. will be accessible as the :attr:`ValidationError.cause` attribute
  795. of the resulting validation error.
  796. """
  797. def _checks(func):
  798. self.checkers[format] = (func, raises)
  799. return func
  800. return _checks
  801. cls_checks = classmethod(checks)
  802. def check(self, instance, format):
  803. """
  804. Check whether the instance conforms to the given format.
  805. :argument instance: the instance to check
  806. :type: any primitive type (str, number, bool)
  807. :argument str format: the format that instance should conform to
  808. :raises: :exc:`FormatError` if instance does not conform to format
  809. """
  810. if format in self.checkers:
  811. func, raises = self.checkers[format]
  812. result, cause = None, None
  813. try:
  814. result = func(instance)
  815. except raises as e:
  816. cause = e
  817. if not result:
  818. raise FormatError(
  819. "%r is not a %r" % (instance, format), cause=cause,
  820. )
  821. def conforms(self, instance, format):
  822. """
  823. Check whether the instance conforms to the given format.
  824. :argument instance: the instance to check
  825. :type: any primitive type (str, number, bool)
  826. :argument str format: the format that instance should conform to
  827. :rtype: bool
  828. """
  829. try:
  830. self.check(instance, format)
  831. except FormatError:
  832. return False
  833. else:
  834. return True
  835. _draft_checkers = {"draft3": [], "draft4": []}
  836. def _checks_drafts(both=None, draft3=None, draft4=None, raises=()):
  837. draft3 = draft3 or both
  838. draft4 = draft4 or both
  839. def wrap(func):
  840. if draft3:
  841. _draft_checkers["draft3"].append(draft3)
  842. func = FormatChecker.cls_checks(draft3, raises)(func)
  843. if draft4:
  844. _draft_checkers["draft4"].append(draft4)
  845. func = FormatChecker.cls_checks(draft4, raises)(func)
  846. return func
  847. return wrap
  848. @_checks_drafts("email")
  849. def is_email(instance):
  850. return "@" in instance
  851. _checks_drafts(draft3="ip-address", draft4="ipv4", raises=socket.error)(
  852. socket.inet_aton
  853. )
  854. if hasattr(socket, "inet_pton"):
  855. @_checks_drafts("ipv6", raises=socket.error)
  856. def is_ipv6(instance):
  857. return socket.inet_pton(socket.AF_INET6, instance)
  858. @_checks_drafts(draft3="host-name", draft4="hostname")
  859. def is_host_name(instance):
  860. pattern = "^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$"
  861. if not re.match(pattern, instance):
  862. return False
  863. components = instance.split(".")
  864. for component in components:
  865. if len(component) > 63:
  866. return False
  867. return True
  868. try:
  869. import rfc3987
  870. except ImportError:
  871. pass
  872. else:
  873. @_checks_drafts("uri", raises=ValueError)
  874. def is_uri(instance):
  875. return rfc3987.parse(instance, rule="URI_reference")
  876. try:
  877. import isodate
  878. except ImportError:
  879. pass
  880. else:
  881. _err = (ValueError, isodate.ISO8601Error)
  882. _checks_drafts("date-time", raises=_err)(isodate.parse_datetime)
  883. _checks_drafts("regex", raises=re.error)(re.compile)
  884. @_checks_drafts(draft3="date", raises=ValueError)
  885. def is_date(instance):
  886. return datetime.datetime.strptime(instance, "%Y-%m-%d")
  887. @_checks_drafts(draft3="time", raises=ValueError)
  888. def is_time(instance):
  889. return datetime.datetime.strptime(instance, "%H:%M:%S")
  890. try:
  891. import webcolors
  892. except ImportError:
  893. pass
  894. else:
  895. def is_css_color_code(instance):
  896. return webcolors.normalize_hex(instance)
  897. @_checks_drafts(draft3="color", raises=(ValueError, TypeError))
  898. def is_css21_color(instance):
  899. if instance.lower() in webcolors.css21_names_to_hex:
  900. return True
  901. return is_css_color_code(instance)
  902. def is_css3_color(instance):
  903. if instance.lower() in webcolors.css3_names_to_hex:
  904. return True
  905. return is_css_color_code(instance)
  906. draft3_format_checker = FormatChecker(_draft_checkers["draft3"])
  907. draft4_format_checker = FormatChecker(_draft_checkers["draft4"])
  908. class RefResolver(object):
  909. """
  910. Resolve JSON References.
  911. :argument str base_uri: URI of the referring document
  912. :argument referrer: the actual referring document
  913. :argument dict store: a mapping from URIs to documents to cache
  914. :argument bool cache_remote: whether remote refs should be cached after
  915. first resolution
  916. :argument dict handlers: a mapping from URI schemes to functions that
  917. should be used to retrieve them
  918. """
  919. def __init__(
  920. self, base_uri, referrer, store=(), cache_remote=True, handlers=(),
  921. ):
  922. self.base_uri = base_uri
  923. self.resolution_scope = base_uri
  924. self.referrer = referrer
  925. self.cache_remote = cache_remote
  926. self.handlers = dict(handlers)
  927. self.store = _URIDict(
  928. (id, validator.META_SCHEMA)
  929. for id, validator in iteritems(meta_schemas)
  930. )
  931. self.store.update(store)
  932. @classmethod
  933. def from_schema(cls, schema, *args, **kwargs):
  934. """
  935. Construct a resolver from a JSON schema object.
  936. :argument schema schema: the referring schema
  937. :rtype: :class:`RefResolver`
  938. """
  939. return cls(schema.get("id", ""), schema, *args, **kwargs)
  940. @contextlib.contextmanager
  941. def in_scope(self, scope):
  942. old_scope = self.resolution_scope
  943. self.resolution_scope = urlparse.urljoin(old_scope, scope)
  944. try:
  945. yield
  946. finally:
  947. self.resolution_scope = old_scope
  948. @contextlib.contextmanager
  949. def resolving(self, ref):
  950. """
  951. Context manager which resolves a JSON ``ref`` and enters the
  952. resolution scope of this ref.
  953. :argument str ref: reference to resolve
  954. """
  955. full_uri = urlparse.urljoin(self.resolution_scope, ref)
  956. uri, fragment = urlparse.urldefrag(full_uri)
  957. if uri in self.store:
  958. document = self.store[uri]
  959. elif not uri or uri == self.base_uri:
  960. document = self.referrer
  961. else:
  962. try:
  963. document = self.resolve_remote(uri)
  964. except Exception as exc:
  965. raise RefResolutionError(exc)
  966. old_base_uri, old_referrer = self.base_uri, self.referrer
  967. self.base_uri, self.referrer = uri, document
  968. try:
  969. with self.in_scope(uri):
  970. yield self.resolve_fragment(document, fragment)
  971. finally:
  972. self.base_uri, self.referrer = old_base_uri, old_referrer
  973. def resolve_fragment(self, document, fragment):
  974. """
  975. Resolve a ``fragment`` within the referenced ``document``.
  976. :argument document: the referrant document
  977. :argument str fragment: a URI fragment to resolve within it
  978. """
  979. fragment = fragment.lstrip("/")
  980. parts = unquote(fragment).split("/") if fragment else []
  981. for part in parts:
  982. part = part.replace("~1", "/").replace("~0", "~")
  983. if part not in document:
  984. raise RefResolutionError(
  985. "Unresolvable JSON pointer: %r" % fragment
  986. )
  987. document = document[part]
  988. return document
  989. def resolve_remote(self, uri):
  990. """
  991. Resolve a remote ``uri``.
  992. Does not check the store first, but stores the retrieved document in
  993. the store if :attr:`RefResolver.cache_remote` is True.
  994. .. note::
  995. If the requests_ library is present, ``jsonschema`` will use it to
  996. request the remote ``uri``, so that the correct encoding is
  997. detected and used.
  998. If it isn't, or if the scheme of the ``uri`` is not ``http`` or
  999. ``https``, UTF-8 is assumed.
  1000. :argument str uri: the URI to resolve
  1001. :returns: the retrieved document
  1002. .. _requests: http://pypi.python.org/pypi/requests/
  1003. """
  1004. scheme = urlparse.urlsplit(uri).scheme
  1005. if scheme in self.handlers:
  1006. result = self.handlers[scheme](uri)
  1007. elif (
  1008. scheme in ["http", "https"] and
  1009. requests and
  1010. getattr(requests.Response, "json", None) is not None
  1011. ):
  1012. # Requests has support for detecting the correct encoding of
  1013. # json over http
  1014. if callable(requests.Response.json):
  1015. result = requests.get(uri).json()
  1016. else:
  1017. result = requests.get(uri).json
  1018. else:
  1019. # Otherwise, pass off to urllib and assume utf-8
  1020. result = json.loads(urlopen(uri).read().decode("utf-8"))
  1021. if self.cache_remote:
  1022. self.store[uri] = result
  1023. return result
  1024. class ErrorTree(object):
  1025. """
  1026. ErrorTrees make it easier to check which validations failed.
  1027. """
  1028. _instance = _unset
  1029. def __init__(self, errors=()):
  1030. self.errors = {}
  1031. self._contents = collections.defaultdict(self.__class__)
  1032. for error in errors:
  1033. container = self
  1034. for element in error.path:
  1035. container = container[element]
  1036. container.errors[error.validator] = error
  1037. self._instance = error.instance
  1038. def __contains__(self, k):
  1039. return k in self._contents
  1040. def __getitem__(self, k):
  1041. """
  1042. Retrieve the child tree with key ``k``.
  1043. If the key is not in the instance that this tree corresponds to and is
  1044. not known by this tree, whatever error would be raised by
  1045. ``instance.__getitem__`` will be propagated (usually this is some
  1046. subclass of :class:`LookupError`.
  1047. """
  1048. if self._instance is not _unset and k not in self:
  1049. self._instance[k]
  1050. return self._contents[k]
  1051. def __setitem__(self, k, v):
  1052. self._contents[k] = v
  1053. def __iter__(self):
  1054. return iter(self._contents)
  1055. def __len__(self):
  1056. return self.total_errors
  1057. def __repr__(self):
  1058. return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
  1059. @property
  1060. def total_errors(self):
  1061. """
  1062. The total number of errors in the entire tree, including children.
  1063. """
  1064. child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
  1065. return len(self.errors) + child_errors
  1066. def _indent(string, times=1):
  1067. """
  1068. A dumb version of :func:`textwrap.indent` from Python 3.3.
  1069. """
  1070. return "\n".join(" " * (4 * times) + line for line in string.splitlines())
  1071. def _format_as_index(indices):
  1072. """
  1073. Construct a single string containing indexing operations for the indices.
  1074. For example, [1, 2, "foo"] -> [1][2]["foo"]
  1075. :type indices: sequence
  1076. """
  1077. if not indices:
  1078. return ""
  1079. return "[%s]" % "][".join(repr(index) for index in indices)
  1080. def _find_additional_properties(instance, schema):
  1081. """
  1082. Return the set of additional properties for the given ``instance``.
  1083. Weeds out properties that should have been validated by ``properties`` and
  1084. / or ``patternProperties``.
  1085. Assumes ``instance`` is dict-like already.
  1086. """
  1087. properties = schema.get("properties", {})
  1088. patterns = "|".join(schema.get("patternProperties", {}))
  1089. for property in instance:
  1090. if property not in properties:
  1091. if patterns and re.search(patterns, property):
  1092. continue
  1093. yield property
  1094. def _extras_msg(extras):
  1095. """
  1096. Create an error message for extra items or properties.
  1097. """
  1098. if len(extras) == 1:
  1099. verb = "was"
  1100. else:
  1101. verb = "were"
  1102. return ", ".join(repr(extra) for extra in extras), verb
  1103. def _types_msg(instance, types):
  1104. """
  1105. Create an error message for a failure to match the given types.
  1106. If the ``instance`` is an object and contains a ``name`` property, it will
  1107. be considered to be a description of that object and used as its type.
  1108. Otherwise the message is simply the reprs of the given ``types``.
  1109. """
  1110. reprs = []
  1111. for type in types:
  1112. try:
  1113. reprs.append(repr(type["name"]))
  1114. except Exception:
  1115. reprs.append(repr(type))
  1116. return "%r is not of type %s" % (instance, ", ".join(reprs))
  1117. def _flatten(suitable_for_isinstance):
  1118. """
  1119. isinstance() can accept a bunch of really annoying different types:
  1120. * a single type
  1121. * a tuple of types
  1122. * an arbitrary nested tree of tuples
  1123. Return a flattened tuple of the given argument.
  1124. """
  1125. types = set()
  1126. if not isinstance(suitable_for_isinstance, tuple):
  1127. suitable_for_isinstance = (suitable_for_isinstance,)
  1128. for thing in suitable_for_isinstance:
  1129. if isinstance(thing, tuple):
  1130. types.update(_flatten(thing))
  1131. else:
  1132. types.add(thing)
  1133. return tuple(types)
  1134. def _list(thing):
  1135. """
  1136. Wrap ``thing`` in a list if it's a single str.
  1137. Otherwise, return it unchanged.
  1138. """
  1139. if isinstance(thing, basestring):
  1140. return [thing]
  1141. return thing
  1142. def _unbool(element, true=object(), false=object()):
  1143. """
  1144. A hack to make True and 1 and False and 0 unique for _uniq.
  1145. """
  1146. if element is True:
  1147. return true
  1148. elif element is False:
  1149. return false
  1150. return element
  1151. def _uniq(container):
  1152. """
  1153. Check if all of a container's elements are unique.
  1154. Successively tries first to rely that the elements are hashable, then
  1155. falls back on them being sortable, and finally falls back on brute
  1156. force.
  1157. """
  1158. try:
  1159. return len(set(_unbool(i) for i in container)) == len(container)
  1160. except TypeError:
  1161. try:
  1162. sort = sorted(_unbool(i) for i in container)
  1163. sliced = itertools.islice(sort, 1, None)
  1164. for i, j in zip(sort, sliced):
  1165. if i == j:
  1166. return False
  1167. except (NotImplementedError, TypeError):
  1168. seen = []
  1169. for e in container:
  1170. e = _unbool(e)
  1171. if e in seen:
  1172. return False
  1173. seen.append(e)
  1174. return True
  1175. def validate(instance, schema, cls=None, *args, **kwargs):
  1176. if cls is None:
  1177. cls = meta_schemas.get(schema.get("$schema", ""), Draft4Validator)
  1178. cls.check_schema(schema)
  1179. cls(schema, *args, **kwargs).validate(instance)