|
| 1 | +from functools import lru_cache |
1 | 2 | from typing import Any |
| 3 | +from typing import Iterator |
| 4 | +from typing import Mapping |
2 | 5 | from typing import cast |
3 | 6 |
|
4 | 7 | from jsonschema import _keywords |
5 | 8 | from jsonschema import _legacy_keywords |
6 | 9 | from jsonschema.exceptions import SchemaError |
7 | 10 | from jsonschema.exceptions import ValidationError |
| 11 | +from jsonschema.protocols import Validator |
8 | 12 | from jsonschema.validators import Draft202012Validator |
9 | 13 | from jsonschema.validators import create |
10 | 14 | from jsonschema.validators import extend |
@@ -187,3 +191,47 @@ def _build_oas32_validator() -> Any: |
187 | 191 | OAS30Validator.check_schema = classmethod(check_openapi_schema) |
188 | 192 | OAS31Validator.check_schema = classmethod(check_openapi_schema) |
189 | 193 | OAS32Validator.check_schema = classmethod(check_openapi_schema) |
| 194 | + |
| 195 | + |
| 196 | +@lru_cache(maxsize=None) |
| 197 | +def build_enforce_properties_required_validator( |
| 198 | + validator_class: Any, |
| 199 | +) -> type[Validator]: |
| 200 | + properties_validator = validator_class.VALIDATORS.get("properties") |
| 201 | + required_validator = validator_class.VALIDATORS.get("required") |
| 202 | + |
| 203 | + def enforce_properties( |
| 204 | + validator: Any, |
| 205 | + properties: Any, |
| 206 | + instance: Any, |
| 207 | + schema: Mapping[str, Any], |
| 208 | + ) -> Iterator[Any]: |
| 209 | + if properties_validator is not None: |
| 210 | + yield from properties_validator( |
| 211 | + validator, properties, instance, schema |
| 212 | + ) |
| 213 | + |
| 214 | + if not validator.is_type(instance, "object"): |
| 215 | + return |
| 216 | + |
| 217 | + if required_validator is not None: |
| 218 | + schema_required = ( |
| 219 | + schema.get("required", []) if isinstance(schema, dict) else [] |
| 220 | + ) |
| 221 | + missing_props = [ |
| 222 | + p for p in properties.keys() if p not in schema_required |
| 223 | + ] |
| 224 | + if missing_props: |
| 225 | + yield from required_validator( |
| 226 | + validator, missing_props, instance, schema |
| 227 | + ) |
| 228 | + |
| 229 | + extended_validator = extend( |
| 230 | + validator_class, |
| 231 | + validators={"properties": enforce_properties}, |
| 232 | + ) |
| 233 | + if hasattr(validator_class, "check_schema"): |
| 234 | + extended_validator.check_schema = classmethod( |
| 235 | + validator_class.check_schema.__func__ |
| 236 | + ) |
| 237 | + return cast(type[Validator], extended_validator) |
0 commit comments