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.
Files changed (163) hide show
  1. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/PKG-INFO +31 -26
  2. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/README.md +30 -25
  3. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/pyproject.toml +1 -1
  4. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/pyproject.toml.orig +3 -4
  5. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/__init__.py +32 -10
  6. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_duckdb.py +0 -2
  7. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_singleflight.py +1 -6
  8. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/_time.py +2 -7
  9. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/_freshness.py +1 -6
  10. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/client.py +4 -89
  11. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/federated.py +4 -9
  12. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/policy.py +0 -2
  13. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/registry.py +111 -346
  14. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/contentconstraint.py +9 -14
  15. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/metadata_annotation.py +6 -13
  16. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/data_store.py +4 -6
  17. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/__init__.py +6 -6
  18. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_index.py +2 -8
  19. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/data_format.py +0 -2
  20. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/gaps.py +1 -7
  21. sdmxlib-0.69.0/src/sdmxlib/formats/sdmx_csv/__init__.py +8 -0
  22. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_csv/reader.py +11 -133
  23. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_csv/writer.py +3 -12
  24. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_data_common.py +28 -14
  25. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_dto.py +1 -6
  26. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_losses.py +13 -0
  27. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_resolve.py +0 -2
  28. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_structure_dto.py +0 -2
  29. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_structure_ingest.py +9 -8
  30. sdmxlib-0.69.0/src/sdmxlib/formats/sdmx_json/data.py +34 -0
  31. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/data_v1.py +17 -37
  32. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/data_v2.py +18 -43
  33. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -2
  34. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json_1_0/writer.py +1 -7
  35. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_common.py +4 -6
  36. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_losses.py +8 -0
  37. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_v3_reader.py +3 -3
  38. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/reader.py +2 -2
  39. sdmxlib-0.69.0/src/sdmxlib/local/_scan.py +148 -0
  40. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/data_store.py +107 -9
  41. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/registry.py +94 -39
  42. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/sql.py +22 -14
  43. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/__init__.py +2 -3
  44. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_binding.py +21 -3
  45. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_items.py +3 -7
  46. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_label_match.py +0 -2
  47. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_rebind.py +2 -1
  48. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_refshape.py +4 -8
  49. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_rules.py +20 -0
  50. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_tree.py +59 -10
  51. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/base.py +3 -1
  52. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/binding.py +6 -11
  53. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/codelist.py +3 -8
  54. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/collections.py +18 -24
  55. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/convert.py +0 -2
  56. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/datastructure.py +13 -6
  57. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/hierarchy.py +8 -13
  58. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/in_memory_registry.py +4 -8
  59. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/istring.py +3 -7
  60. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/message.py +31 -35
  61. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadataflow.py +1 -1
  62. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadataset.py +1 -1
  63. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadatastructure.py +1 -1
  64. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/ref.py +20 -29
  65. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/registry.py +0 -2
  66. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/representation.py +12 -3
  67. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/rest_api.py +0 -2
  68. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/rollup.py +7 -10
  69. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/urn.py +1 -1
  70. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/validation.py +30 -57
  71. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/version.py +0 -2
  72. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/version_rewrite.py +0 -2
  73. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/polars.py +12 -174
  74. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_closure.py +8 -11
  75. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_common.py +2 -4
  76. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_compile.py +79 -23
  77. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_edges.py +0 -2
  78. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_execute.py +7 -14
  79. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_expr.py +318 -46
  80. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_joins.py +2 -9
  81. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_plan.py +2 -5
  82. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_pushdown.py +7 -10
  83. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_rebase.py +7 -6
  84. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/_rest.py +12 -13
  85. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/accessors.py +1 -1
  86. sdmxlib-0.69.0/src/sdmxlib/query/observations.py +805 -0
  87. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/query.py +199 -50
  88. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/raw.py +6 -10
  89. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/schema.py +0 -2
  90. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/rest.py +1 -3
  91. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/session.py +1 -6
  92. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/sql.py +5 -10
  93. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_projections.py +1 -1
  94. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/lazy.py +38 -9
  95. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/readers.py +27 -4
  96. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/resolve.py +8 -13
  97. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/writers.py +2 -2
  98. sdmxlib-0.68.0/src/sdmxlib/api/filters.py +0 -67
  99. sdmxlib-0.68.0/src/sdmxlib/api/query.py +0 -205
  100. sdmxlib-0.68.0/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -6
  101. sdmxlib-0.68.0/src/sdmxlib/model/dataset.py +0 -446
  102. sdmxlib-0.68.0/src/sdmxlib/query/observations.py +0 -387
  103. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/__init__.py +0 -0
  104. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/providers.py +0 -0
  105. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/api/session.py +0 -0
  106. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/builders/__init__.py +0 -0
  107. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_arrow.py +0 -0
  108. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_attachment.py +0 -0
  109. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_coerce.py +0 -0
  110. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_positions.py +0 -0
  111. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/_refuse.py +0 -0
  112. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
  113. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_header.py +0 -0
  114. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/_periods.py +0 -0
  115. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
  116. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/structure_values.py +0 -0
  117. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
  118. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_json_1_0/__init__.py +0 -0
  119. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/__init__.py +0 -0
  120. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_allowed_children.py +0 -0
  121. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_closed_world.py +0 -0
  122. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_header.py +0 -0
  123. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml/_v3_writer.py +0 -0
  124. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
  125. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/_providers.py +0 -0
  126. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/_synthetic.py +0 -0
  127. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
  128. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
  129. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
  130. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
  131. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
  132. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
  133. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
  134. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/__init__.py +0 -0
  135. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/namespaces.py +0 -0
  136. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/reader.py +0 -0
  137. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/formats/sdmx_ml31/writer.py +0 -0
  138. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/local/__init__.py +0 -0
  139. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_container.py +0 -0
  140. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_invariants.py +0 -0
  141. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_referenceable.py +0 -0
  142. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/_resolvers.py +0 -0
  143. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/annotations.py +0 -0
  144. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/category.py +0 -0
  145. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/concept.py +0 -0
  146. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/constraint.py +0 -0
  147. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/dataflow.py +0 -0
  148. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/errors.py +0 -0
  149. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/expr.py +0 -0
  150. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/header.py +0 -0
  151. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/link.py +0 -0
  152. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/mapping.py +0 -0
  153. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/metadata_provision.py +0 -0
  154. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/organisation.py +0 -0
  155. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/provision.py +0 -0
  156. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/model/valuelist.py +0 -0
  157. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/py.typed +0 -0
  158. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/query/__init__.py +0 -0
  159. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/__init__.py +0 -0
  160. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_checked.py +0 -0
  161. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/_kinds.py +0 -0
  162. {sdmxlib-0.68.0 → sdmxlib-0.69.0}/src/sdmxlib/storage/schema.py +0 -0
  163. {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.68.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
- cl = dim.representation()
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 and serialise to SDMX-ML or SDMX-JSON
80
- - `LocalRegistry` — a DuckDB-backed persistent store for offline access, and
81
- a position in a `RestRegistry`'s source chain via `store=`
82
- - A path query language over both planes: `reg.query(sl.q.dataflow)` for
83
- structures, `reg.observations(flow)` for rows
84
- with lazy codelist + hierarchy loading, automatic Ref resolution, and
85
- direct SQL code maps; supports shared connections and any-language queries
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
- `Registry`, JSON, and XML.
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) — full library overview: install, registries,
108
- data fetching, reference metadata, and the domain-type cheat sheet
109
- - [`llms-dsl.txt`](llms-dsl.txt) — focused query-DSL reference:
110
- `sl.item` / `sl.q` / `sl.dim` predicates, the `Query` / `Frame` /
111
- `DataQuery` chains, and the uniform SKOS tree API
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
- Point your assistant at these to keep generated code aligned with the
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. If you are coming from **0.48**, read
120
- [Upgrading](https://pinax-suite.gitlab.io/sdmxlib/upgrading/) — `sl.Registry`
121
- is now `sl.LocalRegistry`, `get()` takes a URN and a resolution depth, and the
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 # live endpoint tests (requires network)
144
- just ci # full pre-commit gate
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
- cl = dim.representation()
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 and serialise to SDMX-ML or SDMX-JSON
51
- - `LocalRegistry` — a DuckDB-backed persistent store for offline access, and
52
- a position in a `RestRegistry`'s source chain via `store=`
53
- - A path query language over both planes: `reg.query(sl.q.dataflow)` for
54
- structures, `reg.observations(flow)` for rows
55
- with lazy codelist + hierarchy loading, automatic Ref resolution, and
56
- direct SQL code maps; supports shared connections and any-language queries
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
- `Registry`, JSON, and XML.
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) — full library overview: install, registries,
79
- data fetching, reference metadata, and the domain-type cheat sheet
80
- - [`llms-dsl.txt`](llms-dsl.txt) — focused query-DSL reference:
81
- `sl.item` / `sl.q` / `sl.dim` predicates, the `Query` / `Frame` /
82
- `DataQuery` chains, and the uniform SKOS tree API
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
- Point your assistant at these to keep generated code aligned with the
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. If you are coming from **0.48**, read
91
- [Upgrading](https://pinax-suite.gitlab.io/sdmxlib/upgrading/) — `sl.Registry`
92
- is now `sl.LocalRegistry`, `get()` takes a URN and a resolution depth, and the
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 # live endpoint tests (requires network)
115
- just ci # full pre-commit gate
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.68.0"
3
+ version = "0.69.0"
4
4
  description = "SDMX structural metadata library for Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sdmxlib"
3
- version = "0.68.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 statcan-catalog-builder (duckdb 1.5.3) writes into
22
- # published bundles. duckdb 1.5.1's ducklake tops out at format 0.4 and
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 Agg, AggPred, AnyPred, Col, Cols, Cursor, Pred, RowKind, Untyped, Win, WinFn, WinPred
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 Frame, Grouped, Page, Query, union
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",
@@ -4,8 +4,6 @@ Lives at the package root because both ``storage/`` and ``query/`` need it, and
4
4
  neither layer may import the other.
5
5
  """
6
6
 
7
- from __future__ import annotations
8
-
9
7
  import duckdb
10
8
 
11
9
 
@@ -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 typing import TYPE_CHECKING
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 __future__ import annotations
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 `observations()`, which parses "
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. `get_data`
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 `get_data` / `stream_data_to`, which
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 the SDMX-CSV reader. Here the caller wants what the provider
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 typing import TYPE_CHECKING, overload
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.