Skip to main content

jsonschema-mojo

Drop-in faster replacement for the jsonschema package (draft 2020-12 subset), powered by a Mojo kernel.

Same call shapes, same pass/fail outcomes, same failing instance paths — validated by a differential test suite against the published jsonschema package on both backends. The native Mojo kernel runs on macOS arm64 and Linux x86_64; everywhere else (Windows, missing/unloadable kernel, or values the native path cannot represent exactly) the package transparently uses a vendored pure-Python fallback with identical semantics — tested on Windows in CI (windows-fallback). Zero dependencies.

import jsonschema_mojo

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "age": {"type": "integer", "minimum": 0},
    },
    "required": ["name"],
    "additionalProperties": False,
}

jsonschema_mojo.validate({"name": "Ada", "age": 36}, schema)   # None (valid)
jsonschema_mojo.validate({"age": -1}, schema)                  # raises ValidationError

validator = jsonschema_mojo.Validator(schema)    # compiles the schema once
validator.is_valid({"name": "Ada"})              # True
for error in validator.iter_errors({"age": -1}):
    print(error.json_path, error.message)
# $.age -1 is less than the minimum of 0
# $ 'name' is a required property

ValidationError mirrors the reference's attributes: message, absolute_path, json_path, absolute_schema_path, validator, validator_value, instance, schema, context. jsonschema_mojo.Draft202012Validator is an alias of Validator.

Supported schema subset (draft 2020-12)

type, properties, required, items, additionalProperties, enum, const, minimum, maximum, exclusiveMinimum, exclusiveMaximum, minLength, maxLength, pattern, minItems, maxItems, uniqueItems, minProperties, maxProperties, multipleOf — plus boolean subschemas (true/false, including additionalProperties: false and items: false).

Annotation/inert keywords are ignored exactly like the reference does by default: $schema, $id, $defs/definitions, $anchor, $dynamicAnchor, $comment, $vocabulary, title, description, default, deprecated, readOnly/writeOnly, examples, format (annotation-only without a format checker), contentEncoding/contentMediaType/contentSchema.

Schemas using any other validation-affecting keyword — $ref, $dynamicRef, allOf/anyOf/oneOf/not, if/then/else, patternProperties, prefixItems, contains, minContains/maxContains, propertyNames, dependentRequired/dependentSchemas/dependencies, unevaluatedItems/unevaluatedProperties — raise jsonschema_mojo.UnsupportedSchemaError at Validator construction (and in validate()), instead of silently validating differently from the reference. Invalid keyword value shapes (e.g. {"type": "frob"}, {"multipleOf": 0}) also raise UnsupportedSchemaError; the reference's validate() raises SchemaError for these via metaschema checking.

Parity contract with jsonschema (measured, not aspirational)

