Skip to content

Commit 515d5a6

Browse files
committed
added unit test and local index improvements
1 parent ef4a4fc commit 515d5a6

2 files changed

Lines changed: 107 additions & 4 deletions

File tree

src/herbie/core.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,29 @@ def find_idx(self, overwrite: bool = False) -> tuple[Optional[Union[Path, str]],
479479
if not overwrite:
480480
local_grib = self.get_localFilePath()
481481
for suffix in self.IDX_SUFFIX:
482-
local_idx = local_grib.with_suffix(suffix)
482+
# There is no easy way to know if the index suffix needs to be appended or overwrite the grib suffix.
483+
# Hence there is a need to do various checks to verify if the index file exists locally or not.
484+
485+
# This is the case of GFS, where the grib file ends with ".grb2" and the index file is ".grb2.inv"
486+
# The index suffix needs to overwrite the grib file suffix, now ending in ".grb2.inv"
487+
if suffix.startswith(local_grib.suffix):
488+
local_idx = local_grib.with_suffix(suffix)
489+
490+
# This is the case of GFS, where the grib file ends with ".f000" and the index file is ".idx"
491+
# The index suffix needs to be appended to the grib file suffix, now ending in ".f000.idx"
492+
else:
493+
local_idx = local_grib.with_suffix(local_grib.suffix + suffix)
494+
483495
if local_idx.exists() and not self.overwrite:
484496
return (local_idx, "local")
497+
498+
# If the index file does not exists locally, we will need to try another variation of the index file name.
499+
# This is the case of IFS, where the grib file ends with ".grib2" and the index file is ".index"
500+
# The index suffix needs to overwrite the grib file suffix, now ending in ".index"
501+
else:
502+
local_idx = local_grib.with_suffix(suffix)
503+
if local_idx.exists() and not self.overwrite:
504+
return (local_idx, "local")
485505

486506
# If priority list is set, we want to search SOURCES in that
487507
# priority order. If priority is None, then search all SOURCES
@@ -740,9 +760,34 @@ def index_as_dataframe(self) -> pd.DataFrame:
740760
# eccodes keywords explained here:
741761
# https://confluence.ecmwf.int/display/UDOC/Identification+keywords
742762

743-
r = requests.get(self.idx)
744-
idxs = [json.loads(x) for x in r.text.split("\n") if x]
745-
r.close()
763+
if self.idx_source in ["local"]:
764+
with open(self.idx, "r") as file:
765+
read_this_idx = StringIO(file.read())
766+
else:
767+
print(f"Downloading inventory file from {self.idx=}")
768+
response = requests.get(self.idx)
769+
if response.status_code != 200:
770+
response.raise_for_status()
771+
response.close()
772+
raise ValueError(
773+
f"\nCant open index file {self.idx}\n"
774+
f"Download the full file first (with `H.download()`).\n"
775+
f"You will need to remake the Herbie object (H = `Herbie()`)\n"
776+
f"or delete this cached property: `del H.index_as_dataframe()`"
777+
)
778+
779+
read_this_idx = StringIO(response.text)
780+
response.close()
781+
782+
index_filepath = self.get_localIndexFilePath()
783+
os.makedirs(os.path.dirname(index_filepath), exist_ok=True)
784+
785+
with open(index_filepath, "w") as file:
786+
file.write(read_this_idx.read())
787+
# reset the cursor to the beggining of the StringIO
788+
read_this_idx.seek(0)
789+
790+
idxs = [json.loads(x) for x in read_this_idx.getvalue().split("\n") if x]
746791
df = pd.DataFrame(idxs)
747792

748793
# Format the DataFrame

tests/test_local.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Test using local directory for data files."""
2+
3+
from herbie import Herbie, config
4+
import pytest
5+
6+
DATA_DIRECTORY = config["default"]["save_dir"] / "Herbie-Tests-Data/"
7+
HERBIE_OPTIONS = {
8+
"verbose": True,
9+
"overwrite": False,
10+
"save_dir": DATA_DIRECTORY,
11+
}
12+
13+
@pytest.fixture
14+
def setup_local_data(request):
15+
date, model, product = request.param
16+
H = Herbie(date,
17+
model=model,
18+
product=product,
19+
**HERBIE_OPTIONS
20+
)
21+
22+
print(f"{H.save_dir=}")
23+
24+
H.download(verbose=True)
25+
print(f"{H.idx_source=}")
26+
27+
print(H.inventory())
28+
return date, model, product
29+
30+
31+
@pytest.mark.parametrize(
32+
"setup_local_data",
33+
[
34+
("2025-08-08", "gfs", "pgrb2.0p25"),
35+
("2020-10-27", "gfs", "0.5-degree"),
36+
("2025-08-10", "hrrr", "sfc"),
37+
("2025-08-01", "rap", "awp200"),
38+
("2025-08-05", "ifs", "wave"),
39+
],
40+
indirect=True,
41+
ids=[
42+
"gfs-2025-08-08",
43+
"gfs-2020-10-27",
44+
"hrrr-2025-08-10",
45+
"rap-2025-08-01",
46+
"ifs-2025-08-05",
47+
],
48+
)
49+
def test_local_data(setup_local_data):
50+
date, model, product = setup_local_data
51+
H = Herbie(date,
52+
model=model,
53+
product=product,
54+
**HERBIE_OPTIONS
55+
)
56+
57+
assert "local" == H.idx_source
58+
assert "local" == H.grib_source

0 commit comments

Comments
 (0)