sdmxlib 0.32.0__tar.gz → 0.32.2__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.
Files changed (83) hide show
  1. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/PKG-INFO +1 -1
  2. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/pyproject.toml +1 -1
  3. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/ref.py +20 -2
  4. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/_kinds.py +2 -1
  5. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/readers.py +37 -1
  6. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/schema.py +9 -1
  7. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/writers.py +35 -1
  8. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/README.md +0 -0
  9. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/__init__.py +0 -0
  10. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/_duckdb.py +0 -0
  11. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/__init__.py +0 -0
  12. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/client.py +0 -0
  13. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/federated.py +0 -0
  14. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/filters.py +0 -0
  15. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/policy.py +0 -0
  16. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/providers.py +0 -0
  17. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/query.py +0 -0
  18. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/registry.py +0 -0
  19. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/api/session.py +0 -0
  20. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/catalog.py +0 -0
  21. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/data_store.py +0 -0
  22. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/__init__.py +0 -0
  23. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -0
  24. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_csv/reader.py +0 -0
  25. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_csv/writer.py +0 -0
  26. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
  27. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -0
  28. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
  29. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
  30. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml/__init__.py +0 -0
  31. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml/_common.py +0 -0
  32. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
  33. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
  34. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml21/reader.py +0 -0
  35. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
  36. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
  37. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
  38. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
  39. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
  40. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
  41. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml31/__init__.py +0 -0
  42. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml31/namespaces.py +0 -0
  43. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml31/reader.py +0 -0
  44. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/formats/sdmx_ml31/writer.py +0 -0
  45. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/local/__init__.py +0 -0
  46. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/local/data_store.py +0 -0
  47. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/local/registry.py +0 -0
  48. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/__init__.py +0 -0
  49. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/annotations.py +0 -0
  50. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/base.py +0 -0
  51. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/binding.py +0 -0
  52. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/category.py +0 -0
  53. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/code_query.py +0 -0
  54. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/codelist.py +0 -0
  55. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/collections.py +0 -0
  56. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/concept.py +0 -0
  57. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/constraint.py +0 -0
  58. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/convert.py +0 -0
  59. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/dataflow.py +0 -0
  60. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/dataset.py +0 -0
  61. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/datastructure.py +0 -0
  62. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/expr.py +0 -0
  63. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/hierarchy.py +0 -0
  64. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/hierarchy_query.py +0 -0
  65. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/in_memory_registry.py +0 -0
  66. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/istring.py +0 -0
  67. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/mapping.py +0 -0
  68. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/message.py +0 -0
  69. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/metadataflow.py +0 -0
  70. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/metadataset.py +0 -0
  71. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/metadatastructure.py +0 -0
  72. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/organisation.py +0 -0
  73. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/provision.py +0 -0
  74. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/registry.py +0 -0
  75. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/representation.py +0 -0
  76. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/urn.py +0 -0
  77. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/model/validation.py +0 -0
  78. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/polars.py +0 -0
  79. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/py.typed +0 -0
  80. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/rest.py +0 -0
  81. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/sql.py +0 -0
  82. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/__init__.py +0 -0
  83. {sdmxlib-0.32.0 → sdmxlib-0.32.2}/src/sdmxlib/storage/lazy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sdmxlib
3
- Version: 0.32.0
3
+ Version: 0.32.2
4
4
  Summary: SDMX structural metadata library for Python
5
5
  Keywords: sdmx,statistics,metadata,datastructure
6
6
  Author: gabrielgellner
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sdmxlib"
3
- version = "0.32.0"
3
+ version = "0.32.2"
4
4
  description = "SDMX structural metadata library for Python"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -78,6 +78,20 @@ class Ref[T]:
78
78
  )
79
79
  if self._resolved is None:
80
80
  self._resolved = obj # type: ignore[assignment]
