RFC-4: making orientation.schema.json usable as a validator

Open
#586 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
json, python, yaml

Research direction

Start with rfc/4/orientation.yml and the gen-json-schema entry point, then inspect the four generated artifacts for RFC 4. Check the sixteen test documents described in the issue, including valid partial orientations and rejected invalid values. Done means the generated schema validates the intended documents and rejects the listed invalid cases without unresolvable references.

Written by the indexing model from the issue text.

Description

rfc-4

I have been using rfc/4/orientation.schema.json to validate axes metadata in
a reader, and noticed it never reports an error, whatever the document:

Draft202012Validator(schema).iter_errors(
    {"axes": ["not an object", {"name": 42, "type": "nonsense"}]}
)  # no errors

The schema has no entry point: gen-json-schema emits root properties only
for the class marked tree_root: true, and no class in orientation.yml
carries it. The root comes out as
{"type": "object", "additionalProperties": true, "$defs": {...}}, so the
eleven definitions are never referenced.

Marking Axes as the root is not quite enough

I tried that first, and it flips the schema to rejecting everything, including
{"axes": [{"name": "x", "type": "space", "unit": "micrometer"}]}, with
is not of type 'string'.

Axes.axes has no range, so default_range: string applies and is emitted
beside the union:

"items": {"anyOf": [{"$ref": "#/$defs/SpaceAxis"}, ...], "type": "string"}

JSON Schema keywords combine with AND, so an axis would have to be an object
and a string at once. SpaceAxis.orientation has the same pair.

Declaring a range clears it: range: AnatomicalOrientation for orientation,
and for the union the linkml:Any idiom, which emits
{"$ref": "#/$defs/Any", "anyOf": [...]} and no type keyword:

  Any:
    class_uri: linkml:Any

  Axes:
    tree_root: true
    attributes:
      axes:
        multivalued: true
        range: Any
        any_of: [{range: SpaceAxis}, {range: TimeAxis}, {range: ChannelAxis}]

One gotcha I ran into: an abstract class cannot serve as the range, since
abstract classes are not emitted into $defs and the $ref is then
unresolvable.

Three constraints that would want relaxing at the same time

They are inert today, but they reject valid OME-Zarr once the root is bound:

  • unit is required on SpaceAxis and TimeAxis. In the 0.4 and 0.6 axes
    schemas an axis requires only name, and unit is optional.
  • AxesNames allows only t, c, z, y, x and SpaceAxesNames only z, y, x,
    while the core spec takes any non-empty string, and RFC 3 widens it further.
    The file already anticipates this:
    # AxesNames are bound to change after RFC3 is implemented.
  • ChannelAxis requires the name to be c.

Trying it out

With tree_root, the declared ranges, optional unit, free-string names and
the vocabulary restored (#585), sixteen test documents come out as
RFC 4 describes: partial orientation and "orientation": null accepted, a
non-anatomical type, a value outside the vocabulary and an orientation on a
non-space axis rejected.

Axis-name uniqueness stays with implementations either way: JSON Schema has no
way to compare one property across items. Mutual exclusion of antonym pairs, on
the other hand, is expressible, at one not / contains / minContains clause
per anatomical pair. I had it wrong when I opened this.

For context, the normative RFC 4 schema for 0.9.dev1 is being written by hand in
ome/ngff-spec#190, as schemas/axis_orientation.schema. This issue is about the
model here and the four artifacts generated from it, which implementations
bundle and read as normative.

Happy to open a PR with these changes if that would be useful.

Dominant language
Python
Stars
171
Forks
75
Avg merge
2d 3h
Merged PRs (30d)
20

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ome/ngff

All issues in ome/ngff

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.