usdata 0.32.0__tar.gz → 0.34.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.32.0 → usdata-0.34.0}/PKG-INFO +2 -2
- {usdata-0.32.0 → usdata-0.34.0}/README.md +1 -1
- {usdata-0.32.0 → usdata-0.34.0}/pyproject.toml +1 -1
- {usdata-0.32.0 → usdata-0.34.0}/pyproject.toml.orig +1 -1
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/__init__.py +3 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_grib.py +7 -2
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/app.py +92 -25
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/data/registry.yaml +357 -21
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/inspect.py +18 -15
- usdata-0.34.0/src/usdata/messages.py +60 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/models.py +67 -0
- usdata-0.34.0/src/usdata/protocols/arcgis.py +405 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/protocols/s3.py +60 -20
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/provenance.py +1 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/base.py +31 -1
- usdata-0.34.0/src/usdata/providers/noaa/enso.py +76 -0
- usdata-0.34.0/src/usdata/providers/noaa/eri.py +468 -0
- usdata-0.34.0/src/usdata/providers/noaa/ersst.py +95 -0
- usdata-0.34.0/src/usdata/providers/noaa/geotiff.py +191 -0
- usdata-0.34.0/src/usdata/providers/noaa/goes.py +222 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/grib_index.py +24 -37
- usdata-0.34.0/src/usdata/providers/noaa/gsom_files.py +83 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/hrrr.py +18 -1
- usdata-0.34.0/src/usdata/providers/noaa/nws_damage.py +381 -0
- usdata-0.34.0/src/usdata/providers/noaa/utm.py +54 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/testing.py +8 -4
- usdata-0.32.0/src/usdata/providers/noaa/goes.py +0 -171
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/__main__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_aqs.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_census.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_fetch.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_files.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_hurdat2.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_netcdf.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_progress.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/_radar.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cache.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cache_ops.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cite.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/cache.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/cite.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/doctor.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/inspect.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/cli/progress.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/data/nexrad_sites.csv +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/data/places.csv +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/data/places.sources.json +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/doctor.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/manifest.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/mirror.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/protocols/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/protocols/erddap.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/protocols/http.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/protocols/listing.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/census/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/census/acs.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/credentials.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/epa/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/epa/aqs.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/fema/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/fema/declarations.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/http.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/coastwatch.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/coops.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/gfs.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/ghcnd.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/glm.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/gsom.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/gsoy.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/hurdat2.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/ibtracs.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/lcd.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/mrms.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/nbm.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/nexrad.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/nexrad_level3.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/normals.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/nws_vtec.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/rap.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/sites.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/spc.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/noaa/storm_events.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/params.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/usgs/__init__.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/usgs/daily.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/providers/usgs/earthquakes.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/pull.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/py.typed +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/query.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/readers.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/registry.py +0 -0
- {usdata-0.32.0 → usdata-0.34.0}/src/usdata/selection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.34.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,open-data,scientific-data,provenance,reproducible-research,weather,climate,meteorology
|
|
6
6
|
Author: Jake Van Slyke
|
|
@@ -113,7 +113,7 @@ breaking changes.
|
|
|
113
113
|
| [docs.usdata.dev](https://docs.usdata.dev/) | How to use it: [install](https://docs.usdata.dev/install/), [getting started](https://docs.usdata.dev/getting-started/), guides, dataset notes, and reference |
|
|
114
114
|
| [Severe-weather case study](https://usdata.dev/studies/severe-weather-case-study/) | One tornado, six sources, one manifest and lockfile, ending in a citation |
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Thirty-four datasets are available today and seventeen more are planned, grouped
|
|
117
117
|
by agency and product family in the [catalog](docs/providers/README.md).
|
|
118
118
|
|
|
119
119
|
## How this compares
|
|
@@ -68,7 +68,7 @@ breaking changes.
|
|
|
68
68
|
| [docs.usdata.dev](https://docs.usdata.dev/) | How to use it: [install](https://docs.usdata.dev/install/), [getting started](https://docs.usdata.dev/getting-started/), guides, dataset notes, and reference |
|
|
69
69
|
| [Severe-weather case study](https://usdata.dev/studies/severe-weather-case-study/) | One tornado, six sources, one manifest and lockfile, ending in a citation |
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Thirty-four datasets are available today and seventeen more are planned, grouped
|
|
72
72
|
by agency and product family in the [catalog](docs/providers/README.md).
|
|
73
73
|
|
|
74
74
|
## How this compares
|
|
@@ -13,6 +13,7 @@ except PackageNotFoundError: # running from a source tree without an install
|
|
|
13
13
|
from usdata._fetch import ChecksumMismatch, FetchedAsset, fetch, fetch_asset
|
|
14
14
|
from usdata.cite import Citation, cite_dataset, cite_lockfile
|
|
15
15
|
from usdata.inspect import inspect_asset, inspect_path
|
|
16
|
+
from usdata.messages import MessageListing, list_messages
|
|
16
17
|
from usdata.models import (
|
|
17
18
|
Asset,
|
|
18
19
|
BBox,
|
|
@@ -36,6 +37,7 @@ __all__ = [
|
|
|
36
37
|
"Dataset",
|
|
37
38
|
"DatasetNotFound",
|
|
38
39
|
"FetchedAsset",
|
|
40
|
+
"MessageListing",
|
|
39
41
|
"Place",
|
|
40
42
|
"Provenance",
|
|
41
43
|
"Query",
|
|
@@ -55,6 +57,7 @@ __all__ = [
|
|
|
55
57
|
"get",
|
|
56
58
|
"inspect_asset",
|
|
57
59
|
"inspect_path",
|
|
60
|
+
"list_messages",
|
|
58
61
|
"pull",
|
|
59
62
|
"search",
|
|
60
63
|
"select_by_time",
|
|
@@ -96,7 +96,9 @@ def import_eccodes() -> Any:
|
|
|
96
96
|
imported later in the same process, both copies free the same memory as the
|
|
97
97
|
interpreter exits, which aborts it with "double free or corruption" and exit
|
|
98
98
|
status 134 after the work is done. Loading pyproj's copy first avoids it.
|
|
99
|
-
A pyproj that fails to import is left for whoever uses it to report.
|
|
99
|
+
A pyproj that fails to import is left for whoever uses it to report. This is
|
|
100
|
+
a workaround for https://github.com/ecmwf/eckit/issues/354; once an eckitlib
|
|
101
|
+
wheel no longer clashes with pyproj's PROJ, it can go.
|
|
100
102
|
|
|
101
103
|
Raises:
|
|
102
104
|
ModuleNotFoundError: eccodes is not installed.
|
|
@@ -481,8 +483,11 @@ def open_grib2(
|
|
|
481
483
|
needs_select = select is None and not fetched.provenance.is_partial
|
|
482
484
|
object_messages = fetched.provenance.object_messages
|
|
483
485
|
selectors = fetched.provenance.selectors if object_messages else []
|
|
486
|
+
fields_seen: dict[int, int] = {}
|
|
484
487
|
for index, h in _messages(eccodes, fetched.path):
|
|
485
488
|
count += 1
|
|
489
|
+
# Fields of one message share its index; the nth of them is the index's field n+1.
|
|
490
|
+
field = fields_seen[index] = fields_seen.get(index, -1) + 1
|
|
486
491
|
if count > 1 and needs_select:
|
|
487
492
|
continue
|
|
488
493
|
hits = {key: _matched(eccodes, h, key, wanted) for key, wanted in options.items()}
|
|
@@ -536,7 +541,7 @@ def open_grib2(
|
|
|
536
541
|
"step": attrs.get("step"),
|
|
537
542
|
}
|
|
538
543
|
if selectors:
|
|
539
|
-
message["selector"] =
|
|
544
|
+
message["selector"] = fetched.provenance.field_selector(index, field)
|
|
540
545
|
short = base_name(
|
|
541
546
|
message["shortName"], [attrs.get(key) for key in PARAMETER_KEYS], fetched.asset.id
|
|
542
547
|
)
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
from collections.abc import Iterable
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Annotated
|
|
8
|
+
from typing import Annotated, Any
|
|
9
9
|
|
|
10
10
|
import httpx
|
|
11
11
|
import typer
|
|
@@ -20,6 +20,7 @@ from usdata.cli.doctor import doctor
|
|
|
20
20
|
from usdata.cli.inspect import inspect
|
|
21
21
|
from usdata.cli.progress import progress
|
|
22
22
|
from usdata.manifest import lockfile_path
|
|
23
|
+
from usdata.messages import list_messages
|
|
23
24
|
from usdata.models import READER_EXTRAS_TEXT, Dataset, Status, describe_duration
|
|
24
25
|
from usdata.providers import adapter_class, load_adapter
|
|
25
26
|
from usdata.providers.base import NotImplementedProvider
|
|
@@ -67,6 +68,33 @@ _QUERY_FLAGS = {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
|
|
71
|
+
def _parse_params(
|
|
72
|
+
param: list[str] | None, command: str, flags: dict[str, str | None]
|
|
73
|
+
) -> dict[str, Any]:
|
|
74
|
+
"""``--param key=value`` pairs as a dict of strings; exit 2 on a malformed or reserved key.
|
|
75
|
+
|
|
76
|
+
``flags`` maps each reserved query option to the flag ``command`` takes it
|
|
77
|
+
through, or to ``None`` when ``command`` does not take it.
|
|
78
|
+
"""
|
|
79
|
+
params: dict[str, Any] = {}
|
|
80
|
+
for item in param or []:
|
|
81
|
+
key, sep, value = item.partition("=")
|
|
82
|
+
if not sep:
|
|
83
|
+
typer.secho(f"--param expects key=value, got {item!r}", err=True, fg="red")
|
|
84
|
+
raise typer.Exit(code=2)
|
|
85
|
+
if key in flags:
|
|
86
|
+
flag = flags[key]
|
|
87
|
+
hint = (
|
|
88
|
+
f"use {flag} instead of --param"
|
|
89
|
+
if flag is not None
|
|
90
|
+
else f"{command} does not support this option"
|
|
91
|
+
)
|
|
92
|
+
typer.secho(f"{key} is a reserved query option; {hint}", err=True, fg="red")
|
|
93
|
+
raise typer.Exit(code=2)
|
|
94
|
+
params[key] = value
|
|
95
|
+
return params
|
|
96
|
+
|
|
97
|
+
|
|
70
98
|
def _size_phrase(known_bytes: int, unknown: int, *, sources: Iterable[str] = ()) -> str:
|
|
71
99
|
"""How many bytes a dry run would move, said honestly when some size is missing.
|
|
72
100
|
|
|
@@ -357,26 +385,7 @@ def fetch(
|
|
|
357
385
|
] = False,
|
|
358
386
|
) -> None:
|
|
359
387
|
"""Resolve a query against one dataset and download the matching assets."""
|
|
360
|
-
params
|
|
361
|
-
for item in param or []:
|
|
362
|
-
key, sep, value = item.partition("=")
|
|
363
|
-
if not sep:
|
|
364
|
-
typer.secho(f"--param expects key=value, got {item!r}", err=True, fg="red")
|
|
365
|
-
raise typer.Exit(code=2)
|
|
366
|
-
if key in _QUERY_FLAGS:
|
|
367
|
-
flag = _QUERY_FLAGS[key]
|
|
368
|
-
hint = (
|
|
369
|
-
f"use {flag} instead of --param"
|
|
370
|
-
if flag is not None
|
|
371
|
-
else "fetch does not support this option"
|
|
372
|
-
)
|
|
373
|
-
typer.secho(
|
|
374
|
-
f"{key} is a reserved query option; {hint}",
|
|
375
|
-
err=True,
|
|
376
|
-
fg="red",
|
|
377
|
-
)
|
|
378
|
-
raise typer.Exit(code=2)
|
|
379
|
-
params[key] = value
|
|
388
|
+
params = _parse_params(param, "fetch", _QUERY_FLAGS)
|
|
380
389
|
box = None
|
|
381
390
|
if bbox:
|
|
382
391
|
try:
|
|
@@ -437,6 +446,64 @@ def fetch(
|
|
|
437
446
|
typer.echo(f"{f.path}\t{tag}\t{f.provenance.size} bytes")
|
|
438
447
|
|
|
439
448
|
|
|
449
|
+
_MESSAGES_FLAGS: dict[str, str | None] = {
|
|
450
|
+
key: flag if key in ("start", "end") else None for key, flag in _QUERY_FLAGS.items()
|
|
451
|
+
}
|
|
452
|
+
"""The query options ``messages`` takes: a model run is selected by its window alone."""
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@app.command()
|
|
456
|
+
def messages(
|
|
457
|
+
dataset_id: Annotated[str, typer.Argument(help="Dataset id, e.g. noaa:hrrr")],
|
|
458
|
+
start: Annotated[str | None, typer.Option(help="ISO date or datetime.")] = None,
|
|
459
|
+
end: Annotated[
|
|
460
|
+
str | None,
|
|
461
|
+
typer.Option(help="ISO date or datetime; a date alone runs to the end of that UTC day."),
|
|
462
|
+
] = None,
|
|
463
|
+
param: Annotated[
|
|
464
|
+
list[str] | None,
|
|
465
|
+
typer.Option("--param", "-p", help="Provider-specific key=value, repeatable."),
|
|
466
|
+
] = None,
|
|
467
|
+
as_json: Annotated[
|
|
468
|
+
bool,
|
|
469
|
+
typer.Option("--json", help="Emit a JSON array of listings and nothing else on stdout."),
|
|
470
|
+
] = False,
|
|
471
|
+
) -> None:
|
|
472
|
+
"""List the GRIB2 messages each file of a query holds, spelled as the messages param takes them.
|
|
473
|
+
|
|
474
|
+
One tab-separated line per field: asset id, message number, bytes, and the
|
|
475
|
+
selector that names exactly that field. Only the index sidecars are read.
|
|
476
|
+
"""
|
|
477
|
+
params = _parse_params(param, "messages", _MESSAGES_FLAGS)
|
|
478
|
+
try:
|
|
479
|
+
ds = default_registry().get(dataset_id)
|
|
480
|
+
listings = list_messages(ds, build_query(start=start, end=end, **params))
|
|
481
|
+
except (DatasetNotFound, ValueError) as e:
|
|
482
|
+
typer.secho(str(e), err=True, fg="red")
|
|
483
|
+
raise typer.Exit(code=2) from None
|
|
484
|
+
except NotImplementedProvider as e:
|
|
485
|
+
typer.secho(str(e), err=True, fg="yellow")
|
|
486
|
+
raise typer.Exit(code=3) from None
|
|
487
|
+
except httpx.HTTPError as e:
|
|
488
|
+
typer.secho(f"request failed: {e}", err=True, fg="red")
|
|
489
|
+
raise typer.Exit(code=4) from None
|
|
490
|
+
if as_json:
|
|
491
|
+
typer.echo(json.dumps([listing.model_dump(mode="json") for listing in listings], indent=2))
|
|
492
|
+
else:
|
|
493
|
+
for listing in listings:
|
|
494
|
+
for entry in listing.messages:
|
|
495
|
+
number = entry.number if entry.field is None else f"{entry.number}.{entry.field}"
|
|
496
|
+
typer.echo(f"{listing.asset.id}\t{number}\t{entry.length}\t{entry.selector}")
|
|
497
|
+
count = sum(len(listing.messages) for listing in listings)
|
|
498
|
+
typer.echo(
|
|
499
|
+
f"{count} message(s) in {len(listings)} file(s); a selector may stop after its "
|
|
500
|
+
"level text, as CAPE:surface, to match every step",
|
|
501
|
+
err=True,
|
|
502
|
+
)
|
|
503
|
+
if not listings:
|
|
504
|
+
raise typer.Exit(code=1)
|
|
505
|
+
|
|
506
|
+
|
|
440
507
|
def _print_plan(plan: Plan, *, as_json: bool, quiet: bool) -> None:
|
|
441
508
|
"""Report what a pull would fetch: assets, a subtotal per source, a total on stderr."""
|
|
442
509
|
if as_json:
|
|
@@ -574,7 +641,10 @@ def verify(
|
|
|
574
641
|
bool, typer.Option("--json", help="With --listing, emit the comparison as JSON on stdout.")
|
|
575
642
|
] = False,
|
|
576
643
|
) -> None:
|
|
577
|
-
"""Check cached files
|
|
644
|
+
"""Check cached files, or with --listing today's source listings, against the lockfile.
|
|
645
|
+
|
|
646
|
+
Exit 1 on any drift.
|
|
647
|
+
"""
|
|
578
648
|
if as_json and not listing:
|
|
579
649
|
typer.secho("--json applies to --listing only", err=True, fg="red")
|
|
580
650
|
raise typer.Exit(code=2)
|
|
@@ -605,9 +675,6 @@ def _verify_listing(manifest: Path, *, as_json: bool) -> None:
|
|
|
605
675
|
"""Compare the lockfile with today's listings; exit 1 when a re-resolve would differ."""
|
|
606
676
|
try:
|
|
607
677
|
comparison = compare_manifest_listing(manifest)
|
|
608
|
-
except EmptySource as e:
|
|
609
|
-
typer.secho(str(e), err=True, fg="yellow")
|
|
610
|
-
raise typer.Exit(code=1) from None
|
|
611
678
|
except (DatasetNotFound, UnknownDatasets, ManifestChanged, UnknownPlace, ValueError) as e:
|
|
612
679
|
typer.secho(str(e), err=True, fg="red")
|
|
613
680
|
raise typer.Exit(code=2) from None
|