Feat/updated since and Fix/for empty responses - #78
Open
geoffhancock wants to merge 5 commits into
Open
geoffhancock wants to merge 5 commits into
geoffhancock wants to merge 5 commits into
Conversation
…ical_csv get_historical_csv passed include_imputed_marker as the sixth positional argument to get_historical_pandas, whose sixth parameter is include_meta. As a result, get_historical_csv(..., include_imputed_marker=True) wrote a CSV containing a meta column and no imputed_data_used column. Pass all defaulted parameters by keyword in the internal call chain (get_historical_csv -> get_historical_pandas -> get_historical_jsons) so argument order differences between signatures cannot misroute a flag. Public signatures are unchanged. Adds a regression test asserting the CSV contains imputed_data_used and not meta when include_imputed_marker=True. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eout Add a keyword-only `timeout` to WattTimeBase.__init__ (default (10, 60), matching the existing login/register literals) and pass it at all three HTTP call sites. The data request previously used a scalar 60, so its default connect timeout drops from 60 s to 10 s; default read timeouts are unchanged. Add a keyword-only `chunk_size` to get_historical_jsons/_pandas/_csv, forwarded to _get_chunks, which now accepts None (30 days) and rejects sizes of 5 minutes or less -- the per-chunk trim -- instead of looping forever or producing inverted chunks. When a request fails on a timeout, append a hint to the RuntimeError naming both knobs. Once the session's retries are exhausted, requests raises ConnectionError -> MaxRetryError -> ReadTimeoutError rather than ReadTimeout, so _is_timeout walks the exception chain. Motivation: in August 2026, 30-day historical pulls for some regions took over 180 s server-side while the same pulls at 10 days took ~11 s. Users hit an opaque read timeout after ~4 minutes with no supported lever; the only workaround was monkeypatching _get_chunks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add a short "Tuning requests that time out" note under the historical data example showing both options and that the timeout is per attempt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Supports the /v3/historical updated_since query parameter across get_historical_jsons, get_historical_pandas, and get_historical_csv, enabling incremental sync: fetch only rows the API has revised since a given datetime. - updated_since accepts str or datetime, normalized to UTC via a new _parse_date helper (extracted from _parse_dates, which now wraps it). - The server-side filter is inclusive (last_updated >= updated_since), verified empirically against the live API; docstrings state this. - When updated_since is passed, the API adds last_updated to each data point; get_historical_pandas parses it to a UTC datetime column. - An updated_since filter can match nothing. get_historical_pandas now returns a typed empty DataFrame with the expected columns instead of raising KeyError on point_time. - get_historical_csv appends _updated-since-<timestamp> to the filename so a partial (filtered) CSV is never mistaken for a full pull. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a "Fetching only revised data" section under the historical data examples covering the inclusive filter, the last_updated column, the empty-result shape, and the CSV filename suffix. Note in the main historical section that a request matching no data points returns an empty dataframe rather than raising. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
geoffhancock
marked this pull request as ready for review
September 18, 2026 19:12
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add updated_since support, and fix a KeyError on empty historical responses
Stacked on #76: this PR's base branch is
feat/configurable-timeout, so the diff below is only this PR's own change. Once #76 merges, GitHub retargets this tomain. It is independent of #77 -- the two touch different code and merge cleanly in either order.Why there is a bug fix in a feature PR.
updated_sincemakes the empty response routine rather than exceptional: "nothing has been revised since X" is the normal answer most of the time, and a client that crashes on it is unusable for incremental sync. So handling the empty case was a prerequisite of the feature, not an extra. Doing it properly turned out to fix aKeyErrorthatget_historical_pandasraises onorigin/maintoday for reasons that have nothing to do withupdated_since-- includingstart == end, the exact span #74 just made reachable.In other words: the feature forced us to look at a code path that was already broken. The fix is one guard, described next; the feature follows.
The empty-response crash
/v3/historicalanswers200with"data": []-- never an error -- when a request matches no points.pd.json_normalizeturns that into a 0x0 frame with no columns, and the next line indexesdf["point_time"]. That is reachable three ways today, only one of which involvesupdated_since. Verified against the live API, then againstorigin/mainitself:The zero-length case is the pointed one. #74 fixed the
IndexErrorin_get_chunksforstart == endand covered it with aget_historical_forecast_pandastest, which passes because the forecast endpoint returns the run generated at that instant./v3/historicalreturns"data": []for the same span, soget_historical_pandas(start=X, end=X)currently trades #74'sIndexErrorfor aKeyError. This closes that gap.The guard is keyed on
df.empty, so one piece of code covers both the feature's normal case and the three pre-existing ones.updated_sinceappears inside it only to decide whetherlast_updatedbelongs in the expected column list -- which is why the two changes are the same change, and why splitting them would mean writing the same guard twice.Why updated_since
WattTime revises historical data after it is first published. Today the only way to pick up those revisions is to re-pull the whole range and diff it locally, which means re-fetching a year of five-minute data to find the handful of points that actually changed. The
/v3/historicalendpoint already supports anupdated_sincequery parameter for exactly this; the client just did not expose it.What changes
updated_sinceonget_historical_jsons,get_historical_pandasandget_historical_csv, accepting astrordatetimeand normalised to UTC. Keyword-only, likechunk_sizein #76.last_updated >= updated_since). Verified empirically against the live API rather than assumed, and stated in the docstrings so callers can page through revisions without off-by-one guessing.last_updatedcomes back as a parsed UTC column inget_historical_pandaswhenupdated_sinceis passed, since the API adds that field to each data point.get_historical_pandasnow returns a typed empty DataFrame carrying the expected columns instead of raisingKeyError: 'point_time', so downstream code can rely on the schema either way.get_historical_csvappends_updated-since-<timestamp>to the filename. A filtered CSV is a partial dataset and should not be mistakable for a full pull sitting in the same directory._parse_dateis extracted from_parse_dates, which now wraps it. No behaviour change;updated_sinceis a single date and needed the same normalisation.Implications
point_timeis now parsed withutc=Trueinget_historical_pandas, making explicit what was previously inferred. Measured on a normal call against both versions:datetime64[ns, UTC]either way, so this is a no-op for existing callers and simply guarantees the dtype when a response is empty or offsets are mixed.updated_sinceis keyword-only, so no positional call is affected and the positional contract of all three signatures is unchanged.updated_sinceparameter is sent, nolast_updatedcolumn appears, and the CSV filename is unchanged.Tests
Four tests against the live API, matching the style of the surrounding
TestWattTimeHistoricalcases.For the crash:
test_get_historical_pandas_empty_without_updated_sincecovers all three empty-response spans -- zero-length, beforedata_start, and future -- with no filter set, asserting an empty frame withpoint_timeandvaluepresent,last_updatedabsent, and a datetime dtype. Each subtest raisesKeyErroronorigin/main.For the feature: a filtered pull returns rows carrying a parsed
last_updatedcolumn; a filter set in the future returns an empty DataFrame with the expected columns; and the CSV path writes a file with the_updated-since-suffix. The existing suite passes unchanged.The README changes are the final commit on its own; drop it if you would rather not have them.
🤖 Generated with Claude Code