From d1c8ce7d994e26b358cf7dcdd42b4f96140a96f8 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Fri, 28 Feb 2025 10:40:41 -0800 Subject: [PATCH 01/10] feat: provide helper func to create jsonschema validator --- .../src/bidsschematools/tests/test_utils.py | 166 ++++++++++++++++++ tools/schemacode/src/bidsschematools/utils.py | 53 ++++++ 2 files changed, 219 insertions(+) create mode 100644 tools/schemacode/src/bidsschematools/tests/test_utils.py diff --git a/tools/schemacode/src/bidsschematools/tests/test_utils.py b/tools/schemacode/src/bidsschematools/tests/test_utils.py new file mode 100644 index 0000000000..d6a87112fc --- /dev/null +++ b/tools/schemacode/src/bidsschematools/tests/test_utils.py @@ -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": "test@example.com"}, 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) diff --git a/tools/schemacode/src/bidsschematools/utils.py b/tools/schemacode/src/bidsschematools/utils.py index 8a47ad0e93..481eb2e7ed 100644 --- a/tools/schemacode/src/bidsschematools/utils.py +++ b/tools/schemacode/src/bidsschematools/utils.py @@ -3,6 +3,10 @@ import logging import os import sys +from typing import Any, Optional + +from jsonschema.protocols import Validator as JsonschemaValidator +from jsonschema.validators import validator_for from . import data @@ -82,3 +86,52 @@ def set_logger_level(lgr, level): lgr.warning("Do not know how to treat loglevel %s" % level) return lgr.setLevel(level) + + +def jsonschema_validator( + schema: dict[str, Any], + *, + check_format: bool, + default_cls: Optional[type[JsonschemaValidator]] = None, +) -> JsonschemaValidator: + """ + Create a jsonschema validator appropriate for validating instances against a given + JSON schema + + Parameters + ---------- + schema : dict[str, Any] + The JSON schema to validate against + check_format : bool + Indicates whether to check the format against format specifications in the + schema + default_cls : type[JsonschemaValidator] or None, optional + The default JSON schema validator class to use to create the + validator should the appropriate validator class cannot be determined based on + the schema (by assessing the `$schema` property). If `None`, the class + representing the latest JSON schema draft supported by the `jsonschema` package + + Returns + ------- + JsonschemaValidator + The JSON schema validator + + Raises + ------ + jsonschema.exceptions.SchemaError + If the JSON schema is invalid + """ + # Retrieve appropriate validator class for validating the given schema + validator_cls: type[JsonschemaValidator] = ( + validator_for(schema, default_cls) if default_cls is not None else validator_for(schema) + ) + + # Ensure the schema is valid + validator_cls.check_schema(schema) + + if check_format: + # Return a validator with format checking enabled + return validator_cls(schema, format_checker=validator_cls.FORMAT_CHECKER) + + # Return a validator with format checking disabled + return validator_cls(schema) From 3571e68cc7ca31893fe5ff21ce4c06927d92cac8 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Mon, 3 Mar 2025 20:43:17 -0800 Subject: [PATCH 02/10] feat: add helper func to get jsonschema validator for BIDS schemas This func returns a jsonschema validator that is bound to the meta schema for validating BIDS schemas. Format validation is enabled in this validator. Additionally, the validator is cached. --- tools/schemacode/src/bidsschematools/schema.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/schemacode/src/bidsschematools/schema.py b/tools/schemacode/src/bidsschematools/schema.py index 53bcbfabf4..12146972b8 100644 --- a/tools/schemacode/src/bidsschematools/schema.py +++ b/tools/schemacode/src/bidsschematools/schema.py @@ -6,10 +6,11 @@ import tempfile from collections.abc import Iterable, Mapping from copy import deepcopy -from functools import lru_cache +from functools import cache, lru_cache from importlib.resources import files from jsonschema import ValidationError, validate +from jsonschema.protocols import Validator as JsonschemaValidator from . import __bids_version__, __version__, utils from .types import Namespace @@ -100,6 +101,13 @@ def _dereference(namespace, base_schema): struct.update({**target, **struct}) +@cache +def get_schema_validator() -> JsonschemaValidator: + """Get the jsonschema validator for validating BIDS schemas.""" + metaschema = json.loads(files("bidsschematools.data").joinpath("metaschema.json").read_text()) + return utils.jsonschema_validator(metaschema, check_format=True) + + def dereference(namespace, inplace=True): """Replace references in namespace with the contents of the referred object. From 5243a4ff19549444bded7c4ce6840a51c818c086 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Mon, 3 Mar 2025 21:09:37 -0800 Subject: [PATCH 03/10] fix: use BIDS schema validator This schema validator have format validation enabled which is needed to correctly validate a BIDS schema against the meta schema --- tools/schemacode/src/bidsschematools/schema.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/schemacode/src/bidsschematools/schema.py b/tools/schemacode/src/bidsschematools/schema.py index 12146972b8..f86e059410 100644 --- a/tools/schemacode/src/bidsschematools/schema.py +++ b/tools/schemacode/src/bidsschematools/schema.py @@ -9,7 +9,7 @@ from functools import cache, lru_cache from importlib.resources import files -from jsonschema import ValidationError, validate +from jsonschema import ValidationError from jsonschema.protocols import Validator as JsonschemaValidator from . import __bids_version__, __version__, utils @@ -301,12 +301,11 @@ def filter_schema(schema, **kwargs): def validate_schema(schema: Namespace): """Validate a schema against the BIDS metaschema.""" - metaschema = json.loads(files("bidsschematools.data").joinpath("metaschema.json").read_text()) # validate is put in this try/except clause because the error is sometimes too long to # print in the terminal try: - validate(instance=schema.to_dict(), schema=metaschema) + get_schema_validator().validate(instance=schema.to_dict()) except ValidationError as e: with tempfile.NamedTemporaryFile( prefix="schema_error_", suffix=".txt", delete=False, mode="w+" From 280ac9ad85a13ec1d1aa7c3df7393e543a6e4396 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Thu, 6 Mar 2025 11:57:59 -0800 Subject: [PATCH 04/10] fix: include extra format dependency for `jsonschema` package To make format validation available in the `jsonschema` package, the `format` extra must be included. See https://python-jsonschema.readthedocs.io/en/latest/validate/#validating-formats for more details. --- tools/schemacode/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/schemacode/pyproject.toml b/tools/schemacode/pyproject.toml index 7e7b10fc52..90f2dc1f97 100644 --- a/tools/schemacode/pyproject.toml +++ b/tools/schemacode/pyproject.toml @@ -13,7 +13,7 @@ requires-python = ">=3.9" dependencies = [ "click", "pyyaml", - "jsonschema" + "jsonschema[format]" ] classifiers = [ "Development Status :: 4 - Beta", From 0c46207e63e7bed80f91af98ef600379a1a200f7 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Mon, 24 Mar 2025 23:51:42 -0700 Subject: [PATCH 05/10] test: validate BIDS schema against metaschema with `check-jsonschema` --- tools/schemacode/pyproject.toml | 1 + .../src/bidsschematools/tests/test_schema.py | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/tools/schemacode/pyproject.toml b/tools/schemacode/pyproject.toml index 90f2dc1f97..c00637cf68 100644 --- a/tools/schemacode/pyproject.toml +++ b/tools/schemacode/pyproject.toml @@ -37,6 +37,7 @@ render = [ ] tests = [ "bidsschematools[expressions,render]", + "check-jsonschema", "codecov", "coverage[toml]", "flake8", diff --git a/tools/schemacode/src/bidsschematools/tests/test_schema.py b/tools/schemacode/src/bidsschematools/tests/test_schema.py index 6b1449e13e..26f02293f0 100644 --- a/tools/schemacode/src/bidsschematools/tests/test_schema.py +++ b/tools/schemacode/src/bidsschematools/tests/test_schema.py @@ -1,7 +1,10 @@ """Tests for the bidsschematools package.""" +import json import os +import subprocess from collections.abc import Mapping +from importlib.resources import files import pytest from jsonschema.exceptions import ValidationError @@ -365,6 +368,41 @@ def test_valid_schema(): schema.validate_schema(namespace) +@pytest.mark.parametrize("regex_variant", ["default", "nonunicode", "python"]) +def test_valid_schema_with_check_jsonschema(tmp_path, regex_variant): + """ + Test that the BIDS schema is valid against the metaschema when validation is done + using the `check-jsonschema` CLI + """ + bids_schema = schema.load_schema().to_dict() + metaschema_path = str(files("bidsschematools.data").joinpath("metaschema.json")) + + # Save BIDS schema to a temporary file + bids_schema_path = tmp_path / "bids_schema.json" + bids_schema_path.write_text(json.dumps(bids_schema)) + + # Invoke the check-jsonschema to validate the BIDS schema + try: + subprocess.run( + [ + "check-jsonschema", + "--regex-variant", + regex_variant, + "--schemafile", + metaschema_path, + str(bids_schema_path), + ], + stdout=subprocess.PIPE, # Capture stdout + stderr=subprocess.STDOUT, # Set stderr to into stdout + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + pytest.fail( + f"check-jsonschema failed with code {e.returncode}:\n{e.stdout}", pytrace=False + ) + + def test_add_legal_field(): """Test that adding a legal field does not raise an error.""" namespace = schema.load_schema() From 3078da665d7807ea00a367a9ae47fb863bbd56bc Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 2 Apr 2025 17:03:26 -0400 Subject: [PATCH 06/10] fix(schema): Unescape regex characters that should not be escaped --- src/schema/objects/formats.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml index 7e0530a1d6..3a4f2c79f8 100644 --- a/src/schema/objects/formats.yaml +++ b/src/schema/objects/formats.yaml @@ -58,7 +58,7 @@ bids_uri: The validation for this format is minimal. It simply ensures that the value is a string with any characters that may appear in a valid URI, starting with "bids:". - pattern: 'bids:[0-9a-zA-Z/#:\?\_\-\.]+' + pattern: 'bids:[0-9a-zA-Z/#:?_\-.]+' dataset_relative: display_name: Path relative to the BIDS dataset directory description: | @@ -67,7 +67,7 @@ dataset_relative: The validation for this format is minimal. It simply ensures that the value is a string with any characters that may appear in a valid path, without starting with "/" (an absolute path). - pattern: '(?!/)[0-9a-zA-Z+/\_\-\.]+' + pattern: '(?!/)[0-9a-zA-Z+/_\-.]+' date: display_name: Date description: | @@ -98,7 +98,7 @@ file_relative: The validation for this format is minimal. It simply ensures that the value is a string with any characters that may appear in a valid path, without starting with "/" (an absolute path). - pattern: '(?!/)[0-9a-zA-Z+/\_\-\.]+' + pattern: '(?!/)[0-9a-zA-Z+/_\-.]+' participant_relative: display_name: Path relative to the participant directory description: | @@ -108,7 +108,7 @@ participant_relative: It simply ensures that the value is a string with any characters that may appear in a valid path, without starting with "/" (an absolute path) or "sub/" (a relative path starting with the participant directory, rather than relative to that directory). - pattern: '(?!/)(?!sub-)[0-9a-zA-Z+/\_\-\.]+' + pattern: '(?!/)(?!sub-)[0-9a-zA-Z+/_\-.]+' rrid: display_name: Research resource identifier description: | @@ -123,7 +123,7 @@ stimuli_relative: It simply ensures that the value is a string with any characters that may appear in a valid path, without starting with "/" (an absolute path) or "stimuli/" (a relative path starting with the stimuli directory, rather than relative to that directory). - pattern: '(?!/)(?!stimuli/)[0-9a-zA-Z+/\_\-\.]+' + pattern: '(?!/)(?!stimuli/)[0-9a-zA-Z+/_\-.]+' time: display_name: Time description: | From c68e0d1b40ca6dc3da349e88cbcf3c25a44c9f8d Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 2 Apr 2025 17:09:39 -0400 Subject: [PATCH 07/10] fix head schema regex --- src/schema/objects/formats.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml index 3a4f2c79f8..dac9273258 100644 --- a/src/schema/objects/formats.yaml +++ b/src/schema/objects/formats.yaml @@ -48,8 +48,8 @@ hed_version: description: | The version string of the used HED schema. pattern: '^(?:[a-zA-Z]+:)?(?:[a-zA-Z]+_)?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\ - (?:-(?:(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?\ - (?:\+(?:[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' + (?:-(?:(?:0|[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*))*))?\ + (?:\+(?:[0-9a-zA-Z\-]+(?:\.[0-9a-zA-Z\-]+)*))?$' bids_uri: display_name: BIDS uniform resource indicator description: | From 2318fab9f7eadc3d98bb376d41720761881e108c Mon Sep 17 00:00:00 2001 From: Isaac To Date: Wed, 2 Apr 2025 17:29:40 -0700 Subject: [PATCH 08/10] rf: use constants to define test schemas Use fixtures are unnecessarily complicated when the schemas don't change --- .../src/bidsschematools/tests/test_utils.py | 123 ++++++++---------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/tools/schemacode/src/bidsschematools/tests/test_utils.py b/tools/schemacode/src/bidsschematools/tests/test_utils.py index d6a87112fc..4ee87663e2 100644 --- a/tools/schemacode/src/bidsschematools/tests/test_utils.py +++ b/tools/schemacode/src/bidsschematools/tests/test_utils.py @@ -1,5 +1,5 @@ from contextlib import nullcontext -from typing import cast +from typing import Any, cast import pytest from jsonschema.exceptions import SchemaError, ValidationError @@ -8,82 +8,68 @@ 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"], - } +DRAFT_7_SCHEMA = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": {"name": {"type": "string"}}, + "required": ["name"], +} +""" +A minimal valid Draft 7 schema requiring a 'name' property of type 'string'. +""" + + +DRAFT_202012_SCHEMA = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": {"title": {"type": "string"}}, + "required": ["title"], +} +""" +A minimal valid Draft 2020-12 schema requiring a 'title' property of type 'string'. +""" + +DRAFT_202012_FORMAT_SCHEMA = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": {"email": {"type": "string", "format": "email"}}, + "required": ["email"], +} +""" +Draft 2020-12 schema that includes a 'format' requirement (e.g., 'email'). +Used to test the 'check_format' parameter. +""" + + +SCHEMA_NO_DOLLAR_SCHEMA = { + "type": "object", + "properties": {"foo": {"type": "string"}}, + "required": ["foo"], +} +""" +Schema that lacks the '$schema' property altogether. +Used to test that 'default_cls' is applied. +""" class TestJsonschemaValidator: @pytest.mark.parametrize( - ("fixture_name", "expected_validator_cls"), + ("schema", "expected_validator_cls"), [ - pytest.param("draft202012_format_schema", Draft202012Validator, id="Draft202012"), - pytest.param("draft7_schema", Draft7Validator, id="Draft7"), + pytest.param(DRAFT_202012_FORMAT_SCHEMA, Draft202012Validator, id="Draft202012"), + pytest.param(DRAFT_7_SCHEMA, Draft7Validator, id="Draft7"), ], ) @pytest.mark.parametrize("check_format", [True, False]) def test_set_by_dollar_schema( self, - request: pytest.FixtureRequest, - fixture_name: str, + schema: dict[str, Any], 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) @@ -103,7 +89,6 @@ def test_set_by_dollar_schema( ) def test_check_format_email_scenarios( self, - draft202012_format_schema: dict, check_format: bool, instance: dict, expect_raises: bool, @@ -112,7 +97,7 @@ def test_check_format_email_scenarios( Parametrized test for check_format usage on valid/invalid email addresses under Draft202012Validator. """ - validator = jsonschema_validator(draft202012_format_schema, check_format=check_format) + validator = jsonschema_validator(DRAFT_202012_FORMAT_SCHEMA, check_format=check_format) # If expect_raises is True, we use pytest.raises(ValidationError) # Otherwise, we enter a no-op context @@ -122,18 +107,17 @@ def test_check_format_email_scenarios( validator.validate(instance) # Should raise or not raise as parametrized @pytest.mark.parametrize( - ("schema_fixture", "expected_validator_cls"), + ("schema", "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"), + 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"), + pytest.param(DRAFT_202012_SCHEMA, Draft202012Validator, id="with-$schema"), ], ) def test_default_cls( self, - request: pytest.FixtureRequest, - schema_fixture: str, + schema: dict[str, Any], expected_validator_cls: type, ) -> None: """ @@ -143,9 +127,6 @@ def test_default_cls( 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, From a90f72d33b966d88ce94dc61ff49665b22177f6e Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 2 Apr 2025 21:36:11 -0400 Subject: [PATCH 09/10] Fully fix HED version regex --- src/schema/objects/formats.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml index dac9273258..11307b2a45 100644 --- a/src/schema/objects/formats.yaml +++ b/src/schema/objects/formats.yaml @@ -47,9 +47,15 @@ hed_version: display_name: HED Version description: | The version string of the used HED schema. - pattern: '^(?:[a-zA-Z]+:)?(?:[a-zA-Z]+_)?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\ - (?:-(?:(?:0|[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*))*))?\ - (?:\+(?:[0-9a-zA-Z\-]+(?:\.[0-9a-zA-Z\-]+)*))?$' + pattern: "\ + (?:[a-zA-Z]+:)?\ + (?:[a-zA-Z]+_)?\ + (?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\ + (?:-(?:\ + (?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z\\-]*)\ + (?:\\.(?:0|[1-9][0-9]*|[0-9]*[a-zA-Z\\-][0-9a-zA-Z\\-]*))*\ + ))?\ + (?:\\+(?:[0-9a-zA-Z\\-]+(?:\\.[0-9a-zA-Z\\-]+)*))?" bids_uri: display_name: BIDS uniform resource indicator description: | From fdf4534d275938c2c959c69142a9919d6b993d23 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 8 Apr 2025 15:03:35 -0400 Subject: [PATCH 10/10] Update src/schema/objects/formats.yaml --- src/schema/objects/formats.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml index 11307b2a45..15a43b7bce 100644 --- a/src/schema/objects/formats.yaml +++ b/src/schema/objects/formats.yaml @@ -47,15 +47,7 @@ hed_version: display_name: HED Version description: | The version string of the used HED schema. - pattern: "\ - (?:[a-zA-Z]+:)?\ - (?:[a-zA-Z]+_)?\ - (?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\ - (?:-(?:\ - (?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z\\-]*)\ - (?:\\.(?:0|[1-9][0-9]*|[0-9]*[a-zA-Z\\-][0-9a-zA-Z\\-]*))*\ - ))?\ - (?:\\+(?:[0-9a-zA-Z\\-]+(?:\\.[0-9a-zA-Z\\-]+)*))?" + pattern: "(?:[a-zA-Z]+:)?(?:[a-zA-Z]+_)?(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)" bids_uri: display_name: BIDS uniform resource indicator description: |