Skip to content

Commit 72854ad

Browse files
thodson-usgsclaude
andcommitted
Apply review feedback to PR DOI-USGS#277 + bump Actions to Node-24
Simplify-review consensus across three reviewers (reuse / quality / efficiency) on the conftest.py from the previous commit: - Drop the `pytest.mark.live` marker double-application: no `-m live` selector is wired anywhere in CI; only `pytest.mark.flaky` carries behavior. The `live` marker plus its `pytest_configure` registration was dead metadata. - Tighten `_TRANSIENT_PATTERNS`: drop `r"timed out"` (subsumed by the `Timeout` arm) and `r"Bad Gateway|Service Unavailable|Gateway Timeout"` (subsumed by the `RuntimeError:\s*5\d\d:` prefix from `_raise_for_non_200`). Three load-bearing patterns left. - Drop defensive `getattr(item, "fixturenames", ())` — pytest items reliably expose `fixturenames`; the default-tuple fallback is dead defense. - Shorten the module docstring: keep the WHY paragraph (PR DOI-USGS#273 → surfaced errors → CI flake risk) and drop the WHAT bullets that narrate the code below. - Add a one-line justification for the 5-second backoff (USGS upstream typically recovers from brief 5xx within that window). - Tighten the NEWS.md entry: drop conftest implementation detail (registers a marker, applies via collection hook, …) and keep the user-relevant claim. Skipped from the review (reviewer can decide): - Inverting the auto-mark heuristic to opt-in `@pytest.mark.live` would force decorating ~35 test functions; the current "everything not using requests_mock is live" is zero-cost on success and self-extending for new tests. - Centralizing the transient-error regex set in `dataretrieval/ waterdata/utils.py` would couple library code to test infrastructure for one pattern. Separately, bump GitHub Actions to Node-24-compatible versions (@v6 across all three workflows) ahead of the deprecation warning surfaced on the previous CI run: actions/checkout@v4 → v6, actions/setup-python@v5 → v6. Node 20 is deprecated and will be removed from runners on 2026-09-16; the default flips on 2026-06-02. NEWS.md entry updated to mention the bump. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7cf9b87 commit 72854ad

5 files changed

Lines changed: 28 additions & 58 deletions

File tree

.github/workflows/python-package.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ jobs:
1313
lint:
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717
- name: Set up Python 3.14
18-
uses: actions/setup-python@v5
18+
uses: actions/setup-python@v6
1919
with:
2020
python-version: "3.14"
2121
cache: "pip"
@@ -36,9 +36,9 @@ jobs:
3636
python-version: ["3.9", "3.13", "3.14"]
3737

3838
steps:
39-
- uses: actions/checkout@v4
39+
- uses: actions/checkout@v6
4040
- name: Set up Python ${{ matrix.python-version }}
41-
uses: actions/setup-python@v5
41+
uses: actions/setup-python@v6
4242
with:
4343
python-version: ${{ matrix.python-version }}
4444
cache: "pip"

.github/workflows/python-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ jobs:
2121
runs-on: ubuntu-latest
2222

2323
steps:
24-
- uses: actions/checkout@v4
24+
- uses: actions/checkout@v6
2525
- name: Set up Python
26-
uses: actions/setup-python@v5
26+
uses: actions/setup-python@v6
2727
with:
2828
python-version: '3.x'
2929
cache: 'pip'

.github/workflows/sphinx-docs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Checkout
14-
uses: actions/checkout@v4
14+
uses: actions/checkout@v6
1515
with:
1616
persist-credentials: false
1717
- name: Set up Python
18-
uses: actions/setup-python@v5
18+
uses: actions/setup-python@v6
1919
with:
2020
python-version: "3.13"
2121
cache: "pip"

NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
**05/15/2026:** Live-API tests are now auto-retried on transient upstream errors. PR #273 (above) intentionally surfaces mid-pagination HTTP failures to callers instead of silently truncating results, which made any CI test that hits the live USGS Water Data API susceptible to flaking on transient 429/5xx/connection blips — a 502 Bad Gateway from upstream broke CI on the #273 merge to main. A new `tests/conftest.py` registers a `live` pytest marker, auto-applies it to every test that does not take `requests_mock` (the existing mock-driven convention), and via `pytest-rerunfailures` retries those tests up to twice on a 5-second backoffbut only when the failure trace matches a narrow transient-upstream pattern (`429:` / `5xx:` prefixes from `_raise_for_non_200`, plus `ConnectionError` / timeout shapes). Library bugs raising other exception types still fail on the first attempt.
1+
**05/15/2026:** CI test suite is now resilient to transient USGS Water Data API blips. PR #273 (above) intentionally surfaces mid-pagination HTTP errors to callers rather than silently truncating — the right behavior, but it made any live-API test in CI flaky against transient 429/5xx/connection errors. A new `tests/conftest.py` uses `pytest-rerunfailures` to retry live-API tests up to twice on a 5-second backoff, but only when the failure trace matches a narrow transient-upstream pattern. Library bugs raising other exception types still fail on the first attempt. Also bumps `actions/checkout` and `actions/setup-python` to Node-24-compatible versions (`@v6`) ahead of the GitHub Actions Node 20 runtime deprecation.
22

33
**05/14/2026:** Fixed two latent bugs in the paginated `waterdata` request loop (`_walk_pages` and `get_stats_data`). Previously, when `requests.Session.request(...)` itself raised mid-pagination (network error, timeout), the except block called `_error_body()` on the *prior page's* response, so the logged "error" described the wrong request and could itself crash on non-JSON bodies. Separately, no status-code check was performed on subsequent paginated responses, so a 5xx body that didn't include `numberReturned` was silently treated as an empty page — pagination quietly stopped and the user got truncated data with no error logged. The loop now status-checks each page like the initial request and reports the actual exception. The "best-effort" behavior (return whatever pages were collected) is preserved.
44

tests/conftest.py

Lines changed: 19 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,41 @@
1-
"""Test infrastructure shared across all test modules.
2-
3-
After #273 the paginated ``waterdata`` getters surface mid-walk HTTP
4-
errors (429 / 5xx / connection drops) to the caller instead of silently
5-
truncating the result. That's the correct behavior for users — but it
6-
makes any CI test that hits the live USGS Water Data API susceptible to
7-
flaking on a transient upstream blip (e.g. the 502 Bad Gateway that
8-
broke CI on #273's merge to main).
9-
10-
This file:
11-
12-
* registers a ``live`` pytest marker;
13-
* auto-applies it to every test that does **not** take ``requests_mock``
14-
as a fixture — the existing convention in this repo for mock-driven
15-
tests, so the marker tracks "this test hits the network" without
16-
needing to decorate 35 functions by hand;
17-
* via ``pytest-rerunfailures``, configures live-marked tests to retry
18-
up to twice (5-second backoff) ONLY when the failure trace matches a
19-
transient-upstream pattern: ``429:`` / ``5xx:`` prefixes that
20-
``_raise_for_non_200`` produces, plus ``ConnectionError`` / timeout
21-
shapes from the ``requests`` library.
22-
23-
Library bugs that raise unrelated exception types are NOT retried —
24-
the regex set deliberately omits generic ``RuntimeError`` matches.
1+
"""Auto-retry live-API tests on transient upstream errors.
2+
3+
After PR #273, paginated ``waterdata`` getters propagate mid-walk HTTP
4+
errors (429 / 5xx / connection drops) instead of silently truncating the
5+
result. That's the correct behavior for users but makes any CI test that
6+
hits the live USGS Water Data API susceptible to flaking on a transient
7+
upstream blip — e.g. the HTTP 502 Bad Gateway that broke CI on PR #273's
8+
merge to main.
9+
10+
Heuristic: any test that does NOT request the ``requests_mock`` fixture
11+
is treated as live and gets retried on transient-error patterns only.
12+
Library bugs raising other exception types still fail on the first try.
2513
"""
2614

2715
import pytest
2816

29-
# Match anywhere in the failure traceback. Anchor-free because the
30-
# trace embeds the exception message after a long preamble.
17+
# Anchored loosely — the failure trace embeds the exception message
18+
# after a long preamble.
3119
_TRANSIENT_PATTERNS = [
3220
r"RuntimeError:\s*(?:429|5\d\d):", # _raise_for_non_200 output
3321
r"ConnectionError", # requests/urllib3
3422
r"ReadTimeout|ConnectTimeout|Timeout",
35-
r"timed out",
36-
r"Bad Gateway|Service Unavailable|Gateway Timeout",
3723
]
3824

25+
# 5 seconds is generous enough for a USGS upstream replica to recover
26+
# from a brief 5xx but short enough that CI doesn't drag.
27+
_RETRY_DELAY_SEC = 5
3928
_MAX_RERUNS = 2
40-
_RERUN_DELAY_SEC = 5
41-
42-
43-
def pytest_configure(config):
44-
"""Register the ``live`` marker so pytest doesn't warn about it."""
45-
config.addinivalue_line(
46-
"markers",
47-
"live: marks tests that hit the live USGS Water Data API; "
48-
"retried up to twice on transient upstream HTTP errors.",
49-
)
5029

5130

5231
def pytest_collection_modifyitems(config, items):
53-
"""Auto-mark live-API tests with ``live`` + ``flaky`` retry config.
54-
55-
Heuristic: any test that does NOT request the ``requests_mock``
56-
fixture is treated as live. This catches every test in
57-
``waterdata_test.py`` and similar modules without needing to
58-
decorate them individually, and it auto-tracks new tests written
59-
in the same style.
60-
"""
6132
for item in items:
62-
if "requests_mock" in getattr(item, "fixturenames", ()):
33+
if "requests_mock" in item.fixturenames:
6334
continue
64-
item.add_marker(pytest.mark.live)
6535
item.add_marker(
6636
pytest.mark.flaky(
6737
reruns=_MAX_RERUNS,
68-
reruns_delay=_RERUN_DELAY_SEC,
38+
reruns_delay=_RETRY_DELAY_SEC,
6939
only_rerun=_TRANSIENT_PATTERNS,
7040
)
7141
)

0 commit comments

Comments
 (0)