All notable changes to this project are documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
- SOAP
DataInventorycalls now go through a dedicated retryingrequests.Session(_SOAP_SESSION) that retries POST as well as GET on transient failures (429/5xx). Gives the data-inventory path the same resiliency the REST path already had.
Major modernization release. Every layer of the project was touched — build system, CI/CD, internal structure, testing, and docs. Behavior for existing users is almost entirely unchanged; two small backward-compatible behavioral changes are called out under Changed below.
- Module-level HTTP session with automatic retries on transient failures
(
429,500,502,503,504) via aurllib3.util.retry.Retryadapter (noaa_coops/_http.py). All calls — includingget_stations_from_bbox— share the session's connection pool and retry policy. - Warn-and-continue behavior on multi-block fetches: when one block in a
multi-month fetch fails, a
RuntimeWarningis emitted anddf.attrs["missing_blocks"]is populated with{begin, end, error}entries instead of silently dropping the block. - Declarative product registry (
noaa_coops/_products.py) replacing the historical 186-line_check_product_paramsif/elif tree + 150-line_build_request_urlif/elif tree. Adding a new NOAA product is now a one-place change. - Public export:
COOPSAPIErroris now importable asfrom noaa_coops import COOPSAPIError(the oldfrom noaa_coops.station import COOPSAPIErrorpath still works). - Offline test suite using pytest-recording
VCR cassettes. Default
pytestruns in ~1.5s with zero network calls. - Nightly live canary (
.github/workflows/nightly.yml) runspytest -m livedaily at 06:00 UTC. On failure, opens a tracking issue so NOAA drift is visible without flooding the inbox. - Manual publish workflows mirroring the PyPA pattern:
.github/workflows/test-publish.ymlpublishes to TestPyPI,.github/workflows/publish.ymlpublishes to PyPI + creates thev{VERSION}tag + cuts a GitHub Release with an auto-generated changelog fromgit log. Bothworkflow_dispatch. - Dependabot configured for
github-actionsand theuvecosystems. - pre-commit hooks (
.pre-commit-config.yaml) — ruff check + format + standard hygiene hooks. Free auto-fix PRs via pre-commit.ci.
- Build backend swapped to hatchling. Version is read from
noaa_coops/__init__.pybyhatchling.build. No morepoetry-core. - CI swapped to
uv+hatchling. Jobs:lint(ruff),typecheck(mypy), andtest(pytest matrix across Python 3.10 – 3.13 onubuntu-latest). - Added HTTP timeouts everywhere. Every
requests.get/_SESSION.getpassestimeout=(5.0, 30.0)(connect, read). No more hangs on stalled NOAA endpoints. - Narrower exception handling. Bare
except:inStation.__init__replaced with(requests.RequestException, zeep.exceptions.Error, AttributeError, TypeError)+ alogging.warning.KeyboardInterruptandSystemExitnow propagate correctly. - Internal module split.
station.pyshrunk from 825 lines to ~350. New internal modules:_exceptions.py,_endpoints.py,_http.py,_parsing.py,_products.py,_metadata.py, plus the publicapi.py. All previous import paths continue to work. pyproject.tomlconverted to PEP 621[project]table with classifiers, keywords, and[project.urls]so the PyPI page renders useful metadata.station.data_inventoryis always set. An empty dict{}now indicates the SOAP fetch failed, rather than the attribute being unset. Callers usinghasattr(station, 'data_inventory')should switch toif not station.data_inventory:.
- O(n²) memory in multi-block fetches. The multi-block loop used to
rebuild the full DataFrame on every iteration via
df = pd.concat([df, df_block]). Now appends to a list and concatenates once at the end. - Silent
KeyErrorcrash inget_data_inventory. If the SOAP response lacked a"parameter"key, the error escaped the narrow catch inStation.__init__and crashed construction. Now handled locally viatry/except (KeyError, TypeError). _parse_known_date_formatsUnboundLocalErrortrap. The oldmatch = Falseflag pattern (set only in theexceptbranch) could raiseUnboundLocalErrorin edge cases. Rewritten as a clean for/try/continue loop that raisesValueErrorafter exhausting all known formats.stale --cov=reflektconfig. pytest coverage was pointing at the wrong project. Fixed to--cov=noaa_coops.
- Python 3.9 support. 3.9 went EOL in October 2025 and the modern testing stack (vcrpy 8.x, urllib3 2.x, types-requests 2.32.4+) requires ≥3.10. Supported versions: 3.10, 3.11, 3.12, 3.13.
- Dead files.
.bumpversion.cfg(stuck at0.1.9targeting nonexistentsetup.py),.flake8(superseded by ruff),mypy.ini(migrated intopyproject.toml),pytest.ini(same), oldpoetry.lock, and a 96-lineif __name__ == "__main__":debug block instation.py. - Third-party publish action. Replaced
JRubics/poetry-publishwith the PyPA-officialpypa/gh-action-pypi-publish.
0.4.0 - 2023-xx-xx
Earlier releases are documented in the GitHub release history.