Source code for xhycom._discovery
"""File discovery helpers for HYCOM archive datasets."""
from __future__ import annotations
import glob
import os
import re
# Matches: archv.YYYY_DDD_HH or archv.YYYY_DDD (and archm equivalents)
_ARCHV_RE = re.compile(r"arch[vm]\.(\d{4})_(\d{3})(?:_(\d{2}))?$")
def _sort_key(basename: str) -> tuple[int, int, int]:
"""Return a sortable (year, doy, hour) tuple from an archv basename."""
m = _ARCHV_RE.search(os.path.basename(basename))
if m:
return (int(m.group(1)), int(m.group(2)), int(m.group(3) or 0))
return (0, 0, 0)
[docs]
def find_archv_files(path: str) -> list[str]:
"""Find HYCOM archive ``.ab`` file pairs and return sorted basenames.
Scans *path* for files whose names match ``archv.YYYY_DDD_HH`` or
``archv.YYYY_DDD``. The argument can be a directory or a glob pattern.
Parameters
----------
path : str
A directory path or a glob pattern (e.g. ``"data/archv.2020_*.a"``).
Returns
-------
list of str
Sorted list of basenames without the ``.a`` / ``.b`` extension,
ordered chronologically by (year, day-of-year, hour).
Raises
------
FileNotFoundError
If *path* is a non-existent directory.
ValueError
If no matching archive file pairs are found.
Examples
--------
>>> files = find_archv_files("data/")
>>> files[0]
'data/archv.2020_001_00'
"""
if os.path.isdir(path):
candidates = glob.glob(os.path.join(path, "archv.*.b")) + glob.glob(
os.path.join(path, "archm.*.b")
)
else:
# Treat as glob; strip any .a/.b suffix before globbing
base_pattern = re.sub(r"\.[ab]$", "", path)
candidates = glob.glob(base_pattern + ".b")
basenames = []
for f in candidates:
base = re.sub(r"\.[ab]$", "", f)
if _ARCHV_RE.search(os.path.basename(base)):
if os.path.exists(base + ".a") and os.path.exists(base + ".b"):
basenames.append(base)
if not basenames:
raise ValueError(
f"No archive .ab file pairs found at {path!r}. "
"Expected files named archv.YYYY_DDD_HH.[ab] or archm.YYYY_DDD_HH.[ab]"
)
return sorted(set(basenames), key=_sort_key)