sdmxlib-cli 0.72.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.
@@ -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,16 @@
1
+ # sdmxlib-cli
2
+
3
+ [sdmxlib](https://github.com/StatCat-Suite/sdmxlib) from the command line. A
4
+ separate install, so the library itself carries no CLI dependency:
5
+
6
+ ```sh
7
+ uv tool install sdmxlib-cli
8
+ sdmxlib bundle check path/to/bundle
9
+ ```
10
+
11
+ `sdmxlib bundle check` runs `sdmxlib.bundle.check_bundle`: the manifest, every
12
+ file's checksum, the DuckLake catalog, the bindings' observation counts, and
13
+ the structural closure (`--no-references` skips that, the slow part). It prints
14
+ each finding and exits 1 if there are any.
15
+
16
+ Released with sdmxlib, at the same version.
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "sdmxlib-cli"
3
+ version = "0.72.0"
4
+ description = "sdmxlib from the command line: check SDMX bundles"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ keywords = [
8
+ "sdmx",
9
+ "statistics",
10
+ "metadata",
11
+ "cli",
12
+ ]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Environment :: Console",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.14",
18
+ ]
19
+ dependencies = [
20
+ "sdmxlib==0.72.0",
21
+ "typer>=0.27.3,<0.28",
22
+ ]
23
+
24
+ [project.license]
25
+ text = "Apache-2.0"
26
+
27
+ [[project.authors]]
28
+ name = "gabrielgellner"
29
+ email = "gabrielgellner@gmail.com"
30
+
31
+ [project.scripts]
32
+ sdmxlib = "sdmxlib_cli:app"
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/StatCat-Suite/sdmxlib"
36
+ Repository = "https://github.com/StatCat-Suite/sdmxlib"
37
+ Documentation = "https://statcat-suite.github.io/sdmxlib"
38
+ Changelog = "https://github.com/StatCat-Suite/sdmxlib/blob/main/CHANGELOG.md"
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.13.0,<0.14.0"]
42
+ build-backend = "uv_build"
43
+
44
+ [tool.uv.sources.sdmxlib]
45
+ workspace = true
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "sdmxlib-cli"
3
+ # Locked to sdmxlib's version and released with it (GH-67): the release steps
4
+ # bump both and the pin below, and a test asserts the three agree, so the
5
+ # command can never be installed against an sdmxlib it was not tested with.
6
+ version = "0.72.0"
7
+ description = "sdmxlib from the command line: check SDMX bundles"
8
+ readme = "README.md"
9
+ license = { text = "Apache-2.0" }
10
+ authors = [{ name = "gabrielgellner", email = "gabrielgellner@gmail.com" }]
11
+ requires-python = ">=3.14"
12
+ keywords = ["sdmx", "statistics", "metadata", "cli"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Environment :: Console",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.14",
18
+ ]
19
+ # A standalone install, so sdmxlib itself never depends on typer:
20
+ # `uv tool install sdmxlib-cli` gives an `sdmxlib` command.
21
+ dependencies = [
22
+ "sdmxlib==0.72.0",
23
+ # Pre-1.0, so capped at the minor.
24
+ "typer>=0.27.3,<0.28",
25
+ ]
26
+
27
+ [project.scripts]
28
+ sdmxlib = "sdmxlib_cli:app"
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/StatCat-Suite/sdmxlib"
32
+ Repository = "https://github.com/StatCat-Suite/sdmxlib"
33
+ Documentation = "https://statcat-suite.github.io/sdmxlib"
34
+ Changelog = "https://github.com/StatCat-Suite/sdmxlib/blob/main/CHANGELOG.md"
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.13.0,<0.14.0"]
38
+ build-backend = "uv_build"
39
+
40
+ [tool.uv.sources]
41
+ sdmxlib = { workspace = true }
@@ -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}")