-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial validate support, with --strict option #142
Changes from 6 commits
ef746b1
ace592a
7d26157
145b938
4888698
a20c8aa
7520081
cbd4ee6
f0683a6
d093cda
33298bd
4c28024
b388a3a
674ebb7
7b3ab42
e380451
d301229
c37e116
c1ed016
9ccebe0
e3abc33
5fef946
3ad5315
5fcfe94
6774922
2bbce18
96961bd
c03b0a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ channels: | |
dependencies: | ||
- flake8 | ||
- ipython | ||
- jsonschema | ||
- mypy | ||
- omero-py | ||
- pip | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import json | ||
import os | ||
from typing import Dict | ||
|
||
from jsonschema import RefResolver | ||
|
||
|
||
class LocalRefResolver(RefResolver): | ||
def resolve_remote(self, url: str) -> Dict: | ||
# Use remote URL to generate local path | ||
rel_path = url.replace("https://ngff.openmicroscopy.org/", "../schemas/") | ||
sbesson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
curr_dir = os.path.dirname(__file__) | ||
path = os.path.join(curr_dir, rel_path) | ||
path = os.path.normpath(path) | ||
# Load local document and cache it | ||
document = load_json(path) | ||
self.store[url] = document | ||
return document | ||
|
||
|
||
def load_json(path: str) -> Dict: | ||
with open(path) as f: | ||
document = json.loads(f.read()) | ||
return document | ||
|
||
|
||
def get_schema(version: str) -> Dict: | ||
curr_dir = os.path.dirname(__file__) | ||
# The paths here match the paths in the ngff repo (and public schemas) | ||
path = os.path.join( | ||
curr_dir, f"../schemas/{version}/schemas/json_schema/image.schema" | ||
) | ||
path = os.path.normpath(path) | ||
return load_json(path) | ||
|
||
|
||
def get_strict_schema(version: str) -> Dict: | ||
sbesson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
curr_dir = os.path.dirname(__file__) | ||
path = os.path.join( | ||
curr_dir, f"../schemas/{version}/schemas/json_schema/strict_image.schema" | ||
) | ||
path = os.path.normpath(path) | ||
return load_json(path) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,26 @@ def info(path: str, stats: bool = False) -> Iterator[Node]: | |
yield node | ||
|
||
|
||
def validate(path: str, warnings: bool) -> Iterator[Node]: | ||
""" | ||
Validate OME-NGFF data | ||
|
||
All :class:`Nodes <ome_utils.reader.Node>` that are found from the given path will | ||
be visited recursively. | ||
""" | ||
zarr = parse_url(path) | ||
assert zarr, f"not a zarr: {zarr}" | ||
reader = Reader(zarr) | ||
for node in reader(): | ||
if not node.specs: | ||
print(f"not an ome-zarr node: {node}") | ||
continue | ||
|
||
if hasattr(node, "validate"): | ||
node.validate(warnings) | ||
yield node | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the logic is duplicate from This raises the question of whether the two commands should be combined e.g. to satisfy the use cases where someone will want to inspect an OME-Zarr, validate it and output a minimal report if the validation passes? There is likely an argument to doing both actions independently (validation or display information) but could that be equally achieved via flags ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, if you just do this: does it
Then you can disable both with: ?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just my 2cents here: I think the cleanest design would be to implement This has the added advantage that it allows further customization by users. If you go for this functionality I would also adopt the "usual" syntax for visit in python: If |
||
|
||
|
||
def download(input_path: str, output_dir: str = ".") -> None: | ||
"""Download an OME-Zarr from the given path. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
{ | ||
will-moore marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"$id": "https://ngff.openmicroscopy.org/0.1/schemas/json_schema/image.schema", | ||
"title": "NGFF Image", | ||
"description": "JSON from OME-NGFF .zattrs", | ||
"type": "object", | ||
"properties": { | ||
"multiscales": { | ||
"description": "The multiscale datasets for this image", | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"name": { | ||
"type": "string" | ||
}, | ||
"datasets": { | ||
"type": "array", | ||
"minItems": 1, | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"path": { | ||
"type": "string" | ||
} | ||
}, | ||
"required": ["path"] | ||
} | ||
}, | ||
"version": { | ||
"type": "string", | ||
"enum": [ | ||
"0.1" | ||
] | ||
}, | ||
"metadata": { | ||
"type": "object", | ||
"properties": { | ||
"method": { | ||
"type": "string" | ||
}, | ||
"version": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
}, | ||
"required": [ | ||
"datasets" | ||
] | ||
}, | ||
"minItems": 1, | ||
"uniqueItems": true | ||
}, | ||
"omero": { | ||
"type": "object", | ||
"properties": { | ||
"channels": { | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"window": { | ||
"type": "object", | ||
"properties": { | ||
"end": { | ||
"type": "number" | ||
}, | ||
"max": { | ||
"type": "number" | ||
}, | ||
"min": { | ||
"type": "number" | ||
}, | ||
"start": { | ||
"type": "number" | ||
} | ||
}, | ||
"required": [ | ||
"start", | ||
"min", | ||
"end", | ||
"max" | ||
] | ||
}, | ||
"label": { | ||
"type": "string" | ||
}, | ||
"family": { | ||
"type": "string" | ||
}, | ||
"color": { | ||
"type": "string" | ||
}, | ||
"active": { | ||
"type": "boolean" | ||
} | ||
}, | ||
"required": [ | ||
"window", | ||
"color" | ||
] | ||
} | ||
} | ||
}, | ||
"required": [ | ||
"channels" | ||
] | ||
} | ||
}, | ||
"required": [ "multiscales" ] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the role of this argument to trigger the validation of the recommended attributes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. We have various terminology: There are Spec rules that
SHOULD
be followed (so it'srecommended
that you do). These are covered by astrict schema
and if you don't then this gives youwarnings
(if you use that flag)!It would be nice to settle on ONE term that could cover all these cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Trying to look into the terminology used by others, I came back to SHACL which we have not decided to adopt at this stage but we might want to come back to in the near future.
A relevant concept is the idea of severity defined here and used by the validation report. In particular, looking at the pySHACL library, the
--allow-warnings
option effectively controls the severity level beyond which the result will be considered as invalid.