Skip to content

Commit 0dfaec4

Browse files
thodson-usgsclaude
andauthored
Fix IndexError in _read_rdb when NWIS returns no data rows (#227)
* Fix IndexError in _read_rdb when NWIS returns no data rows When an NWIS service responds with only comment lines (e.g. "No sites found matching all criteria"), _read_rdb tried to index past the end of the lines list and raised an unhelpful IndexError. Now it detects the empty-data case, extracts the Response-Message from the comment block, and raises a ValueError with that message instead. Fixes #171. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Return empty DataFrame from _read_rdb when NWIS finds no data When the NWIS service responds with only comment lines (e.g. "No sites found matching all criteria"), _read_rdb indexed past the end of the line list and raised an unhelpful IndexError. Since this is a legitimate empty result rather than an error, the fix returns an empty DataFrame so callers can use the idiomatic df.empty check instead of catching an exception. Fixes #171. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 671202a commit 0dfaec4

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

dataretrieval/nwis.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,16 +1039,23 @@ def _read_rdb(rdb):
10391039
)
10401040

10411041
count = 0
1042+
lines = rdb.splitlines()
10421043

1043-
for line in rdb.splitlines():
1044+
for line in lines:
10441045
# ignore comment lines
10451046
if line.startswith("#"):
10461047
count = count + 1
10471048

10481049
else:
10491050
break
10501051

1051-
fields = rdb.splitlines()[count].split("\t")
1052+
if count >= len(lines):
1053+
# All lines are comments — the service returned no data rows (e.g.
1054+
# "No sites found matching all criteria"). This is a legitimate empty
1055+
# result, so return an empty DataFrame rather than raising.
1056+
return pd.DataFrame()
1057+
1058+
fields = lines[count].split("\t")
10521059
fields = [field.replace(",", "").strip() for field in fields if field.strip()]
10531060
dtypes = {
10541061
"site_no": str,

tests/nwis_test.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from dataretrieval.nwis import (
1111
NWIS_Metadata,
12+
_read_rdb,
1213
get_discharge_measurements,
1314
get_gwlevels,
1415
get_info,
@@ -321,3 +322,46 @@ def test_variable_info_deprecated(self):
321322
):
322323
result = md.variable_info
323324
assert result is None
325+
326+
327+
class TestReadRdb:
328+
"""Tests for the _read_rdb helper.
329+
330+
Notes
331+
-----
332+
Related to GitHub Issue #171.
333+
"""
334+
335+
# Minimal valid RDB response with one data row
336+
_VALID_RDB = "# comment\nsite_no\tvalue\n5s\t10n\n01491000\t42\n"
337+
338+
# NWIS response when no sites match the query criteria
339+
_NO_SITES_RDB = (
340+
"# //Output-Format: RDB\n"
341+
"# //Response-Status: OK\n"
342+
"# //Response-Message: No sites found matching all criteria\n"
343+
)
344+
345+
def test_valid_rdb_returns_dataframe(self):
346+
"""_read_rdb returns a DataFrame for a well-formed RDB response."""
347+
df = _read_rdb(self._VALID_RDB)
348+
assert isinstance(df, pd.DataFrame)
349+
assert "site_no" in df.columns
350+
351+
def test_no_sites_returns_empty_dataframe(self):
352+
"""_read_rdb returns an empty DataFrame when NWIS finds no matching sites.
353+
354+
A "No sites found" response is a legitimate empty result, not an error,
355+
so callers can check ``df.empty`` rather than catching an exception.
356+
Regression test for issue #171 (previously raised IndexError).
357+
"""
358+
df = _read_rdb(self._NO_SITES_RDB)
359+
assert isinstance(df, pd.DataFrame)
360+
assert df.empty
361+
362+
def test_all_comments_returns_empty_dataframe(self):
363+
"""_read_rdb returns an empty DataFrame when the response has only comments."""
364+
rdb = "# just a comment\n# another comment\n"
365+
df = _read_rdb(rdb)
366+
assert isinstance(df, pd.DataFrame)
367+
assert df.empty

0 commit comments

Comments
 (0)