Skip to content

Commit c3bc2b8

Browse files
thodson-usgsclaude
andauthored
Auto-retry live-API tests on transient upstream errors (DOI-USGS#277)
DOI-USGS#273 surfaced a transient HTTP 502 from upstream on its merge-to-main CI run that would have been silently swallowed by _walk_pages and turned into an empty DataFrame. The status-code-aware behavior is correct for users, but it makes every test that hits the live USGS Water Data API susceptible to flaking on a transient blip. This PR adds: - pytest-rerunfailures to the `test` optional dependency set. - tests/conftest.py that (a) registers a `live` pytest marker; (b) auto-applies it to every test that does not take `requests_mock` as a fixture (the existing mock-driven convention in this repo); and (c) configures live-marked tests to retry up to twice on a 5-second backoff — but ONLY when the failure trace matches one of a narrow set of transient-upstream patterns: `429:` / `5xx:` prefixes from `_raise_for_non_200`, `ConnectionError` shapes, and timeout strings from the requests/urllib3 stack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0a14ba5 commit c3bc2b8

5 files changed

Lines changed: 29 additions & 8 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"

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dataretrieval = ["py.typed"]
3535
test = [
3636
"pytest > 5.0.0",
3737
"pytest-cov[all]",
38+
"pytest-rerunfailures",
3839
"coverage",
3940
"requests-mock",
4041
"ruff",

tests/waterdata_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@
3333
_normalize_str_iterable,
3434
)
3535

36+
# Most tests in this module call the live USGS Water Data API. After
37+
# PR #273, transient upstream errors (5xx / 429 / connection drops)
38+
# propagate instead of silently truncating, which makes CI susceptible
39+
# to flaking on a brief upstream blip. Auto-retry such failures, but
40+
# only for the narrow set of transient-error trace patterns below —
41+
# library bugs raising other exception types still fail on the first
42+
# try. The marker is attached to every test in the module, but the
43+
# patterns match only traces produced by real network round-trips
44+
# (``_raise_for_non_200`` output, ``requests`` exceptions), so tests
45+
# using ``requests_mock`` or ``mock.patch`` are no-ops for the rerun.
46+
pytestmark = pytest.mark.flaky(
47+
reruns=2,
48+
reruns_delay=5,
49+
only_rerun=[
50+
r"RuntimeError:\s*(?:429|5\d\d):", # _raise_for_non_200 output
51+
r"ConnectionError",
52+
r"ReadTimeout|ConnectTimeout|Timeout",
53+
],
54+
)
55+
3656

3757
def mock_request(requests_mock, request_url, file_path):
3858
"""Mock request code"""

0 commit comments

Comments
 (0)