Skip to content

Commit b2c5339

Browse files
authored
Merge pull request #202 from python-openapi/feature/jsonschem-4.18-compatibility
jsonschema 4.18 compatibility
2 parents ed4d752 + fc2807b commit b2c5339

File tree

5 files changed

+156
-125
lines changed

5 files changed

+156
-125
lines changed

openapi_spec_validator/validation/validators.py

+58-45
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
"""OpenAPI spec validator validation validators module."""
22
import logging
33
import string
4+
import warnings
45
from typing import Any
5-
from typing import Callable
6-
from typing import Hashable
76
from typing import Iterator
87
from typing import List
9-
from typing import Mapping
108
from typing import Optional
119
from typing import Type
1210

1311
from jsonschema._format import FormatChecker
1412
from jsonschema.exceptions import ValidationError
1513
from jsonschema.protocols import Validator
16-
from jsonschema.validators import RefResolver
17-
from jsonschema_spec.accessors import SpecAccessor
18-
from jsonschema_spec.paths import Spec
14+
from jsonschema_spec.handlers import default_handlers
15+
from jsonschema_spec.paths import SchemaPath
16+
from jsonschema_spec.typing import ResolverHandlers
17+
from jsonschema_spec.typing import Schema
1918

2019
from openapi_spec_validator.validation.decorators import ValidationErrorWrapper
2120
from openapi_spec_validator.validation.exceptions import (
@@ -35,12 +34,7 @@
3534
wraps_errors = ValidationErrorWrapper(OpenAPIValidationError)
3635

3736

38-
def is_ref(spec: Any) -> bool:
39-
return isinstance(spec, dict) and "$ref" in spec
40-
41-
4237
class SpecValidator:
43-
4438
OPERATIONS = [
4539
"get",
4640
"put",
@@ -57,7 +51,7 @@ def __init__(
5751
schema_validator: Validator,
5852
value_validator_class: Type[Validator],
5953
value_validator_format_checker: FormatChecker,
60-
resolver_handlers: Optional[Mapping[str, Callable[[str], Any]]] = None,
54+
resolver_handlers: ResolverHandlers = default_handlers,
6155
):
6256
self.schema_validator = schema_validator
6357
self.value_validator_class = value_validator_class
@@ -66,30 +60,48 @@ def __init__(
6660

6761
self.operation_ids_registry: Optional[List[str]] = None
6862
self.schema_ids_registry: Optional[List[int]] = None
69-
self.resolver = None
7063

7164
def validate(
72-
self, instance: Mapping[Hashable, Any], spec_url: str = ""
65+
self,
66+
schema: Schema,
67+
base_uri: str = "",
68+
spec_url: Optional[str] = None,
7369
) -> None:
74-
for err in self.iter_errors(instance, spec_url=spec_url):
70+
for err in self.iter_errors(
71+
schema,
72+
base_uri=base_uri,
73+
spec_url=spec_url,
74+
):
7575
raise err
7676

77-
def is_valid(self, instance: Mapping[Hashable, Any]) -> bool:
78-
error = next(self.iter_errors(instance), None)
77+
def is_valid(self, schema: Schema) -> bool:
78+
error = next(self.iter_errors(schema), None)
7979
return error is None
8080

8181
@wraps_errors
8282
def iter_errors(
83-
self, instance: Mapping[Hashable, Any], spec_url: str = ""
83+
self,
84+
schema: Schema,
85+
base_uri: str = "",
86+
spec_url: Optional[str] = None,
8487
) -> Iterator[ValidationError]:
88+
if spec_url is not None:
89+
warnings.warn(
90+
"spec_url parameter is deprecated. " "Use base_uri instead.",
91+
DeprecationWarning,
92+
)
93+
base_uri = spec_url
94+
8595
self.operation_ids_registry = []
8696
self.schema_ids_registry = []
87-
self.resolver = self._get_resolver(spec_url, instance)
8897

89-
yield from self.schema_validator.iter_errors(instance)
98+
yield from self.schema_validator.iter_errors(schema)
9099

91-
accessor = SpecAccessor(instance, self.resolver)
92-
spec = Spec(accessor)
100+
spec = SchemaPath.from_dict(
101+
schema,
102+
base_uri=base_uri,
103+
handlers=self.resolver_handlers,
104+
)
93105
if "paths" in spec:
94106
paths = spec / "paths"
95107
yield from self._iter_paths_errors(paths)
@@ -98,17 +110,14 @@ def iter_errors(
98110
components = spec / "components"
99111
yield from self._iter_components_errors(components)
100112

101-
def _get_resolver(
102-
self, base_uri: str, referrer: Mapping[Hashable, Any]
103-
) -> RefResolver:
104-
return RefResolver(base_uri, referrer, handlers=self.resolver_handlers)
105-
106-
def _iter_paths_errors(self, paths: Spec) -> Iterator[ValidationError]:
113+
def _iter_paths_errors(
114+
self, paths: SchemaPath
115+
) -> Iterator[ValidationError]:
107116
for url, path_item in paths.items():
108117
yield from self._iter_path_errors(url, path_item)
109118

110119
def _iter_path_errors(
111-
self, url: str, path_item: Spec
120+
self, url: str, path_item: SchemaPath
112121
) -> Iterator[ValidationError]:
113122
parameters = None
114123
if "parameters" in path_item:
@@ -127,8 +136,8 @@ def _iter_operation_errors(
127136
self,
128137
url: str,
129138
name: str,
130-
operation: Spec,
131-
path_parameters: Optional[Spec],
139+
operation: SchemaPath,
140+
path_parameters: Optional[SchemaPath],
132141
) -> Iterator[ValidationError]:
133142
assert self.operation_ids_registry is not None
134143

@@ -168,13 +177,13 @@ def _iter_operation_errors(
168177
return
169178

170179
def _iter_responses_errors(
171-
self, responses: Spec
180+
self, responses: SchemaPath
172181
) -> Iterator[ValidationError]:
173182
for response_code, response in responses.items():
174183
yield from self._iter_response_errors(response_code, response)
175184

176185
def _iter_response_errors(
177-
self, response_code: str, response: Spec
186+
self, response_code: str, response: SchemaPath
178187
) -> Iterator[ValidationError]:
179188
# openapi 2
180189
if "schema" in response:
@@ -185,18 +194,20 @@ def _iter_response_errors(
185194
content = response / "content"
186195
yield from self._iter_content_errors(content)
187196

188-
def _iter_content_errors(self, content: Spec) -> Iterator[ValidationError]:
197+
def _iter_content_errors(
198+
self, content: SchemaPath
199+
) -> Iterator[ValidationError]:
189200
for mimetype, media_type in content.items():
190201
yield from self._iter_media_type_errors(mimetype, media_type)
191202

192203
def _iter_media_type_errors(
193-
self, mimetype: str, media_type: Spec
204+
self, mimetype: str, media_type: SchemaPath
194205
) -> Iterator[ValidationError]:
195206
if "schema" in media_type:
196207
schema = media_type / "schema"
197208
yield from self._iter_schema_errors(schema)
198209

199-
def _get_path_param_names(self, params: Spec) -> Iterator[str]:
210+
def _get_path_param_names(self, params: SchemaPath) -> Iterator[str]:
200211
for param in params:
201212
if param["in"] == "path":
202213
yield param["name"]
@@ -207,7 +218,7 @@ def _get_path_params_from_url(self, url: str) -> Iterator[str]:
207218
return filter(None, path_params)
208219

209220
def _iter_parameters_errors(
210-
self, parameters: Spec
221+
self, parameters: SchemaPath
211222
) -> Iterator[ValidationError]:
212223
seen = set()
213224
for parameter in parameters:
@@ -221,7 +232,7 @@ def _iter_parameters_errors(
221232
seen.add(key)
222233

223234
def _iter_parameter_errors(
224-
self, parameter: Spec
235+
self, parameter: SchemaPath
225236
) -> Iterator[ValidationError]:
226237
if "schema" in parameter:
227238
schema = parameter / "schema"
@@ -234,18 +245,18 @@ def _iter_parameter_errors(
234245
yield from self._iter_value_errors(parameter, default)
235246

236247
def _iter_value_errors(
237-
self, schema: Spec, value: Any
248+
self, schema: SchemaPath, value: Any
238249
) -> Iterator[ValidationError]:
239-
with schema.open() as content:
250+
with schema.resolve() as resolved:
240251
validator = self.value_validator_class(
241-
content,
242-
resolver=self.resolver,
252+
resolved.contents,
253+
_resolver=resolved.resolver,
243254
format_checker=self.value_validator_format_checker,
244255
)
245256
yield from validator.iter_errors(value)
246257

247258
def _iter_schema_errors(
248-
self, schema: Spec, require_properties: bool = True
259+
self, schema: SchemaPath, require_properties: bool = True
249260
) -> Iterator[ValidationError]:
250261
if not hasattr(schema.content(), "__getitem__"):
251262
return
@@ -329,11 +340,13 @@ def _iter_schema_errors(
329340
yield from self._iter_value_errors(schema, default)
330341

331342
def _iter_components_errors(
332-
self, components: Spec
343+
self, components: SchemaPath
333344
) -> Iterator[ValidationError]:
334345
schemas = components.get("schemas", {})
335346
yield from self._iter_schemas_errors(schemas)
336347

337-
def _iter_schemas_errors(self, schemas: Spec) -> Iterator[ValidationError]:
348+
def _iter_schemas_errors(
349+
self, schemas: SchemaPath
350+
) -> Iterator[ValidationError]:
338351
for _, schema in schemas.items():
339352
yield from self._iter_schema_errors(schema)

0 commit comments

Comments
 (0)