Skip to content

Commit 2715587

Browse files
authored
Merge pull request #1277 from VisLab/fix_extras
Fixed prerelease partner loading issue
2 parents 64f93f3 + 21f6210 commit 2715587

5 files changed

Lines changed: 80 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ Previously, loading a schema whose version existed only in the prerelease cache
1111
- Removed the `check_prerelease` parameter from `load_schema_version()`, `load_schema()`, `from_string()`, and `from_dataframes()` in `hed_schema_io.py`.
1212
- Removed the parameter from `SchemaLoader` (base class) and all subclasses (`SchemaLoaderXML`, `SchemaLoaderWiki`, `SchemaLoaderJSON`, `SchemaLoaderDF`).
1313
- `get_hed_version_path()` in `hed_cache.py` now always searches both regular and prerelease directories (regular first).
14-
- `get_hed_versions()` in `hed_cache.py` now defaults to `check_prerelease=True`. **This is a silent API change**: external callers that omitted `check_prerelease` will now receive prerelease versions. Internal callers that need released-only versions (compliance checker, `deprecatedFrom` validation, hedId comparison) explicitly pass `check_prerelease=False`.
14+
- `get_hed_versions()` in `hed_cache.py` retains the default `check_prerelease=False` (no public API change). Internal callers that need prerelease inclusion (`get_hed_version_path`, error messages) now pass `check_prerelease=True` explicitly.
1515
- Default schema version is now resolved dynamically from the cache (highest released version) instead of being hardcoded, so new schema releases no longer require a code change.
16+
- `get_hed_version_path()` now automatically downloads schemas from GitHub when a requested version is not found in the local cache (default cache directory only).
17+
- `_load_schema_version_sub()` now raises `BAD_PARAMETERS` (was `FILE_NOT_FOUND`) when no version is specified and the cache is empty, since the problem is a missing argument rather than a missing file.
1618
- `check_schema_loading.py` simplified — removed `_is_prerelease_partner()` helper.
1719
- `run_loading_check()` now raises `ValueError` immediately for mutually exclusive flag combinations (`prerelease_only` + `exclude_prereleases`, or `library_filter` + `standard_only`), consistent with the existing CLI-level validation.
1820

hed/schema/hed_cache.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def get_cache_directory(cache_folder=None) -> str:
7979
return HED_CACHE_DIRECTORY
8080

8181

82-
def get_hed_versions(local_hed_directory=None, library_name=None, check_prerelease=True) -> Union[list, dict]:
82+
def get_hed_versions(local_hed_directory=None, library_name=None, check_prerelease=False) -> Union[list, dict]:
8383
"""Get the HED versions in the HED directory.
8484
8585
Parameters:
@@ -88,7 +88,7 @@ def get_hed_versions(local_hed_directory=None, library_name=None, check_prerelea
8888
None retrieves the standard schema only.
8989
Pass "all" to retrieve all standard and library schemas as a dict.
9090
check_prerelease (bool): If True, results can include prerelease schemas.
91-
Pass False to get only released versions (used by compliance checks).
91+
Default is False, returning only released versions.
9292
9393
Returns:
9494
Union[list, dict]: List of version numbers or dictionary {library_name: [versions]}.
@@ -136,21 +136,51 @@ def get_hed_versions(local_hed_directory=None, library_name=None, check_prerelea
136136

137137

138138
def get_hed_version_path(xml_version, library_name=None, local_hed_directory=None) -> Union[str, None]:
139-
"""Get HED XML file path in a directory. Only returns filenames that exist.
139+
"""Get the HED XML file path for a given version.
140+
141+
Searches the local cache first. If the version is not found and local_hed_directory
142+
is the default HED cache, the cache is refreshed from GitHub before a second lookup.
143+
No network call is made for custom directories.
140144
141145
Parameters:
142-
xml_version (str): Returns this version if it exists
143-
library_name (str or None): Optional the schema library name.
144-
local_hed_directory (str): Path to local HED directory. Defaults to HED_CACHE_DIRECTORY
146+
xml_version (str): The version string to look up.
147+
library_name (str or None): Optional schema library name.
148+
local_hed_directory (str or None): Path to local HED directory. Defaults to HED_CACHE_DIRECTORY.
149+
Passing a custom path disables the automatic GitHub refresh.
145150
146151
Returns:
147-
Union[str, None]: The path to the requested HED version the HED directory.
152+
Union[str, None]: The path to the requested HED XML file, or None.
148153
149154
"""
150155
if not local_hed_directory:
151156
local_hed_directory = HED_CACHE_DIRECTORY
152157

153-
hed_versions = get_hed_versions(local_hed_directory, library_name)
158+
result = _find_hed_version_path(xml_version, library_name, local_hed_directory)
159+
if result:
160+
return result
161+
162+
# Version not found locally — try refreshing cache from GitHub (default cache only).
163+
# cache_xml_versions() returns -1 on failure (network error, lock contention, rate limit).
164+
# In that case the second lookup will return None, which the caller treats as "version not found".
165+
if not xml_version or local_hed_directory != HED_CACHE_DIRECTORY:
166+
return None
167+
168+
cache_xml_versions()
169+
return _find_hed_version_path(xml_version, library_name, local_hed_directory)
170+
171+
172+
def _find_hed_version_path(xml_version, library_name, local_hed_directory):
173+
"""Look up a HED version path in the given directory without downloading.
174+
175+
Parameters:
176+
xml_version (str): The version to find.
177+
library_name (str or None): Optional schema library name.
178+
local_hed_directory (str): Directory to search.
179+
180+
Returns:
181+
Union[str, None]: The path if found, None otherwise.
182+
"""
183+
hed_versions = get_hed_versions(local_hed_directory, library_name, check_prerelease=True)
154184
if not hed_versions or not xml_version:
155185
return None
156186
if xml_version in hed_versions:

