sdmxlib 0.41.3__tar.gz → 0.43.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 (91) hide show
  1. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/PKG-INFO +1 -1
  2. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/pyproject.toml +1 -1
  3. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/code_query.py +47 -6
  4. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/README.md +0 -0
  5. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/__init__.py +0 -0
  6. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/_duckdb.py +0 -0
  7. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/__init__.py +0 -0
  8. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/client.py +0 -0
  9. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/federated.py +0 -0
  10. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/filters.py +0 -0
  11. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/policy.py +0 -0
  12. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/providers.py +0 -0
  13. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/query.py +0 -0
  14. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/registry.py +0 -0
  15. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/api/session.py +0 -0
  16. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/builders/__init__.py +0 -0
  17. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/builders/contentconstraint.py +0 -0
  18. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/catalog.py +0 -0
  19. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/data_store.py +0 -0
  20. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/__init__.py +0 -0
  21. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -0
  22. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_csv/reader.py +0 -0
  23. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_csv/writer.py +0 -0
  24. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
  25. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/_data_common.py +0 -0
  26. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/data_v1.py +0 -0
  27. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/data_v2.py +0 -0
  28. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -0
  29. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
  30. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/structure_values.py +0 -0
  31. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
  32. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json_1_0/__init__.py +0 -0
  33. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_json_1_0/writer.py +0 -0
  34. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml/__init__.py +0 -0
  35. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml/_common.py +0 -0
  36. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
  37. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
  38. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml21/reader.py +0 -0
  39. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
  40. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
  41. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
  42. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
  43. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
  44. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
  45. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml31/__init__.py +0 -0
  46. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml31/namespaces.py +0 -0
  47. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml31/reader.py +0 -0
  48. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/formats/sdmx_ml31/writer.py +0 -0
  49. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/local/__init__.py +0 -0
  50. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/local/data_store.py +0 -0
  51. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/local/registry.py +0 -0
  52. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/__init__.py +0 -0
  53. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/annotations.py +0 -0
  54. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/base.py +0 -0
  55. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/binding.py +0 -0
  56. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/category.py +0 -0
  57. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/codelist.py +0 -0
  58. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/collections.py +0 -0
  59. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/concept.py +0 -0
  60. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/constraint.py +0 -0
  61. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/convert.py +0 -0
  62. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/dataflow.py +0 -0
  63. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/dataset.py +0 -0
  64. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/datastructure.py +0 -0
  65. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/expr.py +0 -0
  66. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/hierarchy.py +0 -0
  67. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/hierarchy_query.py +0 -0
  68. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/in_memory_registry.py +0 -0
  69. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/istring.py +0 -0
  70. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/mapping.py +0 -0
  71. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/message.py +0 -0
  72. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/metadataflow.py +0 -0
  73. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/metadataset.py +0 -0
  74. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/metadatastructure.py +0 -0
  75. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/organisation.py +0 -0
  76. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/provision.py +0 -0
  77. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/ref.py +0 -0
  78. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/registry.py +0 -0
  79. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/representation.py +0 -0
  80. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/urn.py +0 -0
  81. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/model/validation.py +0 -0
  82. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/polars.py +0 -0
  83. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/py.typed +0 -0
  84. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/rest.py +0 -0
  85. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/sql.py +0 -0
  86. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/__init__.py +0 -0
  87. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/_kinds.py +0 -0
  88. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/lazy.py +0 -0
  89. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/readers.py +0 -0
  90. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/schema.py +0 -0
  91. {sdmxlib-0.41.3 → sdmxlib-0.43.0}/src/sdmxlib/storage/writers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sdmxlib
3
- Version: 0.41.3
3
+ Version: 0.43.0
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.41.3"
3
+ version = "0.43.0"
4
4
  description = "SDMX structural metadata library for Python"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -956,14 +956,20 @@ def _emit_string_match(
956
956
  join_params: list[object],
957
957
  annotation_join_aliases: dict[str, str],
958
958
  ) -> tuple[str, list[object]]:
959
- pattern = _ilike_pattern(pred.op, pred.value)
960
959
  field = pred.field
960
+ # Natural-language label fields (name / annotation title / annotation
961
+ # text) are matched accent- AND punctuation-INSENSITIVELY via
962
+ # :func:`_label_match_sql`, so "Montreal" finds "Montréal" and
963
+ # "Trois Rivieres" finds "Trois-Rivières". The raw value is passed and
964
+ # normalised in SQL. ID fields (code_id / parent_id) keep the plain ILIKE
965
+ # — codes and DGUIDs are ASCII identifiers, so folding them is a no-op
966
+ # that only defeats indexes.
961
967
  if isinstance(field, _IdField):
