Skip to content
OATSCHEMA

Docs / Python

OATS for Python

The oatschema package bundles every OATS schema from 0.4.0 on, and validates records against them with no network access.

pip install 'oatschema[validate]'

PyPIoatschema/oatschema-pyPython 3.11 or later

Validate a record

Install with the validate extra, which adds jsonschema-rs. Then pass a record and a schema name:

Python
import oatschemarecord = {"labels": [{"label": "cat", "score": 0.92}]}oatschema.validate(record, "classification")  # passesbad = {"labels": [{"label": "cat", "score": 1.5}]}oatschema.is_valid(bad, "classification")  # Falseoatschema.validate(bad, "classification")# raises oatschema.ValidationError: classification 0.8.0 at /labels/0/score: … is greater than the maximum of 1

validate raises oatschema.ValidationError, is_valid returns a bool, and iter_errors returns every error. All three use a cached validator that also checks formats such as uri and date-time. You never need jsonschema-rs's own API or types.

By default, a uri that isn't a URI, or a date-time that isn't an RFC 3339 timestamp, is an error. JSON Schema 2020-12 treats format as an annotation unless format checking is turned on, so other validators may accept records that oatschema rejects. To match them, or to keep accepting records you've accepted before, pass check_formats=False to validate, is_valid, iter_errors or get_validator.

pip install oatschema, without the extra, installs the schemas alone, with no dependencies. get_schema works without the extra; the validation functions raise ImportError, naming the extra to install. oatschema[all] installs every optional feature.

Handle errors

Python
try:    oatschema.validate(bad, "classification")except oatschema.ValidationError as error:    error.path  # ("labels", 0, "score"): where in the record    error.schema_path  # ("properties", "labels", "items", "properties", "score", "maximum")    error.keyword  # "maximum"    error.schema_name, error.schema_version  # "classification", "0.8.0"    error.message  # "… is greater than the maximum of 1"for error in oatschema.iter_errors(bad, "classification"):    print(error)  # every error, not just the first

ValidationError is a ValueError. validate raises the first error it finds and stops there; iter_errors yields every error as a ValidationError, but finds them all before yielding the first, so use validate when one error is enough. The error validate raises isn't always the first one iter_errors yields, and the order of errors may change between jsonschema-rs versions.

Errors stay small, however large the record. Messages show values from the record as …, and are at most about 1 KB, so logging an error or printing a traceback never dumps the record. error.path says where the value is.

get_validator(name, version=None) returns a cached oatschema.Validator, with the same validate, is_valid and iter_errors methods, and read-only schema_name, schema_version, schema and check_formats.

Get a schema

Python
schema = oatschema.get_schema("classification")  # a new dict on every callschema["$id"]  # "https://oatschema.org/0.8.0/classification"text = oatschema.get_schema("classification", mode="json")  # the file, exactly as releasedoatschema.SCHEMA_VERSION  # "0.8.0", used when you don't pass a versionoatschema.available_versions()  # ["0.4.0", "0.5.0", "0.6.0", "0.7.0", "0.8.0"]

Each schema is byte-identical to its release on this site, and a checksum manifest ships with every version.

Pin a schema version

Before OATS 1.0, a new release may reject records an older one accepted, or the reverse. Pass the version a record was written for:

Python
oatschema.is_valid({"labels": []}, "classification", version="0.7.0")  # False: 0.7.0 needed a vocabulary or produceroatschema.is_valid({"labels": []}, "classification")  # True: from 0.8.0, an empty list means "checked, found nothing"
  • version must be exact: "0.8.0", never "0.8" or "latest".
  • A name that exists in no bundled version raises ValueError. A version that isn't bundled, or that lacks the schema, raises LookupError.
  • 0.4.0 to 0.6.0 predate OATS and are listed in LEGACY_VERSIONS. They share one $id per schema (https://dorsalhub.com/schemas/open/<name>), so build each validator from a single schema and never load two of these versions into one registry.

More on releases in Versions and URLs.

Use another validator

get_schema returns a plain dict for any JSON Schema draft 2020-12 validator. x-license and x-version are the only non-standard keywords, and draft 2020-12 validators ignore them. With jsonschema:

Python
from jsonschema import Draft202012Validatorimport oatschemaschema = oatschema.get_schema("classification")validator = Draft202012Validator(schema, format_checker=Draft202012Validator.FORMAT_CHECKER)validator.is_valid({"labels": [{"label": "cat", "score": 0.92}]})  # True

API

get_schema(name, version=None) The schema as a new dict on every call.
get_schema(name, version=None, mode="json") The schema file's text, exactly as released.
available_versions() Every bundled schema version, oldest first.
SCHEMA_VERSION The default schema version, the newest bundled.
LEGACY_VERSIONS The bundled versions from before OATS: 0.4.0 to 0.6.0.
SchemaName The schema names, as a Literal, so mypy and pyright catch typos.
validate(record, name, version=None, *, check_formats=True) Raises ValidationError for the first error found. Needs [validate].
is_valid(record, name, version=None, *, check_formats=True) True or False. Needs [validate].
iter_errors(record, name, version=None, *, check_formats=True) An iterator over every error, as ValidationErrors. Needs [validate].
get_validator(name, version=None, *, check_formats=True) The cached Validator. Needs [validate].
Validator validate(record), is_valid(record) and iter_errors(record), plus read-only schema_name, schema_version, schema and check_formats.
ValidationError A ValueError with message, schema_name, schema_version, path, schema_path and keyword.

Package versions

The package version is a.b.c.d. a.b.c is the newest bundled schema version, equal to SCHEMA_VERSION. d is the package's own revision, for fixes and features that add no schema version, and is left out when it is 0: 0.8.0 is followed by 0.8.0.1.

Upgrading never removes a schema version, but it does move the default SCHEMA_VERSION. Before 1.0, pin with oatschema~=0.8.0, because a new schema minor version may change which records are valid.

Other languages: JavaScript and Rust. Schema changes belong in oatschema/oats; package issues go to oatschema/oatschema-py.