Asserted on both backends by tests/test_jsonschema_differential.py (229 tests per backend: curated per-keyword cases, edge semantics, and a seeded randomized schema/instance sweep) against jsonschema 4.26.0:

  • pass/fail outcome — exact (100% agreement);
  • failing instance JSON paths + keywords — exact (multiset comparison, 100% agreement);
  • error message text — exact for every supported keyword (the reference's templates are replicated verbatim; str(error) differs: the reference appends schema/instance excerpts, this package's str() is the message itself);
  • error ordering — not part of the contract (documented divergence): the reference orders errors by schema-dict insertion order, the native backend uses a fixed canonical keyword order. Validator.validate() raises its own first error (identity best-effort; the module-level jsonschema_mojo.validate() mirrors the reference's best_match deepest-path heuristic).

Edge semantics replicated from the reference: 1.0 counts as "integer"; booleans are never numbers and True/False never equal 1/0 for const/enum/uniqueItems (but 1 == 1.0); "array" is list, "object" is dict; multipleOf uses floored-modulo semantics for integer divisors and the int(quotient) != quotient test for float divisors with an exact Fraction fallback; pattern is evaluated with Python's re on the original string (the kernel defers it), so regex behavior is identical —including re.error on invalid patterns.

Native path boundaries (auto-degrade to the exact fallback)

The native kernel validates the JSON-document projection of the instance (what json.dumps produces). It detects and silently routes to the vendored fallback (which mirrors the reference on arbitrary Python objects) when a value is not exactly representable:

  • integers beyond ±2**53 (kernel big-number flag, in schema or instance);
  • non-finite floats (NaN, ±Infinity) and non-JSON-serializable values (Decimal, bytes, sets, arbitrary objects) — json.dumps failure.

Instances containing tuples or non-string dict keys are not JSON documents; the reference does not treat tuples as arrays and does not stringify keys. The native path validates their JSON projection (tuple → array, 1 → "1"), which can differ from the reference for such inputs; use JSONSCHEMA_MOJO_DISABLE_NATIVE=1 (or the oracle) if you rely on Python-object semantics there. JSON documents loaded via json.load are unaffected and always parity-exact.

How it works

Validator(schema) checks the schema against the subset gate, serializes it once (json.dumps), and hands it to the Mojo kernel, which parses it into a flat node arena and compiles a keyword tree. Per validate/iter_errors call the wrapper serializes the instance (C-speed json.dumps) and the kernel parses it and walks the compiled schema tree in one pass, streaming (keyword, instance path, schema path) records back. Error objects and messages are then materialized in Python from the original objects; regex pattern checks and multipleOf edge cases are deferred by the kernel to the wrapper's exact Python path. Schema and instance never round-trip through Python object reconstruction — the walk is fully native.

Benchmarks

Measured on this machine (Apple M4 Max, macOS 26.6.2, Python 3.12.14, Mojo 1.1.0, oracle jsonschema 4.26.0), seed 20260919, median of 5 runs, correctness-gated before timing. Reproduce with PYTHONPATH=python/jsonschema_mojo python benchmarks/bench_jsonschema.py from the repository root. Scenarios: A = 10,000 order records against a shared schema (incl. two pattern checks per record, resolved via Python re); B = one ~3 MB nested document (22,500 leaf objects) per call; C = 2,000 invalid records producing ~4 errors each. Cold = fresh Validator + first call (gate + native compile + first walk); warm = steady-state per document.

scenario cold jsonschema (ms) cold jsonschema_mojo (ms) warm jsonschema (ms/doc) warm jsonschema_mojo (ms/doc) warm speedup
A: 10k order records 0.050 0.047 0.0589 0.0189 3.1x
B: big nested document 222.803 9.470 213.1359 9.0478 23.6x
C: 2k invalid records 0.080 0.063 0.0627 0.0292 2.1x

Warm throughput: A — 16,976 vs 52,984 docs/s; B — 5 vs 111 docs/s; C — 15,947 vs 34,244 docs/s. The vendored pure-Python fallback (what Windows gets) measured 0.0186 / 66.7 / 0.0396 ms/doc warm on A/B/C — itself ~3x faster than the oracle on small documents, and on tiny documents roughly equal to the native path (fixed serialization/FFI cost dominates); the kernel's advantage grows with document size. Numbers on a shared machine varied ±20% run to run; speedup ratios were stable (2.1–3.2x small docs, 21–28x large).

Backend selection & diagnostics

import jsonschema_mojo
jsonschema_mojo.backend_info()       # native source, ABI version, errors
jsonschema_mojo.native_available()   # bool
jsonschema_mojo.Validator(schema).backend  # "native" or "fallback"
  • JSONSCHEMA_MOJO_DISABLE_NATIVE=1 forces the fallback (used by the differential suite).
  • JSONSCHEMA_MOJO_NATIVE_LIB=/path/to/libjsonschemamojo.{dylib,so} overrides kernel resolution (development).

The wrapper verifies an ABI-version handshake with the kernel before use; any mismatch, load failure, or missing library falls back silently.

Tests

From the repository root, inside the pixi environment (the jsonschema oracle, jsonschema==4.26.0, comes from pixi.toml [pypi-dependencies]):

pixi run bash kernels/jsonschema/build.sh
pixi run bash scripts/test_all_jsonschema.sh   # native backend, then forced fallback

229 differential tests + 7 loader tests per backend run (native and forced fallback).

License

Apache-2.0. Copyright Algenta.

Metadata

Release files for jsonschema-mojo 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for jsonschema-mojo 0.1.5
File Interpreter ABI Platform
jsonschema_mojo-0.1.5-py3-none-manylinux_2_35_x86_64.whl Python 3 none Linux glibc 2.35+ x86-64 Details
jsonschema_mojo-0.1.5-py3-none-macosx_14_0_arm64.whl Python 3 none macOS 14.0+ ARM64 Details

Total release size: 2.0 MB

Release files / jsonschema_mojo-0.1.5-py3-none-manylinux_2_35_x86_64.whl

Download URL jsonschema_mojo-0.1.5-py3-none-manylinux_2_35_x86_64.whl
Size 885.0 kB
Tags Linux glibc 2.35+ x86-64 Python 3
SHA-256 checksum
How to use checksums
2ea55f8699122edad0a9849e038d0dce9c5dfbd3b0b3e2398199561fd0226151
BLAKE2b-256 checksum
How to use checksums
d77587b043b3526a412237e22ca321d77e9b0e26680b735d75fdfa838e271b8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release files / jsonschema_mojo-0.1.5-py3-none-macosx_14_0_arm64.whl

Download URL jsonschema_mojo-0.1.5-py3-none-macosx_14_0_arm64.whl
Size 1.2 MB
Tags Python 3 macOS 14.0+ ARM64
SHA-256 checksum
How to use checksums
3a9e903584f6974f2eadf1e8e6d4c8fcec26fc66faa9186893fc1f3f610dc2d3
BLAKE2b-256 checksum
How to use checksums
df16c097c95153ec84403da30628aab4ac7b9942496168d328ddfff1520d7a5a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release history Release notifications | RSS feed

0.2.4

2 release files

0.2.3

2 release files

This release

0.1.5 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page