pysdmx 1.2.2__tar.gz → 1.4.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 (153) hide show
  1. pysdmx-1.4.0/PKG-INFO +119 -0
  2. pysdmx-1.4.0/README.rst +78 -0
  3. {pysdmx-1.2.2 → pysdmx-1.4.0}/pyproject.toml +5 -4
  4. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/__extras_check.py +3 -2
  5. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/__init__.py +1 -1
  6. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/fmr/__init__.py +4 -4
  7. pysdmx-1.4.0/src/pysdmx/api/gds/__init__.py +328 -0
  8. pysdmx-1.4.0/src/pysdmx/api/qb/gds.py +153 -0
  9. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/refmeta.py +33 -0
  10. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/service.py +91 -3
  11. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/structure.py +50 -0
  12. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/util.py +1 -0
  13. pysdmx-1.4.0/src/pysdmx/io/__init__.py +6 -0
  14. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx10/reader/__init__.py +4 -2
  15. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx10/writer/__init__.py +15 -2
  16. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx20/reader/__init__.py +5 -2
  17. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx20/writer/__init__.py +13 -2
  18. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/format.py +4 -0
  19. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/input_processor.py +12 -3
  20. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/core.py +2 -0
  21. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/report.py +13 -7
  22. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/vtl.py +9 -1
  23. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/__init__.py +35 -0
  24. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/agencies.py +41 -0
  25. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/catalog.py +79 -0
  26. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/sdmx_api.py +23 -0
  27. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/services.py +49 -0
  28. pysdmx-1.4.0/src/pysdmx/io/json/gds/messages/urn_resolver.py +43 -0
  29. pysdmx-1.4.0/src/pysdmx/io/json/gds/reader/__init__.py +12 -0
  30. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/__init__.py +12 -4
  31. pysdmx-1.4.0/src/pysdmx/io/json/sdmxjson2/messages/agency.py +72 -0
  32. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/category.py +22 -29
  33. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/code.py +68 -64
  34. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/concept.py +9 -18
  35. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +2 -13
  36. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/core.py +113 -21
  37. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/dataflow.py +51 -21
  38. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/dsd.py +110 -36
  39. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/map.py +61 -49
  40. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/pa.py +9 -17
  41. pysdmx-1.4.0/src/pysdmx/io/json/sdmxjson2/messages/provider.py +88 -0
  42. pysdmx-1.4.0/src/pysdmx/io/json/sdmxjson2/messages/report.py +102 -0
  43. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/schema.py +14 -5
  44. pysdmx-1.4.0/src/pysdmx/io/json/sdmxjson2/messages/structure.py +140 -0
  45. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/messages/vtl.py +55 -99
  46. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/pd.py +2 -9
  47. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/reader.py +76 -51
  48. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/serde.py +11 -0
  49. pysdmx-1.4.0/src/pysdmx/io/writer.py +134 -0
  50. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader → pysdmx-1.4.0/src/pysdmx/io/xml}/__data_aux.py +9 -2
  51. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader → pysdmx-1.4.0/src/pysdmx/io/xml}/__parse_xml.py +30 -6
  52. pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader/structure_specific.py → pysdmx-1.4.0/src/pysdmx/io/xml/__ss_aux_reader.py +13 -40
  53. pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader/structure.py → pysdmx-1.4.0/src/pysdmx/io/xml/__structure_aux_reader.py +465 -123
  54. pysdmx-1.4.0/src/pysdmx/io/xml/__structure_aux_writer.py +1233 -0
  55. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21 → pysdmx-1.4.0/src/pysdmx/io/xml}/__tokens.py +40 -1
  56. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/writer → pysdmx-1.4.0/src/pysdmx/io/xml}/__write_aux.py +137 -42
  57. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/writer → pysdmx-1.4.0/src/pysdmx/io/xml}/__write_data_aux.py +1 -1
  58. pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/writer/structure_specific.py → pysdmx-1.4.0/src/pysdmx/io/xml/__write_structure_specific_aux.py +23 -87
  59. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader → pysdmx-1.4.0/src/pysdmx/io/xml}/doc_validation.py +10 -2
  60. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/reader → pysdmx-1.4.0/src/pysdmx/io/xml}/header.py +42 -7
  61. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/reader/error.py +2 -2
  62. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/reader/generic.py +12 -8
  63. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx21/reader/structure.py +39 -0
  64. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx21/reader/structure_specific.py +39 -0
  65. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/reader/submission.py +2 -2
  66. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/writer/error.py +1 -1
  67. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/writer/generic.py +13 -7
  68. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx21/writer/structure.py +66 -0
  69. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx21/writer/structure_specific.py +93 -0
  70. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/__init__.py +1 -0
  71. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/reader/__init__.py +1 -0
  72. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/reader/structure.py +39 -0
  73. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/reader/structure_specific.py +39 -0
  74. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/writer/__init__.py +1 -0
  75. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/writer/structure.py +67 -0
  76. pysdmx-1.4.0/src/pysdmx/io/xml/sdmx30/writer/structure_specific.py +108 -0
  77. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/__base.py +99 -34
  78. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/__init__.py +4 -0
  79. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/category.py +20 -0
  80. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/code.py +29 -8
  81. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/concept.py +52 -11
  82. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/dataflow.py +117 -33
  83. pysdmx-1.4.0/src/pysdmx/model/dataset.py +133 -0
  84. pysdmx-1.4.0/src/pysdmx/model/gds.py +161 -0
  85. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/map.py +51 -8
  86. pysdmx-1.4.0/src/pysdmx/model/message.py +384 -0
  87. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/metadata.py +79 -16
  88. pysdmx-1.4.0/src/pysdmx/model/submission.py +25 -0
  89. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/vtl.py +36 -16
  90. pysdmx-1.4.0/src/pysdmx/toolkit/__init__.py +1 -0
  91. pysdmx-1.4.0/src/pysdmx/toolkit/pd/__init__.py +85 -0
  92. pysdmx-1.4.0/src/pysdmx/toolkit/vtl/__init__.py +6 -0
  93. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/toolkit/vtl/_validations.py +1 -1
  94. pysdmx-1.2.2/src/pysdmx/toolkit/vtl/generate_vtl_script.py → pysdmx-1.4.0/src/pysdmx/toolkit/vtl/script_generation.py +30 -4
  95. pysdmx-1.4.0/src/pysdmx/toolkit/vtl/validation.py +119 -0
  96. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/util/__init__.py +42 -5
  97. pysdmx-1.4.0/src/pysdmx/util/_model_utils.py +58 -0
  98. pysdmx-1.2.2/PKG-INFO +0 -76
  99. pysdmx-1.2.2/README.rst +0 -35
  100. pysdmx-1.2.2/src/pysdmx/io/__init__.py +0 -5
  101. pysdmx-1.2.2/src/pysdmx/io/json/sdmxjson2/messages/org.py +0 -140
  102. pysdmx-1.2.2/src/pysdmx/io/json/sdmxjson2/messages/report.py +0 -32
  103. pysdmx-1.2.2/src/pysdmx/io/json/sdmxjson2/messages/structure.py +0 -71
  104. pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/writer/structure.py +0 -840
  105. pysdmx-1.2.2/src/pysdmx/model/dataset.py +0 -81
  106. pysdmx-1.2.2/src/pysdmx/model/message.py +0 -204
  107. pysdmx-1.2.2/src/pysdmx/model/submission.py +0 -20
  108. pysdmx-1.2.2/src/pysdmx/toolkit/__init__.py +0 -1
  109. pysdmx-1.2.2/src/pysdmx/toolkit/vtl/__init__.py +0 -5
  110. pysdmx-1.2.2/src/pysdmx/toolkit/vtl/model_validations.py +0 -50
  111. {pysdmx-1.2.2 → pysdmx-1.4.0}/LICENSE +0 -0
  112. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/__init__.py +0 -0
  113. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/__init__.py +0 -0
  114. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/_api.py +0 -0
  115. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/__init__.py +0 -0
  116. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/_model.py +0 -0
  117. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/_parsing_model.py +0 -0
  118. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/_parsing_util.py +0 -0
  119. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/_py_parser.py +0 -0
  120. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/_sql_parser.py +0 -0
  121. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/dc/query/util.py +0 -0
  122. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/__init__.py +0 -0
  123. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/availability.py +0 -0
  124. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/data.py +0 -0
  125. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/registration.py +0 -0
  126. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/api/qb/schema.py +0 -0
  127. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/errors.py +0 -0
  128. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/__init__.py +0 -0
  129. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx10/__init__.py +0 -0
  130. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/csv/sdmx20/__init__.py +0 -0
  131. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/__init__.py +0 -0
  132. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/category.py +0 -0
  133. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/code.py +0 -0
  134. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/concept.py +0 -0
  135. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/constraint.py +0 -0
  136. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/dataflow.py +0 -0
  137. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/dsd.py +0 -0
  138. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/map.py +0 -0
  139. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/org.py +0 -0
  140. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/pa.py +0 -0
  141. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/messages/schema.py +0 -0
  142. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/fusion/reader/__init__.py +0 -0
  143. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/json/sdmxjson2/reader/__init__.py +0 -0
  144. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/__allowed_lxml_errors.py +0 -0
  145. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/__init__.py +0 -0
  146. {pysdmx-1.2.2/src/pysdmx/io/xml/sdmx21/writer → pysdmx-1.4.0/src/pysdmx/io/xml}/config.py +0 -0
  147. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/__init__.py +0 -0
  148. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/reader/__init__.py +0 -0
  149. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/sdmx21/writer/__init__.py +0 -0
  150. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/io/xml/utils.py +0 -0
  151. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/model/organisation.py +0 -0
  152. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/py.typed +0 -0
  153. {pysdmx-1.2.2 → pysdmx-1.4.0}/src/pysdmx/util/_date_pattern_map.py +0 -0
