44- ** ` CONTEXT.md ` is the shared vocabulary** — getter, query, chunk, plan, fan-out,
55 page, adapter, facade, leaf, transport, collection, profile, effective
66 configuration, and the legacy names that are deliberately not renamed. Read it
7- before writing code, docstrings, or commit messages. Two kinds of term live
8- there and they bind differently (ADR 0013). A ** core** term is ours and takes
9- one spelling everywhere * including identifiers* , so a name in the code that
10- conflicts with it is a defect. A ** domain** term -- * monitoring location * ,
11- * collection* -- is fixed for prose only: each adapter keeps its own service's
12- spelling in its parameters, so ` nwis.get_record(service="dv") ` and WQP's
13- ` Station ` are that service's language rather than drift. Each entry states
14- which kind it is.
7+ before writing code, docstrings, or commit messages. Two kinds of term are
8+ defined there and they bind differently (ADR 0013). A ** core** term is ours
9+ and takes one spelling everywhere * including identifiers* , so a name in the
10+ code that conflicts with it is a defect. A ** domain** term -- * monitoring
11+ location * , * collection* -- is fixed for prose only: each adapter keeps its
12+ own service's spelling in its parameters, so ` nwis.get_record(service="dv") `
13+ and WQP's ` Station ` are that service's vocabulary rather than drift. Each
14+ entry states which kind it is.
1515- Architectural decisions and their rationale: ` docs/source/architecture/decisions/ `
1616 (ADRs, referenced by number throughout the code and by ` .importlinter ` ).
1717- Contributor workflow, style, and the quality gates in detail: ` CONTRIBUTING.md ` .
1818
1919## How the tree is organized
2020Use ` ls ` /` grep ` for the file list; what follows is the placement logic, so you
21- can predict where a thing lives .
21+ can predict where a thing is defined .
2222
2323- ` dataretrieval/ ` — the public surface is one * adapter* module per service,
2424 named for the service (` nldi ` , ` nwdc ` , ` ngwmn ` , ` streamstats ` , ` wqp ` , and
2525 legacy ` nwis ` ); each owns that service's URLs, parameters, and response
2626 quirks. ` waterdata/ ` is the one adapter large enough to be a package, split by
2727 collection family; its ` api.py ` is a compatibility facade holding no logic.
28- Everything else in the package is shared machinery the adapters sit on top of
28+ Everything else in the package is shared code the adapters depend on
2929 — configuration, credentials, progress, exceptions, code tables, response
30- formats. Shared machinery below the adapter layer must not know about any
30+ formats. Shared code below the adapter layer must not reference any
3131 particular service.
32- - ` dataretrieval/ogc/ ` — the OGC API protocol machinery (chunk planning,
32+ - ` dataretrieval/ogc/ ` — the OGC API protocol code (chunk planning,
3333 filters, request building, response shaping). Shared by the two OGC services
3434 only; ` .importlinter ` refuses any other importer.
35- - ` dataretrieval/transport/ ` — service-neutral request machinery (HTTP, retry,
35+ - ` dataretrieval/transport/ ` — service-neutral request code (HTTP, retry,
3636 pagination, fan-out). It names no service and no protocol, and is not public API.
3737- Leading-underscore top-level modules are private; the dependency-free * leaves*
38- sit at the floor of the stack so anything may use them without pulling in the
38+ are at the bottom of the stack so anything may use them without pulling in the
3939 rest of the package. Check for an existing leaf before writing a small helper.
40- - ** ` .importlinter ` is the map.** Its ` layers ` contract lists every top-level
41- module in dependency order and is ` exhaustive = True ` , so it is both the
42- authoritative statement of where a module sits and the thing that fails when a
43- new module has no home. Read it before adding a module or an import.
40+ - ** ` .importlinter ` records where every module belongs.** Its ` layers ` contract
41+ lists every top-level module in dependency order and is ` exhaustive = True ` ,
42+ so it is both the authoritative statement of where a module belongs and the
43+ thing that fails when a new module has not been placed. Read it before adding
44+ a module or an import.
4445- ` tests/ ` — flat, one ` *_test.py ` per module or concern, organized into four
4546 dependency-oriented layers (public contract, adapter contract, component,
4647 cross-component) that ` tests/contracts/README.md ` defines and assigns files to.
@@ -74,10 +75,10 @@ can predict where a thing lives.
7475- Tests: ` coverage run -m pytest tests/ && coverage report ` , or focused like
7576 ` pytest tests/waterdata_test.py::test_mock_get_samples ` . ` coverage report ` is
7677 a merge gate: branch coverage with a ` fail_under ` ratchet in
77- ` [tool.coverage.report] ` . Chase the uncovered * branch* , not the number -- a
78- test written to colour a line green catches nothing and costs a maintenance
79- slot . If a path is genuinely unreachable, add it to ` exclude_also ` with a
80- reason, or leave the ratchet alone.
78+ ` [tool.coverage.report] ` . Cover the uncovered * branch* , not the number -- a
79+ test written only to mark a line as covered catches nothing and adds
80+ maintenance . If a path is genuinely unreachable, add it to ` exclude_also `
81+ with a reason, or leave the ratchet alone.
8182- Types: ` mypy ` (` strict = true ` in ` pyproject.toml ` ; CI runs it over the
8283 PR-merged-into-main, so bare ` dict ` /` list ` annotations fail there even if they
8384 pass on your branch).
@@ -88,7 +89,7 @@ can predict where a thing lives.
8889
8990## Testing gotchas
9091- The suite is offline by default: ` addopts = "-m 'not live'" ` . Tests marked
91- ` @pytest.mark.live ` hit real USGS services and run on a schedule
92+ ` @pytest.mark.live ` call real USGS services and run on a schedule
9293 (` .github/workflows/live-api.yml ` ); run them locally with ` pytest tests/ -m live ` .
9394- HTTP is mocked with ` pytest-httpx ` 's ` httpx_mock ` fixture plus fixtures under
9495 ` tests/data/ ` ; keep new API tests offline.
@@ -101,23 +102,23 @@ can predict where a thing lives.
101102## Error messages
102103Most callers here are programs — a script, a pipeline stage, an agent — so a
103104message is the only channel through which a caller can correct itself. Every
104- raise states the problem and then the move that fixes it, in that order.
105+ raise states the problem and then the action that fixes it, in that order.
105106
106107- Name the remedy, not just the fault. ` "Service not recognized" ` gives a caller
107108 nothing to try next; listing the services it does accept does. For a transport
108109 failure the remedy is whether to retry, and `transport.pagination.
109110 paginated_failure_message()` is the model: cause, then ` To recover: …`.
110111- Don't invent a phrasing for a check that recurs. ` dataretrieval/_validation.py `
111- owns the wording for the shared shapes — bad value in a closed vocabulary
112+ owns the wording for the shared cases — bad value in a closed vocabulary
112113 (` require_one_of ` ), missing argument (` require_argument ` ), incomplete group
113114 (` require_together ` ), no filter at all (` require_any_of ` ), and conflicting
114- arguments (` require_exactly_one ` , ` reject_together ` ). Reach for one before
115+ arguments (` require_exactly_one ` , ` reject_together ` ). Use one before
115116 hand-writing a message. A service-specific pointer is not a reason to
116- hand-write: every check takes a ` remedy= ` for the move it cannot derive. Every
117- check raises ` ValueError ` -- one class for a bad argument value, so a caller
118- catches by shape rather than by which module rejected it.
117+ hand-write: every check takes a ` remedy= ` for the action it cannot derive.
118+ Every check raises ` ValueError ` -- one class for a bad argument value, so a
119+ caller catches by error class rather than by which module rejected it.
119120- ` require_argument ` returns the narrowed value and ` require_exactly_one ` the
120- winning ` (name, value) ` pair, so use their results rather than re-testing for
121+ selected ` (name, value) ` pair, so use their results rather than re-testing for
121122 ` None ` to satisfy mypy — a second, unreachable message beside the first is
122123 how the two drift apart.
123124- ** Paste the remedy back before trusting it.** Whatever a message names must be
@@ -127,9 +128,9 @@ raise states the problem and then the move that fixes it, in that order.
127128 private local no getter accepts, ` configure(Configuration(...)) ` was a silent
128129 no-op because ` configure ` is a context manager, `pip install
129130 dataretrieval[ nldi] ` globs in zsh, and a navigation missing its ` data_source`
130- spelled ` None ` into the URL and returned an empty frame. Run the corrected
131+ wrote ` None ` into the URL and returned an empty frame. Run the corrected
131132 call against the real service; wording review does not catch these.
132- - Shared checks take the caller's spelling . ` _validate_data_source ` ,
133+ - Shared checks take the caller's argument name . ` _validate_data_source ` ,
133134 ` _format_api_dates ` , and ` require_one_of ` all accept a ` name= ` so the subject
134135 of the message is the argument that was actually passed. A helper that hard-codes
135136 one noun reports the wrong parameter the moment a second call site reuses it.
@@ -151,7 +152,7 @@ raise states the problem and then the move that fixes it, in that order.
151152- The ` API_USGS_PAT ` credential is owned by the ` credentials ` leaf and applied as
152153 the ` X-Api-Key ` header by ` transport.http.default_headers() ` , which sends it
153154 only to the host it belongs to. Never hard-code tokens in examples or tests.
154- - Water Data request builders translate Python kwargs to API spellings
155+ - Water Data request builders translate Python kwargs to API names
155156 (` skip_geometry ` -> ` skipGeometry ` , ` filter_lang ` -> ` filter-lang ` ); tests
156157 assert exact URLs and query params.
157158- Multi-value OGC params are comma-joined GETs, except ` monitoring-locations `
0 commit comments