Skip to content

Modernize packaging and CI; pin load behavior with an offline test suite - #102

Merged
travis-recurve merged 8 commits into
masterfrom
feature/modernize-and-test-harness
Jul 21, 2026
Merged

Modernize packaging and CI; pin load behavior with an offline test suite#102
travis-recurve merged 8 commits into
masterfrom
feature/modernize-and-test-harness

Conversation

@travis-recurve

Copy link
Copy Markdown
Contributor

Your checklist for this pull request

Please review the guidelines for contributing to this repository.

  • Make sure you are requesting to pull a feature/bugfix branch (right side). Don't request your master!
  • Make sure tests pass and coverage has not fallen (269 tests, run fully offline; coverage 95% with a fail_under floor added).
  • Update the CHANGELOG.md to describe your changes in a bulleted list under the "Development" section at the top of the changelog.
  • Code style: PEP 008. (Not blackened, per maintainer preference.)
  • New functions have numpy-style docstrings; no new public API requiring docs/api.rst entries.
  • All git commits have the "Signed-off-by" message for the Developer Certificate of Origin.

Description

Modernizes the repository and pins current load behavior with a test suite that runs fully offline, ahead of the GHCNh data-source migration (#100).

Packaging and CI:

  • pyproject.toml (hatchling) replaces setup.py, Pipfile, and MANIFEST.in; python >=3.10; dependencies now declare everything actually imported (pytz added; retry, attrs replaced with stdlib equivalents)
  • Test workflow: python 3.10–3.14 × ubuntu/windows/macos matrix, tox-driven, monthly cron; the repository previously had no test CI
  • pytest and coverage configuration consolidated into pyproject.toml; warnings are errors
  • README.md replaces README.rst; CONTRIBUTING.md release steps updated to the GitHub-release publish workflow

Fixes:

  • Key-value cache rewritten on stdlib sqlite3; sqlalchemy (previously required at runtime but never declared) is no longer used. Cache urls keep the sqlite:///path form
  • pkg_resources import (removed in setuptools >=81) replaced with importlib.resources
  • Loaders warn when returned data is empty, truncated, or contains a multi-day internal gap — requests ending after a station's last available observation previously returned NaN-padded series silently
  • Fixed No objects to concatenate crash when all years in a requested range are missing and error_on_missing_years=False; loaders always return a series covering the full requested range
  • error_on_missing_years added to the daily ISD/GSOD loaders and defaulted consistently

Tests:

  • The FTP-proxy mocks no longer intercepted the fetch path after the switch to the access api, so the suite was silently reaching the live api on every run. Tests now replay real captured api payloads through a transport-level mock; unmatched requests fail the test
  • Behavior pins use real captured observations only, including a real truncated station-year and a real mid-year outage; failure paths (retry exhaustion, malformed payloads) are covered

- pyproject.toml (hatchling, PEP 621) replaces setup.py, Pipfile, MANIFEST.in
- pytest and coverage config move from pytest.ini/.coveragerc into pyproject.toml
- tox rewritten: envs for python 3.10-3.14 plus serial coverage env
- add test.yml workflow: python x OS matrix, monthly cron, fan-in job
- delete .travis.yml and bump_version.sh
- README.rst converted to README.md; Dockerfile drops pipenv for pip install -e

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
- KeyValueStore keeps its public interface; urls remain sqlite:///path form,
  non-sqlite urls now raise ValueError
- updated timestamps stored as UTC ISO strings, returned tz-aware
- delete get_datetime_if_exists/is_tz_naive helpers
- tests backdate cache entries via sqlite3 instead of sqlalchemy sessions

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
…ess_api

- pkg_resources (removed in setuptools >=81) replaced with importlib.resources
- CLI test invokes --help explicitly (click >=8.2 exits 2 on bare group)
- retry dependency replaced with an explicit request retry loop
- attrs dependency replaced with stdlib dataclass
- DatasetType.ISD value fixed from 'IDS' to 'ISD'

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
- hourly/daily/gsod loaders emit warnings when returned data is empty, ends
  more than a day before the requested end, or has an internal gap over 7 days
- all-years-missing tolerant path returns a NaN series over the requested
  range instead of crashing on pd.concat of an empty list
- loaders now always cover the full requested range, including when the
  data slice is empty
- error_on_missing_years added to daily/gsod loaders and defaulted True
  everywhere, matching the ISDStation method default

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
- tests/fixtures holds real recorded NCEI responses (gzipped) for station
  722874, years 1800/2005/2006/2007/2050, both datasets
- mock_api_transport fixture intercepts requests.get and serves fixtures;
  unmatched requests fail the test rather than reaching the network
- replaces the FTP-proxy mock, which no longer intercepted the api fetch
  path and let tests reach the live api
- make_api_request monkeypatch fixtures depend on the transport mock so
  delegated calls also stay offline

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
…aths

All data tests pin real captured observations; no synthesized weather data.

- regression pins on captured 2007 data: hourly/daily counts, means, first
  values with explicit tolerances
- truncation warnings pinned against station 724940, whose 2025 data ends
  Aug 27; sub-day trailing gap pinned as non-warning on the same station
- internal-gap warnings pinned against station 720193, whose 2019 data has
  a real mid-year outage (16.1 days hourly, 25 days daily)
- all-NaN-year behavior pinned against station 722874 in 2025 (temperature
  reported missing all year)
- missing-year strict/tolerant behavior tests for hourly, daily, and gsod
- degenerate sub-hour range returns empty without warning
- access_api tests built from captured payload rows: retry exhaustion and
  recovery, missing-temp NaN, gsod unit conversion, malformed-row rejection
- coverage floor set at 94 (measured 95.3; access_api at 100)

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
- releasing goes through the GitHub release publish workflow
- drop gitignore whitelists for deleted config files; ignore coverage.xml

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
- cache operations close their connection on exit (python 3.13 warns on
  connections garbage-collected open, and warnings are errors in tests)
- the metadata db proxy serves one cached read connection per thread, so
  call sites do not leak or close
- datetime.UTC (python 3.11+) replaced with timezone.utc in tests to keep
  the 3.10 floor

Signed-off-by: Travis Sikes <124101151+travis-recurve@users.noreply.github.com>
@travis-recurve
travis-recurve merged commit 0ef6fae into master Jul 21, 2026
17 checks passed
@travis-recurve
travis-recurve deleted the feature/modernize-and-test-harness branch July 21, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants