From b5afb6972d64a9a6ad86f67986c7555d78d5f241 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Thu, 29 Oct 2026 09:32:18 -0500 Subject: [PATCH] fix(nwis): index format_response's frame without set_index(inplace=True) pandas 3.1.0 deprecates the inplace keyword of set_index with a Pandas4Warning, a DeprecationWarning subclass. Every nwis getter that returns a time-indexed frame emitted it, and the 3.13 and 3.14 CI jobs failed test_nested_calls_emit_one_warning on the second warning; the 3.10 jobs install pandas 2 and passed. The in-place call was deliberate: without it, set_index copies every column when copy-on-write is off. Popping the key columns off the shallow copy is what the in-place call did internally, so the frames are identical and peak memory is unchanged on pandas 2.3.3 and 3.1.0. Co-Authored-By: Claude Opus 5.5 --- dataretrieval/nwis.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dataretrieval/nwis.py b/dataretrieval/nwis.py index 3969bcad..41332d8e 100644 --- a/dataretrieval/nwis.py +++ b/dataretrieval/nwis.py @@ -202,11 +202,14 @@ def format_response( else: keys = ["datetime"] - # Index our own frame, never the caller's. The shallow copy shares the - # columns; ``set_index`` without ``inplace`` duplicates them, because - # pandas deep-copies the frame whenever copy-on-write is off. + # Index our own frame, never the caller's. Popping the keys off a shallow + # copy leaves the other columns shared; ``set_index`` copies them when + # copy-on-write is off, and pandas 3.1 deprecates its ``inplace``. df = df.copy(deep=False) - df.set_index(keys, inplace=True) # noqa: PD002 # our copy, not the caller's + levels = [df.pop(key) for key in keys] + df.index = ( + pd.MultiIndex.from_arrays(levels) if len(levels) > 1 else pd.Index(levels[0]) + ) # Retag the index alone; ``DataFrame.tz_localize`` relabels the axis by # duplicating every column whenever copy-on-write is off.