hed/schema/hed_schema_io.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,9 @@ def _load_schema_version_sub(xml_version, schema_namespace="", xml_folder=None,
362362
xml_version = versions[0]
363363
else:
364364
raise HedFileError(
365-
HedExceptions.FILE_NOT_FOUND,
366-
"No HED standard schema versions found in cache. Ensure schemas are installed or cached.",
365+
HedExceptions.BAD_PARAMETERS,
366+
"No version specified and no HED standard schema versions found in cache. "
367+
"Run hed.schema.cache_xml_versions() or install hedtools to populate the cache.",
367368
"",
368369
)
369370

@@ -392,7 +393,9 @@ def _load_schema_version_sub(xml_version, schema_namespace="", xml_folder=None,
392393
hed_schema = load_schema(hed_file_path, schema_namespace=schema_namespace, schema=schema, name=name)
393394
else:
394395
library_string = f"for library '{library_name}'" if library_name else ""
395-
known_versions = hed_cache.get_hed_versions(xml_folder, library_name=library_name if library_name else "all")
396+
known_versions = hed_cache.get_hed_versions(
397+
xml_folder, library_name=library_name if library_name else "all", check_prerelease=True
398+
)
396399
raise HedFileError(
397400
HedExceptions.FILE_NOT_FOUND,
398401
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. "

spec_tests/test_hed_cache.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ def test_get_hed_versions_library_prerelease(self):
8484
os.makedirs(prerelease_dir, exist_ok=True)
8585
fake_prerelease = os.path.join(prerelease_dir, "HED_score_0.0.1-alpha.1.xml")
8686
try:
87+
# Empty file is fine — get_hed_versions only parses filenames, never reads contents
8788
with open(fake_prerelease, "w") as f:
8889
f.write("")
89-
all_versions = hed_cache.get_hed_versions(self.hed_cache_dir, library_name="score")
90-
released_only = hed_cache.get_hed_versions(self.hed_cache_dir, library_name="score", check_prerelease=False)
90+
all_versions = hed_cache.get_hed_versions(self.hed_cache_dir, library_name="score", check_prerelease=True)
91+
released_only = hed_cache.get_hed_versions(self.hed_cache_dir, library_name="score")
9192
self.assertIsInstance(all_versions, list)
9293
self.assertIn("0.0.1-alpha.1", all_versions)
9394
self.assertNotIn("0.0.1-alpha.1", released_only)
@@ -110,6 +111,34 @@ def test_find_hed_expression(self):
110111
final_version = f"HED{version}.xml"
111112
self.assertFalse(hed_cache.version_pattern.match(final_version))
112113

114+
def test_get_hed_version_path_no_auto_refresh_for_custom_directory(self):
115+
"""get_hed_version_path returns None for a nonexistent version in a custom directory without downloading."""
116+
empty_dir = os.path.join(self.hed_cache_dir, "empty_subdir")
117+
os.makedirs(empty_dir, exist_ok=True)
118+
try:
119+
result = hed_cache.get_hed_version_path("99.99.99", local_hed_directory=empty_dir)
120+
self.assertIsNone(result)
121+
finally:
122+
shutil.rmtree(empty_dir)
123+
124+
def test_get_hed_version_path_auto_refresh_downloads_missing_version(self):
125+
"""get_hed_version_path automatically downloads from GitHub when a version is not cached locally."""
126+
# Use a fresh cache directory so the version is definitely not present
127+
fresh_cache = os.path.join(os.path.dirname(self.hed_cache_dir), "schema_cache_auto_refresh/")
128+
if os.path.exists(fresh_cache):
129+
shutil.rmtree(fresh_cache)
130+
os.makedirs(fresh_cache)
131+
saved = hed_cache.HED_CACHE_DIRECTORY
132+
try:
133+
hed_cache.HED_CACHE_DIRECTORY = fresh_cache
134+
# 8.0.0 is a released version that should be downloadable from GitHub
135+
result = hed_cache.get_hed_version_path("8.0.0")
136+
self.assertIsNotNone(result, "Auto-refresh should download 8.0.0 from GitHub")
137+
self.assertTrue(os.path.exists(result))
138+
finally:
139+
hed_cache.HED_CACHE_DIRECTORY = saved
140+
shutil.rmtree(fresh_cache, ignore_errors=True)
141+
113142

114143
class TestLocal(unittest.TestCase):
115144
@classmethod

tests/schema/test_hed_schema_io.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ def test_load_schema_version_default_no_standard_raises(self):
108108

109109
with self.assertRaises(HedFileError) as context:
110110
load_schema_version("", xml_folder=tmp_dir)
111-
self.assertIn("No HED standard schema", str(context.exception))
111+
self.assertEqual(context.exception.args[0], "BAD_PARAMETERS")
112+
self.assertIn("No version specified", str(context.exception))
112113

113114
def test_load_and_verify_tags(self):
114115
# Load 'testlib' by itself

0 commit comments

Comments
 (0)