Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions .importlinter
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@

[importlinter]
root_package = dataretrieval
; Contracts describe what runs, matching the AST suite. ``ogc.interruptions``
; and ``ogc.chunking`` reference each other's types under ``TYPE_CHECKING``;
; that is a documentation edge, not a runtime one, and no cycle exists at
; import time.
; Contracts describe what runs, matching the AST suite. Type-checking imports
; document structural protocols and callback types without creating runtime
; dependency edges.
exclude_type_checking_imports = True

[importlinter:contract:layers]
Expand All @@ -27,7 +26,7 @@ layers =
utils
transport
progress
_ambient | _response_metadata | codes | combining | rdb
_ambient | _response_metadata | codes | combining | interruptions | rdb
credentials
exceptions
; Every top-level module must be placed in the stack deliberately. A new
Expand Down Expand Up @@ -55,10 +54,9 @@ allowed_importers =
dataretrieval.ngwmn
dataretrieval.waterdata
ignore_imports =
; The package __init__ re-exports the resumable-call and interruption types;
; they are part of the documented public surface, not a service reaching in.
; The package __init__ re-exports the parallel-chunks context manager; it is
; part of the documented public surface, not a service reaching into OGC.
dataretrieval -> dataretrieval.ogc.chunking
dataretrieval -> dataretrieval.ogc.interruptions

[importlinter:contract:ogc-facade]
name = NGWMN consumes the OGC facade only, never its internals (ADR 0007)
Expand Down Expand Up @@ -101,6 +99,7 @@ source_modules =
dataretrieval.combining
dataretrieval.credentials
dataretrieval.exceptions
dataretrieval.interruptions
dataretrieval.ngwmn
dataretrieval.nldi
dataretrieval.ogc
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**08/06/2026:** Fan-out execution is now shared across services. Chunking is how a query is divided structurally (a Water Data/NGWMN URL over the byte limit); fan-out is how the pieces are distributed operationally. Only the first is protocol-specific, so the executor moved to `dataretrieval.transport.fanout` (`FanOut`, over a three-member `FanOutPlan` protocol) while chunk planning stays in `dataretrieval.ogc`. Water Use no longer re-implements the fan-out gather and inherits resume, progress reporting, and `API_USGS_CONCURRENT`: a multi-location pull interrupted by a rate limit now raises a resumable interruption whose `.call.resume()` re-issues only the locations that did not finish, instead of discarding every completed one. The interruption taxonomy moved to the `dataretrieval.interruptions` leaf and its base class is now `FanOutInterrupted`; **`ChunkInterrupted` is a permanent alias of the same class**, so `except ChunkInterrupted` keeps working. **Breaking change:** a Water Use fan-out interrupted by a 5xx, 429, or recoverable connection failure now raises `ServiceInterrupted`/`QuotaExhausted` rather than `ServiceUnavailable`/`RateLimited`/`NetworkError` — all remain `DataRetrievalError`, so broad handlers are unaffected, but narrow handlers around a Water Use call must widen. **Breaking change:** `wateruse.MAX_CONCURRENT_REQUESTS` is removed; set `API_USGS_CONCURRENT` (which now outranks any service default) or read `wateruse.DEFAULT_CONCURRENT_REQUESTS`.

**08/03/2026:** Split the Water Data implementation into focused time-series, metadata, measurements, reference, samples, and CQL collection-family modules behind the unchanged `waterdata.api` facade. Active service modules now declare explicit exports; public Water Data imports, signatures, function identities, deprecations, and return contracts are protected by executable contract snapshots. OGC ambient context and schema/queryables execution are separated from request construction, adapter-to-adapter reach-through is prohibited by architecture tests, and service-specific output shapes are documented rather than forced into one model.

**08/02/2026:** Added an internal API-neutral transport layer for guarded HTTP clients, host-scoped authentication, cursor pagination, bounded retry, response aggregation, progress, and sync-over-async dispatch. Water Use and the non-OGC Statistics API now consume transport directly instead of private OGC execution helpers; WQP, NLDI, and StreamStats opt into bounded transient retry while deprecated NWIS behavior remains unchanged. OGC retains CQL2, request construction, feature shaping, chunk planning, resumable calls, and interruption types, with compatibility imports at previous private paths. Failed pagination and fan-out still raise rather than returning partial data, and no public signatures or return shapes changed.
Expand Down
33 changes: 18 additions & 15 deletions dataretrieval/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@

