pysdmx 1.4.0rc1__py3-none-any.whl → 1.5.0__py3-none-any.whl

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.
@@ -193,7 +193,7 @@ def __write_data_single_dataset(
193
193
  prettyprint=prettyprint,
194
194
  )
195
195
  else:
196
- series_codes, obs_codes = get_codes(
196
+ series_codes, obs_codes, group_codes = get_codes(
197
197
  dimension_code=dim,
198
198
  structure=dataset.structure, # type: ignore[arg-type]
199
199
  data=dataset.data,
@@ -205,6 +205,13 @@ def __write_data_single_dataset(
205
205
  series_codes = [x for x in series_codes if x not in series_att_codes]
206
206
  obs_codes = [x for x in obs_codes if x not in obs_att_codes]
207
207
 
208
+ if group_codes:
209
+ data += __group_processing(
210
+ data=dataset.data,
211
+ group_codes=group_codes,
212
+ prettyprint=prettyprint,
213
+ )
214
+
208
215
  data += __series_processing(
209
216
  data=dataset.data,
210
217
  series_codes=series_codes,
@@ -269,6 +276,65 @@ def __obs_processing(
269
276
  return "".join(iterator)
270
277
 
271
278
 
279
+ def __group_processing(
280
+ data: pd.DataFrame,
281
+ group_codes: List[Dict[str, Any]],
282
+ prettyprint: bool = True,
283
+ ) -> str:
284
+ def __format_group_str(
285
+ data_info: Dict[Any, Any],
286
+ group_id: str,
287
+ dimensions: List[str],
288
+ attribute: str,
289
+ ) -> str:
290
+ """Formats a generic SDMX group using __value()."""
291
+ child2 = "\t\t" if prettyprint else ""
292
+ nl = "\n" if prettyprint else ""
293
+
294
+ out_element = f'{child2}<{ABBR_GEN}:Group type="{group_id}">{nl}'
295
+
296
+ # GroupKey block
297
+ out_element += f"{child2}\t<{ABBR_GEN}:GroupKey>{nl}"
298
+ for dim in dimensions:
299
+ out_element += (
300
+ f"{child2}\t\t{__value(dim, data_info.get(dim, ''))}{nl}"
301
+ )
302
+ out_element += f"{child2}\t</{ABBR_GEN}:GroupKey>{nl}"
303
+
304
+ # Attributes block
305
+ out_element += f"{child2}\t<{ABBR_GEN}:Attributes>{nl}"
306
+ out_element += (
307
+ f"{child2}\t\t{__value(attribute, data_info.get(attribute, ''))}"
308
+ f"{nl}"
309
+ )
310
+ out_element += f"{child2}\t</{ABBR_GEN}:Attributes>{nl}"
311
+
312
+ out_element += f"{child2}</{ABBR_GEN}:Group>{nl}"
313
+ return out_element
314
+
315
+ out_list: List[str] = []
316
+
317
+ for group in group_codes:
318
+ group_id = group["group_id"]
319
+ dimensions = group["dimensions"]
320
+ attribute = group["attribute"]
321
+ group_keys = dimensions + [attribute]
322
+
323
+ grouped_data = (
324
+ data[group_keys]
325
+ .drop_duplicates()
326
+ .reset_index(drop=True)
327
+ .to_dict(orient="records")
328
+ )
329
+
330
+ for record in grouped_data:
331
+ out_list.append(
332
+ __format_group_str(record, group_id, dimensions, attribute)
333
+ )
334
+
335
+ return "".join(out_list)
336
+
337
+
272
338
  def __series_processing(
273
339
  data: pd.DataFrame,
274
340
  series_codes: List[str],
@@ -0,0 +1 @@
1
+ """SDMX 3.1 XML reader and writer."""
@@ -0,0 +1 @@
1
+ """SDMX 3.1 XML reader module."""
@@ -0,0 +1,39 @@
1
+ """Parsers for reading metadata."""
2
+
3
+ from typing import Sequence, Union
4
+
5
+ from pysdmx.errors import Invalid
6
+ from pysdmx.io.xml.__parse_xml import parse_xml
7
+ from pysdmx.io.xml.__structure_aux_reader import StructureParser
8
+ from pysdmx.io.xml.__tokens import (
9
+ STRUCTURE,
10
+ STRUCTURES,
11
+ )
12
+ from pysdmx.model.__base import (
13
+ ItemScheme,
14
+ )
15
+ from pysdmx.model.dataflow import (
16
+ Dataflow,
17
+ DataStructureDefinition,
18
+ )
19
+
20
+
21
+ def read(
22
+ input_str: str,
23
+ validate: bool = True,
24
+ ) -> Sequence[Union[ItemScheme, DataStructureDefinition, Dataflow]]:
25
+ """Reads an SDMX-ML 3.1 Structure data and returns the structures.
26
+
27
+ Args:
28
+ input_str: SDMX-ML structure message to read.
29
+ validate: If True, the XML data will be validated against the XSD.
30
+
31
+ Returns:
32
+ dict: Dictionary with the parsed structures.
33
+ """
34
+ dict_info = parse_xml(input_str, validate)
35
+ if STRUCTURE not in dict_info:
36
+ raise Invalid("This SDMX document is not SDMX-ML 3.1 Structure.")
37
+ return StructureParser(is_sdmx_30=True).format_structures(
38
+ dict_info[STRUCTURE][STRUCTURES]
39
+ )
@@ -0,0 +1,39 @@
1
+ """SDMX XML 3.1 StructureSpecificData reader module."""
2
+
3
+ from typing import Sequence
4
+
5
+ from pysdmx.errors import Invalid
6
+ from pysdmx.io.pd import PandasDataset
7
+ from pysdmx.io.xml.__data_aux import (
8
+ get_data_objects,
9
+ )
10
+ from pysdmx.io.xml.__parse_xml import parse_xml
11
+ from pysdmx.io.xml.__ss_aux_reader import (
12
+ _parse_structure_specific_data,
13
+ )
14
+ from pysdmx.io.xml.__tokens import (
15
+ STR_REF,
16
+ STR_SPE,
17
+ )
18
+
19
+
20
+ def read(input_str: str, validate: bool = True) -> Sequence[PandasDataset]:
21
+ """Reads an SDMX-ML 3.1 and returns a Sequence of Datasets.
22
+
23
+ Args:
24
+ input_str: SDMX-ML data to read.
25
+ validate: If True, the XML data will be validated against the XSD.
26
+ """
27
+ dict_info = parse_xml(input_str, validate=validate)
28
+ if STR_SPE not in dict_info:
29
+ raise Invalid(
30
+ "This SDMX document is not an SDMX-ML StructureSpecificData."
31
+ )
32
+ dataset_info, str_info = get_data_objects(dict_info[STR_SPE])
33
+ datasets = []
34
+ for dataset in dataset_info:
35
+ ds = _parse_structure_specific_data(
36
+ dataset, str_info[dataset[STR_REF]]
37
+ )
38
+ datasets.append(ds)
39
+ return datasets
@@ -0,0 +1 @@
1
+ """SDMX 3.1 writer package."""
@@ -0,0 +1,67 @@
1
+ """Module for writing metadata to XML files."""
2
+
3
+ from pathlib import Path
4
+ from typing import Dict, Optional, Sequence, Union
5
+
6
+ from pysdmx.io.format import Format
7
+ from pysdmx.io.xml.__structure_aux_writer import (
8
+ STR_DICT_TYPE_LIST_30,
9
+ __write_structures,
10
+ )
11
+ from pysdmx.io.xml.__write_aux import (
12
+ __write_header,
13
+ create_namespaces,
14
+ get_end_message,
15
+ )
16
+ from pysdmx.model.__base import MaintainableArtefact
17
+ from pysdmx.model.message import Header
18
+
19
+
20
+ def write(
21
+ structures: Sequence[MaintainableArtefact],
22
+ output_path: Optional[Union[str, Path]] = None,
23
+ prettyprint: bool = True,
24
+ header: Optional[Header] = None,
25
+ ) -> Optional[str]:
26
+ """This function writes a SDMX-ML file from the Message Content.
27
+
28
+ Args:
29
+ structures: The content to be written
30
+ output_path: The path to save the file
31
+ prettyprint: Prettyprint or not
32
+ header: The header to be used (generated if None)
33
+
34
+ Returns:
35
+ The XML string if output_path is empty, None otherwise
36
+ """
37
+ type_ = Format.STRUCTURE_SDMX_ML_3_1
38
+ elements = {structure.short_urn: structure for structure in structures}
39
+ if header is None:
40
+ header = Header()
41
+
42
+ content: Dict[str, Dict[str, MaintainableArtefact]] = {}
43
+ for urn, element in elements.items():
44
+ list_ = STR_DICT_TYPE_LIST_30[type(element)]
45
+ if list_ not in content:
46
+ content[list_] = {}
47
+ content[list_][urn] = element
48
+
49
+ # Generating the initial tag with namespaces
50
+ outfile = create_namespaces(type_, prettyprint=prettyprint)
51
+ # Generating the header
52
+ outfile += __write_header(header, prettyprint, data_message=False)
53
+ # Writing the content
54
+ outfile += __write_structures(content, prettyprint, references_30=True)
55
+
56
+ outfile += get_end_message(type_, prettyprint)
57
+
58
+ output_path = (
59
+ str(output_path) if isinstance(output_path, Path) else output_path
60
+ )
61
+
62
+ if output_path is None or output_path == "":
63
+ return outfile
64
+
65
+ with open(output_path, "w", encoding="UTF-8", errors="replace") as f:
66
+ f.write(outfile)
67
+ return None
@@ -0,0 +1,108 @@
1
+ # mypy: disable-error-code="union-attr"
2
+ """Module for writing SDMX-ML 3.1 Structure Specific data messages."""
3
+
4
+ from pathlib import Path
5
+ from typing import Dict, Optional, Sequence, Union
6
+
7
+ from pysdmx.io.format import Format
8
+ from pysdmx.io.pd import PandasDataset
9
+ from pysdmx.io.xml.__tokens import (
10
+ DSD_LOW,
11
+ PROV_AGREEMENT,
12
+ REGISTRY_LOW,
13
+ )
14
+ from pysdmx.io.xml.__write_aux import (
15
+ __write_header,
16
+ create_namespaces,
17
+ get_end_message,
18
+ )
19
+ from pysdmx.io.xml.__write_data_aux import (
20
+ check_content_dataset,
21
+ check_dimension_at_observation,
22
+ )
23
+ from pysdmx.io.xml.__write_structure_specific_aux import (
24
+ __write_data_structure_specific,
25
+ )
26
+ from pysdmx.model.message import Header
27
+ from pysdmx.util import parse_short_urn
28
+
29
+
30
+ def write(
31
+ datasets: Sequence[PandasDataset],
32
+ output_path: Optional[Union[str, Path]] = None,
33
+ prettyprint: bool = True,
34
+ header: Optional[Header] = None,
35
+ dimension_at_observation: Optional[Dict[str, str]] = None,
36
+ ) -> Optional[str]:
37
+ """Write data to SDMX-ML 3.1 Structure Specific format.
38
+
39
+ Args:
40
+ datasets: The datasets to be written.
41
+ output_path: The path to save the file.
42
+ prettyprint: Prettyprint or not.
43
+ header: The header to be used (generated if None).
44
+ dimension_at_observation:
45
+ The mapping between the dataset and the dimension at observation.
46
+
47
+ Returns:
48
+ The XML string if path is empty, None otherwise.
49
+ """
50
+ ss_namespaces = ""
51
+ type_ = Format.DATA_SDMX_ML_3_1
52
+
53
+ # Checking if we have datasets,
54
+ # we need to ensure we can write them correctly
55
+ check_content_dataset(datasets)
56
+ content = {dataset.short_urn: dataset for dataset in datasets}
57
+
58
+ if header is None:
59
+ header = Header()
60
+
61
+ # Checking the dimension at observation mapping
62
+ dim_mapping = check_dimension_at_observation(
63
+ content, dimension_at_observation
64
+ )
65
+ header.structure = dim_mapping
66
+ add_namespace_structure = True
67
+ for i, (short_urn, dimension) in enumerate(header.structure.items()):
68
+ reference = parse_short_urn(short_urn)
69
+ pre_urn = (
70
+ REGISTRY_LOW if reference.sdmx_type == PROV_AGREEMENT else DSD_LOW
71
+ )
72
+ ss_namespaces += (
73
+ f'xmlns:ns{i + 1}="urn:sdmx:org.sdmx'
74
+ f".infomodel.{pre_urn}.{short_urn}"
75
+ f':ObsLevelDim:{dimension}" '
76
+ )
77
+
78
+ # Generating the initial tag with namespaces
79
+ outfile = create_namespaces(type_, ss_namespaces, prettyprint)
80
+ # Generating the header
81
+ outfile += __write_header(
82
+ header,
83
+ prettyprint,
84
+ add_namespace_structure,
85
+ data_message=True,
86
+ references_30=True,
87
+ )
88
+ # Writing the content
89
+ outfile += __write_data_structure_specific(
90
+ datasets=content,
91
+ dim_mapping=dim_mapping,
92
+ prettyprint=prettyprint,
93
+ references_30=True,
94
+ )
95
+
96
+ outfile += get_end_message(type_, prettyprint)
97
+
98
+ output_path = (
99
+ str(output_path) if isinstance(output_path, Path) else output_path
100
+ )
101
+
102
+ if output_path is None or output_path == "":
103
+ return outfile
104
+
105
+ with open(output_path, "w", encoding="UTF-8", errors="replace") as f:
106
+ f.write(outfile)
107
+
108
+ return None
pysdmx/model/code.py CHANGED
@@ -21,7 +21,7 @@ from typing import Iterator, Literal, Optional, Sequence, Union
21
21
 
22
22
  from msgspec import Struct
23
23
 
24
- from pysdmx.model.__base import Item, ItemScheme, MaintainableArtefact
24
+ from pysdmx.model.__base import Agency, Item, ItemScheme, MaintainableArtefact
25
25
 
26
26
 
27
27
  class Code(Item, frozen=True, omit_defaults=True):
@@ -74,6 +74,24 @@ class Codelist(ItemScheme, frozen=True, omit_defaults=True, tag=True):
74
74
  """Extract the items in the Codelist."""
75
75
  return self.items
76
76
 
77
+ @property
78
+ def short_urn(self) -> str:
79
+ """Returns the short URN for Codelist.
80
+
81
+ A short URN follows the syntax: Type=Agency:Id(Version). For example:
82
+ Codelist=SDMX:CL_FREQ(1.0)
83
+
84
+ Returns:
85
+ The short URN for the Codelist .
86
+ """
87
+ agency = (
88
+ self.agency.id if isinstance(self.agency, Agency) else self.agency
89
+ )
90
+ # Value lists are represented as Codelist, but their short URN uses
91
+ # ValueList instead of Codelist.
92
+ typ = "ValueList" if self.sdmx_type == "valuelist" else "Codelist"
93
+ return f"{typ}={agency}:{self.id}({self.version})"
94
+
77
95
  def __iter__(self) -> Iterator[Code]:
78
96
  """Return an iterator over the list of codes."""
79
97
  yield from self.codes
pysdmx/model/dataflow.py CHANGED
@@ -18,6 +18,7 @@ from pysdmx.errors import Invalid
18
18
  from pysdmx.model.__base import (
19
19
  Agency,
20
20
  DataProvider,
21
+ IdentifiableArtefact,
21
22
  ItemReference,
22
23
  MaintainableArtefact,
23
24
  )
@@ -231,6 +232,14 @@ class Component(
231
232
  return f"{self.__class__.__name__}({', '.join(attrs)})"
232
233
 
233
234
 
235
+ class GroupDimension(
236
+ IdentifiableArtefact, frozen=True, omit_defaults=True, kw_only=True
237
+ ):
238
+ """A group of dimensions that can be used to identify a group."""
239
+
240
+ dimensions: Sequence[str]
241
+
242
+
234
243
  class Components(UserList[Component]):
235
244
  """A collection of components describing the data."""
236
245
 
@@ -463,6 +472,7 @@ class Schema(Struct, frozen=True, omit_defaults=True, repr_omit_defaults=True):
463
472
  version: str = "1.0"
464
473
  artefacts: Sequence[str] = ()
465
474
  generated: datetime = datetime.now(timezone.utc)
475
+ groups: Optional[Sequence[GroupDimension]] = None
466
476
 
467
477
  def __str__(self) -> str:
468
478
  """Custom string representation without the class name."""
@@ -528,6 +538,7 @@ class DataStructureDefinition(MaintainableArtefact, frozen=True, kw_only=True):
528
538
  """
529
539
 
530
540
  components: Components
541
+ groups: Optional[Sequence[GroupDimension]] = None
531
542
  evolving_structure: bool = False
532
543
 
533
544
  def __extract_artefacts(self) -> Sequence[str]:
@@ -557,6 +568,7 @@ class DataStructureDefinition(MaintainableArtefact, frozen=True, kw_only=True):
557
568
  else self.agency
558
569
  ),
559
570
  id=self.id,
571
+ groups=self.groups,
560
572
  components=self.components,
561
573
  version=self.version,
562
574
  artefacts=self.__extract_artefacts(),
pysdmx/util/__init__.py CHANGED
@@ -6,6 +6,7 @@ from typing import Any, Sequence, Union
6
6
  from pysdmx.errors import Invalid, NotFound
7
7
  from pysdmx.model import Agency, ItemReference, Reference
8
8
  from pysdmx.util._date_pattern_map import convert_dpm
9
+ from pysdmx.util._net_utils import map_httpx_errors
9
10
 
10
11
  NF = "Not found"
11
12
 
@@ -134,4 +135,5 @@ __all__ = [
134
135
  "parse_short_item_urn",
135
136
  "ItemReference",
136
137
  "Reference",
138
+ "map_httpx_errors",
137
139
  ]
@@ -42,6 +42,7 @@ def schema_generator(message: Message, dataset_ref: Reference) -> Schema:
42
42
  id=dataset_ref.id,
43
43
  version=dataset_ref.version,
44
44
  agency=dataset_ref.agency,
45
+ groups=dsd.groups,
45
46
  components=dsd.components,
46
47
  artefacts=dsd.to_schema().artefacts,
47
48
  )
@@ -0,0 +1,39 @@
1
+ from typing import NoReturn, Union
2
+
3
+ import httpx
4
+
5
+ from pysdmx import errors
6
+
7
+
8
+ def map_httpx_errors(
9
+ e: Union[httpx.RequestError, httpx.HTTPStatusError],
10
+ ) -> NoReturn:
11
+ """Map httpx errors to pysdmx errors."""
12
+ q = e.request.url
13
+ if isinstance(e, httpx.HTTPStatusError):
14
+ s = e.response.status_code
15
+ t = e.response.text
16
+ if s == 404:
17
+ msg = (
18
+ "The requested resource(s) could not be found in the "
19
+ f"targeted service. The query was `{q}`"
20
+ )
21
+ raise errors.NotFound("Not found", msg) from e
22
+ elif s < 500:
23
+ msg = (
24
+ f"The query returned a {s} error code. The query "
25
+ f"was `{q}`. The error message was: `{t}`."
26
+ )
27
+ raise errors.Invalid(f"Client error {s}", msg) from e
28
+ else:
29
+ msg = (
30
+ f"The service returned a {s} error code. The query "
31
+ f"was `{q}`. The error message was: `{t}`."
32
+ )
33
+ raise errors.InternalError(f"Service error {s}", msg) from e
34
+ else:
35
+ msg = (
36
+ f"There was an issue connecting to the targeted service. "
37
+ f"The query was `{q}`. The error message was: `{e}`."
38
+ )
39
+ raise errors.Unavailable("Connection error", msg) from e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pysdmx
3
- Version: 1.4.0rc1
3
+ Version: 1.5.0
4
4
  Summary: Your opinionated Python SDMX library
5
5
  License: Apache-2.0
6
6
  Keywords: sdmx,data discovery,data retrieval,metadata,fmr
@@ -26,8 +26,8 @@ Requires-Dist: pandas (>=2.1.4) ; extra == "data"
26
26
  Requires-Dist: parsy (>=2.1)
27
27
  Requires-Dist: python-dateutil (>=2.8.2) ; extra == "all"
28
28
  Requires-Dist: python-dateutil (>=2.8.2) ; extra == "dc"
29
- Requires-Dist: sdmxschemas (>=0.2.0) ; extra == "all"
30
- Requires-Dist: sdmxschemas (>=0.2.0) ; extra == "xml"
29
+ Requires-Dist: sdmxschemas (>=1.0.0) ; extra == "all"
30
+ Requires-Dist: sdmxschemas (>=1.0.0) ; extra == "xml"
31
31
  Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "all"
32
32
  Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "vtl"
33
33
  Requires-Dist: xmltodict (>=0.13) ; extra == "all"
@@ -1,5 +1,5 @@
1
1
  pysdmx/__extras_check.py,sha256=I39OaM1lAPBnyzHVJ7kA_ZA_tMeDAQLr6ZKqEQ9MK1Q,1659
2
- pysdmx/__init__.py,sha256=pbr9sYLtXXLshnGSuTBilBjXhge-z5bRG99UVZMOw30,70
2
+ pysdmx/__init__.py,sha256=5z10nwG-grwF4ljX4qnTW3mCl2fdff-9iqmtKljWHL0,67
3
3
  pysdmx/api/__init__.py,sha256=8lRaF6kEO51ehl0fmW_pHLvkN_34TtEhqhr3oKo6E6g,26
4
4
  pysdmx/api/dc/__init__.py,sha256=oPU32X8CRZy4T1to9mO5KMqMwxQsVI424dPqai-I8zI,121
5
5
  pysdmx/api/dc/_api.py,sha256=poy1FYFXnF6maBGy5lpOodf32-7QQjH8PCBNDkuOXxQ,7747
@@ -19,7 +19,7 @@ pysdmx/api/qb/gds.py,sha256=Z2KhP6m09_oWI2CbYRhlTsx8VLC-_UZaQEOEqX94SOw,4975
19
19
  pysdmx/api/qb/refmeta.py,sha256=rWhMcUkcQ0zYoiEVAVFOnykLLhMWFFZssFZh8z3kRng,10677
20
20
  pysdmx/api/qb/registration.py,sha256=IURlmXcXQi8e-w5YXCgRNs07EQJZJ2bOdZb7M_k5iZ8,7132
21
21
  pysdmx/api/qb/schema.py,sha256=f0V-fQl9GQ2YWhnZqUF_wogB2L9dRIdY_vT4XvYkqoI,5747
22
- pysdmx/api/qb/service.py,sha256=PyhzVw3X9tupUOrSstwMZm2WeK-PMVeXE1e2XBZjFp4,15274
22
+ pysdmx/api/qb/service.py,sha256=ieLgOR27NNRD2P4FGhFmWVkJweGngAx-mWkTDfsVCyI,14001
23
23
  pysdmx/api/qb/structure.py,sha256=NA4-Xlnj55xNLFlAC2qmaSRgKYbzFjQTKXA5G-SYlBo,17783
24
24
  pysdmx/api/qb/util.py,sha256=Meysdjad-ZyE2WZ1xwMGYrVQPapCKG5QdT4LkdF5CSw,3651
25
25
  pysdmx/errors.py,sha256=9bfujYykzfGMa1TuUOmH9QqghnZGOo556fvbKH2jFa8,3295
@@ -31,8 +31,8 @@ pysdmx/io/csv/sdmx10/writer/__init__.py,sha256=c7WvEufF8UBu6Lf417a4ujY8cQ7QOiQ9_
31
31
  pysdmx/io/csv/sdmx20/__init__.py,sha256=mNfMJKSpmMfk4CbbHhjnBOTQ2IZFY15cNUhHtJPcs90,231
32
32
  pysdmx/io/csv/sdmx20/reader/__init__.py,sha256=HSSUE3iL9g2Bzb_vKw97-Uw_3H3yUQxN8ssAyr1EJkI,4925
33
33
  pysdmx/io/csv/sdmx20/writer/__init__.py,sha256=xqNg2L2BdCkKVIkMrBpkbR6wq2gZ3r1K7oZRkZ1aBoU,2062
34
- pysdmx/io/format.py,sha256=f8bhh3KgEAbR3LligcalaXwdFxMjBP0VPXkrxX_ajno,4117
35
- pysdmx/io/input_processor.py,sha256=HN6GFK3pgdPcTkgmoBIBHzw9ypao_xedOnjGlVxHMOc,5122
34
+ pysdmx/io/format.py,sha256=BJghvHaXNvsfqJCWvB8jhjGMaLPv7zi7N12O0qRwChc,4749
35
+ pysdmx/io/input_processor.py,sha256=MALwyWI26KoxIlRARgIsXxHvEHH9tZ9TTHExtYAPCk8,6406
36
36
  pysdmx/io/json/fusion/messages/__init__.py,sha256=C0LAG6UfIZcQdlCv5d_D1ZNIcCq04RxZYLAp6tLDqwY,1573
37
37
  pysdmx/io/json/fusion/messages/category.py,sha256=E9jrzXenEpqDNUhT1JJLYxty37PSGegRtx45mB3-COg,4589
38
38
  pysdmx/io/json/fusion/messages/code.py,sha256=kB3RJiSA75G4IJYObYR0hdiXYcRs-vNMKqUeA6F4SSo,7529
@@ -73,33 +73,33 @@ pysdmx/io/json/sdmxjson2/messages/structure.py,sha256=WpQttjSdGK1AuWfR69gpe4E7HP
73
73
  pysdmx/io/json/sdmxjson2/messages/vtl.py,sha256=UrcNP5p_R2R_bFrY-vbOKCGYXkD5N4yBAwZlCWetDMQ,14700
74
74
  pysdmx/io/json/sdmxjson2/reader/__init__.py,sha256=f9vTKscFIAQpAKh3pZ4ZKlZSKyGfj1yaZS9w7S0_hIc,1264
75
75
  pysdmx/io/pd.py,sha256=1C5UnAT25EADOpNsRBooEdWNaJEGdmcwh6_R9O5MOrc,464
76
- pysdmx/io/reader.py,sha256=Ejy9YW9QjuwfSKSHmB9EkCVGEKYPvpX5UEesgXE1_FU,8334
76
+ pysdmx/io/reader.py,sha256=saAQ71aRKqze2XeOSV5xcSEB056h8OTKbvm_qwRSfuk,9695
77
77
  pysdmx/io/serde.py,sha256=l8z9qulrUkRNZq63YOUqTTs7zpl1hdYZe3UAZW1aEpM,1062
78
- pysdmx/io/writer.py,sha256=h_2JSaOHSCHjOOIROXNnAakPD2FkSsJ1x42rpK7q7Ag,4564
78
+ pysdmx/io/writer.py,sha256=I7n0yW4c2Hu7eUSWbyHcmWd2VtPkiTUlVE4zTNmdxIg,4759
79
79
  pysdmx/io/xml/__allowed_lxml_errors.py,sha256=PdIK2i6JwwGRh1Ogc5JA0hRySO7QXJKN-DI8EYtdjA0,225
80
80
  pysdmx/io/xml/__data_aux.py,sha256=ztivjZANN1PJ63IvoMfx-5TA2AiPQTVPC_0YYHOT9Ns,4069
81
81
  pysdmx/io/xml/__init__.py,sha256=tcUsSEiM3nBA7lDbZPwGZ7Vu_K9gQd8oliASUMTGjFE,105
82
- pysdmx/io/xml/__parse_xml.py,sha256=mGJMd_e2KWr-X8OtE1dB_CHBtqhMIuMpJwtvUmEX9dk,2137
82
+ pysdmx/io/xml/__parse_xml.py,sha256=SITC7Yptq_qY_exetRez90lJCukH1WsKDMqlk7Q1RaY,2899
83
83
  pysdmx/io/xml/__ss_aux_reader.py,sha256=Tfp7oSb_0-AVEVdu7YFtj-SlBuEKp7fpg3-SxCmOHW0,2868
84
- pysdmx/io/xml/__structure_aux_reader.py,sha256=zCe6-tjNxsTneQcPLodHC91AbLF7wj1CFdRkVRhyPrY,37837
85
- pysdmx/io/xml/__structure_aux_writer.py,sha256=UbTMG3dwFYBdpSxykSOtuQidsTFioZ19izfBp_kjQr8,40162
86
- pysdmx/io/xml/__tokens.py,sha256=5r2GnXX4M43Z33OEV9LwNLafTJ_T6zwZBxmzaqgG1TE,6700
87
- pysdmx/io/xml/__write_aux.py,sha256=hgts820JRF87T28J8mCHp4qpDPwCuEi5izDti1mvnOo,14109
88
- pysdmx/io/xml/__write_data_aux.py,sha256=ypq0SO_bFio5YWIsjIEfXwLU36mGHjAbRZJltVRD8lU,4277
89
- pysdmx/io/xml/__write_structure_specific_aux.py,sha256=gj-q7NjsInV6-3vos2tDQyb9hG5WZGhYqLGiZ5cQu2E,7605
84
+ pysdmx/io/xml/__structure_aux_reader.py,sha256=guK6hEi2TN-6csc6JMeXbW4JDHKA6pKy6GoFzQ6FNJE,40276
85
+ pysdmx/io/xml/__structure_aux_writer.py,sha256=mXsURMFJuaku6Ac-7nWOrerxfPfTVtQxzBwjRgZeQu8,43588
86
+ pysdmx/io/xml/__tokens.py,sha256=Y_SlFWYIXolsgjHoFq7pm3TYlEu07CR9TmJKk2jHfQ0,6823
87
+ pysdmx/io/xml/__write_aux.py,sha256=KHZxEhGSjZPRW93OvniZ0kHmiAJtKJu-2aLtMhsKt90,15396
88
+ pysdmx/io/xml/__write_data_aux.py,sha256=DlFwR1KmVMJmwID_G7Tl9zasah9iiQKHuuHKJpd0huk,5052
89
+ pysdmx/io/xml/__write_structure_specific_aux.py,sha256=Z5CIiy30TxK8igzcnwcruhGdohFxJaFyYyCojMU8nnY,8825
90
90
  pysdmx/io/xml/config.py,sha256=R24cczVkzkhjVLXpv-qfEm88W3_QTqVt2Qofi8IvJ5Y,93
91
- pysdmx/io/xml/doc_validation.py,sha256=O5p5J9o-41I245JR91fzG6k_tI-8Y9EuIwdVluFms3s,1624
91
+ pysdmx/io/xml/doc_validation.py,sha256=qS-riLo3fMNLBrsQWzizeVNePuYT9W8wp9q-xE3QXto,1842
92
92
  pysdmx/io/xml/header.py,sha256=_XiruPAfMtr4wudCpiuhFXDvRgWITGQ9GZVG3lMvWcc,5535
93
93
  pysdmx/io/xml/sdmx21/__init__.py,sha256=_rh_dz1d-LmUw4iVbsZQkTXANTJeR8ov8z_4jNRGhtg,38
94
94
  pysdmx/io/xml/sdmx21/reader/__init__.py,sha256=2jMsySFbbq-So3ymqzKVi5gOPRwO0qasBBwntD03rTw,34
95
95
  pysdmx/io/xml/sdmx21/reader/error.py,sha256=ySY3dBPcOaF_lm8yX1Khc92R5XgyD1u-djjiOrgZJP8,885
96
- pysdmx/io/xml/sdmx21/reader/generic.py,sha256=uGZtqf44NqsZj7vjoE-eE5GBNJKEPmE9i72E9Ihu8Y4,5073
96
+ pysdmx/io/xml/sdmx21/reader/generic.py,sha256=IrdWLVsBsfa8lCG9jtzWc0WzsIemTUWEDLjiwu6P-NA,6086
97
97
  pysdmx/io/xml/sdmx21/reader/structure.py,sha256=bR3rCfY4x5xxkcyLhDSI-6Vw7b52RJY2qCcqr5MBuD4,1088
98
98
  pysdmx/io/xml/sdmx21/reader/structure_specific.py,sha256=S3-gLmaBFjBRIr25qQtlraonKm9XAlvHgUVtdFHEFdM,1158
99
99
  pysdmx/io/xml/sdmx21/reader/submission.py,sha256=8daiBW-sIVGaB6lYwHqJNkLI7IixMSydCK-0ZO8ri4I,1711
100
100
  pysdmx/io/xml/sdmx21/writer/__init__.py,sha256=QQGFAss26njCC4eKYxhBcI9LYm5NHuJaAJGKCrIrL80,31
101
101
  pysdmx/io/xml/sdmx21/writer/error.py,sha256=0wkX7K_n2oZNkOKg_zpl9Id82qP72Lqof-T-ZLGoZ1M,353
102
- pysdmx/io/xml/sdmx21/writer/generic.py,sha256=cnzuawsb5SvzFZZmhiJb8DQPV8tNiH2UowWYGN0DZgc,13750
102
+ pysdmx/io/xml/sdmx21/writer/generic.py,sha256=I5PQCJvoRMHtbr7i2h27NinW4-to6x6jB0ZNIAh9r9g,15722
103
103
  pysdmx/io/xml/sdmx21/writer/structure.py,sha256=S3qoNgXxrakn2V4NLdL5U5mAA16XisI0PpJDuxqalFE,2084
104
104
  pysdmx/io/xml/sdmx21/writer/structure_specific.py,sha256=iXc1J-RzoKyRznvgGgdTSeUfyqZLouI8CtSq2YhGBWI,2877
105
105
  pysdmx/io/xml/sdmx30/__init__.py,sha256=8BScJFEgLy8DoUreu2RBUtxjGjKyClkKBI_Qtarbk-Y,38
@@ -109,13 +109,20 @@ pysdmx/io/xml/sdmx30/reader/structure_specific.py,sha256=BYZmNjGKGTMZLoE28XuYof8
109
109
  pysdmx/io/xml/sdmx30/writer/__init__.py,sha256=cfCVmf82PNRe9A5RYmuKfvxDTSNcsySlJwCBbKfY_O0,31
110
110
  pysdmx/io/xml/sdmx30/writer/structure.py,sha256=T4Ety741N0P648lSN0WszhX9CztboAnWTtahSaeBts4,2105
111
111
  pysdmx/io/xml/sdmx30/writer/structure_specific.py,sha256=ftNFO4RIJnyC-ZmU7SrIhjbJUJPzebDHMUMKyw9QPc4,3236
112
+ pysdmx/io/xml/sdmx31/__init__.py,sha256=OpvPM3qa8v5X6iKP7wqSBOH10R0W6lL5X4rKiJNAWUg,38
113
+ pysdmx/io/xml/sdmx31/reader/__init__.py,sha256=DPY_uGHiMECGIK4fKSQKneltqlbFGGVxpO_tllX0yOQ,34
114
+ pysdmx/io/xml/sdmx31/reader/structure.py,sha256=PJ50N_2cH6YtlADRL8zlzMm_rRSI0bJDaVJDorFvWFU,1103
115
+ pysdmx/io/xml/sdmx31/reader/structure_specific.py,sha256=hX9WSfAlGTvUu4i-MokyMkg9y0r6F8GeiQrj87IcgIs,1158
116
+ pysdmx/io/xml/sdmx31/writer/__init__.py,sha256=wSWTfbD6bBh-gsdn1Z2kyIa5YXq3WEfn-u7ZYxJvjT0,31
117
+ pysdmx/io/xml/sdmx31/writer/structure.py,sha256=hK-BYOsyyuqLTqxe6qC3PxeDJDp3_qyBTqGUfpVjTl8,2105
118
+ pysdmx/io/xml/sdmx31/writer/structure_specific.py,sha256=OtP_uxn-y9tVB9eNRP6ov3zBL1wtr4SRef9llB4A3yk,3236
112
119
  pysdmx/io/xml/utils.py,sha256=ljrocQwPTYycg3I68sqYI5pF8qukNccSCMM9Xf_Tag0,583
113
120
  pysdmx/model/__base.py,sha256=H5OpCpi6fGxB30vFn-lCstTczSdOB4YJiBynThovQs4,13810
114
121
  pysdmx/model/__init__.py,sha256=gmY8uGK0_SK9Ca5TqtYJqPSXvO9godlDKu2rhYouPSU,4984
115
122
  pysdmx/model/category.py,sha256=WWWysaTdlcU8KZpQyZgbZkrOelNVCGOfVUk_APJxm-w,6031
116
- pysdmx/model/code.py,sha256=otfn9WwSE6bBQeHia9T0A5WOQ6N37K3s4DbnZ5hywVM,11950
123
+ pysdmx/model/code.py,sha256=KEG2D74UHbZuOplSrztLWCNTzFTOg22imRe_eQvqAM8,12600
117
124
  pysdmx/model/concept.py,sha256=mQfqJdtWc10WdTKX_Mw7Znw65cN3QO-kCar9MWYeWO4,9645
118
- pysdmx/model/dataflow.py,sha256=Cqi_dJ-Mba0s24Dj8tAsQApcezf6kpomI1KMyX_8kgg,22633
125
+ pysdmx/model/dataflow.py,sha256=9SgbQ9n2j5N_TqvN9eQtshL4Lom4kDnapP97cREP-e4,22999
119
126
  pysdmx/model/dataset.py,sha256=Lbr7tYonGHD3NZUD-M9hK2puaEAluOVPG2DbkOohzMM,4861
120
127
  pysdmx/model/gds.py,sha256=QrnmI8Hn--C95gGXCeUeWwhn-Ur7DuT08Cg7oPJIEVI,4976
121
128
  pysdmx/model/map.py,sha256=IYoxKYpt0-9kYAgU24wryrSchR-lrc3NMacI5Vt5las,17213
@@ -131,10 +138,11 @@ pysdmx/toolkit/vtl/__init__.py,sha256=nVwtISeFiT4tr2gNjb2A-P_IRAdqv6Mn7EOgFkaRle
131
138
  pysdmx/toolkit/vtl/_validations.py,sha256=seDf83sodg9Jn7Y-L4vo2Zmfqyh_ivBLdH7tz1DjyO4,5677
132
139
  pysdmx/toolkit/vtl/script_generation.py,sha256=YOP_-MMcIdDIHYa8HIMuVCN9N-S9CQD8bH5xpF1kc2E,3235
133
140
  pysdmx/toolkit/vtl/validation.py,sha256=UieJUYwxkw9McHZwixKFQdgYKFsgFwFo5XCLIIDcr7Q,3594
134
- pysdmx/util/__init__.py,sha256=6sSo9wq6BQIrCV1aaeNj8AWztxqsEkDZA-pRAFzwmUQ,3921
141
+ pysdmx/util/__init__.py,sha256=m_XWRAmVJ7F6ai4Ckrj_YuPbhg3cJZAXeZrEThrL88k,3997
135
142
  pysdmx/util/_date_pattern_map.py,sha256=IS1qONwVHbTBNIFCT0Rqbijj2a9mYvs7onXSK6GeQAQ,1620
136
- pysdmx/util/_model_utils.py,sha256=gczmenZBkcCTYMOUI4HQeKxodVFcD_RFZEoEYC05XRc,2304
137
- pysdmx-1.4.0rc1.dist-info/LICENSE,sha256=3XTNDPtv2RxDUNkQzn9MIWit2u7_Ob5daMLEq-4pBJs,649
138
- pysdmx-1.4.0rc1.dist-info/METADATA,sha256=pSA1Qo5hZUv1fbM7tk02cWZorUp1byuhTMjFd1bxt5s,4693
139
- pysdmx-1.4.0rc1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
140
- pysdmx-1.4.0rc1.dist-info/RECORD,,
143
+ pysdmx/util/_model_utils.py,sha256=d0XY8cnxvviQtkJJInVik7LWeuqX-eb4-zikFortL58,2335
144
+ pysdmx/util/_net_utils.py,sha256=nOTz_x3FgFrwKh42_J70IqYXz9duQkMFJWtejZXcLHs,1326
145
+ pysdmx-1.5.0.dist-info/LICENSE,sha256=3XTNDPtv2RxDUNkQzn9MIWit2u7_Ob5daMLEq-4pBJs,649
146
+ pysdmx-1.5.0.dist-info/METADATA,sha256=K7v8g6s1ooDheCBRC4DaAg68MA3mixhIGlw3cnAP2gc,4690
147
+ pysdmx-1.5.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
148
+ pysdmx-1.5.0.dist-info/RECORD,,