962
- return f"{row_alias}.code_id ILIKE ?", [pattern]
968
+ return f"{row_alias}.code_id ILIKE ?", [_ilike_pattern(pred.op, pred.value)]
963
969
  if isinstance(field, _ParentIdField):
964
- return f"{row_alias}.parent_id ILIKE ?", [pattern]
970
+ return f"{row_alias}.parent_id ILIKE ?", [_ilike_pattern(pred.op, pred.value)]
965
971
  if isinstance(field, _NameField):
966
- return f"{row_alias}.name[?] ILIKE ?", [field.lang, pattern]
972
+ return _label_match_sql(f"{row_alias}.name[?]", pred.op), [field.lang, pred.value]
967
973
  if isinstance(field, _AnnotationField):
968
974
  alias = _ensure_annotation_join(
969
975
  field.type,
@@ -972,7 +978,7 @@ def _emit_string_match(
972
978
  join_params=join_params,
973
979
  annotation_join_aliases=annotation_join_aliases,
974
980
  )
975
- return f"{alias}.title ILIKE ?", [pattern]
981
+ return _label_match_sql(f"{alias}.title", pred.op), [pred.value]
976
982
  if isinstance(field, _AnnotationTextField):
977
983
  text_alias = _ensure_annotation_text_join(
978
984
  field.type,
@@ -982,7 +988,7 @@ def _emit_string_match(
982
988
  join_params=join_params,
983
989
  annotation_join_aliases=annotation_join_aliases,
984
990
  )
985
- return f"{text_alias}.text ILIKE ?", [pattern]
991
+ return _label_match_sql(f"{text_alias}.text", pred.op), [pred.value]
986
992
  msg = f"Unsupported field for string match: {type(field).__name__}"
987
993
  raise NotImplementedError(msg)
988
994
 
@@ -1160,6 +1166,41 @@ def _ilike_pattern(op: str, value: str) -> str:
1160
1166
  raise NotImplementedError(msg)
1161
1167
 
1162
1168
 
1169
+ # Accent- AND punctuation-insensitive label matching. ``strip_accents`` folds
1170
+ # diacritics; the regexp collapses runs of Unicode punctuation + separators
1171
+ # (``\p{P}\p{Z}`` + whitespace) to a single space, so "Trois Rivieres" matches
1172
+ # "Trois-Rivières", "St Johns" ~ "St. John's", etc. ``\p{P}\p{Z}`` keeps
1173
+ # letters of any script (Latin, Inuktitut syllabics, …) — unlike a naive
1174
+ # ``[^a-zA-Z0-9]``, which would destroy non-Latin names. The regexp also
1175
+ # strips ILIKE wildcards (``%`` / ``_`` are punctuation), so the bound value
1176
+ # needs no LIKE-escaping.
1177
+ _LABEL_NORM_REGEX = r"[\p{P}\p{Z}\s]+"
1178
+
1179
+
1180
+ def _label_norm(expr: str) -> str:
1181
+ """Wrap ``expr`` in the accent + punctuation fold used for label search."""
1182
+ return f"trim(regexp_replace(strip_accents({expr}), '{_LABEL_NORM_REGEX}', ' ', 'g'))"
1183
+
1184
+
1185
+ def _label_match_sql(col_expr: str, op: str) -> str:
1186
+ """Build an accent/punctuation-insensitive ILIKE of ``col_expr`` vs a bound ``?``.
1187
+
1188
+ Both sides are folded by :func:`_label_norm`, then the op decides where
1189
+ the ``%`` wildcards go. The value placeholder is normalised in SQL (not
1190
+ pre-wrapped), so callers pass the raw value, not an ILIKE pattern.
1191
+ """
1192
+ col = _label_norm(col_expr)
1193
+ val = _label_norm("?")
1194
+ if op == "contains":
1195
+ return f"{col} ILIKE '%' || {val} || '%'"
1196
+ if op == "starts_with":
1197
+ return f"{col} ILIKE {val} || '%'"
1198
+ if op == "ends_with":
1199
+ return f"{col} ILIKE '%' || {val}"
1200
+ msg = f"Unsupported string op: {op}"
1201
+ raise NotImplementedError(msg)
1202
+
1203
+
1163
1204
  # ── Row → Code construction ─────────────────────────────────────────────
1164
1205
 
1165
1206
 
File without changes
File without changes
File without changes
File without changes
File without changes