Validation adapters
JSON Schema
Validate operation results against a JSON Schema with the optional relprim.jsonschema adapter. Draft selection, schema errors, failure reasons and paths.
json_schema_validator turns a JSON Schema into a RelPrim
validator. It works wherever a validator does:
validation_policy(...), @resilient(validation=...) and
.with_validation(...). Schema semantics come from the
jsonschema package; RelPrim only
translates its results.
Install
pip install "relprim[jsonschema]"
Supported versions are jsonschema>=4.3.0,<5.0.0. import relprim never
imports it; only relprim.jsonschema does, and importing that module without
the extra raises ModuleNotFoundError with the pip install command.
Usage
from relprim import resilient, validation_policy
from relprim.jsonschema import json_schema_validator
USER_SCHEMA = {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
},
}
user_validator = json_schema_validator(USER_SCHEMA, name="user_response")
@resilient(retries=2, timeout=10, validation=validation_policy(user_validator))
async def fetch_user(user_id: int) -> dict[str, object]:
return await user_service.fetch(user_id)
name defaults to "json_schema" and must not be blank. It appears in reports
and validation events. json_schema_validator(...) returns a
JSONSchemaValidator.
The validator can also be called directly:
result = user_validator.validate({"id": 1})
result.valid # False
result.reason # "'name' is a required property (path: $)"
Schema drafts
The draft is selected from the schema’s $schema keyword using jsonschema’s own
validator discovery. Schemas without $schema use the latest draft supported by
the installed jsonschema version.
json_schema_validator({"$schema": "http://json-schema.org/draft-07/schema#", "type": "object"})
Invalid schemas
The schema is checked against its metaschema when the validator is created. An
invalid schema raises jsonschema.exceptions.SchemaError immediately, unwrapped,
so configuration mistakes never surface as invalid operation results:
json_schema_validator({"type": "not-a-real-type"}) # raises SchemaError
Validation failures
A value that does not match returns a failed ValidationResult, and RelPrim
raises ValidationFailedError for that attempt. The reason combines jsonschema’s
message with the location of the invalid value:
'name' is a required property (path: $)
123 is not of type 'string' (path: $.name)
7 is not of type 'string' (path: $.user.tags[1])
Paths start at $, the whole value; properties are written as .name and array
indexes as [1]. When a value has several errors, the single most relevant one
selected by jsonschema.exceptions.best_match is reported, deterministically
for the same schema and value. Reasons longer than 200 characters are truncated.
Retries and fallbacks
Schema failures are ordinary ValidationFailedError failures, so the usual
validation rules apply: they are
retried when they match retry_on, and fallback results are validated with the
same policy. A fallback can therefore recover from a primary result that failed
the schema:
from relprim import ValidationFailedError, resilient, validation_policy
@resilient(
retries=2,
retry_on=(ValidationFailedError,),
fallback=fetch_user_from_replica,
validation=validation_policy(json_schema_validator(USER_SCHEMA)),
)
async def fetch_user(user_id: int) -> dict[str, object]:
return await user_service.fetch(user_id)
Limits
- Values are validated as they are. They are never converted, mutated or filled with schema defaults.
- Only the most relevant error is reported.
formatis an annotation, following jsonschema’s default; format assertions are not enabled.- RelPrim does not configure remote
$refresolution. - Values that are not JSON-compatible, such as dataclass instances, fail the schema’s type checks instead of raising.
The validator accepts values of any type, so
validation_policy(json_schema_validator(...)) is a ValidationPolicy[object]
and type-checks with operations of any return type.
