A modern Python library for formatting JSON Schema validation errors into ExceptionGroup trees.
Project description
tortoise-json-diagnostics
A modern, highly customizable Python library for formatting JSON Schema validation errors into clear, human-readable code snippets and nested ExceptionGroup trees.
Key Features
- 🌳 Nested ExceptionGroup Trees: Automatically groups flat
jsonschemavalidation errors into structured hierarchy matching your JSON schema layout. - 🧩 Extensible Handler Pipeline: Allows custom error handlers to intercept, transform, and prune specific validation errors before fallback processing.
- ⚙️ Global Formatter Registry: Easily switch or implement custom location and code snippet formatters (e.g., plain text, rich, ...).
- 🐍 Modern Python Native: Built for modern Python with strict typing
Visual Output
Instead of unreadable raw validation objects, tortoise-json-diagnostics formats error groups like this:
| ExceptionGroup: JSON Validation Error
| File "input.json" (2 sub-exceptions)
+-+---------------- 1 ----------------
| ExceptionGroup: Property 'name'
| File "input.json", line 1, column 2 (1 sub-exception)
+-+---------------- 1 ----------------
| tortoise_json_diagnostics.errors.JsonValidationError: 123 is not of type 'string'
| File "input.json", line 1, column 10
| 1 | {"name": 123, "age": -5}
| ^^^
+------------------------------------
+---------------- 2 ----------------
| ExceptionGroup: Property 'age'
| File "input.json", line 1, column 15 (1 sub-exception)
+-+---------------- 1 ----------------
| tortoise_json_diagnostics.errors.JsonValidationError: -5 is less than the minimum of 0
| File "input.json", line 1, column 22
| 1 | {"name": 123, "age": -5}
| ^^
+------------------------------------
Installation
Using uv (recommended):
uv add tortoise-json-diagnostics
Using pip:
pip install tortoise-json-diagnostics
Quick Start
from jsonschema import Draft202012Validator
from tortoise_json_diagnostics import DiagnosticJsonParser
schema = {
"type": "object",
"required": ["name", "age"],
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0},
},
}
validator = Draft202012Validator(schema)
parser = DiagnosticJsonParser(validator)
json_text = '{"name": 123, "age": -5}'
data = parser.parse_text(json_text, path="input.json")
Advanced Usage
Custom Error Handlers
You can intercept specific ValidationErrors before they hit the default handler by implementing IErrorHandler:
from tortoise_json_diagnostics import IErrorHandler, JsonValidationError
class CustomTypeMismatchHandler(IErrorHandler):
def handle(self, validator, validation_errors, source_map, json_text, file_path, /):
handled: list[JsonValidationError] = []
unhandled = []
for error in validation_errors:
if error.validator == "type":
message = f"[Type Mismatch] {error.message}"
handled.append(JsonValidationError(message, validator, [error]))
else:
unhandled.append(error)
return handled, unhandled
parser = DiagnosticJsonParser(validator, handlers=[CustomTypeMismatchHandler()])
| ExceptionGroup: JSON Validation Error
| File "input.json" (2 sub-exceptions)
+-+---------------- 1 ----------------
| ExceptionGroup: Property 'name'
| File "input.json", line 1, column 2 (1 sub-exception)
+-+---------------- 1 ----------------
| json_diagnostics.errors.JsonValidationError: [Type Mismatch] 123 is not of type 'string'
+------------------------------------
+---------------- 2 ----------------
| ExceptionGroup: Property 'age'
| File "input.json", line 1, column 15 (1 sub-exception)
+-+---------------- 1 ----------------
| json_diagnostics.errors.JsonValidationError: -5 is less than the minimum of 0
| File "input.json", line 1, column 22
| 1 | {"name": 123, "age": -5}
| ^^
+------------------------------------
Global Formatters
Register custom formatters for file locations or text snippets:
from tortoise_json_diagnostics import LocationFormatter, set_global_location_formatter
class CompactLocationFormatter(LocationFormatter):
def format(self, file_path, span, /) -> str:
if not file_path:
return ""
if not span:
return str(file_path)
return f"{file_path}:{span.end.line + 1}:{span.end.column + 1}"
set_global_location_formatter(CompactLocationFormatter())
| ExceptionGroup: JSON Validation Error
| input.json (2 sub-exceptions)
+-+---------------- 1 ----------------
| ExceptionGroup: Property 'name'
| input.json:1:8 (1 sub-exception)
+-+---------------- 1 ----------------
| json_diagnostics.errors.JsonValidationError: 123 is not of type 'string'
| input.json:1:13
| 1 | {"name": 123, "age": -5}
| ^^^
+------------------------------------
+---------------- 2 ----------------
| ExceptionGroup: Property 'age'
| input.json:1:20 (1 sub-exception)
+-+---------------- 1 ----------------
| json_diagnostics.errors.JsonValidationError: -5 is less than the minimum of 0
| input.json:1:24
| 1 | {"name": 123, "age": -5}
| ^^
+------------------------------------
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tortoise_json_diagnostics-0.1.1.tar.gz.
File metadata
- Download URL: tortoise_json_diagnostics-0.1.1.tar.gz
- Upload date:
- Size: 5.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa0de06767b9a1f17af766c1615c2c970df12d96dc7f08cf3410aadfeeaf4fea
|
|
| MD5 |
1ace873dc00ff2b825710b5bd7b423af
|
|
| BLAKE2b-256 |
8788e1d74a607cd28e6ae8d3e3dbc8657f55005da63fbdc83fcfb3038ac11f6b
|
Provenance
The following attestation bundles were made for tortoise_json_diagnostics-0.1.1.tar.gz:
Publisher:
publish.yml on Testudinidae/tortoise-json-diagnostics
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tortoise_json_diagnostics-0.1.1.tar.gz -
Subject digest:
aa0de06767b9a1f17af766c1615c2c970df12d96dc7f08cf3410aadfeeaf4fea - Sigstore transparency entry: 2339416070
- Sigstore integration time:
-
Permalink:
Testudinidae/tortoise-json-diagnostics@e8001f51e1c6685152e27533866c44525530cb57 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Testudinidae
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e8001f51e1c6685152e27533866c44525530cb57 -
Trigger Event:
release
-
Statement type:
File details
Details for the file tortoise_json_diagnostics-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tortoise_json_diagnostics-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6f103cf78a740f3b8fc459d9f0f0b984e8ae1564e5e00ecc629e931b76a8654
|
|
| MD5 |
b79bcee1330e86c29bfc6e4b81da388c
|
|
| BLAKE2b-256 |
c39a2ace16a5b2c24b19fe6f7f230d188d462987f6262726d070c07c224f02e4
|
Provenance
The following attestation bundles were made for tortoise_json_diagnostics-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Testudinidae/tortoise-json-diagnostics
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tortoise_json_diagnostics-0.1.1-py3-none-any.whl -
Subject digest:
e6f103cf78a740f3b8fc459d9f0f0b984e8ae1564e5e00ecc629e931b76a8654 - Sigstore transparency entry: 2339416132
- Sigstore integration time:
-
Permalink:
Testudinidae/tortoise-json-diagnostics@e8001f51e1c6685152e27533866c44525530cb57 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Testudinidae
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e8001f51e1c6685152e27533866c44525530cb57 -
Trigger Event:
release
-
Statement type: