Source code for fair_data_schema.validator

"""
Schema validator.

Wraps jsonschema's Draft202012Validator with a local referencing.Registry
so that cross-schema $ref resolution works offline using the local file
mappings defined in registry.py.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import jsonschema
import referencing
import referencing.jsonschema

from fair_data_schema.registry import all_schemas

# ── Build a local referencing Registry ───────────────────────────────────────


def _build_registry() -> referencing.Registry[Any]:
    """Construct a referencing.Registry pre-loaded with all local FAIR schemas."""
    resources: list[tuple[str, referencing.Resource[Any]]] = []
    for uri, schema in all_schemas().items():
        resource = referencing.Resource.from_contents(
            schema,
            default_specification=referencing.jsonschema.DRAFT202012,
        )
        resources.append((uri, resource))
    registry: referencing.Registry[Any] = referencing.Registry().with_resources(resources)
    return registry


_REGISTRY: referencing.Registry[Any] = _build_registry()


# ── Public API ────────────────────────────────────────────────────────────────


[docs] def validate(instance: object, schema: dict[str, object]) -> list[jsonschema.ValidationError]: """ Validate *instance* against *schema* using the FAIR dialect-aware validator. Returns a (possibly empty) list of ValidationError objects. Raises nothing — all errors are collected and returned. """ validator_cls = jsonschema.Draft202012Validator validator = validator_cls(schema, registry=_REGISTRY) return list(validator.iter_errors(instance))
[docs] def validate_file( schema_path: Path, instance_path: Path | None = None ) -> list[jsonschema.ValidationError]: """ Validate a schema file (optionally against an instance file). If *instance_path* is None, validates the schema itself against the standard JSON Schema 2020-12 meta-schema (i.e. checks the schema is a valid schema document). """ schema = json.loads(schema_path.read_text(encoding="utf-8")) if instance_path is None: # Validate the schema document against the 2020-12 meta-meta-schema meta_schema: dict[str, object] = {"$ref": "https://json-schema.org/draft/2020-12/schema"} return validate(schema, meta_schema) instance = json.loads(instance_path.read_text(encoding="utf-8")) return validate(instance, schema)
[docs] def is_valid_json(path: Path) -> bool: """Return True if *path* contains valid JSON, False otherwise.""" try: json.loads(path.read_text(encoding="utf-8")) return True except json.JSONDecodeError: return False