1""" 2Creation and extension of validators, with implementations for existing drafts. 3""" 4from __future__ import division 5 6from warnings import warn 7import contextlib 8import json 9import numbers 10 11from six import add_metaclass 12 13from jsonschema import ( 14 _legacy_validators, 15 _types, 16 _utils, 17 _validators, 18 exceptions, 19) 20from jsonschema.compat import ( 21 Sequence, 22 int_types, 23 iteritems, 24 lru_cache, 25 str_types, 26 unquote, 27 urldefrag, 28 urljoin, 29 urlopen, 30 urlsplit, 31) 32 33# Sigh. https://gitlab.com/pycqa/flake8/issues/280 34# https://github.com/pyga/ebb-lint/issues/7 35# Imported for backwards compatibility. 36from jsonschema.exceptions import ErrorTree 37ErrorTree 38 39 40class _DontDoThat(Exception): 41 """ 42 Raised when a Validators with non-default type checker is misused. 43 44 Asking one for DEFAULT_TYPES doesn't make sense, since type checkers 45 exist for the unrepresentable cases where DEFAULT_TYPES can't 46 represent the type relationship. 47 """ 48 49 def __str__(self): 50 return "DEFAULT_TYPES cannot be used on Validators using TypeCheckers" 51 52 53validators = {} 54meta_schemas = _utils.URIDict() 55 56 57def _generate_legacy_type_checks(types=()): 58 """ 59 Generate newer-style type checks out of JSON-type-name-to-type mappings. 60 61 Arguments: 62 63 types (dict): 64 65 A mapping of type names to their Python types 66 67 Returns: 68 69 A dictionary of definitions to pass to `TypeChecker` 70 """ 71 types = dict(types) 72 73 def gen_type_check(pytypes): 74 pytypes = _utils.flatten(pytypes) 75 76 def type_check(checker, instance): 77 if isinstance(instance, bool): 78 if bool not in pytypes: 79 return False 80 return isinstance(instance, pytypes) 81 82 return type_check 83 84 definitions = {} 85 for typename, pytypes in iteritems(types): 86 definitions[typename] = gen_type_check(pytypes) 87 88 return definitions 89 90 91_DEPRECATED_DEFAULT_TYPES = { 92 u"array": list, 93 u"boolean": bool, 94 u"integer": int_types, 95 u"null": type(None), 96 u"number": numbers.Number, 97 u"object": dict, 98 u"string": str_types, 99} 100_TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES = _types.TypeChecker( 101 type_checkers=_generate_legacy_type_checks(_DEPRECATED_DEFAULT_TYPES), 102) 103 104 105def validates(version): 106 """ 107 Register the decorated validator for a ``version`` of the specification. 108 109 Registered validators and their meta schemas will be considered when 110 parsing ``$schema`` properties' URIs. 111 112 Arguments: 113 114 version (str): 115 116 An identifier to use as the version's name 117 118 Returns: 119 120 collections.Callable: 121 122 a class decorator to decorate the validator with the version 123 """ 124 125 def _validates(cls): 126 validators[version] = cls 127 meta_schema_id = cls.ID_OF(cls.META_SCHEMA) 128 if meta_schema_id: 129 meta_schemas[meta_schema_id] = cls 130 return cls 131 return _validates 132 133 134def _DEFAULT_TYPES(self): 135 if self._CREATED_WITH_DEFAULT_TYPES is None: 136 raise _DontDoThat() 137 138 warn( 139 ( 140 "The DEFAULT_TYPES attribute is deprecated. " 141 "See the type checker attached to this validator instead." 142 ), 143 DeprecationWarning, 144 stacklevel=2, 145 ) 146 return self._DEFAULT_TYPES 147 148 149class _DefaultTypesDeprecatingMetaClass(type): 150 DEFAULT_TYPES = property(_DEFAULT_TYPES) 151 152 153def _id_of(schema): 154 if schema is True or schema is False: 155 return u"" 156 return schema.get(u"$id", u"") 157 158 159def create( 160 meta_schema, 161 validators=(), 162 version=None, 163 default_types=None, 164 type_checker=None, 165 id_of=_id_of, 166): 167 """ 168 Create a new validator class. 169 170 Arguments: 171 172 meta_schema (collections.Mapping): 173 174 the meta schema for the new validator class 175 176 validators (collections.Mapping): 177 178 a mapping from names to callables, where each callable will 179 validate the schema property with the given name. 180 181 Each callable should take 4 arguments: 182 183 1. a validator instance, 184 2. the value of the property being validated within the 185 instance 186 3. the instance 187 4. the schema 188 189 version (str): 190 191 an identifier for the version that this validator class will 192 validate. If provided, the returned validator class will 193 have its ``__name__`` set to include the version, and also 194 will have `jsonschema.validators.validates` automatically 195 called for the given version. 196 197 type_checker (jsonschema.TypeChecker): 198 199 a type checker, used when applying the :validator:`type` validator. 200 201 If unprovided, a `jsonschema.TypeChecker` will be created 202 with a set of default types typical of JSON Schema drafts. 203 204 default_types (collections.Mapping): 205 206 .. deprecated:: 3.0.0 207 208 Please use the type_checker argument instead. 209 210 If set, it provides mappings of JSON types to Python types 211 that will be converted to functions and redefined in this 212 object's `jsonschema.TypeChecker`. 213 214 id_of (collections.Callable): 215 216 A function that given a schema, returns its ID. 217 218 Returns: 219 220 a new `jsonschema.IValidator` class 221 """ 222 223 if default_types is not None: 224 if type_checker is not None: 225 raise TypeError( 226 "Do not specify default_types when providing a type checker.", 227 ) 228 _created_with_default_types = True 229 warn( 230 ( 231 "The default_types argument is deprecated. " 232 "Use the type_checker argument instead." 233 ), 234 DeprecationWarning, 235 stacklevel=2, 236 ) 237 type_checker = _types.TypeChecker( 238 type_checkers=_generate_legacy_type_checks(default_types), 239 ) 240 else: 241 default_types = _DEPRECATED_DEFAULT_TYPES 242 if type_checker is None: 243 _created_with_default_types = False 244 type_checker = _TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES 245 elif type_checker is _TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES: 246 _created_with_default_types = False 247 else: 248 _created_with_default_types = None 249 250 @add_metaclass(_DefaultTypesDeprecatingMetaClass) 251 class Validator(object): 252 253 VALIDATORS = dict(validators) 254 META_SCHEMA = dict(meta_schema) 255 TYPE_CHECKER = type_checker 256 ID_OF = staticmethod(id_of) 257 258 DEFAULT_TYPES = property(_DEFAULT_TYPES) 259 _DEFAULT_TYPES = dict(default_types) 260 _CREATED_WITH_DEFAULT_TYPES = _created_with_default_types 261 262 def __init__( 263 self, 264 schema, 265 types=(), 266 resolver=None, 267 format_checker=None, 268 ): 269 if types: 270 warn( 271 ( 272 "The types argument is deprecated. Provide " 273 "a type_checker to jsonschema.validators.extend " 274 "instead." 275 ), 276 DeprecationWarning, 277 stacklevel=2, 278 ) 279 280 self.TYPE_CHECKER = self.TYPE_CHECKER.redefine_many( 281 _generate_legacy_type_checks(types), 282 ) 283 284 if resolver is None: 285 resolver = RefResolver.from_schema(schema, id_of=id_of) 286 287 self.resolver = resolver 288 self.format_checker = format_checker 289 self.schema = schema 290 291 @classmethod 292 def check_schema(cls, schema): 293 for error in cls(cls.META_SCHEMA).iter_errors(schema): 294 raise exceptions.SchemaError.create_from(error) 295 296 def iter_errors(self, instance, _schema=None): 297 if _schema is None: 298 _schema = self.schema 299 300 if _schema is True: 301 return 302 elif _schema is False: 303 yield exceptions.ValidationError( 304 "False schema does not allow %r" % (instance,), 305 validator=None, 306 validator_value=None, 307 instance=instance, 308 schema=_schema, 309 ) 310 return 311 312 scope = id_of(_schema) 313 if scope: 314 self.resolver.push_scope(scope) 315 try: 316 ref = _schema.get(u"$ref") 317 if ref is not None: 318 validators = [(u"$ref", ref)] 319 else: 320 validators = iteritems(_schema) 321 322 for k, v in validators: 323 validator = self.VALIDATORS.get(k) 324 if validator is None: 325 continue 326 327 errors = validator(self, v, instance, _schema) or () 328 for error in errors: 329 # set details if not already set by the called fn 330 error._set( 331 validator=k, 332 validator_value=v, 333 instance=instance, 334 schema=_schema, 335 ) 336 if k != u"$ref": 337 error.schema_path.appendleft(k) 338 yield error 339 finally: 340 if scope: 341 self.resolver.pop_scope() 342 343 def descend(self, instance, schema, path=None, schema_path=None): 344 for error in self.iter_errors(instance, schema): 345 if path is not None: 346 error.path.appendleft(path) 347 if schema_path is not None: 348 error.schema_path.appendleft(schema_path) 349 yield error 350 351 def validate(self, *args, **kwargs): 352 for error in self.iter_errors(*args, **kwargs): 353 raise error 354 355 def is_type(self, instance, type): 356 try: 357 return self.TYPE_CHECKER.is_type(instance, type) 358 except exceptions.UndefinedTypeCheck: 359 raise exceptions.UnknownType(type, instance, self.schema) 360 361 def is_valid(self, instance, _schema=None): 362 error = next(self.iter_errors(instance, _schema), None) 363 return error is None 364 365 if version is not None: 366 Validator = validates(version)(Validator) 367 Validator.__name__ = version.title().replace(" ", "") + "Validator" 368 369 return Validator 370 371 372def extend(validator, validators=(), version=None, type_checker=None): 373 """ 374 Create a new validator class by extending an existing one. 375 376 Arguments: 377 378 validator (jsonschema.IValidator): 379 380 an existing validator class 381 382 validators (collections.Mapping): 383 384 a mapping of new validator callables to extend with, whose 385 structure is as in `create`. 386 387 .. note:: 388 389 Any validator callables with the same name as an 390 existing one will (silently) replace the old validator 391 callable entirely, effectively overriding any validation 392 done in the "parent" validator class. 393 394 If you wish to instead extend the behavior of a parent's 395 validator callable, delegate and call it directly in 396 the new validator function by retrieving it using 397 ``OldValidator.VALIDATORS["validator_name"]``. 398 399 version (str): 400 401 a version for the new validator class 402 403 type_checker (jsonschema.TypeChecker): 404 405 a type checker, used when applying the :validator:`type` validator. 406 407 If unprovided, the type checker of the extended 408 `jsonschema.IValidator` will be carried along.` 409 410 Returns: 411 412 a new `jsonschema.IValidator` class extending the one provided 413 414 .. note:: Meta Schemas 415 416 The new validator class will have its parent's meta schema. 417 418 If you wish to change or extend the meta schema in the new 419 validator class, modify ``META_SCHEMA`` directly on the returned 420 class. Note that no implicit copying is done, so a copy should 421 likely be made before modifying it, in order to not affect the 422 old validator. 423 """ 424 425 all_validators = dict(validator.VALIDATORS) 426 all_validators.update(validators) 427 428 if type_checker is None: 429 type_checker = validator.TYPE_CHECKER 430 elif validator._CREATED_WITH_DEFAULT_TYPES: 431 raise TypeError( 432 "Cannot extend a validator created with default_types " 433 "with a type_checker. Update the validator to use a " 434 "type_checker when created." 435 ) 436 return create( 437 meta_schema=validator.META_SCHEMA, 438 validators=all_validators, 439 version=version, 440 type_checker=type_checker, 441 id_of=validator.ID_OF, 442 ) 443 444 445Draft3Validator = create( 446 meta_schema=_utils.load_schema("draft3"), 447 validators={ 448 u"$ref": _validators.ref, 449 u"additionalItems": _validators.additionalItems, 450 u"additionalProperties": _validators.additionalProperties, 451 u"dependencies": _legacy_validators.dependencies_draft3, 452 u"disallow": _legacy_validators.disallow_draft3, 453 u"divisibleBy": _validators.multipleOf, 454 u"enum": _validators.enum, 455 u"extends": _legacy_validators.extends_draft3, 456 u"format": _validators.format, 457 u"items": _legacy_validators.items_draft3_draft4, 458 u"maxItems": _validators.maxItems, 459 u"maxLength": _validators.maxLength, 460 u"maximum": _legacy_validators.maximum_draft3_draft4, 461 u"minItems": _validators.minItems, 462 u"minLength": _validators.minLength, 463 u"minimum": _legacy_validators.minimum_draft3_draft4, 464 u"pattern": _validators.pattern, 465 u"patternProperties": _validators.patternProperties, 466 u"properties": _legacy_validators.properties_draft3, 467 u"type": _legacy_validators.type_draft3, 468 u"uniqueItems": _validators.uniqueItems, 469 }, 470 type_checker=_types.draft3_type_checker, 471 version="draft3", 472 id_of=lambda schema: schema.get(u"id", ""), 473) 474 475Draft4Validator = create( 476 meta_schema=_utils.load_schema("draft4"), 477 validators={ 478 u"$ref": _validators.ref, 479 u"additionalItems": _validators.additionalItems, 480 u"additionalProperties": _validators.additionalProperties, 481 u"allOf": _validators.allOf, 482 u"anyOf": _validators.anyOf, 483 u"dependencies": _validators.dependencies, 484 u"enum": _validators.enum, 485 u"format": _validators.format, 486 u"items": _legacy_validators.items_draft3_draft4, 487 u"maxItems": _validators.maxItems, 488 u"maxLength": _validators.maxLength, 489 u"maxProperties": _validators.maxProperties, 490 u"maximum": _legacy_validators.maximum_draft3_draft4, 491 u"minItems": _validators.minItems, 492 u"minLength": _validators.minLength, 493 u"minProperties": _validators.minProperties, 494 u"minimum": _legacy_validators.minimum_draft3_draft4, 495 u"multipleOf": _validators.multipleOf, 496 u"not": _validators.not_, 497 u"oneOf": _validators.oneOf, 498 u"pattern": _validators.pattern, 499 u"patternProperties": _validators.patternProperties, 500 u"properties": _validators.properties, 501 u"required": _validators.required, 502 u"type": _validators.type, 503 u"uniqueItems": _validators.uniqueItems, 504 }, 505 type_checker=_types.draft4_type_checker, 506 version="draft4", 507 id_of=lambda schema: schema.get(u"id", ""), 508) 509 510Draft6Validator = create( 511 meta_schema=_utils.load_schema("draft6"), 512 validators={ 513 u"$ref": _validators.ref, 514 u"additionalItems": _validators.additionalItems, 515 u"additionalProperties": _validators.additionalProperties, 516 u"allOf": _validators.allOf, 517 u"anyOf": _validators.anyOf, 518 u"const": _validators.const, 519 u"contains": _validators.contains, 520 u"dependencies": _validators.dependencies, 521 u"enum": _validators.enum, 522 u"exclusiveMaximum": _validators.exclusiveMaximum, 523 u"exclusiveMinimum": _validators.exclusiveMinimum, 524 u"format": _validators.format, 525 u"items": _validators.items, 526 u"maxItems": _validators.maxItems, 527 u"maxLength": _validators.maxLength, 528 u"maxProperties": _validators.maxProperties, 529 u"maximum": _validators.maximum, 530 u"minItems": _validators.minItems, 531 u"minLength": _validators.minLength, 532 u"minProperties": _validators.minProperties, 533 u"minimum": _validators.minimum, 534 u"multipleOf": _validators.multipleOf, 535 u"not": _validators.not_, 536 u"oneOf": _validators.oneOf, 537 u"pattern": _validators.pattern, 538 u"patternProperties": _validators.patternProperties, 539 u"properties": _validators.properties, 540 u"propertyNames": _validators.propertyNames, 541 u"required": _validators.required, 542 u"type": _validators.type, 543 u"uniqueItems": _validators.uniqueItems, 544 }, 545 type_checker=_types.draft6_type_checker, 546 version="draft6", 547) 548 549Draft7Validator = create( 550 meta_schema=_utils.load_schema("draft7"), 551 validators={ 552 u"$ref": _validators.ref, 553 u"additionalItems": _validators.additionalItems, 554 u"additionalProperties": _validators.additionalProperties, 555 u"allOf": _validators.allOf, 556 u"anyOf": _validators.anyOf, 557 u"const": _validators.const, 558 u"contains": _validators.contains, 559 u"dependencies": _validators.dependencies, 560 u"enum": _validators.enum, 561 u"exclusiveMaximum": _validators.exclusiveMaximum, 562 u"exclusiveMinimum": _validators.exclusiveMinimum, 563 u"format": _validators.format, 564 u"if": _validators.if_, 565 u"items": _validators.items, 566 u"maxItems": _validators.maxItems, 567 u"maxLength": _validators.maxLength, 568 u"maxProperties": _validators.maxProperties, 569 u"maximum": _validators.maximum, 570 u"minItems": _validators.minItems, 571 u"minLength": _validators.minLength, 572 u"minProperties": _validators.minProperties, 573 u"minimum": _validators.minimum, 574 u"multipleOf": _validators.multipleOf, 575 u"oneOf": _validators.oneOf, 576 u"not": _validators.not_, 577 u"pattern": _validators.pattern, 578 u"patternProperties": _validators.patternProperties, 579 u"properties": _validators.properties, 580 u"propertyNames": _validators.propertyNames, 581 u"required": _validators.required, 582 u"type": _validators.type, 583 u"uniqueItems": _validators.uniqueItems, 584 }, 585 type_checker=_types.draft7_type_checker, 586 version="draft7", 587) 588 589_LATEST_VERSION = Draft7Validator 590 591 592class RefResolver(object): 593 """ 594 Resolve JSON References. 595 596 Arguments: 597 598 base_uri (str): 599 600 The URI of the referring document 601 602 referrer: 603 604 The actual referring document 605 606 store (dict): 607 608 A mapping from URIs to documents to cache 609 610 cache_remote (bool): 611 612 Whether remote refs should be cached after first resolution 613 614 handlers (dict): 615 616 A mapping from URI schemes to functions that should be used 617 to retrieve them 618 619 urljoin_cache (:func:`functools.lru_cache`): 620 621 A cache that will be used for caching the results of joining 622 the resolution scope to subscopes. 623 624 remote_cache (:func:`functools.lru_cache`): 625 626 A cache that will be used for caching the results of 627 resolved remote URLs. 628 629 Attributes: 630 631 cache_remote (bool): 632 633 Whether remote refs should be cached after first resolution 634 """ 635 636 def __init__( 637 self, 638 base_uri, 639 referrer, 640 store=(), 641 cache_remote=True, 642 handlers=(), 643 urljoin_cache=None, 644 remote_cache=None, 645 ): 646 if urljoin_cache is None: 647 urljoin_cache = lru_cache(1024)(urljoin) 648 if remote_cache is None: 649 remote_cache = lru_cache(1024)(self.resolve_from_url) 650 651 self.referrer = referrer 652 self.cache_remote = cache_remote 653 self.handlers = dict(handlers) 654 655 self._scopes_stack = [base_uri] 656 self.store = _utils.URIDict( 657 (id, validator.META_SCHEMA) 658 for id, validator in iteritems(meta_schemas) 659 ) 660 self.store.update(store) 661 self.store[base_uri] = referrer 662 663 self._urljoin_cache = urljoin_cache 664 self._remote_cache = remote_cache 665 666 @classmethod 667 def from_schema(cls, schema, id_of=_id_of, *args, **kwargs): 668 """ 669 Construct a resolver from a JSON schema object. 670 671 Arguments: 672 673 schema: 674 675 the referring schema 676 677 Returns: 678 679 `RefResolver` 680 """ 681 682 return cls(base_uri=id_of(schema), referrer=schema, *args, **kwargs) 683 684 def push_scope(self, scope): 685 """ 686 Enter a given sub-scope. 687 688 Treats further dereferences as being performed underneath the 689 given scope. 690 """ 691 self._scopes_stack.append( 692 self._urljoin_cache(self.resolution_scope, scope), 693 ) 694 695 def pop_scope(self): 696 """ 697 Exit the most recent entered scope. 698 699 Treats further dereferences as being performed underneath the 700 original scope. 701 702 Don't call this method more times than `push_scope` has been 703 called. 704 """ 705 try: 706 self._scopes_stack.pop() 707 except IndexError: 708 raise exceptions.RefResolutionError( 709 "Failed to pop the scope from an empty stack. " 710 "`pop_scope()` should only be called once for every " 711 "`push_scope()`" 712 ) 713 714 @property 715 def resolution_scope(self): 716 """ 717 Retrieve the current resolution scope. 718 """ 719 return self._scopes_stack[-1] 720 721 @property 722 def base_uri(self): 723 """ 724 Retrieve the current base URI, not including any fragment. 725 """ 726 uri, _ = urldefrag(self.resolution_scope) 727 return uri 728 729 @contextlib.contextmanager 730 def in_scope(self, scope): 731 """ 732 Temporarily enter the given scope for the duration of the context. 733 """ 734 self.push_scope(scope) 735 try: 736 yield 737 finally: 738 self.pop_scope() 739 740 @contextlib.contextmanager 741 def resolving(self, ref): 742 """ 743 Resolve the given ``ref`` and enter its resolution scope. 744 745 Exits the scope on exit of this context manager. 746 747 Arguments: 748 749 ref (str): 750 751 The reference to resolve 752 """ 753 754 url, resolved = self.resolve(ref) 755 self.push_scope(url) 756 try: 757 yield resolved 758 finally: 759 self.pop_scope() 760 761 def resolve(self, ref): 762 """ 763 Resolve the given reference. 764 """ 765 url = self._urljoin_cache(self.resolution_scope, ref) 766 return url, self._remote_cache(url) 767 768 def resolve_from_url(self, url): 769 """ 770 Resolve the given remote URL. 771 """ 772 url, fragment = urldefrag(url) 773 try: 774 document = self.store[url] 775 except KeyError: 776 try: 777 document = self.resolve_remote(url) 778 except Exception as exc: 779 raise exceptions.RefResolutionError(exc) 780 781 return self.resolve_fragment(document, fragment) 782 783 def resolve_fragment(self, document, fragment): 784 """ 785 Resolve a ``fragment`` within the referenced ``document``. 786 787 Arguments: 788 789 document: 790 791 The referent document 792 793 fragment (str): 794 795 a URI fragment to resolve within it 796 """ 797 798 fragment = fragment.lstrip(u"/") 799 parts = unquote(fragment).split(u"/") if fragment else [] 800 801 for part in parts: 802 part = part.replace(u"~1", u"/").replace(u"~0", u"~") 803 804 if isinstance(document, Sequence): 805 # Array indexes should be turned into integers 806 try: 807 part = int(part) 808 except ValueError: 809 pass 810 try: 811 document = document[part] 812 except (TypeError, LookupError): 813 raise exceptions.RefResolutionError( 814 "Unresolvable JSON pointer: %r" % fragment 815 ) 816 817 return document 818 819 def resolve_remote(self, uri): 820 """ 821 Resolve a remote ``uri``. 822 823 If called directly, does not check the store first, but after 824 retrieving the document at the specified URI it will be saved in 825 the store if :attr:`cache_remote` is True. 826 827 .. note:: 828 829 If the requests_ library is present, ``jsonschema`` will use it to 830 request the remote ``uri``, so that the correct encoding is 831 detected and used. 832 833 If it isn't, or if the scheme of the ``uri`` is not ``http`` or 834 ``https``, UTF-8 is assumed. 835 836 Arguments: 837 838 uri (str): 839 840 The URI to resolve 841 842 Returns: 843 844 The retrieved document 845 846 .. _requests: https://pypi.org/project/requests/ 847 """ 848 try: 849 import requests 850 except ImportError: 851 requests = None 852 853 scheme = urlsplit(uri).scheme 854 855 if scheme in self.handlers: 856 result = self.handlers[scheme](uri) 857 elif scheme in [u"http", u"https"] and requests: 858 # Requests has support for detecting the correct encoding of 859 # json over http 860 result = requests.get(uri).json() 861 else: 862 # Otherwise, pass off to urllib and assume utf-8 863 with urlopen(uri) as url: 864 result = json.loads(url.read().decode("utf-8")) 865 866 if self.cache_remote: 867 self.store[uri] = result 868 return result 869 870 871def validate(instance, schema, cls=None, *args, **kwargs): 872 """ 873 Validate an instance under the given schema. 874 875 >>> validate([2, 3, 4], {"maxItems": 2}) 876 Traceback (most recent call last): 877 ... 878 ValidationError: [2, 3, 4] is too long 879 880 :func:`validate` will first verify that the provided schema is 881 itself valid, since not doing so can lead to less obvious error 882 messages and fail in less obvious or consistent ways. 883 884 If you know you have a valid schema already, especially if you 885 intend to validate multiple instances with the same schema, you 886 likely would prefer using the `IValidator.validate` method directly 887 on a specific validator (e.g. ``Draft7Validator.validate``). 888 889 890 Arguments: 891 892 instance: 893 894 The instance to validate 895 896 schema: 897 898 The schema to validate with 899 900 cls (IValidator): 901 902 The class that will be used to validate the instance. 903 904 If the ``cls`` argument is not provided, two things will happen 905 in accordance with the specification. First, if the schema has a 906 :validator:`$schema` property containing a known meta-schema [#]_ 907 then the proper validator will be used. The specification recommends 908 that all schemas contain :validator:`$schema` properties for this 909 reason. If no :validator:`$schema` property is found, the default 910 validator class is the latest released draft. 911 912 Any other provided positional and keyword arguments will be passed 913 on when instantiating the ``cls``. 914 915 Raises: 916 917 `jsonschema.exceptions.ValidationError` if the instance 918 is invalid 919 920 `jsonschema.exceptions.SchemaError` if the schema itself 921 is invalid 922 923 .. rubric:: Footnotes 924 .. [#] known by a validator registered with 925 `jsonschema.validators.validates` 926 """ 927 if cls is None: 928 cls = validator_for(schema) 929 930 cls.check_schema(schema) 931 validator = cls(schema, *args, **kwargs) 932 error = exceptions.best_match(validator.iter_errors(instance)) 933 if error is not None: 934 raise error 935 936 937def validator_for(schema, default=_LATEST_VERSION): 938 """ 939 Retrieve the validator class appropriate for validating the given schema. 940 941 Uses the :validator:`$schema` property that should be present in the 942 given schema to look up the appropriate validator class. 943 944 Arguments: 945 946 schema (collections.Mapping or bool): 947 948 the schema to look at 949 950 default: 951 952 the default to return if the appropriate validator class 953 cannot be determined. 954 955 If unprovided, the default is to return the latest supported 956 draft. 957 """ 958 if schema is True or schema is False or u"$schema" not in schema: 959 return default 960 if schema[u"$schema"] not in meta_schemas: 961 warn( 962 ( 963 "The metaschema specified by $schema was not found. " 964 "Using the latest draft to validate, but this will raise " 965 "an error in the future." 966 ), 967 DeprecationWarning, 968 stacklevel=2, 969 ) 970 return meta_schemas.get(schema[u"$schema"], _LATEST_VERSION) 971