-
Notifications
You must be signed in to change notification settings - Fork 188
[FIX] Enable format validation in validating BIDS schemas #2077
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
Merged
effigies
merged 10 commits into
bids-standard:master
from
candleindark:jsonschema-validator
Apr 9, 2025
Merged
Changes from 5 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d1c8ce7
feat: provide helper func to create jsonschema validator
candleindark 3571e68
feat: add helper func to get jsonschema validator for BIDS schemas
candleindark 5243a4f
fix: use BIDS schema validator
candleindark 280ac9a
fix: include extra format dependency for `jsonschema` package
candleindark 0c46207
test: validate BIDS schema against metaschema with `check-jsonschema`
candleindark 3078da6
fix(schema): Unescape regex characters that should not be escaped
effigies c68e0d1
fix head schema regex
effigies 2318fab
rf: use constants to define test schemas
candleindark a90f72d
Fully fix HED version regex
effigies fdf4534
Update src/schema/objects/formats.yaml
effigies File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 166 additions & 0 deletions
166
tools/schemacode/src/bidsschematools/tests/test_utils.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
from contextlib import nullcontext | ||
from typing import cast | ||
|
||
import pytest | ||
from jsonschema.exceptions import SchemaError, ValidationError | ||
from jsonschema.protocols import Validator as JsonschemaValidator | ||
from jsonschema.validators import Draft7Validator, Draft202012Validator | ||
|
||
from bidsschematools.utils import jsonschema_validator | ||
|
||
|
||
@pytest.fixture | ||
def draft7_schema() -> dict: | ||
""" | ||
A minimal valid Draft 7 schema requiring a 'name' property of type 'string'. | ||
""" | ||
return { | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"type": "object", | ||
"properties": {"name": {"type": "string"}}, | ||
"required": ["name"], | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def draft202012_schema() -> dict: | ||
""" | ||
A minimal valid Draft 2020-12 schema requiring a 'title' property of type 'string'. | ||
""" | ||
return { | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"type": "object", | ||
"properties": {"title": {"type": "string"}}, | ||
"required": ["title"], | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def draft202012_format_schema() -> dict: | ||
""" | ||
Draft 2020-12 schema that includes a 'format' requirement (e.g., 'email'). | ||
Used to test the 'check_format' parameter. | ||
""" | ||
return { | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"type": "object", | ||
"properties": {"email": {"type": "string", "format": "email"}}, | ||
"required": ["email"], | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def schema_no_dollar_schema() -> dict: | ||
""" | ||
Schema that lacks the '$schema' property altogether. | ||
Used to test that 'default_cls' is applied. | ||
""" | ||
return { | ||
"type": "object", | ||
"properties": {"foo": {"type": "string"}}, | ||
"required": ["foo"], | ||
} | ||
|
||
|
||
class TestJsonschemaValidator: | ||
@pytest.mark.parametrize( | ||
("fixture_name", "expected_validator_cls"), | ||
[ | ||
pytest.param("draft202012_format_schema", Draft202012Validator, id="Draft202012"), | ||
pytest.param("draft7_schema", Draft7Validator, id="Draft7"), | ||
], | ||
) | ||
@pytest.mark.parametrize("check_format", [True, False]) | ||
def test_set_by_dollar_schema( | ||
self, | ||
request: pytest.FixtureRequest, | ||
fixture_name: str, | ||
expected_validator_cls: type, | ||
check_format: bool, | ||
) -> None: | ||
""" | ||
Test that the correct validator class is returned for different '$schema' values | ||
""" | ||
# Dynamically retrieve the appropriate fixture schema based on fixture_name | ||
schema = request.getfixturevalue(fixture_name) | ||
|
||
validator = jsonschema_validator(schema, check_format=check_format) | ||
|
||
assert isinstance(validator, expected_validator_cls) | ||
|
||
@pytest.mark.parametrize( | ||
("check_format", "instance", "expect_raises"), | ||
[ | ||
(True, {"email": "[email protected]"}, False), | ||
(True, {"email": "not-an-email"}, True), | ||
(False, {"email": "not-an-email"}, False), | ||
], | ||
ids=[ | ||
"check_format=True, valid email", | ||
"check_format=True, invalid email", | ||
"check_format=False, invalid email", | ||
], | ||
) | ||
def test_check_format_email_scenarios( | ||
self, | ||
draft202012_format_schema: dict, | ||
check_format: bool, | ||
instance: dict, | ||
expect_raises: bool, | ||
) -> None: | ||
""" | ||
Parametrized test for check_format usage on valid/invalid email addresses under | ||
Draft202012Validator. | ||
""" | ||
validator = jsonschema_validator(draft202012_format_schema, check_format=check_format) | ||
|
||
# If expect_raises is True, we use pytest.raises(ValidationError) | ||
# Otherwise, we enter a no-op context | ||
ctx = pytest.raises(ValidationError) if expect_raises else nullcontext() | ||
|
||
with ctx: | ||
validator.validate(instance) # Should raise or not raise as parametrized | ||
|
||
@pytest.mark.parametrize( | ||
("schema_fixture", "expected_validator_cls"), | ||
[ | ||
# Scenario 1: no $schema => we expect the default_cls=Draft7Validator is used | ||
pytest.param("schema_no_dollar_schema", Draft7Validator, id="no-$schema"), | ||
# Scenario 2: has $schema => draft 2020-12 overrides the default_cls | ||
pytest.param("draft202012_schema", Draft202012Validator, id="with-$schema"), | ||
], | ||
) | ||
def test_default_cls( | ||
self, | ||
request: pytest.FixtureRequest, | ||
schema_fixture: str, | ||
expected_validator_cls: type, | ||
) -> None: | ||
""" | ||
If the schema has no '$schema' property, and we provide a 'default_cls', | ||
the returned validator should be an instance of that class. | ||
|
||
If the schema *does* have '$schema', then the default_cls is ignored, and | ||
the validator class is inferred from the schema's '$schema' field. | ||
""" | ||
# Dynamically grab whichever fixture is specified by schema_fixture: | ||
schema = request.getfixturevalue(schema_fixture) | ||
|
||
# Provide default_cls=Draft7Validator | ||
validator = jsonschema_validator( | ||
schema, | ||
check_format=False, | ||
default_cls=cast(type[JsonschemaValidator], Draft7Validator), | ||
) | ||
assert isinstance(validator, expected_validator_cls) | ||
|
||
def test_invalid_schema_raises_schema_error(self) -> None: | ||
""" | ||
Provide an invalid schema, ensuring that 'SchemaError' is raised. | ||
""" | ||
invalid_schema = { | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"type": 123, # 'type' must be string/array, so this is invalid | ||
} | ||
with pytest.raises(SchemaError): | ||
jsonschema_validator(invalid_schema, check_format=False) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.