|
| 1 | +""" |
| 2 | +typing.Protocol classes for jsonschema interfaces. |
| 3 | +""" |
| 4 | + |
| 5 | +# for reference material on Protocols, see |
| 6 | +# https://www.python.org/dev/peps/pep-0544/ |
| 7 | + |
| 8 | +from typing import Any, ClassVar, Iterator, Optional |
| 9 | + |
| 10 | +try: |
| 11 | + from typing import Protocol, runtime_checkable |
| 12 | +except ImportError: |
| 13 | + from typing_extensions import Protocol, runtime_checkable |
| 14 | + |
| 15 | +from ._format import FormatChecker |
| 16 | +from ._types import TypeChecker |
| 17 | +from .exceptions import ValidationError |
| 18 | +from .validators import RefResolver |
| 19 | + |
| 20 | +# For code authors working on the validator protocol, these are the three |
| 21 | +# use-cases which should be kept in mind: |
| 22 | +# |
| 23 | +# 1. As a protocol class, it can be used in type annotations to describe the |
| 24 | +# available methods and attributes of a validator |
| 25 | +# 2. It is the source of autodoc for the validator documentation |
| 26 | +# 3. It is runtime_checkable, meaning that it can be used in isinstance() |
| 27 | +# checks. |
| 28 | +# |
| 29 | +# Since protocols are not base classes, isinstance() checking is limited in |
| 30 | +# its capabilities. See docs on runtime_checkable for detail |
| 31 | + |
| 32 | + |
| 33 | +@runtime_checkable |
| 34 | +class IValidator(Protocol): |
| 35 | + """ |
| 36 | + The protocol to which all validator classes should adhere. |
| 37 | +
|
| 38 | + :argument dict schema: the schema that the validator object |
| 39 | + will validate with. It is assumed to be valid, and providing |
| 40 | + an invalid schema can lead to undefined behavior. See |
| 41 | + `IValidator.check_schema` to validate a schema first. |
| 42 | + :argument resolver: an instance of `jsonschema.RefResolver` that will be |
| 43 | + used to resolve :validator:`$ref` properties (JSON references). If |
| 44 | + unprovided, one will be created. |
| 45 | + :argument format_checker: an instance of `jsonschema.FormatChecker` |
| 46 | + whose `jsonschema.FormatChecker.conforms` method will be called to |
| 47 | + check and see if instances conform to each :validator:`format` |
| 48 | + property present in the schema. If unprovided, no validation |
| 49 | + will be done for :validator:`format`. Certain formats require |
| 50 | + additional packages to be installed (ipv5, uri, color, date-time). |
| 51 | + The required packages can be found at the bottom of this page. |
| 52 | + """ |
| 53 | + |
| 54 | + #: An object representing the validator's meta schema (the schema that |
| 55 | + #: describes valid schemas in the given version). |
| 56 | + META_SCHEMA: ClassVar[dict] |
| 57 | + |
| 58 | + #: A mapping of validator names (`str`\s) to functions |
| 59 | + #: that validate the validator property with that name. For more |
| 60 | + #: information see `creating-validators`. |
| 61 | + VALIDATORS: ClassVar[dict] |
| 62 | + |
| 63 | + #: A `jsonschema.TypeChecker` that will be used when validating |
| 64 | + #: :validator:`type` properties in JSON schemas. |
| 65 | + TYPE_CHECKER: ClassVar[TypeChecker] |
| 66 | + |
| 67 | + #: The schema that was passed in when initializing the object. |
| 68 | + schema: dict |
| 69 | + |
| 70 | + def __init__( |
| 71 | + self, |
| 72 | + schema: dict, |
| 73 | + resolver: Optional[RefResolver] = None, |
| 74 | + format_checker: Optional[FormatChecker] = None, |
| 75 | + ) -> None: |
| 76 | + ... |
| 77 | + |
| 78 | + @classmethod |
| 79 | + def check_schema(cls, schema: dict) -> None: |
| 80 | + """ |
| 81 | + Validate the given schema against the validator's `META_SCHEMA`. |
| 82 | +
|
| 83 | + :raises: `jsonschema.exceptions.SchemaError` if the schema |
| 84 | + is invalid |
| 85 | + """ |
| 86 | + |
| 87 | + def is_type(self, instance: Any, type: str) -> bool: |
| 88 | + """ |
| 89 | + Check if the instance is of the given (JSON Schema) type. |
| 90 | +
|
| 91 | + :type type: str |
| 92 | + :rtype: bool |
| 93 | + :raises: `jsonschema.exceptions.UnknownType` if ``type`` |
| 94 | + is not a known type. |
| 95 | + """ |
| 96 | + |
| 97 | + def is_valid(self, instance: dict) -> bool: |
| 98 | + """ |
| 99 | + Check if the instance is valid under the current `schema`. |
| 100 | +
|
| 101 | + :rtype: bool |
| 102 | +
|
| 103 | + >>> schema = {"maxItems" : 2} |
| 104 | + >>> Draft3Validator(schema).is_valid([2, 3, 4]) |
| 105 | + False |
| 106 | + """ |
| 107 | + |
| 108 | + def iter_errors(self, instance: dict) -> Iterator[ValidationError]: |
| 109 | + r""" |
| 110 | + Lazily yield each of the validation errors in the given instance. |
| 111 | +
|
| 112 | + :rtype: an `collections.abc.Iterable` of |
| 113 | + `jsonschema.exceptions.ValidationError`\s |
| 114 | +
|
| 115 | + >>> schema = { |
| 116 | + ... "type" : "array", |
| 117 | + ... "items" : {"enum" : [1, 2, 3]}, |
| 118 | + ... "maxItems" : 2, |
| 119 | + ... } |
| 120 | + >>> v = Draft3Validator(schema) |
| 121 | + >>> for error in sorted(v.iter_errors([2, 3, 4]), key=str): |
| 122 | + ... print(error.message) |
| 123 | + 4 is not one of [1, 2, 3] |
| 124 | + [2, 3, 4] is too long |
| 125 | + """ |
| 126 | + |
| 127 | + def validate(self, instance: dict) -> None: |
| 128 | + """ |
| 129 | + Check if the instance is valid under the current `schema`. |
| 130 | +
|
| 131 | + :raises: `jsonschema.exceptions.ValidationError` if the |
| 132 | + instance is invalid |
| 133 | +
|
| 134 | + >>> schema = {"maxItems" : 2} |
| 135 | + >>> Draft3Validator(schema).validate([2, 3, 4]) |
| 136 | + Traceback (most recent call last): |
| 137 | + ... |
| 138 | + ValidationError: [2, 3, 4] is too long |
| 139 | + """ |
| 140 | + |
| 141 | + def evolve(self, **kwargs) -> "IValidator": |
| 142 | + """ |
| 143 | + Create a new validator like this one, but with given changes. |
| 144 | +
|
| 145 | + Preserves all other attributes, so can be used to e.g. create a |
| 146 | + validator with a different schema but with the same :validator:`$ref` |
| 147 | + resolution behavior. |
| 148 | +
|
| 149 | + >>> validator = Draft202012Validator({}) |
| 150 | + >>> validator.evolve(schema={"type": "number"}) |
| 151 | + Draft202012Validator(schema={'type': 'number'}, format_checker=None) |
| 152 | + """ |
0 commit comments