sdmxlib 0.68.0__tar.gz → 0.69.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.
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/PKG-INFO +31 -26
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/README.md +30 -25
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/pyproject.toml +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/pyproject.toml.orig +3 -4
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/__init__.py +32 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_duckdb.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_singleflight.py +1 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_time.py +2 -7
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/_freshness.py +1 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/client.py +4 -89
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/federated.py +4 -9
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/policy.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/registry.py +111 -346
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/contentconstraint.py +9 -14
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/metadata_annotation.py +6 -13
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/data_store.py +4 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/__init__.py +6 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_index.py +2 -8
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/data_format.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/gaps.py +1 -7
- sdmxlib-0.69.0/src/sdmxlib/formats/sdmx_csv/__init__.py +8 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_csv/reader.py +11 -133
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_csv/writer.py +3 -12
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_data_common.py +28 -14
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_dto.py +1 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_losses.py +13 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_resolve.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_structure_dto.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_structure_ingest.py +9 -8
- sdmxlib-0.69.0/src/sdmxlib/formats/sdmx_json/data.py +34 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/data_v1.py +17 -37
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/data_v2.py +18 -43
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json_1_0/writer.py +1 -7
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_common.py +4 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_losses.py +8 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_v3_reader.py +3 -3
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/reader.py +2 -2
- sdmxlib-0.69.0/src/sdmxlib/local/_scan.py +148 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/data_store.py +107 -9
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/registry.py +94 -39
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/sql.py +22 -14
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/__init__.py +2 -3
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_binding.py +21 -3
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_items.py +3 -7
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_label_match.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_rebind.py +2 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_refshape.py +4 -8
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_rules.py +20 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_tree.py +59 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/base.py +3 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/binding.py +6 -11
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/codelist.py +3 -8
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/collections.py +18 -24
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/convert.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/datastructure.py +13 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/hierarchy.py +8 -13
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/in_memory_registry.py +4 -8
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/istring.py +3 -7
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/message.py +31 -35
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadataflow.py +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadataset.py +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadatastructure.py +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/ref.py +20 -29
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/registry.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/representation.py +12 -3
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/rest_api.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/rollup.py +7 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/urn.py +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/validation.py +30 -57
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/version.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/version_rewrite.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/polars.py +12 -174
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_closure.py +8 -11
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_common.py +2 -4
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_compile.py +79 -23
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_edges.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_execute.py +7 -14
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_expr.py +318 -46
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_joins.py +2 -9
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_plan.py +2 -5
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_pushdown.py +7 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_rebase.py +7 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_rest.py +12 -13
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/accessors.py +1 -1
- sdmxlib-0.69.0/src/sdmxlib/query/observations.py +805 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/query.py +199 -50
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/raw.py +6 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/schema.py +0 -2
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/rest.py +1 -3
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/session.py +1 -6
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/sql.py +5 -10
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_projections.py +1 -1
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/lazy.py +38 -9
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/readers.py +27 -4
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/resolve.py +8 -13
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/writers.py +2 -2
- sdmxlib-0.68.0/src/sdmxlib/api/filters.py +0 -67
- sdmxlib-0.68.0/src/sdmxlib/api/query.py +0 -205
- sdmxlib-0.68.0/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -6
- sdmxlib-0.68.0/src/sdmxlib/model/dataset.py +0 -446
- sdmxlib-0.68.0/src/sdmxlib/query/observations.py +0 -387
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/providers.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/session.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_arrow.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_attachment.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_coerce.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_positions.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_refuse.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_header.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_periods.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/structure_values.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json_1_0/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_allowed_children.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_closed_world.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_header.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_v3_writer.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/_providers.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/_synthetic.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/namespaces.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/reader.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/writer.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_container.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_invariants.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_referenceable.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_resolvers.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/annotations.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/category.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/concept.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/constraint.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/dataflow.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/errors.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/expr.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/header.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/link.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/mapping.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadata_provision.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/organisation.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/provision.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/valuelist.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/py.typed +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/__init__.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_checked.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_kinds.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/schema.py +0 -0
- {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sdmxlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.69.0
|
|
4
4
|
Summary: SDMX structural metadata library for Python
|
|
5
5
|
Keywords: sdmx,statistics,metadata,datastructure
|
|
6
6
|
Author: gabrielgellner
|
|
@@ -48,11 +48,10 @@ with sl.RestRegistry(sl.Provider.BIS) as reg:
|
|
|
48
48
|
|
|
49
49
|
for dim in dsd.dimensions:
|
|
50
50
|
if dim.is_enumerated:
|
|
51
|
-
|
|
52
|
-
print(f"{dim.id}: {cl.id} ({len(cl.codes)} codes)")
|
|
51
|
+
print(f"{dim.id}: {dim.codelist.id} ({len(dim.code_ids())} codes)")
|
|
53
52
|
```
|
|
54
53
|
|
|
55
|
-
```
|
|
54
|
+
```text
|
|
56
55
|
FREQ: CL_FREQ (7 codes)
|
|
57
56
|
REF_AREA: CL_BIS_IF_REF_AREA (44 codes)
|
|
58
57
|
INSTR_ASSET: CL_INSTR_ASSET (12 codes)
|
|
@@ -66,6 +65,8 @@ uv add sdmxlib
|
|
|
66
65
|
# or: pip install sdmxlib
|
|
67
66
|
```
|
|
68
67
|
|
|
68
|
+
sdmxlib needs Python 3.14 or later.
|
|
69
|
+
|
|
69
70
|
## What it does
|
|
70
71
|
|
|
71
72
|
- Connects to any SDMX 2.1 or 3.0 REST endpoint; built-in shortcuts for ECB,
|
|
@@ -76,25 +77,27 @@ uv add sdmxlib
|
|
|
76
77
|
- Returns fully resolved domain objects with plain attribute access — no XML,
|
|
77
78
|
no URN chasing
|
|
78
79
|
- Filter collections with lambdas or the `item()` expression API
|
|
79
|
-
- Build SDMX artefacts locally
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
- Build SDMX artefacts locally — frozen, checked as they are built — and
|
|
81
|
+
serialise to SDMX-ML 2.1, 3.0 and 3.1 or SDMX-JSON 2.0
|
|
82
|
+
- `LocalRegistry` — a DuckDB-backed persistent store with lazy codelists and
|
|
83
|
+
hierarchies and direct SQL code maps; pass it as a `RestRegistry`'s
|
|
84
|
+
`store=` and everything fetched is kept for offline use
|
|
85
|
+
- A path query language over both planes, on a `LocalRegistry` or straight
|
|
86
|
+
against REST: `reg.query(sl.q.dataflow)` for structures,
|
|
87
|
+
`reg.obs(flow)` for rows, `reg.sql(...)` as the escape hatch
|
|
86
88
|
- Uniform SKOS-aligned tree API (`roots`/`leaves`/`narrower`/`broader`/
|
|
87
89
|
`*_transitive`/`depth_of` plus bulk projections `depths`/`broader_map`/
|
|
88
90
|
`ancestors_map`/`inherit_along_broader`) on `Codelist`, `Hierarchy`,
|
|
89
91
|
`CategoryScheme`, `MetadataStructure` — and their lazy SQL variants
|
|
90
|
-
`LazyCodelist`/`LazyHierarchy
|
|
92
|
+
`LazyCodelist`/`LazyHierarchy`. A codelist is keyed by code id; the other
|
|
93
|
+
trees by path (`h["CA.ON"]`), since a node id may repeat across branches
|
|
91
94
|
- Reference metadata via `MetadataStructure` (MSD), `Metadataflow`, and
|
|
92
95
|
`MetadataSet` — attach typed footnotes, methodology notes, or quality
|
|
93
96
|
reports to any identifiable artefact. Definitions ride in
|
|
94
97
|
`StructureMessage`; populated payloads ride in `MetadataMessage`
|
|
95
98
|
(`<mes:GenericMetadata>` in SDMX-ML 3.0; metadata-schema envelope in
|
|
96
99
|
SDMX-JSON 2.0). Both message kinds round-trip through
|
|
97
|
-
`
|
|
100
|
+
`LocalRegistry`, JSON, and XML.
|
|
98
101
|
- Integrates with [Polars](https://pola.rs) for schema generation and data
|
|
99
102
|
validation
|
|
100
103
|
- Fully typed — IDE completion and static analysis work throughout
|
|
@@ -104,22 +107,24 @@ uv add sdmxlib
|
|
|
104
107
|
Two condensed reference files at the repo root are written for LLM tools
|
|
105
108
|
(Claude Code, Cursor, Copilot, etc.):
|
|
106
109
|
|
|
107
|
-
- [`llms.txt`](llms.txt) —
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`
|
|
110
|
+
- [`llms.txt`](llms.txt) — the whole library in one pass: registries and
|
|
111
|
+
`get`, navigation, building artefacts, the local store, trees, observation
|
|
112
|
+
data, reading and writing SDMX, reference metadata, and the type table
|
|
113
|
+
- [`llms-dsl.txt`](llms-dsl.txt) — the path query language in depth:
|
|
114
|
+
`sl.q`, `Query` / `Select`, projection and aggregation, edges, windows,
|
|
115
|
+
observation queries (`ObsQuery`), and `reg.sql()`
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
library's actual surface.
|
|
117
|
+
Every Python block in both is executed by the test suite, so they track the
|
|
118
|
+
library's actual surface. Point your assistant at them.
|
|
115
119
|
|
|
116
120
|
## Upgrading
|
|
117
121
|
|
|
118
122
|
sdmxlib is pre-stability and breaks cleanly rather than carrying two spellings
|
|
119
|
-
of the same idea.
|
|
120
|
-
|
|
121
|
-
is now `sl.LocalRegistry`, `get()` takes a URN and a resolution
|
|
122
|
-
four query DSLs are one path language
|
|
123
|
+
of the same idea. [Upgrading](https://pinax-suite.gitlab.io/sdmxlib/upgrading/)
|
|
124
|
+
lists every breaking change since 0.48, with the old and new spelling of each —
|
|
125
|
+
`sl.Registry` is now `sl.LocalRegistry`, `get()` takes a URN and a resolution
|
|
126
|
+
depth, the four query DSLs are one path language, and every model class is
|
|
127
|
+
frozen.
|
|
123
128
|
|
|
124
129
|
## Documentation
|
|
125
130
|
|
|
@@ -140,6 +145,6 @@ including:
|
|
|
140
145
|
```bash
|
|
141
146
|
just check # lint + typecheck
|
|
142
147
|
just test # unit tests
|
|
143
|
-
just test-integration #
|
|
144
|
-
just ci # full
|
|
148
|
+
just test-integration # end-to-end tests, replayed from recorded cassettes
|
|
149
|
+
just ci # the full gate: lint, typecheck, unit, docs, integration
|
|
145
150
|
```
|
|
@@ -19,11 +19,10 @@ with sl.RestRegistry(sl.Provider.BIS) as reg:
|
|
|
19
19
|
|
|
20
20
|
for dim in dsd.dimensions:
|
|
21
21
|
if dim.is_enumerated:
|
|
22
|
-
|
|
23
|
-
print(f"{dim.id}: {cl.id} ({len(cl.codes)} codes)")
|
|
22
|
+
print(f"{dim.id}: {dim.codelist.id} ({len(dim.code_ids())} codes)")
|
|
24
23
|
```
|
|
25
24
|
|
|
26
|
-
```
|
|
25
|
+
```text
|
|
27
26
|
FREQ: CL_FREQ (7 codes)
|
|
28
27
|
REF_AREA: CL_BIS_IF_REF_AREA (44 codes)
|
|
29
28
|
INSTR_ASSET: CL_INSTR_ASSET (12 codes)
|
|
@@ -37,6 +36,8 @@ uv add sdmxlib
|
|
|
37
36
|
# or: pip install sdmxlib
|
|
38
37
|
```
|
|
39
38
|
|
|
39
|
+
sdmxlib needs Python 3.14 or later.
|
|
40
|
+
|
|
40
41
|
## What it does
|
|
41
42
|
|
|
42
43
|
- Connects to any SDMX 2.1 or 3.0 REST endpoint; built-in shortcuts for ECB,
|
|
@@ -47,25 +48,27 @@ uv add sdmxlib
|
|
|
47
48
|
- Returns fully resolved domain objects with plain attribute access — no XML,
|
|
48
49
|
no URN chasing
|
|
49
50
|
- Filter collections with lambdas or the `item()` expression API
|
|
50
|
-
- Build SDMX artefacts locally
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
- Build SDMX artefacts locally — frozen, checked as they are built — and
|
|
52
|
+
serialise to SDMX-ML 2.1, 3.0 and 3.1 or SDMX-JSON 2.0
|
|
53
|
+
- `LocalRegistry` — a DuckDB-backed persistent store with lazy codelists and
|
|
54
|
+
hierarchies and direct SQL code maps; pass it as a `RestRegistry`'s
|
|
55
|
+
`store=` and everything fetched is kept for offline use
|
|
56
|
+
- A path query language over both planes, on a `LocalRegistry` or straight
|
|
57
|
+
against REST: `reg.query(sl.q.dataflow)` for structures,
|
|
58
|
+
`reg.obs(flow)` for rows, `reg.sql(...)` as the escape hatch
|
|
57
59
|
- Uniform SKOS-aligned tree API (`roots`/`leaves`/`narrower`/`broader`/
|
|
58
60
|
`*_transitive`/`depth_of` plus bulk projections `depths`/`broader_map`/
|
|
59
61
|
`ancestors_map`/`inherit_along_broader`) on `Codelist`, `Hierarchy`,
|
|
60
62
|
`CategoryScheme`, `MetadataStructure` — and their lazy SQL variants
|
|
61
|
-
`LazyCodelist`/`LazyHierarchy
|
|
63
|
+
`LazyCodelist`/`LazyHierarchy`. A codelist is keyed by code id; the other
|
|
64
|
+
trees by path (`h["CA.ON"]`), since a node id may repeat across branches
|
|
62
65
|
- Reference metadata via `MetadataStructure` (MSD), `Metadataflow`, and
|
|
63
66
|
`MetadataSet` — attach typed footnotes, methodology notes, or quality
|
|
64
67
|
reports to any identifiable artefact. Definitions ride in
|
|
65
68
|
`StructureMessage`; populated payloads ride in `MetadataMessage`
|
|
66
69
|
(`<mes:GenericMetadata>` in SDMX-ML 3.0; metadata-schema envelope in
|
|
67
70
|
SDMX-JSON 2.0). Both message kinds round-trip through
|
|
68
|
-
`
|
|
71
|
+
`LocalRegistry`, JSON, and XML.
|
|
69
72
|
- Integrates with [Polars](https://pola.rs) for schema generation and data
|
|
70
73
|
validation
|
|
71
74
|
- Fully typed — IDE completion and static analysis work throughout
|
|
@@ -75,22 +78,24 @@ uv add sdmxlib
|
|
|
75
78
|
Two condensed reference files at the repo root are written for LLM tools
|
|
76
79
|
(Claude Code, Cursor, Copilot, etc.):
|
|
77
80
|
|
|
78
|
-
- [`llms.txt`](llms.txt) —
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`
|
|
81
|
+
- [`llms.txt`](llms.txt) — the whole library in one pass: registries and
|
|
82
|
+
`get`, navigation, building artefacts, the local store, trees, observation
|
|
83
|
+
data, reading and writing SDMX, reference metadata, and the type table
|
|
84
|
+
- [`llms-dsl.txt`](llms-dsl.txt) — the path query language in depth:
|
|
85
|
+
`sl.q`, `Query` / `Select`, projection and aggregation, edges, windows,
|
|
86
|
+
observation queries (`ObsQuery`), and `reg.sql()`
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
library's actual surface.
|
|
88
|
+
Every Python block in both is executed by the test suite, so they track the
|
|
89
|
+
library's actual surface. Point your assistant at them.
|
|
86
90
|
|
|
87
91
|
## Upgrading
|
|
88
92
|
|
|
89
93
|
sdmxlib is pre-stability and breaks cleanly rather than carrying two spellings
|
|
90
|
-
of the same idea.
|
|
91
|
-
|
|
92
|
-
is now `sl.LocalRegistry`, `get()` takes a URN and a resolution
|
|
93
|
-
four query DSLs are one path language
|
|
94
|
+
of the same idea. [Upgrading](https://pinax-suite.gitlab.io/sdmxlib/upgrading/)
|
|
95
|
+
lists every breaking change since 0.48, with the old and new spelling of each —
|
|
96
|
+
`sl.Registry` is now `sl.LocalRegistry`, `get()` takes a URN and a resolution
|
|
97
|
+
depth, the four query DSLs are one path language, and every model class is
|
|
98
|
+
frozen.
|
|
94
99
|
|
|
95
100
|
## Documentation
|
|
96
101
|
|
|
@@ -111,6 +116,6 @@ including:
|
|
|
111
116
|
```bash
|
|
112
117
|
just check # lint + typecheck
|
|
113
118
|
just test # unit tests
|
|
114
|
-
just test-integration #
|
|
115
|
-
just ci # full
|
|
119
|
+
just test-integration # end-to-end tests, replayed from recorded cassettes
|
|
120
|
+
just ci # the full gate: lint, typecheck, unit, docs, integration
|
|
116
121
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sdmxlib"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.69.0"
|
|
4
4
|
description = "SDMX structural metadata library for Python"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Apache-2.0" }
|
|
@@ -18,9 +18,8 @@ classifiers = [
|
|
|
18
18
|
dependencies = [
|
|
19
19
|
"attrs>=26.1.0",
|
|
20
20
|
# >=1.5.3: ducklake extension must support DuckLake catalog format 1.0,
|
|
21
|
-
# which is what
|
|
22
|
-
#
|
|
23
|
-
# fails to ATTACH such bundles with "DuckLake catalog version mismatch".
|
|
21
|
+
# which is what a bundle built with duckdb 1.5.3 carries. duckdb 1.5.1's
|
|
22
|
+
# ducklake tops out at format 0.4 and fails to ATTACH such bundles with "DuckLake catalog version mismatch".
|
|
24
23
|
#
|
|
25
24
|
# Moved to >=1.5.5 while investigating #400 — two patch releases of fixes,
|
|
26
25
|
# taken on their own merits. It is **not** a fix for #400: 1.5.0, 1.5.1,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"""sdmxlib — SDMX structural metadata library for Python."""
|
|
2
2
|
|
|
3
3
|
from sdmxlib.api.federated import FederatedRegistry
|
|
4
|
-
from sdmxlib.api.filters import DimFilter, dim
|
|
5
4
|
from sdmxlib.api.policy import RegistryUnavailable
|
|
6
5
|
from sdmxlib.api.providers import Provider
|
|
7
|
-
from sdmxlib.api.query import DataQuery
|
|
8
6
|
from sdmxlib.formats.data_format import DataFormat
|
|
9
7
|
from sdmxlib.formats.gaps import DroppedArtefact, Gap, dropped, unrepresentable
|
|
10
8
|
from sdmxlib.api.client import ResponseTooLargeError
|
|
@@ -21,13 +19,32 @@ from sdmxlib import versions
|
|
|
21
19
|
from sdmxlib.versions import RewrittenVersion, VersionRewrite
|
|
22
20
|
from sdmxlib.query.accessors import ArtefactRow, CodeRow, HCodeRow, ReportedAttributeRow, q
|
|
23
21
|
from sdmxlib.query._compile import CompiledQuery
|
|
24
|
-
from sdmxlib.query._expr import
|
|
22
|
+
from sdmxlib.query._expr import (
|
|
23
|
+
Agg,
|
|
24
|
+
AggPred,
|
|
25
|
+
AnyPred,
|
|
26
|
+
Col,
|
|
27
|
+
Cols,
|
|
28
|
+
ColumnKind,
|
|
29
|
+
Cursor,
|
|
30
|
+
Num,
|
|
31
|
+
Pred,
|
|
32
|
+
RowKind,
|
|
33
|
+
Str,
|
|
34
|
+
Untyped,
|
|
35
|
+
Win,
|
|
36
|
+
WinFn,
|
|
37
|
+
WinPred,
|
|
38
|
+
)
|
|
25
39
|
from sdmxlib.query.observations import ObsQuery, ObsRow
|
|
26
|
-
from sdmxlib.query.query import
|
|
40
|
+
from sdmxlib.query.query import CrossSourceError, Select, Grouped, Page, Query, SliceError, union
|
|
41
|
+
from sdmxlib.query._joins import PathError
|
|
42
|
+
from sdmxlib.query._pushdown import PushdownError
|
|
27
43
|
from sdmxlib.sql import SqlRelation
|
|
28
44
|
from sdmxlib.formats import parse, read, serialize, to_json, to_xml
|
|
29
45
|
from sdmxlib.local import LazyCodelist, LazyHierarchy, QueryPlan, LocalRegistry, SchemaInspector
|
|
30
46
|
from sdmxlib.local.data_store import DuckDBDataStore, DuckLakeVersionError
|
|
47
|
+
from sdmxlib.local.sql import SidecarNotUniqueError
|
|
31
48
|
from sdmxlib.rest import ComponentFilter, parse_components, widen_key
|
|
32
49
|
from sdmxlib.query._common import Descending
|
|
33
50
|
from sdmxlib.model.convert import from_dict, to_dict
|
|
@@ -36,6 +53,7 @@ from sdmxlib.model.validation import (
|
|
|
36
53
|
LossyReadError,
|
|
37
54
|
ReferenceIssue,
|
|
38
55
|
ReferenceValidationError,
|
|
56
|
+
SdmxConformanceError,
|
|
39
57
|
SdmxParseError,
|
|
40
58
|
ValidationError,
|
|
41
59
|
ValidationIssue,
|
|
@@ -110,7 +128,6 @@ from sdmxlib.model import (
|
|
|
110
128
|
OrganisationUnit,
|
|
111
129
|
OrganisationUnitScheme,
|
|
112
130
|
DataStructure,
|
|
113
|
-
Dataset,
|
|
114
131
|
Dataflow,
|
|
115
132
|
DataflowBinding,
|
|
116
133
|
DataflowEntry,
|
|
@@ -230,6 +247,7 @@ __all__ = [
|
|
|
230
247
|
"CodelistLike",
|
|
231
248
|
"Col",
|
|
232
249
|
"Cols",
|
|
250
|
+
"ColumnKind",
|
|
233
251
|
"CompiledQuery",
|
|
234
252
|
"ComponentFilter",
|
|
235
253
|
"ComponentMap",
|
|
@@ -241,6 +259,7 @@ __all__ = [
|
|
|
241
259
|
"ConstraintAttachment",
|
|
242
260
|
"ConstraintType",
|
|
243
261
|
"Contact",
|
|
262
|
+
"CrossSourceError",
|
|
244
263
|
"CubeRegion",
|
|
245
264
|
"Cursor",
|
|
246
265
|
"DataConstraint",
|
|
@@ -251,7 +270,6 @@ __all__ = [
|
|
|
251
270
|
"DataKeySet",
|
|
252
271
|
"DataProvider",
|
|
253
272
|
"DataProviderScheme",
|
|
254
|
-
"DataQuery",
|
|
255
273
|
"DataSetReference",
|
|
256
274
|
"DataStore",
|
|
257
275
|
"DataStructure",
|
|
@@ -259,10 +277,8 @@ __all__ = [
|
|
|
259
277
|
"Dataflow",
|
|
260
278
|
"DataflowBinding",
|
|
261
279
|
"DataflowEntry",
|
|
262
|
-
"Dataset",
|
|
263
280
|
"DatePatternMap",
|
|
264
281
|
"Descending",
|
|
265
|
-
"DimFilter",
|
|
266
282
|
"Dimension",
|
|
267
283
|
"DriftError",
|
|
268
284
|
"DriftIssue",
|
|
@@ -276,7 +292,6 @@ __all__ = [
|
|
|
276
292
|
"FederatedRegistry",
|
|
277
293
|
"FixedValueMap",
|
|
278
294
|
"FmrRegistry",
|
|
279
|
-
"Frame",
|
|
280
295
|
"FrequencyFormatMapping",
|
|
281
296
|
"Gap",
|
|
282
297
|
"GeoFeatureSetCode",
|
|
@@ -323,6 +338,7 @@ __all__ = [
|
|
|
323
338
|
"MetadataSet",
|
|
324
339
|
"MetadataStructure",
|
|
325
340
|
"Metadataflow",
|
|
341
|
+
"Num",
|
|
326
342
|
"ObsQuery",
|
|
327
343
|
"ObsRow",
|
|
328
344
|
"OrganisationSchemeMap",
|
|
@@ -330,9 +346,11 @@ __all__ = [
|
|
|
330
346
|
"OrganisationUnitScheme",
|
|
331
347
|
"Page",
|
|
332
348
|
"Party",
|
|
349
|
+
"PathError",
|
|
333
350
|
"Pred",
|
|
334
351
|
"Provider",
|
|
335
352
|
"ProvisionAgreement",
|
|
353
|
+
"PushdownError",
|
|
336
354
|
"Query",
|
|
337
355
|
"QueryPlan",
|
|
338
356
|
"QueryableDataSource",
|
|
@@ -361,16 +379,21 @@ __all__ = [
|
|
|
361
379
|
"RewrittenVersion",
|
|
362
380
|
"RowKind",
|
|
363
381
|
"SchemaInspector",
|
|
382
|
+
"SdmxConformanceError",
|
|
364
383
|
"SdmxParseError",
|
|
365
384
|
"SdmxRef",
|
|
366
385
|
"SdmxUrn",
|
|
367
386
|
"SdmxWarning",
|
|
387
|
+
"Select",
|
|
368
388
|
"Sender",
|
|
369
389
|
"SentinelValue",
|
|
370
390
|
"Session",
|
|
371
391
|
"SessionHook",
|
|
392
|
+
"SidecarNotUniqueError",
|
|
393
|
+
"SliceError",
|
|
372
394
|
"SourceValue",
|
|
373
395
|
"SqlRelation",
|
|
396
|
+
"Str",
|
|
374
397
|
"StructureMap",
|
|
375
398
|
"StructureMessage",
|
|
376
399
|
"StructureSet",
|
|
@@ -392,7 +415,6 @@ __all__ = [
|
|
|
392
415
|
"WinPred",
|
|
393
416
|
"WriteSource",
|
|
394
417
|
"artefact_urn",
|
|
395
|
-
"dim",
|
|
396
418
|
"dropped",
|
|
397
419
|
"from_dict",
|
|
398
420
|
"item",
|
|
@@ -21,13 +21,8 @@ Example:
|
|
|
21
21
|
flight.run(key, lambda: fetch_into(store, plan, client))
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
from __future__ import annotations
|
|
25
|
-
|
|
26
24
|
import threading
|
|
27
|
-
from
|
|
28
|
-
|
|
29
|
-
if TYPE_CHECKING:
|
|
30
|
-
from collections.abc import Callable, Hashable
|
|
25
|
+
from collections.abc import Callable, Hashable
|
|
31
26
|
|
|
32
27
|
__all__ = ["SingleFlight"]
|
|
33
28
|
|
|
@@ -32,13 +32,8 @@ which is the property that matters and the one `TIMESTAMP` was silently
|
|
|
32
32
|
breaking.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
from
|
|
36
|
-
|
|
37
|
-
from datetime import UTC
|
|
38
|
-
from typing import TYPE_CHECKING, overload
|
|
39
|
-
|
|
40
|
-
if TYPE_CHECKING:
|
|
41
|
-
from datetime import datetime
|
|
35
|
+
from datetime import UTC, datetime
|
|
36
|
+
from typing import overload
|
|
42
37
|
|
|
43
38
|
|
|
44
39
|
# Overloaded so a value known to be present stays `datetime` on the way out —
|
|
@@ -30,17 +30,12 @@ more permissive, never shorten what a generous one offered. A caller who wants
|
|
|
30
30
|
less can pass a pinned URN, which is not a pointer at all.
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
from __future__ import annotations
|
|
34
|
-
|
|
35
33
|
import re
|
|
34
|
+
from collections.abc import Mapping
|
|
36
35
|
from datetime import UTC, datetime, timedelta
|
|
37
|
-
from typing import TYPE_CHECKING
|
|
38
36
|
|
|
39
37
|
from attrs import frozen
|
|
40
38
|
|
|
41
|
-
if TYPE_CHECKING:
|
|
42
|
-
from collections.abc import Mapping
|
|
43
|
-
|
|
44
39
|
__all__ = ["Freshness", "read_freshness"]
|
|
45
40
|
|
|
46
41
|
_MAX_AGE = re.compile(r"\bmax-age\s*=\s*(\d+)", re.IGNORECASE)
|
|
@@ -210,7 +210,7 @@ def _check_content_type(response: httpx.Response, fmt: DataFormat, *, url: str)
|
|
|
210
210
|
msg = (
|
|
211
211
|
f"Asked for {fmt.media_type!r} but the response says {header!r}. The "
|
|
212
212
|
f"provider ignored `Accept`, so the body is not the format requested — "
|
|
213
|
-
f"pass the format it does serve, or use `
|
|
213
|
+
f"pass the format it does serve, or use `obs()`, which parses "
|
|
214
214
|
f"whatever arrives. URL: {url}"
|
|
215
215
|
)
|
|
216
216
|
raise ValueError(msg)
|
|
@@ -550,51 +550,11 @@ class SdmxClient:
|
|
|
550
550
|
return f"{self._endpoint}/data/dataflow/{agency}/{id}/{ver}/{key}"
|
|
551
551
|
return f"{self._endpoint}/data/{agency},{id},{version}/{key}"
|
|
552
552
|
|
|
553
|
-
def get_data(
|
|
554
|
-
self,
|
|
555
|
-
agency: str,
|
|
556
|
-
id: str,
|
|
557
|
-
version: str = "latest",
|
|
558
|
-
*,
|
|
559
|
-
key: str = "all",
|
|
560
|
-
start_period: str | None = None,
|
|
561
|
-
end_period: str | None = None,
|
|
562
|
-
) -> bytes:
|
|
563
|
-
"""Fetch observation data as SDMX-CSV bytes.
|
|
564
|
-
|
|
565
|
-
Args:
|
|
566
|
-
agency: Dataflow maintainer agency id.
|
|
567
|
-
id: Dataflow id.
|
|
568
|
-
version: Dataflow version. Defaults to ``"latest"``.
|
|
569
|
-
key: SDMX key filter string, e.g. ``"M.AT+DE.INDPRO"``.
|
|
570
|
-
Defaults to ``"all"`` (all observations).
|
|
571
|
-
start_period: ISO period start, e.g. ``"2020-01"``.
|
|
572
|
-
end_period: ISO period end.
|
|
573
|
-
|
|
574
|
-
Returns:
|
|
575
|
-
Raw SDMX-CSV bytes.
|
|
576
|
-
"""
|
|
577
|
-
url = self._data_url(agency, id, version, key)
|
|
578
|
-
params: dict[str, str] = {}
|
|
579
|
-
if start_period is not None:
|
|
580
|
-
params["startPeriod"] = start_period
|
|
581
|
-
if end_period is not None:
|
|
582
|
-
params["endPeriod"] = end_period
|
|
583
|
-
with self._reaching(url):
|
|
584
|
-
response = self._client.get(
|
|
585
|
-
url,
|
|
586
|
-
params=params,
|
|
587
|
-
headers={"Accept": "text/csv"},
|
|
588
|
-
)
|
|
589
|
-
_raise_for_status(response, url=url)
|
|
590
|
-
return response.content
|
|
591
|
-
|
|
592
553
|
def get_data_at(self, path: str, params: Mapping[str, str]) -> bytes:
|
|
593
554
|
"""Fetch SDMX-CSV bytes for an already-built request path and parameters.
|
|
594
555
|
|
|
595
556
|
The seam `query._rest.DataRequest` needs: it owns the URL's shape —
|
|
596
|
-
which differs by API version — and this owns the HTTP.
|
|
597
|
-
builds the path from parts instead, for the older `reg.data()` surface.
|
|
557
|
+
which differs by API version — and this owns the HTTP.
|
|
598
558
|
|
|
599
559
|
Args:
|
|
600
560
|
path: Path relative to the service root, as `DataRequest.path`
|
|
@@ -634,51 +594,6 @@ class SdmxClient:
|
|
|
634
594
|
for chunk in response.iter_bytes():
|
|
635
595
|
handle.write(chunk)
|
|
636
596
|
|
|
637
|
-
def stream_data_to(
|
|
638
|
-
self,
|
|
639
|
-
agency: str,
|
|
640
|
-
id: str,
|
|
641
|
-
version: str = "latest",
|
|
642
|
-
*,
|
|
643
|
-
path: Path,
|
|
644
|
-
key: str = "all",
|
|
645
|
-
start_period: str | None = None,
|
|
646
|
-
end_period: str | None = None,
|
|
647
|
-
) -> None:
|
|
648
|
-
"""Stream observation data directly to a file.
|
|
649
|
-
|
|
650
|
-
Uses httpx chunked streaming — the response is never fully loaded into
|
|
651
|
-
memory. Suitable for multi-GB datasets.
|
|
652
|
-
|
|
653
|
-
Args:
|
|
654
|
-
agency: Dataflow maintainer agency id.
|
|
655
|
-
id: Dataflow id.
|
|
656
|
-
version: Dataflow version.
|
|
657
|
-
path: Destination file path.
|
|
658
|
-
key: SDMX key filter string.
|
|
659
|
-
start_period: ISO period start.
|
|
660
|
-
end_period: ISO period end.
|
|
661
|
-
"""
|
|
662
|
-
url = self._data_url(agency, id, version, key)
|
|
663
|
-
params: dict[str, str] = {}
|
|
664
|
-
if start_period is not None:
|
|
665
|
-
params["startPeriod"] = start_period
|
|
666
|
-
if end_period is not None:
|
|
667
|
-
params["endPeriod"] = end_period
|
|
668
|
-
with (
|
|
669
|
-
self._reaching(url),
|
|
670
|
-
self._client.stream(
|
|
671
|
-
"GET",
|
|
672
|
-
url,
|
|
673
|
-
params=params,
|
|
674
|
-
headers={"Accept": "text/csv"},
|
|
675
|
-
) as response,
|
|
676
|
-
):
|
|
677
|
-
_raise_for_status(response, url=url)
|
|
678
|
-
with path.open("wb") as f:
|
|
679
|
-
for chunk in response.iter_bytes():
|
|
680
|
-
f.write(chunk)
|
|
681
|
-
|
|
682
597
|
def download_data(
|
|
683
598
|
self,
|
|
684
599
|
agency: str,
|
|
@@ -693,9 +608,9 @@ class SdmxClient:
|
|
|
693
608
|
) -> bytes | None:
|
|
694
609
|
"""Fetch a dataflow's data in `fmt`, unparsed.
|
|
695
610
|
|
|
696
|
-
The byte-faithful counterpart to `
|
|
611
|
+
The byte-faithful counterpart to `get_data_at` / `stream_data_at`, which
|
|
697
612
|
hardcode ``Accept: text/csv`` because their caller is about to hand the
|
|
698
|
-
body to
|
|
613
|
+
body to DuckDB's CSV reader. Here the caller wants what the provider
|
|
699
614
|
sent, so the format is the caller's to choose and nothing inspects the
|
|
700
615
|
body afterwards.
|
|
701
616
|
|
|
@@ -33,22 +33,17 @@ Example:
|
|
|
33
33
|
import annotationlib
|
|
34
34
|
import functools
|
|
35
35
|
import inspect
|
|
36
|
-
from
|
|
36
|
+
from collections.abc import Sequence
|
|
37
|
+
from typing import overload
|
|
37
38
|
|
|
38
39
|
import httpx
|
|
39
40
|
|
|
40
41
|
from sdmxlib.api.policy import RegistryUnavailable
|
|
41
|
-
from sdmxlib.model.binding import DriftError, _validate_binding
|
|
42
|
+
from sdmxlib.model.binding import DataflowBinding, DriftError, DriftIssue, _validate_binding
|
|
42
43
|
from sdmxlib.model.ref import Ref, target_urn
|
|
44
|
+
from sdmxlib.model.registry import RegistryReader
|
|
43
45
|
from sdmxlib.model.urn import SdmxUrn
|
|
44
46
|
|
|
45
|
-
if TYPE_CHECKING:
|
|
46
|
-
from collections.abc import Sequence
|
|
47
|
-
|
|
48
|
-
from sdmxlib.model.binding import DataflowBinding, DriftIssue
|
|
49
|
-
from sdmxlib.model.registry import RegistryReader
|
|
50
|
-
from sdmxlib.model.urn import SdmxUrn
|
|
51
|
-
|
|
52
47
|
|
|
53
48
|
@functools.cache
|
|
54
49
|
def _get_parameters(source_type: type) -> frozenset[str]:
|
|
@@ -27,8 +27,6 @@ so, which cannot mislabel anything:
|
|
|
27
27
|
... # genuinely absent, and only that
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
-
from __future__ import annotations
|
|
31
|
-
|
|
32
30
|
|
|
33
31
|
class RegistryUnavailable(Exception): # noqa: N818 # name matches #46 vocabulary
|
|
34
32
|
"""A source could not be reached, so no read of it can be believed.
|