pysdmx-1.4.0/PKG-INFO ADDED
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.3
2
+ Name: pysdmx
3
+ Version: 1.4.0
4
+ Summary: Your opinionated Python SDMX library
5
+ License: Apache-2.0
6
+ Keywords: sdmx,data discovery,data retrieval,metadata,fmr
7
+ Author: Xavier Sosnovsky
8
+ Author-email: <xavier.sosnovsky@bis.org>
9
+ Requires-Python: >=3.9
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Typing :: Typed
15
+ Provides-Extra: all
16
+ Provides-Extra: data
17
+ Provides-Extra: dc
18
+ Provides-Extra: vtl
19
+ Provides-Extra: xml
20
+ Requires-Dist: httpx (>=0)
21
+ Requires-Dist: lxml (>=5.2) ; extra == "all"
22
+ Requires-Dist: lxml (>=5.2) ; extra == "xml"
23
+ Requires-Dist: msgspec (>=0)
24
+ Requires-Dist: pandas (>=2.1.4) ; extra == "all"
25
+ Requires-Dist: pandas (>=2.1.4) ; extra == "data"
26
+ Requires-Dist: parsy (>=2.1)
27
+ Requires-Dist: python-dateutil (>=2.8.2) ; extra == "all"
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"
31
+ Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "all"
32
+ Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "vtl"
33
+ Requires-Dist: xmltodict (>=0.13) ; extra == "all"
34
+ Requires-Dist: xmltodict (>=0.13) ; extra == "xml"
35
+ Project-URL: Bug Tracker, https://bis-med-it.github.io/pysdmx/issues
36
+ Project-URL: Documentation, https://bis-med-it.github.io/pysdmx
37
+ Project-URL: Homepage, https://sdmx.io/tools/pysdmx
38
+ Project-URL: Repository, https://github.com/bis-med-it/pysdmx
39
+ Description-Content-Type: text/x-rst
40
+
41
+ .. |pypi badge| image:: https://img.shields.io/pypi/v/pysdmx.svg
42
+ :target: https://pypi.org/project/pysdmx/
43
+
44
+ .. |awesome badge| image:: https://awesome.re/mentioned-badge.svg
45
+ :target: http://www.awesomeofficialstatistics.org
46
+
47
+ |pypi badge| |awesome badge|
48
+
49
+ ``pysdmx`` in a nutshell
50
+ ************************
51
+
52
+ What is pysdmx?
53
+ ===============
54
+
55
+ ``pysdmx`` is a **pragmatic** and **opinionated** SDMX library written in
56
+ **Python**. It focuses on **simplicity**, providing a subset of SDMX functionalities
57
+ without requiring advanced knowledge of SDMX. ``pysdmx`` is developed as part of
58
+ the `sdmx.io <http://sdmx.io/>`_ project under the **BIS Open Tech initiative**.
59
+
60
+ What does it do?
61
+ ================
62
+
63
+ ``pysdmx`` aspires to be a versatile SDMX toolbox for Python, covering various
64
+ use cases. Here are some highlights:
65
+
66
+ SDMX information model in Python
67
+ --------------------------------
68
+
69
+ ``pysdmx`` offers Python classes representing a **simplified subset of the SDMX
70
+ information model**, enabling a domain-driven development of SDMX processes in
71
+ Python. The model classes support serialization in formats like JSON, YAML, or
72
+ MessagePack. This functionality relies on the
73
+ `msgspec library <https://jcristharif.com/msgspec/>`_.
74
+
75
+ Metadata in action
76
+ ------------------
77
+
78
+ SDMX metadata are very useful for documenting statistical processes. For example,
79
+ they can define the structure we expect for a data collection process and share
80
+ it with the organizations providing data so that they know what to send.
81
+
82
+ However, metadata can do so much more than that, i.e., they can be “active” and
83
+ **drive various types of statistical processes**, such as generating the filesystem
84
+ layout, creating the physical data model, validating data, mapping data, and
85
+ configuring processes. To drive such processes, ``pysdmx`` supports retrieving
86
+ metadata from an SDMX Registry or any service compliant with the SDMX-REST 2.0.0 (or
87
+ above) API. Use these metadata to power your own statistical processes!
88
+
89
+ Reading and writing SDMX files
90
+ ------------------------------
91
+
92
+ ``pysdmx`` supports reading and writing SDMX data and structure messages, in various
93
+ formats, such as SDMX-CSV, SDMX-JSON, and SDMX-ML.
94
+
95
+ Data discovery and data retrieval
96
+ ---------------------------------
97
+
98
+ This functionality is under development. Once ready, ``pysdmx`` will allow:
99
+
100
+ - **Listing public SDMX services**.
101
+ - **Discovering data** available in these services.
102
+ - **Retrieving data** from these services.
103
+
104
+ This functionality is based on the **SDMX Global Discovery Service initiative**.
105
+
106
+ Integration with the ecosystem
107
+ ------------------------------
108
+
109
+ ``pysdmx`` integrates nicely with other standards, like the `Validation and
110
+ Transformation Language (VTL) <https://sdmx.org/about-sdmx/about-vtl/>`_,
111
+ and major Python libraries like `Pandas <https://pandas.pydata.org/>`_.
112
+ Take a look at the ``pysdmx`` toolkit module to learn more.
113
+
114
+ ``pysdmx`` is available on `PyPI <https://pypi.org/>`_ and can be
115
+ installed using options such as pip, pipx, poetry, etc.
116
+
117
+ For more details, check the `project documentation
118
+ <https://py.sdmx.io>`_.
119
+
@@ -0,0 +1,78 @@
1
+ .. |pypi badge| image:: https://img.shields.io/pypi/v/pysdmx.svg
2
+ :target: https://pypi.org/project/pysdmx/
3
+
4
+ .. |awesome badge| image:: https://awesome.re/mentioned-badge.svg
5
+ :target: http://www.awesomeofficialstatistics.org
6
+
7
+ |pypi badge| |awesome badge|
8
+
9
+ ``pysdmx`` in a nutshell
10
+ ************************
11
+
12
+ What is pysdmx?
13
+ ===============
14
+
15
+ ``pysdmx`` is a **pragmatic** and **opinionated** SDMX library written in
16
+ **Python**. It focuses on **simplicity**, providing a subset of SDMX functionalities
17
+ without requiring advanced knowledge of SDMX. ``pysdmx`` is developed as part of
18
+ the `sdmx.io <http://sdmx.io/>`_ project under the **BIS Open Tech initiative**.
19
+
20
+ What does it do?
21
+ ================
22
+
23
+ ``pysdmx`` aspires to be a versatile SDMX toolbox for Python, covering various
24
+ use cases. Here are some highlights:
25
+
26
+ SDMX information model in Python
27
+ --------------------------------
28
+
29
+ ``pysdmx`` offers Python classes representing a **simplified subset of the SDMX
30
+ information model**, enabling a domain-driven development of SDMX processes in
31
+ Python. The model classes support serialization in formats like JSON, YAML, or
32
+ MessagePack. This functionality relies on the
33
+ `msgspec library <https://jcristharif.com/msgspec/>`_.
34
+
35
+ Metadata in action
36
+ ------------------
37
+
38
+ SDMX metadata are very useful for documenting statistical processes. For example,
39
+ they can define the structure we expect for a data collection process and share
40
+ it with the organizations providing data so that they know what to send.
41
+
42
+ However, metadata can do so much more than that, i.e., they can be “active” and
43
+ **drive various types of statistical processes**, such as generating the filesystem
44
+ layout, creating the physical data model, validating data, mapping data, and
45
+ configuring processes. To drive such processes, ``pysdmx`` supports retrieving
46
+ metadata from an SDMX Registry or any service compliant with the SDMX-REST 2.0.0 (or
47
+ above) API. Use these metadata to power your own statistical processes!
48
+
49
+ Reading and writing SDMX files
50
+ ------------------------------
51
+
52
+ ``pysdmx`` supports reading and writing SDMX data and structure messages, in various
53
+ formats, such as SDMX-CSV, SDMX-JSON, and SDMX-ML.
54
+
55
+ Data discovery and data retrieval
56
+ ---------------------------------
57
+
58
+ This functionality is under development. Once ready, ``pysdmx`` will allow:
59
+
60
+ - **Listing public SDMX services**.
61
+ - **Discovering data** available in these services.
62
+ - **Retrieving data** from these services.
63
+
64
+ This functionality is based on the **SDMX Global Discovery Service initiative**.
65
+
66
+ Integration with the ecosystem
67
+ ------------------------------
68
+
69
+ ``pysdmx`` integrates nicely with other standards, like the `Validation and
70
+ Transformation Language (VTL) <https://sdmx.org/about-sdmx/about-vtl/>`_,
71
+ and major Python libraries like `Pandas <https://pandas.pydata.org/>`_.
72
+ Take a look at the ``pysdmx`` toolkit module to learn more.
73
+
74
+ ``pysdmx`` is available on `PyPI <https://pypi.org/>`_ and can be
75
+ installed using options such as pip, pipx, poetry, etc.
76
+
77
+ For more details, check the `project documentation
78
+ <https://py.sdmx.io>`_.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pysdmx"
3
- version = "1.2.2"
3
+ version = "1.4.0"
4
4
  description = "Your opinionated Python SDMX library"
