sdmxlib 0.43.0__tar.gz → 0.44.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.43.0 → sdmxlib-0.44.0}/PKG-INFO +1 -1
  2. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/pyproject.toml +1 -1
  3. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/__init__.py +2 -0
  4. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/local/registry.py +166 -1
  5. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/__init__.py +2 -0
  6. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/metadataset.py +24 -1
  7. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/README.md +0 -0
  8. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/_duckdb.py +0 -0
  9. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/__init__.py +0 -0
  10. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/client.py +0 -0
  11. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/federated.py +0 -0
  12. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/filters.py +0 -0
  13. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/policy.py +0 -0
  14. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/providers.py +0 -0
  15. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/query.py +0 -0
  16. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/registry.py +0 -0
  17. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/api/session.py +0 -0
  18. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/builders/__init__.py +0 -0
  19. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/builders/contentconstraint.py +0 -0
  20. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/catalog.py +0 -0
  21. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/data_store.py +0 -0
  22. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/__init__.py +0 -0
  23. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -0
  24. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_csv/reader.py +0 -0
  25. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_csv/writer.py +0 -0
  26. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
  27. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/_data_common.py +0 -0
  28. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/data_v1.py +0 -0
  29. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/data_v2.py +0 -0
  30. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -0
  31. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
  32. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/structure_values.py +0 -0
  33. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
  34. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json_1_0/__init__.py +0 -0
  35. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_json_1_0/writer.py +0 -0
  36. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml/__init__.py +0 -0
  37. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml/_common.py +0 -0
  38. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
  39. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
  40. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml21/reader.py +0 -0
  41. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
  42. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
  43. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
  44. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
  45. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
  46. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
  47. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml31/__init__.py +0 -0
  48. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml31/namespaces.py +0 -0
  49. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml31/reader.py +0 -0
  50. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/formats/sdmx_ml31/writer.py +0 -0
  51. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/local/__init__.py +0 -0
  52. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/local/data_store.py +0 -0
  53. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/annotations.py +0 -0
  54. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/base.py +0 -0
  55. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/binding.py +0 -0
  56. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/category.py +0 -0
  57. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/code_query.py +0 -0
  58. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/codelist.py +0 -0
  59. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/collections.py +0 -0
  60. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/concept.py +0 -0
  61. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/constraint.py +0 -0
  62. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/convert.py +0 -0
  63. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/dataflow.py +0 -0
  64. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/dataset.py +0 -0
  65. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/datastructure.py +0 -0
  66. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/expr.py +0 -0
  67. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/hierarchy.py +0 -0
  68. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/hierarchy_query.py +0 -0
  69. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/in_memory_registry.py +0 -0
  70. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/istring.py +0 -0
  71. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/mapping.py +0 -0
  72. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/message.py +0 -0
  73. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/metadataflow.py +0 -0
  74. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/metadatastructure.py +0 -0
  75. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/organisation.py +0 -0
  76. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/provision.py +0 -0
  77. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/ref.py +0 -0
  78. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/registry.py +0 -0
  79. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/representation.py +0 -0
  80. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/urn.py +0 -0
  81. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/model/validation.py +0 -0
  82. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/polars.py +0 -0
  83. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/py.typed +0 -0
  84. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/rest.py +0 -0
  85. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/sql.py +0 -0
  86. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/storage/__init__.py +0 -0
  87. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/storage/_kinds.py +0 -0
  88. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/storage/lazy.py +0 -0
  89. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/storage/readers.py +0 -0
  90. {sdmxlib-0.43.0 → sdmxlib-0.44.0}/src/sdmxlib/storage/schema.py +0 -0
  91. {sdmxlib-0.43.0 → sdmxlib-0.44.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.43.0
3
+ Version: 0.44.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.43.0"
3
+ version = "0.44.0"
4
4
  description = "SDMX structural metadata library for Python"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -90,6 +90,7 @@ from sdmxlib.model import (
90
90
  MeasureDimension,
91
91
  MetadataAttribute,
92
92
  MetadataMessage,
93
+ MetadataSection,
93
94
  MetadataSet,
94
95
  MetadataStructure,
95
96
  Metadataflow,
@@ -198,6 +199,7 @@ __all__ = [
198
199
  "MemberValue",
199
200
  "MetadataAttribute",
200
201
  "MetadataMessage",
202
+ "MetadataSection",
201
203
  "MetadataSet",
202
204
  "MetadataStructure",
203
205
  "Metadataflow",
@@ -36,13 +36,14 @@ import time
36
36
  from collections.abc import Iterable, Iterator
37
37
  from contextlib import contextmanager
38
38
  from pathlib import Path
39
- from typing import TYPE_CHECKING, Any, Self
39
+ from typing import TYPE_CHECKING, Any, Self, cast
40
40
 
41
41
  import attrs
42
42
  import duckdb
43
43
 
44
44
  from sdmxlib._duckdb import scalar
45
45
  from sdmxlib.model.collections import ItemList
46
+ from sdmxlib.model.istring import InternationalString
46
47
  from sdmxlib.model.ref import Ref
47
48
  from sdmxlib.model.registry import InMemoryRegistry, artefact_urn
48
49
  from sdmxlib.model.urn import SdmxUrn
@@ -60,6 +61,19 @@ if TYPE_CHECKING:
60
61
  from sdmxlib.model.binding import DataflowBinding, DriftIssue
61
62
  from sdmxlib.model.dataflow import Dataflow
62
63
  from sdmxlib.model.hierarchy import Hierarchy
64
+ from sdmxlib.model.metadataset import MetadataSection, MetadataSet
65
+
66
+
67
+ def _leaf_attribute_id(attribute_path: str) -> str:
68
+ """Leaf attribute id from a dotted ``attribute_path``.
69
+
70
+ Strips the parent path and any ``[n]`` sibling suffix, e.g.
71
+ ``"QUALITY.COVERAGE[1]"`` -> ``"COVERAGE"``.
72
+ """
73
+ leaf = attribute_path.rsplit(".", 1)[-1]
74
+ bracket = leaf.find("[")
75
+ return leaf[:bracket] if bracket != -1 else leaf
76
+
63
77
 
64
78
  # Map item-level artefact class -> (parent scheme type name, items attribute name)
65
79
  _ITEM_TO_SCHEME: dict[str, tuple[str, str]] = {
@@ -657,6 +671,157 @@ class Registry:
657
671
  if issues:
658
672
  raise DriftError(issues)
659
673
 
674
+ # ── Reference-metadata queries ──────────────────────────────────────
675
+
676
+ def metadata_for_target(
677
+ self,
678
+ target_urn: "str | SdmxUrn[Any]",
679
+ *,
680
+ structure_urn: "str | SdmxUrn[Any] | None" = None,
681
+ ) -> "list[MetadataSet]":
682
+ """All MetadataSets whose targets include ``target_urn``.
683
+
684
+ The reverse of ``MetadataSet.targets`` — given any artefact URN
685
+ (a Code, Codelist, Concept, Dataflow, …), return every reported
686
+ MetadataSet attached to it. This is the SDMX 3.0
687
+ ``/metadata/structure/{artefactType}/…`` by-target lookup realised
688
+ locally, and the primitive both the REST view and the search
689
+ indexer navigate.
690
+
691
+ Sets are returned without resolving their target/structure Refs
692
+ (the URNs are all present) — cheap even for sets with many
693
+ targets. Ordered by set URN for determinism.
694
+
695
+ Args:
696
+ target_urn: The artefact URN to find attached metadata for.
697
+ structure_urn: Optional MSD or Metadataflow URN to filter by,
698
+ e.g. return only reference-guide sets targeting the code.
699
+
700
+ Returns:
701
+ The matching MetadataSets (empty list if none).
702
+ """
703
+ cur = self._read_cursor()
704
+ params: list[str] = [str(target_urn)]
705
+ sql = "SELECT DISTINCT t.set_urn FROM metadata_target t"
706
+ if structure_urn is not None:
707
+ sql += " JOIN metadata_set s ON s.urn = t.set_urn AND s.structure_urn = ?"
708
+ params.append(str(structure_urn))
709
+ sql += " WHERE t.target_urn = ? ORDER BY t.set_urn"
710
+ # target_urn is the last positional param; structure_urn (if any)
711
+ # precedes it in SQL text, so order params to match.
712
+ ordered = ([params[1]] if structure_urn is not None else []) + [params[0]]
713
+ rows = cur.execute(sql, ordered).fetchall()
714
+
715
+ sets: list[MetadataSet] = []
716
+ for (set_urn,) in rows:
717
+ artefact = readers.get_artefact(cur, set_urn, lazy_conn=self._conn)
718
+ if artefact is not None:
719
+ sets.append(cast("MetadataSet", artefact))
720
+ return sets
721
+
722
+ def metadata_report(
723
+ self,
724
+ set_urn: "str | SdmxUrn[Any]",
725
+ ) -> "list[MetadataSection]":
726
+ """Flattened, MSD-typed view of a MetadataSet's reported values.
727
+
728
+ Returns one ``MetadataSection`` per ``metadata_value`` row,
729
+ each carrying the leaf attribute's declared ``text_type`` (e.g.
730
+ ``"String"`` / ``"XHTML"``) and ``is_presentational`` flag joined
731
+ from the MSD. Sections are ordered by ``(path, sibling_index)``.
732
+
733
+ This is the sectioned input a chunker / search indexer consumes —
734
+ it turns the reported tree + schema into a flat, typed list without
735
+ the caller re-joining the MSD. Returns an empty list if the set has
736
+ no values (or does not exist).
737
+
738
+ Args:
739
+ set_urn: The MetadataSet URN to project.
740
+ """
741
+ from sdmxlib.model.metadataset import MetadataSection # noqa: PLC0415
742
+
743
+ cur = self._read_cursor()
744
+ type_by_id = self._msd_attribute_types(cur, str(set_urn))
745
+
746
+ rows = cur.execute(
747
+ """
748
+ SELECT attribute_path, sibling_index, value_kind, scalar, i18n
749
+ FROM metadata_value
750
+ WHERE set_urn = ?
751
+ ORDER BY attribute_path, sibling_index
752
+ """,
753
+ [str(set_urn)],
754
+ ).fetchall()
755
+
756
+ sections: list[MetadataSection] = []
757
+ for attribute_path, sibling_index, value_kind, scalar_val, i18n_val in rows:
758
+ leaf_id = _leaf_attribute_id(attribute_path)
759
+ text_type, is_pres = type_by_id.get(leaf_id, (None, False))
760
+ value: Any
761
+ if value_kind == "i18n":
762
+ value = InternationalString.of(dict(i18n_val)) if i18n_val else None
763
+ else:
764
+ value = scalar_val
765
+ sections.append(
766
+ MetadataSection(
767
+ path=attribute_path,
768
+ attribute_id=leaf_id,
769
+ sibling_index=sibling_index,
770
+ value=value,
771
+ text_type=text_type,
772
+ is_presentational=is_pres,
773
+ )
774
+ )
775
+ return sections
776
+
777
+ def _msd_attribute_types(
778
+ self,
779
+ cur: "duckdb.DuckDBPyConnection",
780
+ set_urn: str,
781
+ ) -> "dict[str, tuple[str | None, bool]]":
782
+ """Map ``attribute id → (text_type, is_presentational)`` for a set's MSD.
783
+
784
+ Resolves the set's structure (an MSD directly, or the MSD behind a
785
+ Metadataflow) and walks its attribute tree. Empty if the structure
786
+ is absent.
787
+ """
788
+ from sdmxlib.model.metadatastructure import MetadataAttribute, MetadataStructure # noqa: PLC0415
789
+ from sdmxlib.model.representation import Facet # noqa: PLC0415
790
+
791
+ set_row = cur.execute(
792
+ "SELECT structure_urn, structure_kind FROM metadata_set WHERE urn = ?",
793
+ [set_urn],
794
+ ).fetchone()
795
+ if set_row is None:
796
+ return {}
797
+ structure_urn, structure_kind = set_row
798
+
799
+ msd_urn = structure_urn
800
+ if structure_kind == "Metadataflow":
801
+ flow = readers.get_artefact(cur, structure_urn, lazy_conn=self._conn)
802
+ structure_ref = getattr(flow, "structure", None)
803
+ if structure_ref is None:
804
+ return {}
805
+ msd_urn = str(structure_ref.urn)
806
+
807
+ msd = readers.get_artefact(cur, msd_urn, lazy_conn=self._conn)
808
+ if not isinstance(msd, MetadataStructure):
809
+ return {}
810
+
811
+ out: dict[str, tuple[str | None, bool]] = {}
812
+
813
+ def _walk(attr: MetadataAttribute) -> None:
814
+ text_type: str | None = None
815
+ if isinstance(attr.representation, Facet) and attr.representation.type is not None:
816
+ text_type = str(attr.representation.type)
817
+ out[attr.id] = (text_type, attr.is_presentational)
818
+ for child in attr.children:
819
+ _walk(child)
820
+
821
+ for root in msd.attributes:
822
+ _walk(root)
823
+ return out
824
+
660
825
  # ── Ref resolution ─────────────────────────────────────────────────
661
826
 
662
827
  def _resolve_refs( # noqa: C901
@@ -78,6 +78,7 @@ from sdmxlib.model.message import MetadataMessage, StructureMessage
78
78
  from sdmxlib.model.metadataflow import Metadataflow
79
79
  from sdmxlib.model.metadatastructure import MetadataAttribute, MetadataStructure
80
80
  from sdmxlib.model.metadataset import (
81
+ MetadataSection,
81
82
  MetadataSet,
82
83
  ReportedAttribute,
83
84
  ReportedAttributeValue,
@@ -157,6 +158,7 @@ __all__ = [
157
158
  "MemberValue",
158
159
  "MetadataAttribute",
159
160
  "MetadataMessage",
161
+ "MetadataSection",
160
162
  "MetadataSet",
161
163
  "MetadataStructure",
162
164
  "Metadataflow",
@@ -19,7 +19,7 @@ from collections.abc import Sequence
19
19
  from datetime import datetime
20
20
  from typing import Any, ClassVar, cast
21
21
 
22
- from attrs import define, evolve, field
22
+ from attrs import define, evolve, field, frozen
23
23
 
24
24
  from sdmxlib.model.annotations import Annotations
25
25
  from sdmxlib.model.base import MaintainableArtefact
@@ -70,6 +70,29 @@ class ReportedAttribute:
70
70
  annotations: Annotations = field(factory=Annotations, converter=_to_annotations)
71
71
 
72
72
 
73
+ @frozen
74
+ class MetadataSection:
75
+ """A flattened, MSD-typed view of one reported metadata value.
76
+
77
+ The retrieval projection of a MetadataSet: one ``MetadataSection`` per
78
+ ``metadata_value`` row, with the leaf attribute's declared data type
79
+ (``text_type``, e.g. ``"String"`` / ``"XHTML"``) joined in from the
80
+ MSD. This is the sectioned input a chunker / search indexer wants —
81
+ ``path`` gives the dotted hierarchical location (with ``[n]`` sibling
82
+ suffixes preserved), ``attribute_id`` the leaf id, and ``value`` the
83
+ scalar or ``InternationalString`` content.
84
+
85
+ Produced by ``Registry.metadata_report``.
86
+ """
87
+
88
+ path: str
89
+ attribute_id: str
90
+ sibling_index: int
91
+ value: ReportedAttributeValue | None = None
92
+ text_type: str | None = None
93
+ is_presentational: bool = False
94
+
95
+
73
96
  # ── MetadataSet ──────────────────────────────────────────────────────────
74
97
 
75
98
 
File without changes
File without changes
File without changes
File without changes
File without changes