81
+ elif not isinstance(self.urn, SdmxUrn):
82
+ # Reject anything that's neither a SdmxUrn nor a MaintainableArtefact.
83
+ # Most common offender: Ref.to(dim) on a child item (Dimension,
84
+ # Code, Concept, ...) — these are identifiable but not maintainable,
85
+ # so their identity lives inside the parent's URN as an item_id
86
+ # fragment. Construct the URN explicitly via SdmxUrn.make(...).
87
+ msg = (
88
+ f"Ref expects a SdmxUrn or a MaintainableArtefact, got {type(self.urn).__name__}. "
89
+ "Child items (Dimension, Code, Concept, ...) are not maintainable — "
90
+ "build the URN with SdmxUrn.make(<Class>, package=..., artefact_class=..., "
91
+ "agency=..., id=..., version=..., item_id=...) and pass Ref.unresolved(urn) "
92
+ "or Ref.of(urn, obj)."
93
+ )
94
+ raise TypeError(msg)
81
95
 
82
96
  # ── Resolution ────────────────────────────────────────────────────────
83
97
 
@@ -144,8 +158,12 @@ class Ref[T]:
144
158
  def to(cls, obj: T) -> "Ref[T]":
145
159
  """Create a resolved Ref from a MaintainableArtefact instance.
146
160
 
147
- The object must satisfy HasUrn it needs ``agency_id``, ``id``,
148
- ``version``, ``sdmx_package``, and ``sdmx_class`` attributes.
161
+ The object must be a ``MaintainableArtefact`` (Codelist, DataStructure,
162
+ Dataflow, …). Child items like ``Dimension``, ``Code``, or ``Concept``
163
+ are not maintainable — their identity lives inside the parent's URN as
164
+ an item_id fragment, so this method raises ``TypeError`` on them.
165
+ Construct their URN explicitly via ``SdmxUrn.make(...)`` and pass it
166
+ to ``Ref.unresolved(urn)`` or ``Ref.of(urn, obj)`` instead.
149
167
 
150
168
  Example:
151
169
  cl = Codelist(id="CL_FREQ", maintainer="SDMX", version="1.0", ...)
@@ -20,7 +20,7 @@ from sdmxlib.model.concept import ConceptScheme
20
20
  from sdmxlib.model.constraint import DataConstraint
21
21
  from sdmxlib.model.dataflow import Dataflow
22
22
  from sdmxlib.model.datastructure import DataStructure
23
- from sdmxlib.model.hierarchy import Hierarchy
23
+ from sdmxlib.model.hierarchy import Hierarchy, HierarchyAssociation
24
24
  from sdmxlib.model.mapping import RepresentationMap
25
25
  from sdmxlib.model.metadataflow import Metadataflow
26
26
  from sdmxlib.model.metadataset import MetadataSet
@@ -43,6 +43,7 @@ ARTEFACT_KINDS: tuple[ArtefactKind, ...] = (
43
43
  ArtefactKind(DataStructure, "DataStructure"),
44
44
  ArtefactKind(Dataflow, "Dataflow"),
45
45
  ArtefactKind(Hierarchy, "Hierarchy"),
46
+ ArtefactKind(HierarchyAssociation, "HierarchyAssociation"),
46
47
  ArtefactKind(DataConstraint, "DataConstraint"),
47
48
  ArtefactKind(CategoryScheme, "CategoryScheme"),
48
49
  ArtefactKind(Categorisation, "Categorisation"),
@@ -42,7 +42,7 @@ from sdmxlib.model.datastructure import (
42
42
  MeasureDimension,
43
43
  TimeDimension,
44
44
  )
45
- from sdmxlib.model.hierarchy import HierarchicalCode, Hierarchy, Level
45
+ from sdmxlib.model.hierarchy import HierarchicalCode, Hierarchy, HierarchyAssociation, Level
46
46
  from sdmxlib.model.istring import InternationalString
47
47
  from sdmxlib.model.mapping import LiteralValueMap, RepresentationMap
48
48
  from sdmxlib.model.metadataflow import Metadataflow
@@ -900,6 +900,41 @@ def _read_categorisation(conn: duckdb.DuckDBPyConnection, urn: str) -> "Categori
900
900
  )
901
901
 
902
902
 
903
+ def _read_hierarchy_association(conn: duckdb.DuckDBPyConnection, urn: str) -> "HierarchyAssociation | None":
904
+ """Read a HierarchyAssociation from spine + extension."""
905
+ meta = _read_spine(conn, urn)
906
+ if meta is None:
907
+ return None
908
+ name = _read_istring(conn, urn, "name")
909
+ desc = _read_istring(conn, urn, "description")
910
+ anns = _read_annotations(conn, urn)
911
+
912
+ row = conn.execute(
913
+ "SELECT linked_hierarchy_urn, linked_object_urn, context_object_urn FROM hierarchy_association WHERE urn = ?",
914
+ [urn],
915
+ ).fetchone()
916
+ if row is None:
917
+ return None
918
+ linked_h, linked_o, context_o = row
919
+ linked_h_ref: Ref[Hierarchy] | None = Ref[Hierarchy].unresolved(SdmxUrn.parse(linked_h)) if linked_h else None
920
+ linked_o_ref = Ref[object].unresolved(SdmxUrn.parse(linked_o)) if linked_o else None
921
+ context_o_ref = Ref[object].unresolved(SdmxUrn.parse(context_o)) if context_o else None
922
+
923
+ return HierarchyAssociation(
924
+ id=meta["resource_id"],
925
+ maintainer=Agency(id=meta["agency_id"]),
926
+ version=meta["version"],
927
+ name=name,
928
+ description=desc,
929
+ annotations=anns,
930
+ valid_from=meta["valid_from"],
931
+ valid_to=meta["valid_to"],
932
+ linked_hierarchy=linked_h_ref,
933
+ linked_object=linked_o_ref,
934
+ context_object=context_o_ref,
935
+ )
936
+
937
+
903
938
  def _read_provision_agreement(conn: duckdb.DuckDBPyConnection, urn: str) -> "ProvisionAgreement | None":
904
939
  """Read a ProvisionAgreement from spine + extension."""
905
940
  meta = _read_spine(conn, urn)
@@ -1386,6 +1421,7 @@ _READER_DISPATCH: dict[str, Any] = {
1386
1421
  "Dataflow": _read_dataflow,
1387
1422
  "ConceptScheme": _read_concept_scheme,
1388
1423
  "Hierarchy": _read_hierarchy,
1424
+ "HierarchyAssociation": _read_hierarchy_association,
1389
1425
  "DataConstraint": _read_data_constraint,
1390
1426
  "CategoryScheme": _read_category_scheme,
1391
1427
  "Categorisation": _read_categorisation,
@@ -16,7 +16,7 @@ Example:
16
16
 
17
17
  import duckdb
18
18
 
19
- SCHEMA_VERSION = "2.1"
19
+ SCHEMA_VERSION = "2.2"
20
20
 
21
21
 
22
22
  # ── DDL ─────────────────────────────────────────────────────────────────
@@ -259,6 +259,13 @@ CREATE TABLE IF NOT EXISTS categorisation (
259
259
  target_urn TEXT NOT NULL
260
260
  );
261
261
 
262
+ CREATE TABLE IF NOT EXISTS hierarchy_association (
263
+ urn TEXT PRIMARY KEY,
264
+ linked_hierarchy_urn TEXT,
265
+ linked_object_urn TEXT,
266
+ context_object_urn TEXT
267
+ );
268
+
262
269
  CREATE TABLE IF NOT EXISTS hierarchy_meta (
263
270
  urn TEXT PRIMARY KEY,
264
271
  has_formal_levels BOOLEAN DEFAULT FALSE
@@ -470,6 +477,7 @@ ALL_TABLES: frozenset[str] = frozenset(
470
477
  "dataflow",
471
478
  "provision_agreement",
472
479
  "categorisation",
480
+ "hierarchy_association",
473
481
  "hierarchy_meta",
474
482
  "hierarchy_level",
475
483
  "hierarchy_node",
@@ -26,7 +26,7 @@ from sdmxlib.model.concept import ConceptScheme
26
26
  from sdmxlib.model.constraint import DataConstraint
27
27
  from sdmxlib.model.dataflow import Dataflow
28
28
  from sdmxlib.model.datastructure import DataStructure
29
- from sdmxlib.model.hierarchy import HierarchicalCode, Hierarchy
29
+ from sdmxlib.model.hierarchy import HierarchicalCode, Hierarchy, HierarchyAssociation
30
30
  from sdmxlib.model.istring import InternationalString
31
31
  from sdmxlib.model.mapping import LiteralValueMap, RepresentationMap
32
32
  from sdmxlib.model.metadataflow import Metadataflow
@@ -398,6 +398,7 @@ def _delete_artefact_cascade(conn: duckdb.DuckDBPyConnection, urn: str) -> None:
398
398
  conn.execute("DELETE FROM dataflow WHERE urn = ?", p)
399
399
  conn.execute("DELETE FROM provision_agreement WHERE urn = ?", p)
400
400
  conn.execute("DELETE FROM categorisation WHERE urn = ?", p)
401
+ conn.execute("DELETE FROM hierarchy_association WHERE urn = ?", p)
401
402
  conn.execute("DELETE FROM hierarchy_meta WHERE urn = ?", p)
402
403
  # Constraint tables
403
404
  conn.execute("DELETE FROM region_key_value WHERE constraint_urn = ?", p)
@@ -979,6 +980,38 @@ def put_categorisation(
979
980
  _put_refs(conn, refs)
980
981
 
981
982
 
983
+ def put_hierarchy_association(
984
+ conn: duckdb.DuckDBPyConnection,
985
+ ha: HierarchyAssociation,
986
+ *,
987
+ source_registry: str | None = None,
988
+ ) -> None:
989
+ """Write a HierarchyAssociation to artefact + hierarchy_association extension."""
990
+ urn = str(artefact_urn(ha))
991
+
992
+ _delete_artefact_cascade(conn, urn)
993
+ _put_spine(conn, urn, "HierarchyAssociation", ha, source_registry=source_registry)
994
+ _put_artefact_text_and_annotations(conn, urn, ha)
995
+
996
+ linked_h = str(ha.linked_hierarchy.urn) if ha.linked_hierarchy is not None else ""
997
+ linked_o = str(ha.linked_object.urn) if ha.linked_object is not None else ""
998
+ context_o = str(ha.context_object.urn) if ha.context_object is not None else ""
999
+ conn.execute(
1000
+ "INSERT INTO hierarchy_association (urn, linked_hierarchy_urn, linked_object_urn, context_object_urn) "
1001
+ "VALUES (?, ?, ?, ?)",
1002
+ [urn, linked_h, linked_o, context_o],
1003
+ )
1004
+
1005
+ refs: list[tuple[str, str, str, int | None]] = []
1006
+ if ha.linked_hierarchy is not None:
1007
+ refs.append((urn, linked_h, "linked_hierarchy", None))
1008
+ if ha.linked_object is not None:
1009
+ refs.append((urn, linked_o, "linked_object", None))
1010
+ if ha.context_object is not None:
1011
+ refs.append((urn, context_o, "context_object", None))
1012
+ _put_refs(conn, refs)
1013
+
1014
+
982
1015
  def put_provision_agreement(
983
1016
  conn: duckdb.DuckDBPyConnection, pa: "ProvisionAgreement", *, source_registry: str | None = None
984
1017
  ) -> None:
@@ -1435,6 +1468,7 @@ _WRITER_DISPATCH: dict[type, Any] = {
1435
1468
  Dataflow: put_dataflow,
1436
1469
  ConceptScheme: put_concept_scheme,
1437
1470
  Hierarchy: put_hierarchy,
1471
+ HierarchyAssociation: put_hierarchy_association,
1438
1472
  DataConstraint: put_data_constraint,
1439
1473
  CategoryScheme: put_category_scheme,
1440
1474
  Categorisation: put_categorisation,
File without changes
File without changes
File without changes
File without changes
File without changes