A failed request raises a subclass of :class:`dataretrieval.DataRetrievalError`
(the taxonomy lives in ``dataretrieval.exceptions``); connection-level failures
(timeouts, DNS) are wrapped as :class:`dataretrieval.NetworkError`. A large
request interrupted mid-stream raises :class:`dataretrieval.ChunkInterrupted`,
whose ``.call.resume()`` continues from the work already completed.
(timeouts, DNS) are wrapped as :class:`dataretrieval.NetworkError`. A fanned-out
request interrupted mid-stream raises :class:`dataretrieval.FanOutInterrupted`
(also available under its original ``ChunkInterrupted`` name), whose
``.call.resume()`` continues from the work already completed.
"""

from importlib.metadata import PackageNotFoundError, version
Expand All @@ -45,23 +46,24 @@
URLTooLong,
)

# Parallel-chunks control (a context manager). Defined with the chunker in
# ``dataretrieval.ogc.chunking``; surfaced here for a stable public path
# ``from dataretrieval import parallel_chunks``.
from dataretrieval.ogc.chunking import parallel_chunks

# Resumable chunk-interruption exceptions. They are defined in
# ``dataretrieval.ogc.interruptions`` rather than ``dataretrieval.exceptions``
# because they carry pandas/httpx state and a resumable ``ChunkedCall`` handle,
# Resumable fan-out interruption exceptions. They are defined in
# ``dataretrieval.interruptions`` rather than ``dataretrieval.exceptions``
# because they carry pandas/httpx state and a resumable ``FanOut`` handle,
# which would pull heavy dependencies into the lightweight exceptions module.
# Surfaced here so callers get a stable public path:
# ``from dataretrieval import ChunkInterrupted``.
from dataretrieval.ogc.interruptions import (
# They are not under ``ogc`` because Water Use raises them too. Surfaced here so
# callers get a stable public path: ``from dataretrieval import ChunkInterrupted``.
from dataretrieval.interruptions import (
ChunkInterrupted,
FanOutInterrupted,
QuotaExhausted,
ServiceInterrupted,
)

# Parallel-chunks control (a context manager). Defined with the chunker in
# ``dataretrieval.ogc.chunking``; surfaced here for a stable public path
# ``from dataretrieval import parallel_chunks``.
from dataretrieval.ogc.chunking import parallel_chunks

from . import (
exceptions,
ngwmn,
Expand Down Expand Up @@ -96,8 +98,9 @@
"TransientError",
"URLTooLong",
"Unchunkable",
# resumable chunk-interruption exceptions (defined in ogc.interruptions)
# resumable fan-out interruption exceptions (defined in interruptions)
"ChunkInterrupted",
"FanOutInterrupted",
"QuotaExhausted",
"ServiceInterrupted",
# parallel-chunks control (defined in ogc.chunking)
Expand Down
10 changes: 6 additions & 4 deletions dataretrieval/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
``streamstats``) raises a subclass of :class:`DataRetrievalError` when a request
fails, so one ``except dataretrieval.DataRetrievalError`` catches them all. That
includes connection-level failures (timeouts, DNS, refused connections), which
are wrapped as :class:`NetworkError` with the underlying ``httpx`` exception on
``__cause__``.
remain inside this taxonomy rather than leaking ``httpx`` exceptions. A
deterministic failure is :class:`NetworkError`; a recoverable failure that
exhausts retries during fan-out is a resumable ``ServiceInterrupted``.

Most failures are an :class:`HTTPError` carrying the response ``.status_code``,
of which :class:`TransientError` (429 / 5xx) is the retryable subset. The rest
Expand Down Expand Up @@ -59,8 +60,9 @@ class DataRetrievalError(Exception):
else:
raise

Connection-level failures (timeouts, DNS) are wrapped as
:class:`NetworkError`, so this single clause covers them too.
Connection-level failures (timeouts, DNS) remain subclasses of this base:
:class:`NetworkError` when deterministic, or a resumable
``ServiceInterrupted`` when recoverable fan-out retries are exhausted.
"""

#: HTTP status that triggered the error, or ``None`` for errors without one
Expand Down
Loading