Source code for rath.adapters.schema

"""Small fail-closed JSON Schema subset used for Tool runtime validation."""

from __future__ import annotations

from collections.abc import Mapping, Sequence

__all__ = ["SchemaValidationError", "validate_json"]


[docs] class SchemaValidationError(ValueError): pass
[docs] def validate_json( value: object, schema: Mapping[str, object], *, path: str = "$" ) -> None: expected = schema.get("type") if expected == "object": if not isinstance(value, Mapping): raise SchemaValidationError(f"{path} must be an object") required = schema.get("required", ()) if isinstance(required, Sequence) and not isinstance(required, str): for key in required: if str(key) not in value: raise SchemaValidationError(f"{path}.{key} is required") properties = schema.get("properties", {}) if isinstance(properties, Mapping): for key, item in value.items(): child_schema = properties.get(key) if isinstance(child_schema, Mapping): validate_json(item, child_schema, path=f"{path}.{key}") elif schema.get("additionalProperties") is False: raise SchemaValidationError(f"{path}.{key} is not allowed") elif expected == "array": if not isinstance(value, Sequence) or isinstance(value, (str, bytes)): raise SchemaValidationError(f"{path} must be an array") items = schema.get("items") if isinstance(items, Mapping): for index, item in enumerate(value): validate_json(item, items, path=f"{path}[{index}]") elif expected == "string": if not isinstance(value, str): raise SchemaValidationError(f"{path} must be a string") max_length = schema.get("maxLength") if isinstance(max_length, int) and len(value) > max_length: raise SchemaValidationError(f"{path} exceeds maxLength") elif expected == "integer": if not isinstance(value, int) or isinstance(value, bool): raise SchemaValidationError(f"{path} must be an integer") elif expected == "number": if not isinstance(value, (int, float)) or isinstance(value, bool): raise SchemaValidationError(f"{path} must be a number") elif expected == "boolean" and not isinstance(value, bool): raise SchemaValidationError(f"{path} must be a boolean") elif expected == "null" and value is not None: raise SchemaValidationError(f"{path} must be null") elif expected not in ( None, "object", "array", "string", "integer", "number", "boolean", "null", ): raise SchemaValidationError(f"{path} uses unsupported schema type {expected!r}") enum = schema.get("enum") if isinstance(enum, Sequence) and value not in enum: raise SchemaValidationError(f"{path} is not an allowed enum value")