5
5
  license = { text = "Apache-2.0" }
6
6
  readme = "README.rst"
@@ -9,7 +9,8 @@ authors = [
9
9
  { name = "Xavier Sosnovsky", email = "<xavier.sosnovsky@bis.org>" },
10
10
  { name = "Stratos Nikoloutsos", email = "<stratos.nikoloutsos@bis.org>" },
11
11
  { name = "Francisco Javier Hernandez del Caño", email = "<javier.hernandez@meaningfuldata.eu>" },
12
- { name = "Mateo de Lorenzo Argelés", email = "<mateo.delorenzo@meaningfuldata.eu>" }
12
+ { name = "Mateo de Lorenzo Argelés", email = "<mateo.delorenzo@meaningfuldata.eu>" },
13
+ { name = "Jose Antonio Franco Martin", email = "<jose.franco@meaningfuldata.eu>" }
13
14
  ]
14
15
  keywords = ["sdmx", "data discovery", "data retrieval", "metadata", "fmr"]
15
16
  classifiers = [
@@ -34,9 +35,9 @@ documentation = "https://bis-med-it.github.io/pysdmx"
34
35
  [project.optional-dependencies]
35
36
  data = ["pandas>=2.1.4"]
36
37
  dc = ["python-dateutil>=2.8.2"]
37
- vtl = ["vtlengine>=1.1rc1"]
38
+ vtl = ["vtlengine>=1.1,<2.0"]
38
39
  xml = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=0.2.0"]
39
- all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=0.2.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.1rc1"]
40
+ all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=0.2.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.1,<2.0"]
40
41
 
41
42
  [tool.poetry]
42
43
  requires-poetry = ">=2.0"
@@ -50,7 +50,8 @@ def __check_vtl_extra() -> None:
50
50
  except ImportError:
51
51
  raise ImportError(
52
52
  ERROR_MESSAGE.format(
53
- extra_name="vtlengine",
54
- extra_desc="validations for VTL engine",
53
+ extra_name="vtl",
54
+ extra_desc="VTL Scripts, SDMX-VTL model validations"
55
+ " and prettify",
55
56
  )
56
57
  ) from None
@@ -1,3 +1,3 @@
1
1
  """Your opinionated Python SDMX library."""
2
2
 
3
- __version__ = "1.2.2"
3
+ __version__ = "1.4.0"
@@ -642,7 +642,7 @@ class RegistryClient(__BaseRegistryClient):
642
642
  """
643
643
  query = super()._report_q(provider, id, version)
644
644
  out = self.__fetch(query)
645
- return super()._out(out, self.deser.report)[0]
645
+ return super()._out(out, self.deser.report).reports[0]
646
646
 
647
647
  def get_reports(
648
648
  self,
@@ -667,7 +667,7 @@ class RegistryClient(__BaseRegistryClient):
667
667
  """
668
668
  query = super()._reports_q(artefact_type, agency, id, version)
669
669
  out = self.__fetch(query)
670
- return super()._out(out, self.deser.report, True)
670
+ return super()._out(out, self.deser.report).reports
671
671
 
672
672
  def get_mapping(
673
673
  self,
@@ -1104,7 +1104,7 @@ class AsyncRegistryClient(__BaseRegistryClient):
1104
1104
  """
1105
1105
  query = super()._report_q(provider, id, version)
1106
1106
  out = await self.__fetch(query)
1107
- return super()._out(out, self.deser.report)[0]
1107
+ return super()._out(out, self.deser.report).reports[0]
1108
1108
 
1109
1109
  async def get_reports(
1110
1110
  self,
@@ -1129,7 +1129,7 @@ class AsyncRegistryClient(__BaseRegistryClient):
1129
1129
  """
1130
1130
  query = super()._reports_q(artefact_type, agency, id, version)
1131
1131
  out = await self.__fetch(query)
1132
- return super()._out(out, self.deser.report, True)
1132
+ return super()._out(out, self.deser.report).reports
1133
1133
 
1134
1134
  async def get_mapping(
1135
1135
  self,
@@ -0,0 +1,328 @@
1
+ """API client for interacting with the GDS (Global Discovery Service) service.
2
+
3
+ This module provides classes and utilities to interact with the GDS service,
4
+ allowing retrieval of metadata such as agency information in SDMX-JSON format.
5
+
6
+ Exports: GdsClient: A synchronous client for retrieving metadata from
7
+ the GDS.
8
+ """
9
+
10
+ from typing import Any, Literal, Optional, Sequence
11
+
12
+ from msgspec.json import decode
13
+
14
+ from pysdmx.api.qb.gds import GdsQuery, GdsType
15
+ from pysdmx.api.qb.service import GdsAsyncRestService, GdsRestService
16
+ from pysdmx.api.qb.util import REST_ALL
17
+ from pysdmx.io.json.gds.reader import deserializers as gds_readers
18
+ from pysdmx.io.serde import Deserializer
19
+ from pysdmx.model import Agency
20
+ from pysdmx.model.gds import (
21
+ GdsCatalog,
22
+ GdsSdmxApi,
23
+ GdsService,
24
+ GdsUrnResolver,
25
+ )
26
+
27
+ GDS_BASE_ENDPOINT = "https://gds.sdmx.io/"
28
+
29
+ READERS = gds_readers
30
+
31
+
32
+ class __BaseGdsClient:
33
+ def __init__(
34
+ self,
35
+ api_endpoint: str = GDS_BASE_ENDPOINT,
36
+ pem: Optional[str] = None,
37
+ ):
38
+ """Instantiate a new client against the target endpoint."""
39
+ if api_endpoint.endswith("/"):
40
+ api_endpoint = api_endpoint[0:-1]
41
+ self.api_endpoint = api_endpoint
42
+ self.reader = READERS
43
+
44
+ def _out(self, response: bytes, typ: Deserializer, *params: Any) -> Any:
45
+ return decode(response, type=typ).to_model(*params)
46
+
47
+ def _agencies_q(self, agency: str) -> GdsQuery:
48
+ return GdsQuery(artefact_type=GdsType.GDS_AGENCY, resource_id=agency)
49
+
50
+ def _catalogs_q(
51
+ self,
52
+ agency: str,
53
+ resource: str = REST_ALL,
54
+ version: str = REST_ALL,
55
+ resource_type: Optional[Literal["data", "metadata"]] = None,
56
+ message_format: Optional[Literal["json", "csv", "xml"]] = None,
57
+ api_version: Optional[str] = None,
58
+ detail: Optional[Literal["full", "raw"]] = None,
59
+ references: Optional[Literal["none", "children"]] = None,
60
+ ) -> GdsQuery:
61
+ return GdsQuery(
62
+ artefact_type=GdsType.GDS_CATALOG,
63
+ agency=agency,
64
+ resource_id=resource,
65
+ version=version,
66
+ resource_type=resource_type,
67
+ message_format=message_format,
68
+ api_version=api_version,
69
+ detail=detail,
70
+ references=references,
71
+ )
72
+
73
+ def _sdmx_api_q(self, id: str = REST_ALL) -> GdsQuery:
74
+ return GdsQuery(artefact_type=GdsType.GDS_SDMX_API, resource_id=id)
75
+
76
+ def _services_q(
77
+ self, agency: str, resource: str = REST_ALL, version: str = REST_ALL
78
+ ) -> GdsQuery:
79
+ return GdsQuery(
80
+ artefact_type=GdsType.GDS_SERVICE,
81
+ agency=agency,
82
+ resource_id=resource,
83
+ version=version,
84
+ )
85
+
86
+ def _urn_resolver_q(self, urn: str) -> GdsQuery:
87
+ return GdsQuery(
88
+ artefact_type=GdsType.GDS_URN_RESOLVER, resource_id=urn
89
+ )
90
+
91
+
92
+ class GdsClient(__BaseGdsClient):
93
+ """A client to be used to retrieve metadata from the GDS.
94
+
95
+ With this client, metadata will be retrieved in a synchronous fashion.
96
+ """
97
+
98
+ def __init__(
99
+ self,
100
+ api_endpoint: str = GDS_BASE_ENDPOINT,
101
+ pem: Optional[str] = None,
102
+ ) -> None:
103
+ """Instantiate a new client against the target endpoint.
104
+
105
+ Args:
106
+ api_endpoint: The endpoint of the targeted service.
107
+ pem: In case the service exposed a certificate created
108
+ by an unknown certificate authority, you can pass
109
+ a pem file for this authority using this parameter.
110
+ """
111
+ super().__init__(api_endpoint, pem)
112
+ self.__service = GdsRestService(
113
+ self.api_endpoint,
114
+ pem=pem,
115
+ timeout=10.0,
116
+ )
117
+
118
+ def __fetch(
119
+ self,
120
+ query: GdsQuery,
121
+ ) -> bytes:
122
+ """Fetch the requested metadata from the GDS service."""
123
+ return self.__service.gds(query)
124
+
125
+ def get_agencies(self, agency: str) -> Sequence[Agency]:
126
+ """Get the list of agencies for the supplied name.
127
+
128
+ Args:
129
+ agency: The agency maintaining the agency scheme from
130
+ which sub-agencies must be returned.
131
+
132
+ Returns:
133
+ The requested list of agencies.
134
+ """
135
+ query = super()._agencies_q(agency)
136
+ out = self.__fetch(query)
137
+ agencies = super()._out(out, self.reader.agencies)
138
+ return agencies
139
+
140
+ def get_catalogs(
141
+ self,
142
+ catalog: str,
143
+ resource: str = REST_ALL,
144
+ version: str = REST_ALL,
145
+ resource_type: Optional[Literal["data", "metadata"]] = None,
146
+ message_format: Optional[Literal["json", "csv", "xml"]] = None,
147
+ api_version: Optional[str] = None,
148
+ detail: Optional[Literal["full", "raw"]] = None,
149
+ references: Optional[Literal["none", "children"]] = None,
150
+ ) -> Sequence[GdsCatalog]:
151
+ """Get the list of catalogs for the supplied parameters.
152
+
153
+ Args:
154
+ catalog: The agency maintaining the catalog.
155
+ resource: The resource ID(s) to query. Defaults to '*'.
156
+ version: The version(s) of the resource. Defaults to '*'.
157
+ resource_type: Filters the endpoints that support
158
+ the requested resource type (eg, 'data', 'metadata')
159
+ message_format: Filters the endpoints that support any
160
+ of the requested message formats.
161
+ api_version: Filters the endpoints that is in a
162
+ specific SDMX API version.
163
+ Multiple values separated by commas are possible.
164
+ By default (if nothing is sent) it returns everything.
165
+ detail: The amount of information to be returned.
166
+ If detail=full: All available information for all artefacts
167
+ should be returned.
168
+ If detail=raw: Any nested service will be referenced.
169
+ references: Instructs the web service to return
170
+ (or not) the artefacts referenced by the
171
+ artefact to be returned.
172
+ If references=none: No referenced artefacts will be returned.
173
+ If references=children: Returns the artefacts
174
+ referenced by the artefact to be returned.
175
+
176
+ Returns:
177
+ A list of GdsCatalog objects.
178
+ """
179
+ query = super()._catalogs_q(
180
+ catalog,
181
+ resource,
182
+ version,
183
+ resource_type,
184
+ message_format,
185
+ api_version,
186
+ detail,
187
+ references,
188
+ )
189
+ response = self.__fetch(query)
190
+ catalogs = super()._out(response, self.reader.catalogs)
191
+ return catalogs
192
+
193
+ def get_sdmx_apis(
194
+ self, api_version: str = REST_ALL
195
+ ) -> Sequence[GdsSdmxApi]:
196
+ """Get the list of SDMX API versions.
197
+
198
+ Args:
199
+ api_version: The version of the SDMX API to be returned.
200
+ Defaults to '*'.
201
+ """
202
+ query = super()._sdmx_api_q(api_version)
203
+ response = self.__fetch(query)
204
+ sdmx_api = super()._out(response, self.reader.sdmx_api)
205
+ return sdmx_api
206
+
207
+ def get_services(
208
+ self, service: str, resource: str = REST_ALL, version: str = REST_ALL
209
+ ) -> Sequence[GdsService]:
210
+ """Get the list of services for the supplied parameters.
211
+
212
+ Args:
213
+ service: The agency maintaining the service.
214
+ resource: The resource ID(s) to query. Defaults to '*'.
215
+ version: The version(s) of the resource. Defaults to '*'.
216
+
217
+ Returns:
218
+ A list of GdsService objects.
219
+ """
220
+ query = super()._services_q(service, resource, version)
221
+ response = self.__fetch(query)
222
+ services = super()._out(response, self.reader.services)
223
+ return services
224
+
225
+ def get_urn_resolver(self, urn: str) -> GdsUrnResolver:
226
+ """Resolve a URN to its corresponding resource.
227
+
228
+ Args:
229
+ urn: The URN to resolve.
230
+
231
+ Returns:
232
+ A GdsUrnResolver object with the resolved information.
233
+ """
234
+ query = super()._urn_resolver_q(urn)
235
+ response = self.__fetch(query)
236
+ urn_resolution = super()._out(response, self.reader.urn_resolver)
237
+ return urn_resolution
238
+
239
+
240
+ class AsyncGdsClient(__BaseGdsClient):
241
+ """A client to be used to retrieve metadata from the GDS asynchronously."""
242
+
243
+ def __init__(
244
+ self,
245
+ api_endpoint: str = GDS_BASE_ENDPOINT,
246
+ pem: Optional[str] = None,
247
+ ) -> None:
248
+ """Instantiate a new client against the target endpoint.
249
+
250
+ Args:
251
+ api_endpoint: The endpoint of the targeted service.
252
+ pem: PEM file for unknown certificate authorities.
253
+ """
254
+ super().__init__(api_endpoint, pem)
255
+ self.__service = GdsAsyncRestService(
256
+ self.api_endpoint,
257
+ pem=pem,
258
+ timeout=10.0,
259
+ )
260
+
261
+ async def __fetch(self, query: GdsQuery) -> bytes:
262
+ """Fetch the requested metadata from the GDS service asynchronously."""
263
+ return await self.__service.gds(query)
264
+
265
+ async def get_agencies(self, agency: str) -> Sequence[Agency]:
266
+ """Get the list of agencies for the supplied name asynchronously.
267
+
268
+ Args:
269
+ agency: The agency maintaining the agency scheme.
270
+
271
+ Returns:
272
+ The requested list of agencies.
273
+ """
274
+ query = super(AsyncGdsClient, self)._agencies_q(agency)
275
+ out = await self.__fetch(query)
276
+ agencies = super(AsyncGdsClient, self)._out(out, self.reader.agencies)
277
+ return agencies
278
+
279
+ async def get_catalogs(
280
+ self,
281
+ catalog: str,
282
+ resource: str = REST_ALL,
283
+ version: str = REST_ALL,
284
+ resource_type: Optional[Literal["data", "metadata"]] = None,
285
+ message_format: Optional[Literal["json", "csv", "xml"]] = None,
286
+ api_version: Optional[str] = None,
287
+ detail: Optional[Literal["full", "raw"]] = None,
288
+ references: Optional[Literal["none", "children"]] = None,
289
+ ) -> Sequence[GdsCatalog]:
290
+ """Get the list of catalogs for the supplied params asynchronously."""
291
+ query = super()._catalogs_q(
292
+ catalog,
293
+ resource,
294
+ version,
295
+ resource_type,
296
+ message_format,
297
+ api_version,
298
+ detail,
299
+ references,
300
+ )
301
+ response = await self.__fetch(query)
302
+ catalogs = super()._out(response, self.reader.catalogs)
303
+ return catalogs
304
+
305
+ async def get_sdmx_apis(
306
+ self, api_version: str = REST_ALL
307
+ ) -> Sequence[GdsSdmxApi]:
308
+ """Get the list of SDMX API versions asynchronously."""
309
+ query = super()._sdmx_api_q(api_version)
310
+ response = await self.__fetch(query)
311
+ sdmx_api = super()._out(response, self.reader.sdmx_api)
312
+ return sdmx_api
313
+
314
+ async def get_services(
315
+ self, service: str, resource: str = REST_ALL, version: str = REST_ALL
316
+ ) -> Sequence[GdsService]:
317
+ """Get a list of services for the supplied params asynchronously."""
318
+ query = super()._services_q(service, resource, version)
319
+ response = await self.__fetch(query)
320
+ services = super()._out(response, self.reader.services)
321
+ return services
322
+
323
+ async def get_urn_resolver(self, urn: str) -> GdsUrnResolver:
324
+ """Resolve a URN to its corresponding resource asynchronously."""
325
+ query = super()._urn_resolver_q(urn)
326
+ response = await self.__fetch(query)
327
+ urn_resolution = super()._out(response, self.reader.urn_resolver)
328
+ return urn_resolution