sdmxlib-cli 0.72.0__py3-none-any.whl
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.
sdmxlib_cli/__init__.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""``sdmxlib``: the command line over the library's bundle tools (GH-67).
|
|
2
|
+
|
|
3
|
+
A separate distribution, ``sdmxlib-cli``, so ``sdmxlib`` itself never depends on
|
|
4
|
+
typer. Each command is a thin wrapper: the work is the library's, and is
|
|
5
|
+
tested there.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Annotated
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
import sdmxlib as sl
|
|
14
|
+
from sdmxlib.api.providers import ProviderInfo
|
|
15
|
+
from sdmxlib.bundle import check_bundle, fetch_bundle
|
|
16
|
+
from sdmxlib.model.dataflow import Dataflow
|
|
17
|
+
from sdmxlib.model.urn import SdmxUrn
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(help="sdmxlib from the command line.", no_args_is_help=True)
|
|
20
|
+
bundle = typer.Typer(help="Published SDMX bundles.", no_args_is_help=True)
|
|
21
|
+
app.add_typer(bundle, name="bundle")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@bundle.command("check")
|
|
25
|
+
def check(
|
|
26
|
+
path: Annotated[Path, typer.Argument(help="The bundle directory.")],
|
|
27
|
+
*,
|
|
28
|
+
references: Annotated[
|
|
29
|
+
bool,
|
|
30
|
+
typer.Option(
|
|
31
|
+
"--references/--no-references",
|
|
32
|
+
help="Check that every reference resolves within the bundle (loads every artefact; the slow part).",
|
|
33
|
+
),
|
|
34
|
+
] = True,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Check a published bundle against its manifest and against itself.
|
|
37
|
+
|
|
38
|
+
Prints each finding and what was checked. Exits 1 if anything was found.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
Exit: With status 1 when the bundle has findings, 0 otherwise.
|
|
42
|
+
"""
|
|
43
|
+
report = check_bundle(path, references=references)
|
|
44
|
+
for finding in report.findings:
|
|
45
|
+
typer.echo(str(finding))
|
|
46
|
+
found = len(report.findings)
|
|
47
|
+
typer.echo(f"{path}: {found} finding{'' if found == 1 else 's'}; checked {', '.join(report.checked)}")
|
|
48
|
+
raise typer.Exit(code=0 if report.ok else 1)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _source(text: str) -> ProviderInfo | str:
|
|
52
|
+
"""A `Provider` by name (``ECB``, case-insensitive), else the text as a REST endpoint URL."""
|
|
53
|
+
if "://" in text:
|
|
54
|
+
return text
|
|
55
|
+
try:
|
|
56
|
+
return sl.Provider[text.upper()]
|
|
57
|
+
except (AttributeError, KeyError) as exc:
|
|
58
|
+
msg = f"{text!r} is neither a known provider nor a URL"
|
|
59
|
+
raise typer.BadParameter(msg) from exc
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _flow(text: str) -> SdmxUrn[Dataflow]:
|
|
63
|
+
"""A dataflow as ``AGENCY:ID(VERSION)``, ``AGENCY:ID`` (latest), or a full URN."""
|
|
64
|
+
if text.startswith("urn:"):
|
|
65
|
+
return Dataflow.parse_urn(text)
|
|
66
|
+
agency, _, rest = text.partition(":")
|
|
67
|
+
flow_id, _, version = rest.partition("(")
|
|
68
|
+
if not agency or not flow_id:
|
|
69
|
+
msg = f"{text!r} is not AGENCY:ID(VERSION), AGENCY:ID or a dataflow URN"
|
|
70
|
+
raise typer.BadParameter(msg)
|
|
71
|
+
return Dataflow.urn_for(agency=agency, id=flow_id, version=version.rstrip(")") or "latest")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@bundle.command("fetch")
|
|
75
|
+
def fetch(
|
|
76
|
+
source: Annotated[str, typer.Argument(help="A provider (ECB, ESTAT, BIS, OECD, ...) or a REST endpoint URL.")],
|
|
77
|
+
flow: Annotated[str, typer.Argument(help="The dataflow: AGENCY:ID(VERSION), AGENCY:ID, or a URN.")],
|
|
78
|
+
path: Annotated[Path, typer.Argument(help="The bundle directory to create; must not exist or be empty.")],
|
|
79
|
+
*,
|
|
80
|
+
key: Annotated[str | None, typer.Option(help="SDMX key, e.g. M.USD+GBP.EUR.SP00.A.")] = None,
|
|
81
|
+
start: Annotated[str | None, typer.Option(help="Inclusive startPeriod.")] = None,
|
|
82
|
+
end: Annotated[str | None, typer.Option(help="Inclusive endPeriod.")] = None,
|
|
83
|
+
structures_only: Annotated[bool, typer.Option(help="Fetch the structures and no observations.")] = False,
|
|
84
|
+
api: Annotated[str, typer.Option(help="REST API version of a URL source.")] = "2.1",
|
|
85
|
+
source_id: Annotated[str | None, typer.Option(help="Registry id of a URL source, recorded as provenance.")] = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Fetch one dataflow, narrowed by key and period, into a new published bundle.
|
|
88
|
+
|
|
89
|
+
Prints the bundle's token and what it holds.
|
|
90
|
+
"""
|
|
91
|
+
manifest = fetch_bundle(
|
|
92
|
+
_source(source),
|
|
93
|
+
_flow(flow),
|
|
94
|
+
path,
|
|
95
|
+
key=key,
|
|
96
|
+
start=start,
|
|
97
|
+
end=end,
|
|
98
|
+
structures_only=structures_only,
|
|
99
|
+
api=api,
|
|
100
|
+
source_id=source_id,
|
|
101
|
+
)
|
|
102
|
+
counts = ", ".join(f"{b.observations} observations" for b in manifest.bindings) or "structures only"
|
|
103
|
+
typer.echo(f"{path}: {manifest.bundle_id}, {counts}; token {manifest.token}")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: sdmxlib-cli
|
|
3
|
+
Version: 0.72.0
|
|
4
|
+
Summary: sdmxlib from the command line: check SDMX bundles
|
|
5
|
+
Keywords: sdmx,statistics,metadata,cli
|
|
6
|
+
Author: gabrielgellner
|
|
7
|
+
Author-email: gabrielgellner <gabrielgellner@gmail.com>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: sdmxlib==0.72.0
|
|
14
|
+
Requires-Dist: typer>=0.27.3,<0.28
|
|
15
|
+
Requires-Python: >=3.14
|
|
16
|
+
Project-URL: Homepage, https://github.com/StatCat-Suite/sdmxlib
|
|
17
|
+
Project-URL: Repository, https://github.com/StatCat-Suite/sdmxlib
|
|
18
|
+
Project-URL: Documentation, https://statcat-suite.github.io/sdmxlib
|
|
19
|
+
Project-URL: Changelog, https://github.com/StatCat-Suite/sdmxlib/blob/main/CHANGELOG.md
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# sdmxlib-cli
|
|
23
|
+
|
|
24
|
+
[sdmxlib](https://github.com/StatCat-Suite/sdmxlib) from the command line. A
|
|
25
|
+
separate install, so the library itself carries no CLI dependency:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
uv tool install sdmxlib-cli
|
|
29
|
+
sdmxlib bundle check path/to/bundle
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`sdmxlib bundle check` runs `sdmxlib.bundle.check_bundle`: the manifest, every
|
|
33
|
+
file's checksum, the DuckLake catalog, the bindings' observation counts, and
|
|
34
|
+
the structural closure (`--no-references` skips that, the slow part). It prints
|
|
35
|
+
each finding and exits 1 if there are any.
|
|
36
|
+
|
|
37
|
+
Released with sdmxlib, at the same version.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
sdmxlib_cli/__init__.py,sha256=MNznl5-Z7heSIJCFlHLlI3kxIL4ZjkhKrWZAQrNZ93k,4118
|
|
2
|
+
sdmxlib_cli-0.72.0.dist-info/WHEEL,sha256=si2p8vW03RnhyBnhWl4tj-ObJa78SpE-c9HsEtblGR0,80
|
|
3
|
+
sdmxlib_cli-0.72.0.dist-info/entry_points.txt,sha256=nni5NOVMshJOBOByM9CIsQ1jlf-XIJSbu6j1Jmy6bxw,45
|
|
4
|
+
sdmxlib_cli-0.72.0.dist-info/METADATA,sha256=H1DqFhYuYl9fmn7-Z3hbrwHYVK6FuyrqWFqXFcZrfhE,1406
|
|
5
|
+
sdmxlib_cli-0.72.0.dist-info/RECORD,,
|