Skip to content

Commit 3731abf

Browse files
committed
Added a prerelease version to load_schema_version
1 parent 55fa874 commit 3731abf

4 files changed

Lines changed: 111 additions & 18 deletions

File tree

docs/index.rst

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
Python HEDTools
66
===============
77

8-
Welcome to the Python HEDTools documentation! This package provides comprehensive tools for working with **Hierarchical Event Descriptors (HED)** - a standardized framework for annotating events and experimental metadata in neuroscience and beyond.
9-
10-
What is HED?
11-
------------
12-
13-
HED is a standardized vocabulary and annotation framework designed to systematically describe events experimental data, particularly neuroimaging and behavioral data. It's integrated into major neuroimaging standards:
8+
Welcome to the Python HEDTools documentation!
9+
This package provides comprehensive tools for working with
10+
**Hierarchical Event Descriptors (HED)** - a standardized framework
11+
for annotating events and experimental metadata in neuroscience and beyond.
12+
HED is integrated into major neuroimaging standards:
1413

1514
* `BIDS <https://bids.neuroimaging.io/>`_ (Brain Imaging Data Structure)
1615
* `NWB <https://www.nwb.org/>`_ (Neurodata Without Borders)
1716

18-
Key features
19-
------------
17+
and this package enables you to validate, analyze, and manipulate HED annotations in various formats.
18+
19+
Pythn HEDTools features
20+
-----------------------
2021

2122
* **Validation**: Verify HED annotations against official schemas
2223
* **Analysis**: Search, filter, and summarize HED-annotated data

