Skip to content

Commit 48387fc

Browse files
committed
Add jsonschema_dingus
1 parent d5a054d commit 48387fc

3 files changed

Lines changed: 117 additions & 12 deletions

File tree

README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,26 @@ The built documentation including contribution hints can be found **[here](https
1313

1414
Conformance can be tested at several levels.
1515

16-
1. Validating a single zarr attributes object (i.e. containing OME-Zarr metadata)
16+
1. Validating that individual fields of a zarr attributes object are valid.
17+
2. Validating a single zarr attributes object (i.e. containing OME-Zarr metadata)
1718

1819
- Validates that correct data can be represented, and that internally inconsistent data can be caught
1920
- Cannot validate references to other objects in the zarr hierarchy
2021
- Cannot validate conformance to other zarr metadata e.g. array data type, dimensionality
2122

22-
2. Validating a metadata-only zarr hierarchy
23+
3. Validating a metadata-only zarr hierarchy
2324

2425
- Can validate references to other objects and other zarr metadata
2526
- Cannot validate values e.g. the invertibility of an affine matrix defined as a zarr array
2627

27-
3. Validating a zarr hierarchy with data
28+
4. Validating a zarr hierarchy with data
29+
30+
This repository contains
31+
32+
- JSON schemas which handle level 1
33+
- a set of test zarr attributes JSON for level 2 ([`./tests/attributes`](./tests/attributes/))
34+
- a set of metadata-only zarr hierarchies for level 3 ([`./tests/zarr`](./tests/zarr/))
2835

29-
This repository contains a set of test cases for levels
30-
1 ([`./tests/attributes`](./tests/attributes/)) and
31-
2 ([`./tests/zarr`](./tests/zarr/)),
3236
as well as a tool for feeding these test cases into an external validator.
3337

3438
### Testing tool
@@ -47,7 +51,7 @@ which takes as its last argument the path to either
4751
The dingus should print to STDOUT a JSON object with the keys:
4852

4953
- `"valid"`: boolean, whether this is valid
50-
- optionally `"message"`: str, free text describing the success/ failure
54+
- optionally `"message"`: string, free text describing the success/ failure
5155

5256
Call the tool like
5357

@@ -66,3 +70,13 @@ Each call to the dingus will then look like
6670
`ome_zarr_conformance.py` will parse the JSON output and format the results of all requested tests in a tab-separated table.
6771

6872
Full usage information is available with `./ome_zarr_conformance.py --help`.
73+
74+
### JSON Schema tests
75+
76+
You can use the conformance testing tool to test JSON Schema-based validation with
77+
78+
```sh
79+
>>> ./ome_zarr_conformance.py attributes -- uv run jsonschema_dingus.py attributes
80+
```
81+
82+
Some failures are expected as JSON Schema can only handle level 1 validation.

jsonschema_dingus.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python3
2+
# /// script
3+
# dependencies = [
4+
# "jsonschema",
5+
# "referencing",
6+
# ]
7+
# ///
8+
from __future__ import annotations
9+
from dataclasses import dataclass
10+
from pathlib import Path
11+
import json
12+
from argparse import ArgumentParser
13+
from typing import Any, Self
14+
15+
from referencing import Registry, Resource
16+
from jsonschema import Draft202012Validator as Validator
17+
from jsonschema.exceptions import ValidationError
18+
19+
HERE = Path(__file__).resolve().parent
20+
SCHEMA_DIR = HERE / "ngff_spec" / "schemas"
21+
22+
23+
@dataclass
24+
class SchemasInfo:
25+
generic_id: str
26+
base_url: str
27+
registry: Registry
28+
29+
@classmethod
30+
def load(cls) -> Self:
31+
"""Get the ID of the top-level schema, and the mapping of schema IDs to objects"""
32+
registry = Registry()
33+
generic = None
34+
base_url = None
35+
for p in SCHEMA_DIR.glob("*.schema*"):
36+
schema = json.loads(p.read_text())
37+
resource = Resource.from_contents(schema)
38+
registry = resource @ registry
39+
40+
schema_id = resource.id()
41+
if schema_id is None:
42+
raise RuntimeError("schema has no ID")
43+
if p.stem == "ome_zarr":
44+
generic = schema_id
45+
base_url = generic.split("/schemas/")[0]
46+
47+
if generic is None or base_url is None:
48+
raise RuntimeError("Could not find generic ome_zarr schema")
49+
50+
return cls(generic, base_url, registry)
51+
52+
53+
def main(raw_args=None):
54+
parser = ArgumentParser()
55+
parser.add_argument("mode", choices=["attributes", "zarr"])
56+
parser.add_argument("path", type=Path)
57+
58+
args = parser.parse_args(raw_args)
59+
60+
p: Path = args.path
61+
attrs: None | dict[str, Any] = None
62+
match args.mode:
63+
case "attributes":
64+
attrs = json.loads(p.read_text())
65+
case "zarr":
66+
attrs = json.loads(p.joinpath("zarr.json").read_text())["attributes"]
67+
case _:
68+
raise RuntimeError("unreachable")
69+
70+
if attrs is None:
71+
raise RuntimeError("unreachable")
72+
73+
schemas = SchemasInfo.load()
74+
75+
generic_schema = schemas.registry.get(schemas.generic_id)
76+
if generic_schema is None:
77+
raise RuntimeError("could not find generic schema")
78+
79+
validator = Validator(
80+
generic_schema.contents,
81+
registry=schemas.registry,
82+
)
83+
84+
result = dict()
85+
try:
86+
validator.validate(attrs)
87+
result["valid"] = True
88+
except ValidationError as e:
89+
result["valid"] = False
90+
result["message"] = str(e)
91+
92+
print(json.dumps(result))
93+
94+
95+
if __name__ == "__main__":
96+
main()

tests/test_validation.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,6 @@ def pytest_generate_tests(metafunc):
6363

6464
suites: List[Suite] = []
6565
ids: List[str] = []
66-
schema_store = {}
67-
for filename in glob.glob("ngff_spec/schemas/*.schema"):
68-
with open(filename) as o:
69-
schema = json.load(o)
70-
schema_store[schema["$id"]] = schema
7166

7267
# Validation
7368
for filename in glob.glob("tests/*.json"):

0 commit comments

Comments
 (0)