usdata 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usdata-0.3.0 → usdata-0.5.0}/PKG-INFO +38 -12
- {usdata-0.3.0 → usdata-0.5.0}/README.md +37 -11
- {usdata-0.3.0 → usdata-0.5.0}/pyproject.toml +1 -1
- {usdata-0.3.0 → usdata-0.5.0}/pyproject.toml.orig +1 -1
- usdata-0.5.0/src/usdata/_files.py +29 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/cache.py +14 -2
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/cli/app.py +26 -12
- usdata-0.5.0/src/usdata/data/places.csv +3292 -0
- usdata-0.5.0/src/usdata/data/places.sources.json +20 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/data/registry.yaml +23 -19
- usdata-0.5.0/src/usdata/fetch.py +79 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/manifest.py +31 -3
- usdata-0.5.0/src/usdata/protocols/erddap.py +115 -0
- usdata-0.5.0/src/usdata/protocols/http.py +96 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/protocols/s3.py +1 -1
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/provenance.py +2 -1
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/base.py +17 -0
- usdata-0.5.0/src/usdata/providers/noaa/coastwatch.py +148 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/noaa/ghcnd.py +19 -3
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/noaa/nexrad.py +35 -5
- usdata-0.5.0/src/usdata/providers/usgs/__init__.py +1 -0
- usdata-0.5.0/src/usdata/providers/usgs/daily.py +145 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/pull.py +45 -22
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/query.py +50 -16
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/registry.py +2 -1
- usdata-0.3.0/src/usdata/data/places.yaml +0 -10
- usdata-0.3.0/src/usdata/fetch.py +0 -54
- usdata-0.3.0/src/usdata/protocols/http.py +0 -43
- usdata-0.3.0/src/usdata/providers/noaa/coastwatch.py +0 -20
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/__init__.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/cli/__init__.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/data/nexrad_sites.csv +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/models.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/protocols/__init__.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/__init__.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/noaa/__init__.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/providers/noaa/sites.py +0 -0
- {usdata-0.3.0 → usdata-0.5.0}/src/usdata/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Unified Python SDK and CLI for discovering, fetching, and tracking provenance of U.S. public scientific data
|
|
5
5
|
Keywords: noaa,usgs,nasa,open-data,scientific-data,provenance
|
|
6
6
|
Author: Jake Van Slyke
|
|
@@ -24,24 +24,25 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
Unified Python SDK and CLI for discovering, fetching, and tracking the
|
|
25
25
|
provenance of U.S. public scientific data (NOAA, USGS, NASA, and more).
|
|
26
26
|
|
|
27
|
-
> Status: pre-alpha.
|
|
28
|
-
>
|
|
27
|
+
> Status: pre-alpha. v0.5 supports GHCN-Daily, NEXRAD Level II, USGS daily
|
|
28
|
+
> values, and CoastWatch SST subsets with provenance, plus Census state/county
|
|
29
|
+
> lookup. Other datasets are planned.
|
|
29
30
|
> See [docs/roadmap.md](docs/roadmap.md).
|
|
30
31
|
|
|
31
32
|
## Providers
|
|
32
33
|
|
|
33
34
|
<!-- registry:start -->
|
|
34
|
-
| Provider | Available | Stub | Planned | Next up (0.
|
|
35
|
+
| Provider | Available | Stub | Planned | Next up (0.6) | Datasets |
|
|
35
36
|
|---|---:|---:|---:|---|---|
|
|
36
|
-
| [NOAA](docs/providers/noaa.md) |
|
|
37
|
+
| [NOAA](docs/providers/noaa.md) | 3 | 0 | 26 | `gfs`, `hrrr`, `oisst`, `etopo` | `ghcn-daily`, `nexrad-level2`, `coastwatch-sst`, +26 planned |
|
|
38
|
+
| [USGS](docs/providers/usgs.md) | 1 | 0 | 2 | — | `water-daily`, +2 planned |
|
|
37
39
|
| [Census Bureau](docs/providers/census.md) | 0 | 0 | 1 | — | +1 planned |
|
|
38
40
|
| [EPA](docs/providers/epa.md) | 0 | 0 | 1 | — | +1 planned |
|
|
39
41
|
| [FEMA](docs/providers/fema.md) | 0 | 0 | 1 | — | +1 planned |
|
|
40
|
-
| [NASA](docs/providers/nasa.md) | 0 | 0 | 1 |
|
|
42
|
+
| [NASA](docs/providers/nasa.md) | 0 | 0 | 1 | `gpm-imerg` | +1 planned |
|
|
41
43
|
| [USDA](docs/providers/usda.md) | 0 | 0 | 1 | — | +1 planned |
|
|
42
|
-
| [USGS](docs/providers/usgs.md) | 0 | 0 | 3 | — | +3 planned |
|
|
43
44
|
|
|
44
|
-
Available datasets are in `code`, stubs in _italics_; planned ones are counted. Each provider page has access notes and full dataset details; [docs/roadmap.md](docs/roadmap.md) lists datasets by target version.
|
|
45
|
+
Available datasets are in `code`, stubs in _italics_; planned ones are counted. Available means implemented in this source checkout; consult the [releases](https://github.com/jakeryderv/usdata/releases) for published support. Each provider page has access notes and full dataset details; [docs/roadmap.md](docs/roadmap.md) lists datasets by target version.
|
|
45
46
|
<!-- registry:end -->
|
|
46
47
|
|
|
47
48
|
## Install
|
|
@@ -74,6 +75,7 @@ for item in fetch(ds, query):
|
|
|
74
75
|
|
|
75
76
|
```sh
|
|
76
77
|
usdata search "tornado radar" --state OK
|
|
78
|
+
usdata search precipitation --location "Cleveland County, OK"
|
|
77
79
|
usdata info noaa:ghcn-daily
|
|
78
80
|
usdata fetch noaa:ghcn-daily --lat 35.39 --lon -97.60 --radius-km 15 \
|
|
79
81
|
--start 2024-05-06 --end 2024-05-07 --vars PRCP,TMAX
|
|
@@ -81,6 +83,10 @@ usdata fetch noaa:ghcn-daily -p stations=USW00013967 --start 2024-01-01 --end 20
|
|
|
81
83
|
usdata fetch noaa:nexrad-level2 --lat 35.47 --lon -97.52 \
|
|
82
84
|
--start 2024-05-06T20:00 --end 2024-05-06T23:00 # nearest radar (KTLX)
|
|
83
85
|
usdata fetch noaa:nexrad-level2 -p site=KTLX --start 2024-05-06T20:00 --end 2024-05-06T20:30 --dry-run
|
|
86
|
+
usdata fetch usgs:water-daily -p sites=07164500 --vars 00060 \
|
|
87
|
+
--start 2024-05-06 --end 2024-05-07
|
|
88
|
+
usdata fetch noaa:coastwatch-sst --bbox=-80.08,30.02,-80.02,30.08 \
|
|
89
|
+
--start 2024-05-06T12:00Z --end 2024-05-06T12:00Z # four grid cells
|
|
84
90
|
usdata pull dataset.yaml # resolve, fetch, write dataset.lock.json
|
|
85
91
|
usdata verify dataset.yaml # exit 1 if any cached input drifted
|
|
86
92
|
```
|
|
@@ -89,12 +95,27 @@ Fetched files land in `~/.cache/usdata/<provider>/<dataset>/` (override with
|
|
|
89
95
|
`USDATA_CACHE_DIR` or `--cache-dir`), each with a `.provenance.json` sidecar
|
|
90
96
|
recording source URL, retrieval time, checksum, size, and license.
|
|
91
97
|
|
|
98
|
+
Locations accept state names/postal codes, county/state names, and quoted FIPS
|
|
99
|
+
codes. These select bounding rectangles; see [place lookup](docs/reference/places.md)
|
|
100
|
+
for coverage, ambiguity, and antimeridian limits. CoastWatch CSV includes a
|
|
101
|
+
second header row containing units; see its [access notes](docs/providers/noaa.md#coastwatch-sst).
|
|
102
|
+
|
|
103
|
+
Manifest and source fields are validated strictly; unknown fields are errors.
|
|
104
|
+
Provider-specific options belong under `params`.
|
|
105
|
+
|
|
92
106
|
A manifest declares every input a project needs. `pull` resolves each source,
|
|
93
107
|
fetches it, and writes `dataset.lock.json` pinning every asset with its checksum
|
|
94
108
|
and provenance. A second `pull` restores exactly what the lockfile pins without
|
|
95
109
|
re-querying upstream, so the inputs stay reproducible even if the source
|
|
96
|
-
changes. `verify`
|
|
97
|
-
manifest after locking requires `pull --force`
|
|
110
|
+
changes. `verify` checks the manifest checksum and re-hashes cached files
|
|
111
|
+
against the lockfile. Editing the manifest after locking requires `pull --force`
|
|
112
|
+
to re-resolve. A required source matching no assets fails the pull; set
|
|
113
|
+
`allow_empty: true` on a source only when an empty result is intentional.
|
|
114
|
+
|
|
115
|
+
Checksums detect upstream changes; they cannot recover historical bytes that
|
|
116
|
+
are no longer available. Preserve the cache for long-lived reproducibility.
|
|
117
|
+
See the [manifest reference](docs/reference/manifests.md) and the small
|
|
118
|
+
[NOAA/USGS example](examples/weather-and-streamflow/README.md).
|
|
98
119
|
|
|
99
120
|
```yaml
|
|
100
121
|
name: tornado-environment
|
|
@@ -117,11 +138,16 @@ Requires [uv](https://docs.astral.sh/uv/) and [just](https://just.systems/).
|
|
|
117
138
|
git clone https://github.com/jakeryderv/usdata && cd usdata
|
|
118
139
|
just setup # install toolchain and dependencies
|
|
119
140
|
just test # unit tests
|
|
120
|
-
just check # format, lint, typecheck, tests
|
|
141
|
+
just check # format, lint, typecheck, offline tests, generated docs
|
|
142
|
+
just build # build wheel and sdist
|
|
143
|
+
just smoke # install and exercise the built wheel outside the checkout
|
|
121
144
|
just run search radar
|
|
122
145
|
```
|
|
123
146
|
|
|
124
|
-
|
|
147
|
+
Unit tests mechanically block network connections. Integration tests that hit
|
|
148
|
+
live services run with `just test-integration`. CI checks Python 3.11 and 3.14 on
|
|
149
|
+
Linux and smoke-tests the installed wheel on Linux, macOS, and Windows. The
|
|
150
|
+
full unit and live-service suites currently run on Linux.
|
|
125
151
|
|
|
126
152
|
Releases: `just release minor` opens a version-bump PR; merging it publishes
|
|
127
153
|
to PyPI and creates the tag and GitHub release. See
|
|
@@ -3,24 +3,25 @@
|
|
|
3
3
|
Unified Python SDK and CLI for discovering, fetching, and tracking the
|
|
4
4
|
provenance of U.S. public scientific data (NOAA, USGS, NASA, and more).
|
|
5
5
|
|
|
6
|
-
> Status: pre-alpha.
|
|
7
|
-
>
|
|
6
|
+
> Status: pre-alpha. v0.5 supports GHCN-Daily, NEXRAD Level II, USGS daily
|
|
7
|
+
> values, and CoastWatch SST subsets with provenance, plus Census state/county
|
|
8
|
+
> lookup. Other datasets are planned.
|
|
8
9
|
> See [docs/roadmap.md](docs/roadmap.md).
|
|
9
10
|
|
|
10
11
|
## Providers
|
|
11
12
|
|
|
12
13
|
<!-- registry:start -->
|
|
13
|
-
| Provider | Available | Stub | Planned | Next up (0.
|
|
14
|
+
| Provider | Available | Stub | Planned | Next up (0.6) | Datasets |
|
|
14
15
|
|---|---:|---:|---:|---|---|
|
|
15
|
-
| [NOAA](docs/providers/noaa.md) |
|
|
16
|
+
| [NOAA](docs/providers/noaa.md) | 3 | 0 | 26 | `gfs`, `hrrr`, `oisst`, `etopo` | `ghcn-daily`, `nexrad-level2`, `coastwatch-sst`, +26 planned |
|
|
17
|
+
| [USGS](docs/providers/usgs.md) | 1 | 0 | 2 | — | `water-daily`, +2 planned |
|
|
16
18
|
| [Census Bureau](docs/providers/census.md) | 0 | 0 | 1 | — | +1 planned |
|
|
17
19
|
| [EPA](docs/providers/epa.md) | 0 | 0 | 1 | — | +1 planned |
|
|
18
20
|
| [FEMA](docs/providers/fema.md) | 0 | 0 | 1 | — | +1 planned |
|
|
19
|
-
| [NASA](docs/providers/nasa.md) | 0 | 0 | 1 |
|
|
21
|
+
| [NASA](docs/providers/nasa.md) | 0 | 0 | 1 | `gpm-imerg` | +1 planned |
|
|
20
22
|
| [USDA](docs/providers/usda.md) | 0 | 0 | 1 | — | +1 planned |
|
|
21
|
-
| [USGS](docs/providers/usgs.md) | 0 | 0 | 3 | — | +3 planned |
|
|
22
23
|
|
|
23
|
-
Available datasets are in `code`, stubs in _italics_; planned ones are counted. Each provider page has access notes and full dataset details; [docs/roadmap.md](docs/roadmap.md) lists datasets by target version.
|
|
24
|
+
Available datasets are in `code`, stubs in _italics_; planned ones are counted. Available means implemented in this source checkout; consult the [releases](https://github.com/jakeryderv/usdata/releases) for published support. Each provider page has access notes and full dataset details; [docs/roadmap.md](docs/roadmap.md) lists datasets by target version.
|
|
24
25
|
<!-- registry:end -->
|
|
25
26
|
|
|
26
27
|
## Install
|
|
@@ -53,6 +54,7 @@ for item in fetch(ds, query):
|
|
|
53
54
|
|
|
54
55
|
```sh
|
|
55
56
|
usdata search "tornado radar" --state OK
|
|
57
|
+
usdata search precipitation --location "Cleveland County, OK"
|
|
56
58
|
usdata info noaa:ghcn-daily
|
|
57
59
|
usdata fetch noaa:ghcn-daily --lat 35.39 --lon -97.60 --radius-km 15 \
|
|
58
60
|
--start 2024-05-06 --end 2024-05-07 --vars PRCP,TMAX
|
|
@@ -60,6 +62,10 @@ usdata fetch noaa:ghcn-daily -p stations=USW00013967 --start 2024-01-01 --end 20
|
|
|
60
62
|
usdata fetch noaa:nexrad-level2 --lat 35.47 --lon -97.52 \
|
|
61
63
|
--start 2024-05-06T20:00 --end 2024-05-06T23:00 # nearest radar (KTLX)
|
|
62
64
|
usdata fetch noaa:nexrad-level2 -p site=KTLX --start 2024-05-06T20:00 --end 2024-05-06T20:30 --dry-run
|
|
65
|
+
usdata fetch usgs:water-daily -p sites=07164500 --vars 00060 \
|
|
66
|
+
--start 2024-05-06 --end 2024-05-07
|
|
67
|
+
usdata fetch noaa:coastwatch-sst --bbox=-80.08,30.02,-80.02,30.08 \
|
|
68
|
+
--start 2024-05-06T12:00Z --end 2024-05-06T12:00Z # four grid cells
|
|
63
69
|
usdata pull dataset.yaml # resolve, fetch, write dataset.lock.json
|
|
64
70
|
usdata verify dataset.yaml # exit 1 if any cached input drifted
|
|
65
71
|
```
|
|
@@ -68,12 +74,27 @@ Fetched files land in `~/.cache/usdata/<provider>/<dataset>/` (override with
|
|
|
68
74
|
`USDATA_CACHE_DIR` or `--cache-dir`), each with a `.provenance.json` sidecar
|
|
69
75
|
recording source URL, retrieval time, checksum, size, and license.
|
|
70
76
|
|
|
77
|
+
Locations accept state names/postal codes, county/state names, and quoted FIPS
|
|
78
|
+
codes. These select bounding rectangles; see [place lookup](docs/reference/places.md)
|
|
79
|
+
for coverage, ambiguity, and antimeridian limits. CoastWatch CSV includes a
|
|
80
|
+
second header row containing units; see its [access notes](docs/providers/noaa.md#coastwatch-sst).
|
|
81
|
+
|
|
82
|
+
Manifest and source fields are validated strictly; unknown fields are errors.
|
|
83
|
+
Provider-specific options belong under `params`.
|
|
84
|
+
|
|
71
85
|
A manifest declares every input a project needs. `pull` resolves each source,
|
|
72
86
|
fetches it, and writes `dataset.lock.json` pinning every asset with its checksum
|
|
73
87
|
and provenance. A second `pull` restores exactly what the lockfile pins without
|
|
74
88
|
re-querying upstream, so the inputs stay reproducible even if the source
|
|
75
|
-
changes. `verify`
|
|
76
|
-
manifest after locking requires `pull --force`
|
|
89
|
+
changes. `verify` checks the manifest checksum and re-hashes cached files
|
|
90
|
+
against the lockfile. Editing the manifest after locking requires `pull --force`
|
|
91
|
+
to re-resolve. A required source matching no assets fails the pull; set
|
|
92
|
+
`allow_empty: true` on a source only when an empty result is intentional.
|
|
93
|
+
|
|
94
|
+
Checksums detect upstream changes; they cannot recover historical bytes that
|
|
95
|
+
are no longer available. Preserve the cache for long-lived reproducibility.
|
|
96
|
+
See the [manifest reference](docs/reference/manifests.md) and the small
|
|
97
|
+
[NOAA/USGS example](examples/weather-and-streamflow/README.md).
|
|
77
98
|
|
|
78
99
|
```yaml
|
|
79
100
|
name: tornado-environment
|
|
@@ -96,11 +117,16 @@ Requires [uv](https://docs.astral.sh/uv/) and [just](https://just.systems/).
|
|
|
96
117
|
git clone https://github.com/jakeryderv/usdata && cd usdata
|
|
97
118
|
just setup # install toolchain and dependencies
|
|
98
119
|
just test # unit tests
|
|
99
|
-
just check # format, lint, typecheck, tests
|
|
120
|
+
just check # format, lint, typecheck, offline tests, generated docs
|
|
121
|
+
just build # build wheel and sdist
|
|
122
|
+
just smoke # install and exercise the built wheel outside the checkout
|
|
100
123
|
just run search radar
|
|
101
124
|
```
|
|
102
125
|
|
|
103
|
-
|
|
126
|
+
Unit tests mechanically block network connections. Integration tests that hit
|
|
127
|
+
live services run with `just test-integration`. CI checks Python 3.11 and 3.14 on
|
|
128
|
+
Linux and smoke-tests the installed wheel on Linux, macOS, and Windows. The
|
|
129
|
+
full unit and live-service suites currently run on Linux.
|
|
104
130
|
|
|
105
131
|
Releases: `just release minor` opens a version-bump PR; merging it publishes
|
|
106
132
|
to PyPI and creates the tag and GitHub release. See
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Atomic replacement helpers for downloads and JSON records."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from tempfile import NamedTemporaryFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@contextmanager
|
|
12
|
+
def staged_path(dest: Path) -> Iterator[Path]:
|
|
13
|
+
"""Replace dest only after successful work in a unique sibling temporary file."""
|
|
14
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
with NamedTemporaryFile(
|
|
16
|
+
prefix=f".{dest.name}.", suffix=".part", dir=dest.parent, delete=False
|
|
17
|
+
) as f:
|
|
18
|
+
tmp = Path(f.name)
|
|
19
|
+
try:
|
|
20
|
+
yield tmp
|
|
21
|
+
tmp.replace(dest)
|
|
22
|
+
finally:
|
|
23
|
+
tmp.unlink(missing_ok=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def atomic_write_text(dest: Path, text: str) -> None:
|
|
27
|
+
"""Write UTF-8 text without exposing an incomplete record to readers."""
|
|
28
|
+
with staged_path(dest) as tmp:
|
|
29
|
+
tmp.write_text(text, encoding="utf-8")
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import os
|
|
7
|
+
import re
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
from usdata.models import Asset
|
|
@@ -22,9 +23,20 @@ def cache_dir() -> Path:
|
|
|
22
23
|
|
|
23
24
|
def asset_path(asset: Asset, root: Path | None = None) -> Path:
|
|
24
25
|
"""Where an asset lives in the cache: <root>/<provider>/<dataset>/<asset id>."""
|
|
25
|
-
provider, name = asset.dataset_id.
|
|
26
|
+
provider, sep, name = asset.dataset_id.partition(":")
|
|
27
|
+
if not sep or any(
|
|
28
|
+
not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", part) for part in (provider, name)
|
|
29
|
+
):
|
|
30
|
+
raise ValueError(f"unsafe dataset id: {asset.dataset_id!r}")
|
|
26
31
|
safe_id = asset.id.replace("/", "_")
|
|
27
|
-
|
|
32
|
+
if not safe_id or safe_id in {".", ".."} or "\\" in safe_id or "\x00" in safe_id:
|
|
33
|
+
raise ValueError(f"unsafe asset id: {asset.id!r}")
|
|
34
|
+
base = (root or cache_dir()).expanduser().resolve()
|
|
35
|
+
path = base / provider / name / safe_id
|
|
36
|
+
for candidate in (path, path.with_name(path.name + ".provenance.json")):
|
|
37
|
+
if not candidate.resolve().is_relative_to(base):
|
|
38
|
+
raise ValueError(f"cache path escapes root: {candidate}")
|
|
39
|
+
return path
|
|
28
40
|
|
|
29
41
|
|
|
30
42
|
def sha256_file(path: Path, chunk_size: int = 1 << 20) -> str:
|
|
@@ -9,11 +9,12 @@ import httpx
|
|
|
9
9
|
import typer
|
|
10
10
|
|
|
11
11
|
from usdata import __version__, build_query, default_registry
|
|
12
|
-
from usdata.fetch import ChecksumMismatch
|
|
12
|
+
from usdata.fetch import ChecksumMismatch
|
|
13
|
+
from usdata.fetch import fetch as fetch_query
|
|
13
14
|
from usdata.manifest import lockfile_path
|
|
14
15
|
from usdata.providers import load_adapter
|
|
15
16
|
from usdata.providers.base import NotImplementedProvider
|
|
16
|
-
from usdata.pull import ManifestChanged, UnknownDatasets
|
|
17
|
+
from usdata.pull import EmptySource, ManifestChanged, UnknownDatasets
|
|
17
18
|
from usdata.pull import pull as pull_manifest
|
|
18
19
|
from usdata.pull import verify as verify_manifest
|
|
19
20
|
from usdata.query import UnknownPlace
|
|
@@ -49,7 +50,10 @@ def search(
|
|
|
49
50
|
provider: Annotated[
|
|
50
51
|
str | None, typer.Option(help="Restrict to one provider, e.g. noaa.")
|
|
51
52
|
] = None,
|
|
52
|
-
state: Annotated[
|
|
53
|
+
state: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
typer.Option("--location", "--state", help="State, 'County, ST', or quoted FIPS code."),
|
|
56
|
+
] = None,
|
|
53
57
|
start: Annotated[str | None, typer.Option(help="ISO date or datetime.")] = None,
|
|
54
58
|
end: Annotated[str | None, typer.Option(help="ISO date or datetime.")] = None,
|
|
55
59
|
planned: Annotated[
|
|
@@ -59,8 +63,8 @@ def search(
|
|
|
59
63
|
"""Search the curated dataset registry."""
|
|
60
64
|
try:
|
|
61
65
|
query = build_query(text, provider=provider, location=state, start=start, end=end)
|
|
62
|
-
except
|
|
63
|
-
typer.secho(
|
|
66
|
+
except ValueError as e:
|
|
67
|
+
typer.secho(str(e), err=True, fg="red")
|
|
64
68
|
raise typer.Exit(code=2) from None
|
|
65
69
|
results = default_registry().search(query, include_planned=planned)
|
|
66
70
|
if not results:
|
|
@@ -103,7 +107,10 @@ def info(
|
|
|
103
107
|
@app.command()
|
|
104
108
|
def fetch(
|
|
105
109
|
dataset_id: Annotated[str, typer.Argument(help="Dataset id, e.g. noaa:ghcn-daily")],
|
|
106
|
-
state: Annotated[
|
|
110
|
+
state: Annotated[
|
|
111
|
+
str | None,
|
|
112
|
+
typer.Option("--location", "--state", help="State, 'County, ST', or quoted FIPS code."),
|
|
113
|
+
] = None,
|
|
107
114
|
bbox: Annotated[str | None, typer.Option(help="west,south,east,north in degrees.")] = None,
|
|
108
115
|
lat: Annotated[float | None, typer.Option()] = None,
|
|
109
116
|
lon: Annotated[float | None, typer.Option()] = None,
|
|
@@ -152,21 +159,21 @@ def fetch(
|
|
|
152
159
|
variables=[v.strip() for v in variables.split(",")] if variables else None,
|
|
153
160
|
**params,
|
|
154
161
|
)
|
|
155
|
-
adapter = load_adapter(ds)
|
|
156
|
-
assets = adapter.list_assets(query)
|
|
157
162
|
if dry_run:
|
|
163
|
+
with load_adapter(ds) as adapter:
|
|
164
|
+
assets = adapter.list_assets(query)
|
|
158
165
|
for a in assets:
|
|
159
166
|
typer.echo(f"{a.id}\t{a.href}")
|
|
160
167
|
typer.echo(f"{len(assets)} asset(s) matched", err=True)
|
|
161
168
|
return
|
|
162
|
-
fetched =
|
|
169
|
+
fetched = fetch_query(ds, query, root=cache_dir, force=force)
|
|
163
170
|
except (DatasetNotFound, UnknownPlace, ValueError) as e:
|
|
164
171
|
typer.secho(str(e), err=True, fg="red")
|
|
165
172
|
raise typer.Exit(code=2) from None
|
|
166
173
|
except NotImplementedProvider as e:
|
|
167
174
|
typer.secho(str(e), err=True, fg="yellow")
|
|
168
175
|
raise typer.Exit(code=3) from None
|
|
169
|
-
except httpx.HTTPError as e:
|
|
176
|
+
except (httpx.HTTPError, ChecksumMismatch) as e:
|
|
170
177
|
typer.secho(f"request failed: {e}", err=True, fg="red")
|
|
171
178
|
raise typer.Exit(code=4) from None
|
|
172
179
|
if not fetched:
|
|
@@ -189,7 +196,10 @@ def pull(
|
|
|
189
196
|
"""Fetch every source in a manifest and write (or restore from) its lockfile."""
|
|
190
197
|
try:
|
|
191
198
|
result = pull_manifest(manifest, root=cache_dir, force=force)
|
|
192
|
-
except
|
|
199
|
+
except EmptySource as e:
|
|
200
|
+
typer.secho(str(e), err=True, fg="yellow")
|
|
201
|
+
raise typer.Exit(code=1) from None
|
|
202
|
+
except (DatasetNotFound, UnknownDatasets, ManifestChanged, UnknownPlace, ValueError) as e:
|
|
193
203
|
typer.secho(str(e), err=True, fg="red")
|
|
194
204
|
raise typer.Exit(code=2) from None
|
|
195
205
|
except NotImplementedProvider as e:
|
|
@@ -215,7 +225,11 @@ def verify(
|
|
|
215
225
|
if not lock.exists():
|
|
216
226
|
typer.secho(f"no lockfile at {lock}; run pull first", err=True, fg="red")
|
|
217
227
|
raise typer.Exit(code=2)
|
|
218
|
-
|
|
228
|
+
try:
|
|
229
|
+
drift = verify_manifest(manifest, root=cache_dir)
|
|
230
|
+
except (ManifestChanged, ValueError, OSError) as e:
|
|
231
|
+
typer.secho(str(e), err=True, fg="red")
|
|
232
|
+
raise typer.Exit(code=2) from None
|
|
219
233
|
for d in drift:
|
|
220
234
|
typer.echo(f"{d.asset_id}\t{d.problem}\t{d.path}")
|
|
221
235
|
if drift:
|