hed/schema/hed_cache.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,16 @@ def get_hed_version_path(xml_version, library_name=None, local_hed_directory=Non
154154
if not hed_versions or not xml_version:
155155
return None
156156
if xml_version in hed_versions:
157-
return _create_xml_filename(xml_version, library_name, local_hed_directory, check_prerelease)
157+
# Check regular directory first
158+
regular_path = _create_xml_filename(xml_version, library_name, local_hed_directory, False)
159+
if os.path.exists(regular_path):
160+
return regular_path
161+
162+
# If check_prerelease is True, also check prerelease directory
163+
if check_prerelease:
164+
prerelease_path = _create_xml_filename(xml_version, library_name, local_hed_directory, True)
165+
if os.path.exists(prerelease_path):
166+
return prerelease_path
158167
return None
159168

160169

hed/schema/hed_schema_io.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
MAX_MEMORY_CACHE = 40
2323

2424

25-
def load_schema_version(xml_version=None, xml_folder=None) -> Union["HedSchema", "HedSchemaGroup"]:
25+
def load_schema_version(xml_version=None, xml_folder=None, check_prerelease=False) -> Union["HedSchema", "HedSchemaGroup"]:
2626
"""Return a HedSchema or HedSchemaGroup extracted from xml_version
2727
2828
Parameters:
@@ -31,6 +31,7 @@ def load_schema_version(xml_version=None, xml_folder=None) -> Union["HedSchema",
3131
based on the output of HedSchema.get_formatted_version
3232
Basic format: `[schema_namespace:][library_name_]X.Y.Z`.
3333
xml_folder (str): Path to a folder containing schema.
34+
check_prerelease (bool): If True, check the prerelease directory for schemas.
3435
3536
Returns:
3637
Union[HedSchema, HedSchemaGroup]: The schema or schema group extracted.
@@ -49,14 +50,17 @@ def load_schema_version(xml_version=None, xml_folder=None) -> Union["HedSchema",
4950
raise HedFileError(HedExceptions.CANNOT_PARSE_JSON, str(e), xml_version) from e
5051
if xml_version and isinstance(xml_version, list):
5152
xml_versions = parse_version_list(xml_version)
52-
schemas = [_load_schema_version(xml_version=version, xml_folder=xml_folder) for version in xml_versions.values()]
53+
schemas = [
54+
_load_schema_version(xml_version=version, xml_folder=xml_folder, check_prerelease=check_prerelease)
55+
for version in xml_versions.values()
56+
]
5357
if len(schemas) == 1:
5458
return schemas[0]
5559

5660
name = ",".join([schema.version for schema in schemas])
5761
return HedSchemaGroup(schemas, name=name)
5862
else:
59-
return _load_schema_version(xml_version=xml_version, xml_folder=xml_folder)
63+
return _load_schema_version(xml_version=xml_version, xml_folder=xml_folder, check_prerelease=check_prerelease)
6064

6165

6266
def load_schema(hed_path, schema_namespace=None, schema=None, name=None) -> "HedSchema":
@@ -246,7 +250,7 @@ def parse_version_list(xml_version_list) -> dict:
246250

247251

248252
@functools.lru_cache(maxsize=MAX_MEMORY_CACHE)
249-
def _load_schema_version(xml_version=None, xml_folder=None):
253+
def _load_schema_version(xml_version=None, xml_folder=None, check_prerelease=False):
250254
"""Return specified version
251255
252256
Parameters:
@@ -256,6 +260,7 @@ def _load_schema_version(xml_version=None, xml_folder=None):
256260
The schema namespace must be the same and not repeated if loading multiple merged schemas.
257261
258262
xml_folder (str): Path to a folder containing schema.
263+
check_prerelease (bool): If True, check the prerelease directory for schemas.
259264
260265
Returns:
261266
Union[HedSchema, HedSchemaGroup]: The requested HedSchema object.
@@ -279,14 +284,18 @@ def _load_schema_version(xml_version=None, xml_folder=None):
279284
else:
280285
xml_versions = [""]
281286

282-
first_schema = _load_schema_version_sub(xml_versions[0], schema_namespace, xml_folder=xml_folder, name=name)
287+
first_schema = _load_schema_version_sub(
288+
xml_versions[0], schema_namespace, xml_folder=xml_folder, check_prerelease=check_prerelease, name=name
289+
)
283290
filenames = [os.path.basename(first_schema.filename)]
284291

285292
# Collect all duplicate issues for proper error reporting
286293
all_duplicate_issues = []
287294

288295
for version in xml_versions[1:]:
289-
_load_schema_version_sub(version, schema_namespace, xml_folder=xml_folder, schema=first_schema, name=name)
296+
_load_schema_version_sub(
297+
version, schema_namespace, xml_folder=xml_folder, check_prerelease=check_prerelease, schema=first_schema, name=name
298+
)
290299

291300
# Collect duplicate errors when merging schemas in the same namespace
292301
current_filename = os.path.basename(first_schema.filename)
@@ -319,13 +328,14 @@ def _load_schema_version(xml_version=None, xml_folder=None):
319328
return first_schema
320329

321330

322-
def _load_schema_version_sub(xml_version, schema_namespace="", xml_folder=None, schema=None, name=""):
331+
def _load_schema_version_sub(xml_version, schema_namespace="", xml_folder=None, check_prerelease=False, schema=None, name=""):
323332
"""Return specified version(single version only for this one)
324333
325334
Parameters:
326335
xml_version (str): HED version format string. Expected format: '[library_name_]X.Y.Z'
327336
schema_namespace (str): The prefix this will have
328337
xml_folder (str): Path to a folder containing schema
338+
check_prerelease (bool): If True, check the prerelease directory for schemas
329339
schema (HedSchema or None): A HED schema to merge this new file into.
330340
name (str): User supplied identifier for this schema
331341
@@ -358,14 +368,16 @@ def _load_schema_version_sub(xml_version, schema_namespace="", xml_folder=None,
358368
)
359369

360370
hed_file_path = hed_cache.get_hed_version_path(
361-
version_to_validate, library_name=library_name, local_hed_directory=xml_folder
371+
version_to_validate, library_name=library_name, local_hed_directory=xml_folder, check_prerelease=check_prerelease
362372
)
363373

364374
if hed_file_path:
365375
hed_schema = load_schema(hed_file_path, schema_namespace=schema_namespace, schema=schema, name=name)
366376
else:
367377
library_string = f"for library '{library_name}'" if library_name else ""
368-
known_versions = hed_cache.get_hed_versions(xml_folder, library_name=library_name if library_name else "all")
378+
known_versions = hed_cache.get_hed_versions(
379+
xml_folder, library_name=library_name if library_name else "all", check_prerelease=check_prerelease
380+
)
369381
raise HedFileError(
370382
HedExceptions.FILE_NOT_FOUND,
371383
f"HED version {library_string}: '{version_to_validate}' not found. Check {hed_cache.get_cache_directory(xml_folder)} for cache or https://github.com/hed-standard/hed-schemas/tree/main/library_schemas. "

tests/schema/test_hed_schema_io.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,74 @@ def test_triple_prefixes(self):
569569
parse_version_list(["test:score", "ol:otherlib", "test:testlib", "abc:anotherlib"]),
570570
{"test": "test:score,testlib", "ol": "ol:otherlib", "abc": "abc:anotherlib"},
571571
)
572+
573+
574+
class TestPrereleaseParameter(unittest.TestCase):
575+
"""Test the check_prerelease parameter functionality."""
576+
577+
@classmethod
578+
def setUpClass(cls):
579+
"""Set up test fixtures."""
580+
cls.schema_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../data/schema_tests/")
581+
582+
def test_check_prerelease_parameter_exists(self):
583+
"""Test that check_prerelease parameter is accepted by load_schema_version."""
584+
# This should not raise an error about unexpected keyword argument
585+
try:
586+
# Try to load a nonexistent version with check_prerelease parameter
587+
load_schema_version("99.99.99", xml_folder=self.schema_dir, check_prerelease=True)
588+
except HedFileError:
589+
# Expected - version doesn't exist, but parameter was accepted
590+
pass
591+
except TypeError as e:
592+
self.fail(f"check_prerelease parameter not accepted: {e}")
593+
594+
def test_check_prerelease_default_false(self):
595+
"""Test that check_prerelease defaults to False for backward compatibility."""
596+
# Load a regular schema without the parameter (should work)
597+
schema = load_schema_version("8.2.0", xml_folder=self.schema_dir)
598+
self.assertIsInstance(schema, HedSchema, "Should load regular schema without check_prerelease")
599+
self.assertEqual(schema.version_number, "8.2.0", "Should have correct version")
600+
601+
def test_check_prerelease_false_explicit(self):
602+
"""Test that check_prerelease=False works explicitly."""
603+
# Load a regular schema with check_prerelease explicitly set to False
604+
schema = load_schema_version("8.2.0", xml_folder=self.schema_dir, check_prerelease=False)
605+
self.assertIsInstance(schema, HedSchema, "Regular schema should load with check_prerelease=False")
606+
self.assertEqual(schema.version_number, "8.2.0", "Should have correct version")
607+
608+
def test_check_prerelease_with_namespace(self):
609+
"""Test that check_prerelease parameter works with namespace."""
610+
# Load regular schema with namespace and check_prerelease=False
611+
schema = load_schema_version("test:8.2.0", xml_folder=self.schema_dir, check_prerelease=False)
612+
self.assertIsInstance(schema, HedSchema, "Should load with namespace")
613+
self.assertEqual(schema._namespace, "test:", "Should have correct namespace")
614+
self.assertEqual(schema.version_number, "8.2.0", "Should have correct version")
615+
616+
def test_nonexistent_version_error_message(self):
617+
"""Test that error messages are consistent with/without check_prerelease."""
618+
# Both should give similar error messages for nonexistent versions
619+
with self.assertRaises(HedFileError) as context1:
620+
load_schema_version("99.99.99", xml_folder=self.schema_dir, check_prerelease=False)
621+
622+
with self.assertRaises(HedFileError) as context2:
623+
load_schema_version("99.99.99", xml_folder=self.schema_dir, check_prerelease=True)
624+
625+
# Both should mention "not found"
626+
self.assertIn("not found", str(context1.exception).lower())
627+
self.assertIn("not found", str(context2.exception).lower())
628+
629+
def test_check_prerelease_parameter_in_signature(self):
630+
"""Test that check_prerelease is properly defined in function signature."""
631+
import inspect
632+
633+
sig = inspect.signature(load_schema_version)
634+
self.assertIn("check_prerelease", sig.parameters, "check_prerelease should be in function signature")
635+
self.assertEqual(sig.parameters["check_prerelease"].default, False, "check_prerelease should default to False")
636+
637+
def test_check_prerelease_with_regular_schema(self):
638+
"""Test that regular schemas load correctly with check_prerelease=True."""
639+
# This tests the bug fix: regular schemas should still be found when check_prerelease=True
640+
schema = load_schema_version("8.2.0", xml_folder=self.schema_dir, check_prerelease=True)
641+
self.assertIsInstance(schema, HedSchema, "Regular schema should load with check_prerelease=True")
642+
self.assertEqual(schema.version_number, "8.2.0", "Should have correct version")

0 commit comments

Comments
 (0)