pysdmx 1.9.0__tar.gz → 1.10.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 (171) hide show
  1. {pysdmx-1.9.0 → pysdmx-1.10.0}/PKG-INFO +3 -3
  2. {pysdmx-1.9.0 → pysdmx-1.10.0}/pyproject.toml +4 -4
  3. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/__extras_check.py +1 -1
  4. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/__init__.py +1 -1
  5. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/fmr/__init__.py +3 -2
  6. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/input_processor.py +9 -6
  7. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/__init__.py +4 -0
  8. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/code.py +16 -6
  9. pysdmx-1.10.0/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +272 -0
  10. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/dsd.py +35 -7
  11. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/map.py +5 -4
  12. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/metadataflow.py +1 -0
  13. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/msd.py +18 -10
  14. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/schema.py +2 -2
  15. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/structure.py +81 -44
  16. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/vtl.py +13 -9
  17. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/reader/doc_validation.py +4 -0
  18. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/header.py +41 -34
  19. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/__base.py +46 -1
  20. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/__init__.py +18 -0
  21. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/category.py +17 -0
  22. pysdmx-1.10.0/src/pysdmx/model/constraint.py +69 -0
  23. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/message.py +80 -71
  24. pysdmx-1.10.0/src/pysdmx/toolkit/vtl/__init__.py +15 -0
  25. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/vtl/_validations.py +8 -12
  26. pysdmx-1.10.0/src/pysdmx/toolkit/vtl/convert.py +333 -0
  27. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/vtl/script_generation.py +1 -1
  28. pysdmx-1.9.0/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +0 -53
  29. pysdmx-1.9.0/src/pysdmx/toolkit/vtl/__init__.py +0 -6
  30. {pysdmx-1.9.0 → pysdmx-1.10.0}/LICENSE +0 -0
  31. {pysdmx-1.9.0 → pysdmx-1.10.0}/README.rst +0 -0
  32. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/__init__.py +0 -0
  33. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/__init__.py +0 -0
  34. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/_api.py +0 -0
  35. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/__init__.py +0 -0
  36. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/_model.py +0 -0
  37. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/_parsing_model.py +0 -0
  38. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/_parsing_util.py +0 -0
  39. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/_py_parser.py +0 -0
  40. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/_sql_parser.py +0 -0
  41. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/dc/query/util.py +0 -0
  42. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/fmr/maintenance.py +0 -0
  43. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/gds/__init__.py +0 -0
  44. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/__init__.py +0 -0
  45. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/availability.py +0 -0
  46. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/data.py +0 -0
  47. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/gds.py +0 -0
  48. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/refmeta.py +0 -0
  49. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/registration.py +0 -0
  50. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/schema.py +0 -0
  51. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/service.py +0 -0
  52. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/structure.py +0 -0
  53. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/api/qb/util.py +0 -0
  54. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/errors.py +0 -0
  55. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/__init__.py +0 -0
  56. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/__csv_aux_reader.py +0 -0
  57. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/__csv_aux_writer.py +0 -0
  58. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/__init__.py +0 -0
  59. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx10/__init__.py +0 -0
  60. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx10/reader/__init__.py +0 -0
  61. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx10/writer/__init__.py +0 -0
  62. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx20/__init__.py +0 -0
  63. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx20/reader/__init__.py +0 -0
  64. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx20/writer/__init__.py +0 -0
  65. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx21/__init__.py +0 -0
  66. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx21/reader/__init__.py +0 -0
  67. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/csv/sdmx21/writer/__init__.py +0 -0
  68. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/format.py +0 -0
  69. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/__init__.py +0 -0
  70. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/category.py +0 -0
  71. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/code.py +0 -0
  72. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/concept.py +0 -0
  73. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/constraint.py +0 -0
  74. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/core.py +0 -0
  75. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/dataflow.py +0 -0
  76. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/dsd.py +0 -0
  77. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/map.py +0 -0
  78. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/metadataflow.py +0 -0
  79. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/mpa.py +0 -0
  80. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/msd.py +0 -0
  81. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/org.py +0 -0
  82. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/pa.py +0 -0
  83. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/report.py +0 -0
  84. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/schema.py +0 -0
  85. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/messages/vtl.py +0 -0
  86. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/fusion/reader/__init__.py +0 -0
  87. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/__init__.py +0 -0
  88. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/agencies.py +0 -0
  89. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/catalog.py +0 -0
  90. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/sdmx_api.py +0 -0
  91. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/services.py +0 -0
  92. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/messages/urn_resolver.py +0 -0
  93. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/gds/reader/__init__.py +0 -0
  94. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/agency.py +0 -0
  95. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/category.py +0 -0
  96. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/concept.py +0 -0
  97. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/core.py +0 -0
  98. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/dataflow.py +0 -0
  99. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/mpa.py +0 -0
  100. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/pa.py +0 -0
  101. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/provider.py +0 -0
  102. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/messages/report.py +0 -0
  103. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/reader/__init__.py +0 -0
  104. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/reader/metadata.py +0 -0
  105. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/reader/structure.py +0 -0
  106. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/writer/__init__.py +0 -0
  107. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/writer/metadata.py +0 -0
  108. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/json/sdmxjson2/writer/structure.py +0 -0
  109. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/pd.py +0 -0
  110. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/reader.py +0 -0
  111. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/serde.py +0 -0
  112. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/writer.py +0 -0
  113. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__allowed_lxml_errors.py +0 -0
  114. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__data_aux.py +0 -0
  115. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__init__.py +0 -0
  116. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__parse_xml.py +0 -0
  117. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__ss_aux_reader.py +0 -0
  118. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__structure_aux_reader.py +0 -0
  119. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__structure_aux_writer.py +0 -0
  120. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__tokens.py +0 -0
  121. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__write_aux.py +0 -0
  122. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__write_data_aux.py +0 -0
  123. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/__write_structure_specific_aux.py +0 -0
  124. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/config.py +0 -0
  125. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/doc_validation.py +0 -0
  126. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/__init__.py +0 -0
  127. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/__init__.py +0 -0
  128. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/error.py +0 -0
  129. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/generic.py +0 -0
  130. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/structure.py +0 -0
  131. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/structure_specific.py +0 -0
  132. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/reader/submission.py +0 -0
  133. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/writer/__init__.py +0 -0
  134. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/writer/error.py +0 -0
  135. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/writer/generic.py +0 -0
  136. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/writer/structure.py +0 -0
  137. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx21/writer/structure_specific.py +0 -0
  138. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/__init__.py +0 -0
  139. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/reader/__init__.py +0 -0
  140. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/reader/structure.py +0 -0
  141. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/reader/structure_specific.py +0 -0
  142. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/writer/__init__.py +0 -0
  143. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/writer/structure.py +0 -0
  144. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx30/writer/structure_specific.py +0 -0
  145. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/__init__.py +0 -0
  146. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/reader/__init__.py +0 -0
  147. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/reader/structure.py +0 -0
  148. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/reader/structure_specific.py +0 -0
  149. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/writer/__init__.py +0 -0
  150. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/writer/structure.py +0 -0
  151. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/sdmx31/writer/structure_specific.py +0 -0
  152. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/io/xml/utils.py +0 -0
  153. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/code.py +0 -0
  154. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/concept.py +0 -0
  155. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/dataflow.py +0 -0
  156. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/dataset.py +0 -0
  157. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/gds.py +0 -0
  158. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/map.py +0 -0
  159. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/metadata.py +0 -0
  160. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/organisation.py +0 -0
  161. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/submission.py +0 -0
  162. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/model/vtl.py +0 -0
  163. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/py.typed +0 -0
  164. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/__init__.py +0 -0
  165. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/pd/__init__.py +0 -0
  166. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/pd/_data_utils.py +0 -0
  167. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/toolkit/vtl/validation.py +0 -0
  168. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/util/__init__.py +0 -0
  169. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/util/_date_pattern_map.py +0 -0
  170. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/util/_model_utils.py +0 -0
  171. {pysdmx-1.9.0 → pysdmx-1.10.0}/src/pysdmx/util/_net_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysdmx
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Your opinionated Python SDMX library
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -32,8 +32,8 @@ Requires-Dist: python-dateutil (>=2.8.2) ; extra == "dc"
32
32
  Requires-Dist: sdmxschemas (>=1.0.0) ; extra == "all"
33
33
  Requires-Dist: sdmxschemas (>=1.0.0) ; extra == "json"
34
34
  Requires-Dist: sdmxschemas (>=1.0.0) ; extra == "xml"
35
- Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "all"
36
- Requires-Dist: vtlengine (>=1.1,<2.0) ; extra == "vtl"
35
+ Requires-Dist: vtlengine (>=1.2,<2.0) ; extra == "all"
36
+ Requires-Dist: vtlengine (>=1.2,<2.0) ; extra == "vtl"
37
37
  Requires-Dist: xmltodict (>=0.13) ; extra == "all"
38
38
  Requires-Dist: xmltodict (>=0.13) ; extra == "xml"
39
39
  Project-URL: Bug Tracker, https://bis-med-it.github.io/pysdmx/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pysdmx"
3
- version = "1.9.0"
3
+ version = "1.10.0"
4
4
  description = "Your opinionated Python SDMX library"
5
5
  license = { text = "Apache-2.0" }
6
6
  readme = "README.rst"
@@ -35,10 +35,10 @@ documentation = "https://bis-med-it.github.io/pysdmx"
35
35
  [project.optional-dependencies]
36
36
  data = ["pandas>=2.1.4"]
37
37
  dc = ["python-dateutil>=2.8.2"]
38
- vtl = ["vtlengine>=1.1,<2.0"]
38
+ vtl = ["vtlengine>=1.2,<2.0"]
39
39
  json = ["sdmxschemas>=1.0.0", "jsonschema>=4.10"]
40
40
  xml = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0"]
41
- all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.1,<2.0"]
41
+ all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.2,<2.0"]
42
42
 
43
43
  [tool.poetry]
44
44
  requires-poetry = ">=2.0"
@@ -48,7 +48,7 @@ python = ">=3.9,<4.0"
48
48
 
49
49
  [tool.poetry.group.dev.dependencies]
50
50
  ruff = ">=0.13.2"
51
- mypy = "^1.1.1"
51
+ mypy = "^1.18.2"
52
52
  pytest = "^8.3.2"
53
53
  pytest-asyncio = "^0.21.1"
54
54
  pytest-cov = "^4.0.0"
@@ -46,7 +46,7 @@ def __check_xml_extra() -> None:
46
46
 
47
47
  def __check_vtl_extra() -> None:
48
48
  try:
49
- import vtlengine # type: ignore[import-untyped] # noqa: F401
49
+ import vtlengine # noqa: F401
50
50
  except ImportError:
51
51
  raise ImportError(
52
52
  ERROR_MESSAGE.format(
@@ -1,3 +1,3 @@
1
1
  """Your opinionated Python SDMX library."""
2
2
 
3
- __version__ = "1.9.0"
3
+ __version__ = "1.10.0"
@@ -44,6 +44,7 @@ from pysdmx.model import (
44
44
  Metadataflow,
45
45
  MetadataProvisionAgreement,
46
46
  MetadataReport,
47
+ MetadataStructure,
47
48
  MultiRepresentationMap,
48
49
  ProvisionAgreement,
49
50
  RepresentationMap,
@@ -767,7 +768,7 @@ class RegistryClient(__BaseRegistryClient):
767
768
  agency: str = "*",
768
769
  id: str = "*",
769
770
  version: str = "+",
770
- ) -> Sequence[Dataflow]:
771
+ ) -> Sequence[MetadataStructure]:
771
772
  """Get the metadata structures (MSD) matching the supplied parameters.
772
773
 
773
774
  Args:
@@ -1341,7 +1342,7 @@ class AsyncRegistryClient(__BaseRegistryClient):
1341
1342
  agency: str = "*",
1342
1343
  id: str = "*",
1343
1344
  version: str = "+",
1344
- ) -> Sequence[Dataflow]:
1345
+ ) -> Sequence[MetadataStructure]:
1345
1346
  """Get the metadata structures (MSD) matching the supplied parameters.
1346
1347
 
1347
1348
  Args:
@@ -29,16 +29,19 @@ def __check_xml(input_str: str) -> bool:
29
29
 
30
30
  def __check_csv(input_str: str) -> bool:
31
31
  try:
32
- max_length = min(2048, len(input_str))
33
- dialect = csv.Sniffer().sniff(input_str[:max_length])
32
+ lines = input_str.splitlines()
33
+
34
+ # Use the first N complete lines
35
+ # (1 should be enough)
36
+ max_lines = 1
37
+ sample = "\n".join(lines[:max_lines])
38
+
39
+ dialect = csv.Sniffer().sniff(sample)
34
40
  control_csv_format = (
35
41
  dialect.delimiter == "," and dialect.quotechar == '"'
36
42
  )
37
43
  # Check we can access the data and it is not empty
38
- if (
39
- len(input_str.splitlines()) > 1
40
- or input_str.splitlines()[0].count(",") > 1
41
- ) and control_csv_format:
44
+ if (len(lines) > 1 or lines[0].count(",") > 1) and control_csv_format:
42
45
  return True
43
46
  except Exception:
44
47
  return False
@@ -12,6 +12,9 @@ from pysdmx.io.json.sdmxjson2.messages.code import (
12
12
  JsonHierarchyMessage,
13
13
  )
14
14
  from pysdmx.io.json.sdmxjson2.messages.concept import JsonConceptSchemeMessage
15
+ from pysdmx.io.json.sdmxjson2.messages.constraint import (
16
+ JsonDataConstraintMessage,
17
+ )
15
18
  from pysdmx.io.json.sdmxjson2.messages.dataflow import (
16
19
  JsonDataflowMessage,
17
20
  JsonDataflowsMessage,
@@ -50,6 +53,7 @@ __all__ = [
50
53
  "JsonCategorySchemeMessage",
51
54
  "JsonCodelistMessage",
52
55
  "JsonConceptSchemeMessage",
56
+ "JsonDataConstraintMessage",
53
57
  "JsonDataflowMessage",
54
58
  "JsonDataflowsMessage",
55
59
  "JsonDataStructuresMessage",
@@ -231,7 +231,7 @@ class JsonCodelists(Struct, frozen=True, omit_defaults=True):
231
231
  """SDMX-JSON payload for lists of codes."""
232
232
 
233
233
  codelists: Sequence[JsonCodelist] = ()
234
- valuelists: Sequence[JsonValuelist] = ()
234
+ valueLists: Sequence[JsonValuelist] = ()
235
235
 
236
236
 
237
237
  class JsonCodelistMessage(Struct, frozen=True, omit_defaults=True):
@@ -244,7 +244,7 @@ class JsonCodelistMessage(Struct, frozen=True, omit_defaults=True):
244
244
  if self.data.codelists:
245
245
  return self.data.codelists[0].to_model()
246
246
  else:
247
- return self.data.valuelists[0].to_model()
247
+ return self.data.valueLists[0].to_model()
248
248
 
249
249
 
250
250
  class JsonHierarchicalCode(Struct, frozen=True, omit_defaults=True):
@@ -329,10 +329,10 @@ class JsonHierarchicalCode(Struct, frozen=True, omit_defaults=True):
329
329
  code=code.urn,
330
330
  validFrom=code.rel_valid_from,
331
331
  validTo=code.rel_valid_to,
332
- annotations=tuple(annotations),
333
- hierarchicalCodes=[
334
- JsonHierarchicalCode.from_model(c) for c in code.codes
335
- ],
332
+ annotations=tuple(annotations) if annotations else None,
333
+ hierarchicalCodes=tuple(
334
+ [JsonHierarchicalCode.from_model(c) for c in code.codes]
335
+ ),
336
336
  )
337
337
 
338
338
 
@@ -475,6 +475,15 @@ class JsonHierarchyAssociation(
475
475
  "SDMX-JSON hierarchy associations must reference a context",
476
476
  {"hierarchy_association": ha.id},
477
477
  )
478
+ lnk = (
479
+ JsonLink(
480
+ rel="UserDefinedOperator",
481
+ type="sdmx_artefact",
482
+ urn=ha.operator,
483
+ )
484
+ if ha.operator
485
+ else None
486
+ )
478
487
  return JsonHierarchyAssociation(
479
488
  agency=(
480
489
  ha.agency.id if isinstance(ha.agency, Agency) else ha.agency
@@ -492,6 +501,7 @@ class JsonHierarchyAssociation(
492
501
  linkedHierarchy=href,
493
502
  linkedObject=ha.component_ref,
494
503
  contextObject=ha.context_ref,
504
+ links=[lnk] if lnk else (),
495
505
  )
496
506
 
497
507
 
@@ -0,0 +1,272 @@
1
+ """Collection of SDMX-JSON schemas for content constraints."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional, Sequence
5
+
6
+ from msgspec import Struct
7
+
8
+ from pysdmx import errors
9
+ from pysdmx.io.json.sdmxjson2.messages.core import (
10
+ JsonAnnotation,
11
+ MaintainableType,
12
+ )
13
+ from pysdmx.model import (
14
+ Agency,
15
+ ConstraintAttachment,
16
+ CubeKeyValue,
17
+ CubeRegion,
18
+ CubeValue,
19
+ DataConstraint,
20
+ DataKey,
21
+ DataKeyValue,
22
+ KeySet,
23
+ )
24
+
25
+
26
+ class JsonValue(Struct, frozen=True, omit_defaults=True):
27
+ """SDMX-JSON payload for a cube value."""
28
+
29
+ value: str
30
+ validFrom: Optional[datetime] = None
31
+ validTo: Optional[datetime] = None
32
+
33
+ def to_model(self) -> CubeValue:
34
+ """Converts a JsonValue to a CubeValue."""
35
+ return CubeValue(self.value, self.validFrom, self.validTo)
36
+
37
+ @classmethod
38
+ def from_model(self, cv: CubeValue) -> "JsonValue":
39
+ """Converts a pysdmx cube value to an SDMX-JSON one."""
40
+ return JsonValue(cv.value, cv.valid_from, cv.valid_to)
41
+
42
+
43
+ class JsonKeyValue(Struct, frozen=True, omit_defaults=True):
44
+ """SDMX-JSON payload for the list of allowed values per component."""
45
+
46
+ id: str
47
+ values: Sequence[JsonValue]
48
+ # Additional properties are supported in the model (include,
49
+ # removePrefix, validFrom, validTo, timeRange) but not by the FMR.
50
+ # Therefore, they are ignored for now.
51
+
52
+ def to_model(self) -> CubeKeyValue:
53
+ """Converts a JsonKeyValue to a CubeKeyValue."""
54
+ return CubeKeyValue(self.id, [v.to_model() for v in self.values])
55
+
56
+ @classmethod
57
+ def from_model(self, key_value: CubeKeyValue) -> "JsonKeyValue":
58
+ """Converts a pysdmx cube key value to an SDMX-JSON one."""
59
+ return JsonKeyValue(
60
+ key_value.id, [JsonValue.from_model(v) for v in key_value.values]
61
+ )
62
+
63
+
64
+ class JsonCubeRegion(Struct, frozen=True, omit_defaults=True):
65
+ """SDMX-JSON payload for a cube region."""
66
+
67
+ # The property `components` is ignored as it's not used in the FMR`
68
+ keyValues: Sequence[JsonKeyValue]
69
+ include: bool = True
70
+
71
+ def to_model(self) -> CubeRegion:
72
+ """Converts a JsonCubeRegion to a CubeRegion."""
73
+ return CubeRegion(
74
+ [kv.to_model() for kv in self.keyValues], self.include
75
+ )
76
+
77
+ @classmethod
78
+ def from_model(self, region: CubeRegion) -> "JsonCubeRegion":
79
+ """Converts a pysdmx cube region to an SDMX-JSON one."""
80
+ return JsonCubeRegion(
81
+ [JsonKeyValue.from_model(kv) for kv in region.key_values],
82
+ region.is_included,
83
+ )
84
+
85
+
86
+ class JsonConstraintAttachment(Struct, frozen=True, omit_defaults=True):
87
+ """SDMX-JSON payload for a constraint attachment."""
88
+
89
+ dataProvider: Optional[str] = None
90
+ dataStructures: Sequence[str] = ()
91
+ dataflows: Sequence[str] = ()
92
+ provisionAgreements: Sequence[str] = ()
93
+
94
+ def to_model(self) -> ConstraintAttachment:
95
+ """Converts a JsonConstraintAttachment to a ConstraintAttachment."""
96
+ return ConstraintAttachment(
97
+ self.dataProvider,
98
+ self.dataStructures,
99
+ self.dataflows,
100
+ self.provisionAgreements,
101
+ )
102
+
103
+ @classmethod
104
+ def from_model(
105
+ self, attachment: ConstraintAttachment
106
+ ) -> "JsonConstraintAttachment":
107
+ """Converts a pysdmx constraint attachment to an SDMX-JSON one."""
108
+ ds = attachment.data_structures if attachment.data_structures else ()
109
+ df = attachment.dataflows if attachment.dataflows else ()
110
+ pa = (
111
+ attachment.provision_agreements
112
+ if attachment.provision_agreements
113
+ else ()
114
+ )
115
+ return JsonConstraintAttachment(attachment.data_provider, ds, df, pa)
116
+
117
+
118
+ class JsonDataKeyValue(Struct, frozen=True, omit_defaults=True):
119
+ """SDMX-JSON payload for a data key value."""
120
+
121
+ id: str
122
+ value: str
123
+
124
+ def to_model(self) -> DataKeyValue:
125
+ """Converts a JsonDataKeyValue to a DataKeyValue."""
126
+ return DataKeyValue(self.id, self.value)
127
+
128
+ @classmethod
129
+ def from_model(self, kv: DataKeyValue) -> "JsonDataKeyValue":
130
+ """Converts a pysdmx key value to an SDMX-JSON one."""
131
+ return JsonDataKeyValue(kv.id, kv.value)
132
+
133
+
134
+ class JsonDataKey(Struct, frozen=True, omit_defaults=True):
135
+ """SDMX-JSON payload for a data key."""
136
+
137
+ keyValues: Sequence[JsonDataKeyValue]
138
+ validFrom: Optional[datetime] = None
139
+ validTo: Optional[datetime] = None
140
+
141
+ def to_model(self) -> DataKey:
142
+ """Converts a JsonDataKey to a DataKey."""
143
+ return DataKey(
144
+ [kv.to_model() for kv in self.keyValues],
145
+ self.validFrom,
146
+ self.validTo,
147
+ )
148
+
149
+ @classmethod
150
+ def from_model(self, kv: DataKey) -> "JsonDataKey":
151
+ """Converts a pysdmx key constraint to an SDMX-JSON one."""
152
+ return JsonDataKey(
153
+ [JsonDataKeyValue.from_model(val) for val in kv.keys_values],
154
+ kv.valid_from,
155
+ kv.valid_to,
156
+ )
157
+
158
+
159
+ class JsonKeySet(Struct, frozen=True, omit_defaults=True):
160
+ """SDMX-JSON payload for a keyset."""
161
+
162
+ keys: Sequence[JsonDataKey]
163
+ isIncluded: bool
164
+
165
+ def to_model(self) -> KeySet:
166
+ """Converts a JsonKeySet to a KeySet."""
167
+ return KeySet([k.to_model() for k in self.keys], self.isIncluded)
168
+
169
+ @classmethod
170
+ def from_model(self, ks: KeySet) -> "JsonKeySet":
171
+ """Converts a pysdmx key set constraint to an SDMX-JSON one."""
172
+ return JsonKeySet(
173
+ [JsonDataKey.from_model(k) for k in ks.keys], ks.is_included
174
+ )
175
+
176
+
177
+ class JsonDataConstraint(MaintainableType, frozen=True, omit_defaults=True):
178
+ """SDMX-JSON payload for a content constraint."""
179
+
180
+ constraintAttachment: Optional[JsonConstraintAttachment] = None
181
+ cubeRegions: Optional[Sequence[JsonCubeRegion]] = None
182
+ dataKeySets: Optional[Sequence[JsonKeySet]] = None
183
+
184
+ def to_model(self) -> DataConstraint:
185
+ """Converts a JsonDataConstraint to a pysdmx Data Constraint."""
186
+ at = self.constraintAttachment.to_model() # type: ignore[union-attr]
187
+ return DataConstraint(
188
+ id=self.id,
189
+ name=self.name,
190
+ agency=self.agency,
191
+ description=self.description,
192
+ version=self.version,
193
+ annotations=tuple([a.to_model() for a in self.annotations]),
194
+ is_external_reference=self.isExternalReference,
195
+ valid_from=self.validFrom,
196
+ valid_to=self.validTo,
197
+ constraint_attachment=at,
198
+ cube_regions=[r.to_model() for r in self.cubeRegions]
199
+ if self.cubeRegions
200
+ else (),
201
+ key_sets=[s.to_model() for s in self.dataKeySets]
202
+ if self.dataKeySets
203
+ else (),
204
+ )
205
+
206
+ @classmethod
207
+ def from_model(self, cons: DataConstraint) -> "JsonDataConstraint":
208
+ """Converts a pysdmx constraint to an SDMX-JSON one."""
209
+ crs = (
210
+ [JsonCubeRegion.from_model(r) for r in cons.cube_regions]
211
+ if cons.cube_regions
212
+ else None
213
+ )
214
+ dks = (
215
+ [JsonKeySet.from_model(s) for s in cons.key_sets]
216
+ if cons.key_sets
217
+ else None
218
+ )
219
+ if not cons.name:
220
+ raise errors.Invalid(
221
+ "Invalid input",
222
+ "SDMX-JSON data constraints must have a name",
223
+ {"data_constraint": cons.id},
224
+ )
225
+ if not cons.constraint_attachment:
226
+ raise errors.Invalid(
227
+ "Invalid input",
228
+ "SDMX-JSON data constraints must have a constraint attachment",
229
+ {"data_constraint": cons.id},
230
+ )
231
+ return JsonDataConstraint(
232
+ id=cons.id,
233
+ name=cons.name,
234
+ agency=(
235
+ cons.agency.id
236
+ if isinstance(cons.agency, Agency)
237
+ else cons.agency
238
+ ),
239
+ description=cons.description,
240
+ version=cons.version,
241
+ annotations=tuple(
242
+ [JsonAnnotation.from_model(a) for a in cons.annotations]
243
+ ),
244
+ isExternalReference=cons.is_external_reference,
245
+ validFrom=cons.valid_from,
246
+ validTo=cons.valid_to,
247
+ constraintAttachment=JsonConstraintAttachment.from_model(
248
+ cons.constraint_attachment
249
+ ),
250
+ cubeRegions=crs,
251
+ dataKeySets=dks,
252
+ )
253
+
254
+
255
+ class JsonDataConstraints(Struct, frozen=True, omit_defaults=True):
256
+ """SDMX-JSON payload for data constraints."""
257
+
258
+ dataConstraints: Sequence[JsonDataConstraint] = ()
259
+
260
+ def to_model(self) -> Sequence[DataConstraint]:
261
+ """Returns the requested data constraints."""
262
+ return [cc.to_model() for cc in self.dataConstraints]
263
+
264
+
265
+ class JsonDataConstraintMessage(Struct, frozen=True, omit_defaults=True):
266
+ """SDMX-JSON payload for /dataconstraint queries."""
267
+
268
+ data: JsonDataConstraints
269
+
270
+ def to_model(self) -> Sequence[DataConstraint]:
271
+ """Returns the requested data constraints."""
272
+ return self.data.to_model()
@@ -265,12 +265,17 @@ class JsonAttribute(Struct, frozen=True, omit_defaults=True):
265
265
  attribute.attachment_level # type: ignore[arg-type]
266
266
  )
267
267
  repr = _get_json_representation(attribute)
268
+ # The line below will need to be changed when we work on
269
+ # Measure Relationship (cf. issue #467)
270
+ mr = ["OBS_VALUE"] if attribute.attachment_level == "O" else None
271
+
268
272
  return JsonAttribute(
269
273
  id=attribute.id,
270
274
  conceptIdentity=concept,
271
275
  attributeRelationship=level,
272
276
  usage=usage,
273
277
  localRepresentation=repr,
278
+ measureRelationship=mr,
274
279
  )
275
280
 
276
281
 
@@ -447,19 +452,42 @@ class JsonComponents(Struct, frozen=True, omit_defaults=True):
447
452
  enums = [cl.to_model() for cl in cls]
448
453
  enums.extend([vl.to_model() for vl in vls])
449
454
  comps = []
450
- if constraints and constraints[0].cubeRegions:
451
- cons = constraints[0].cubeRegions[0].to_map()
455
+ if constraints:
456
+ incl_cubes = []
457
+ for const in constraints:
458
+ incl_cubes.extend(
459
+ [cr for cr in (const.cubeRegions or []) if cr.include]
460
+ )
461
+ if len(incl_cubes) == 1:
462
+ cons = {
463
+ kv.id: [v.value for v in kv.values]
464
+ for kv in incl_cubes[0].keyValues
465
+ }
466
+ else:
467
+ cons = {}
452
468
  else:
453
469
  cons = {}
454
- comps.extend(self.dimensionList.to_model(cs, enums, cons))
470
+ comps.extend(
471
+ self.dimensionList.to_model(
472
+ cs,
473
+ enums,
474
+ cons, # type: ignore[arg-type]
475
+ )
476
+ )
455
477
  if self.measureList:
456
- comps.extend(self.measureList.to_model(cs, enums, cons))
478
+ comps.extend(
479
+ self.measureList.to_model(
480
+ cs,
481
+ enums,
482
+ cons, # type: ignore[arg-type]
483
+ )
484
+ )
457
485
  if self.attributeList:
458
486
  comps.extend(
459
487
  self.attributeList.to_model(
460
488
  cs,
461
489
  enums,
462
- cons,
490
+ cons, # type: ignore[arg-type]
463
491
  self.groups,
464
492
  )
465
493
  )
@@ -556,7 +584,7 @@ class JsonDataStructures(Struct, frozen=True, omit_defaults=True):
556
584
  conceptSchemes: Sequence[JsonConceptScheme] = ()
557
585
  valuelists: Sequence[JsonValuelist] = ()
558
586
  codelists: Sequence[JsonCodelist] = ()
559
- contentConstraints: Sequence[JsonDataConstraint] = ()
587
+ dataConstraints: Sequence[JsonDataConstraint] = ()
560
588
 
561
589
  def to_model(self) -> Sequence[DataStructureDefinition]:
562
590
  """Returns the requested dsds."""
@@ -565,7 +593,7 @@ class JsonDataStructures(Struct, frozen=True, omit_defaults=True):
565
593
  self.conceptSchemes,
566
594
  self.codelists,
567
595
  self.valuelists,
568
- self.contentConstraints,
596
+ self.dataConstraints,
569
597
  )
570
598
  for dsd in self.dataStructures
571
599
  ]
@@ -87,19 +87,20 @@ class JsonRepresentationMapping(Struct, frozen=True, omit_defaults=True):
87
87
  self, vm: Union[MultiValueMap, ValueMap]
88
88
  ) -> "JsonRepresentationMapping":
89
89
  """Converts a value map to an SDMX-JSON JsonRepresentationMapping."""
90
+ fmt = r"%Y-%m-%dT%H:%M:%S"
90
91
  if isinstance(vm, ValueMap):
91
92
  return JsonRepresentationMapping(
92
93
  [JsonSourceValue.from_model(vm.source)],
93
94
  [vm.target],
94
- vm.valid_from.strftime("%Y-%m-%d") if vm.valid_from else None,
95
- vm.valid_to.strftime("%Y-%m-%d") if vm.valid_to else None,
95
+ vm.valid_from.strftime(fmt) if vm.valid_from else None,
96
+ vm.valid_to.strftime(fmt) if vm.valid_to else None,
96
97
  )
97
98
  else:
98
99
  return JsonRepresentationMapping(
99
100
  [JsonSourceValue.from_model(s) for s in vm.source],
100
101
  vm.target,
101
- vm.valid_from.strftime("%Y-%m-%d") if vm.valid_from else None,
102
- vm.valid_to.strftime("%Y-%m-%d") if vm.valid_to else None,
102
+ vm.valid_from.strftime(fmt) if vm.valid_from else None,
103
+ vm.valid_to.strftime(fmt) if vm.valid_to else None,
103
104
  )
104
105
 
105
106
 
@@ -65,6 +65,7 @@ class JsonMetadataflow(MaintainableType, frozen=True, omit_defaults=True):
65
65
  [JsonAnnotation.from_model(a) for a in df.annotations]
66
66
  ),
67
67
  structure=dsdref,
68
+ targets=df.targets, # type: ignore[arg-type]
68
69
  )
69
70
 
70
71
 
@@ -15,7 +15,6 @@ from pysdmx.io.json.sdmxjson2.messages.core import (
15
15
  from pysdmx.io.json.sdmxjson2.messages.dsd import (
16
16
  _find_concept,
17
17
  _get_concept_reference,
18
- _get_json_representation,
19
18
  _get_representation,
20
19
  )
21
20
  from pysdmx.model import (
@@ -28,6 +27,13 @@ from pysdmx.model import (
28
27
  from pysdmx.util import parse_item_urn
29
28
 
30
29
 
30
+ def _get_attr_repr(comp: MetadataComponent) -> Optional[JsonRepresentation]:
31
+ enum = comp.local_enum_ref if comp.local_enum_ref else None
32
+ return JsonRepresentation.from_model(
33
+ comp.local_dtype, enum, comp.local_facets, None
34
+ )
35
+
36
+
31
37
  class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
32
38
  """SDMX-JSON payload for an attribute."""
33
39
 
@@ -80,11 +86,13 @@ class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
80
86
  def from_model(self, cmp: MetadataComponent) -> "JsonMetadataAttribute":
81
87
  """Converts a pysdmx metadata attribute to an SDMX-JSON one."""
82
88
  concept = _get_concept_reference(cmp)
83
- repr = _get_json_representation(cmp)
89
+ repr = _get_attr_repr(cmp)
84
90
 
85
91
  min_occurs = cmp.array_def.min_size if cmp.array_def else 0
86
- if cmp.array_def is None or cmp.array_def.max_size is None:
87
- max_occurs: Union[int, Literal["unbounded"]] = "unbounded"
92
+ if cmp.array_def is None:
93
+ max_occurs: Union[int, Literal["unbounded"]] = 1
94
+ elif cmp.array_def.max_size is None:
95
+ max_occurs = "unbounded"
88
96
  else:
89
97
  max_occurs = cmp.array_def.max_size
90
98
 
@@ -95,9 +103,9 @@ class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
95
103
  minOccurs=min_occurs,
96
104
  maxOccurs=max_occurs,
97
105
  isPresentational=cmp.is_presentational,
98
- metadataAttributes=[
99
- JsonMetadataAttribute.from_model(c) for c in cmp.components
100
- ],
106
+ metadataAttributes=tuple(
107
+ [JsonMetadataAttribute.from_model(c) for c in cmp.components]
108
+ ),
101
109
  )
102
110
 
103
111
 
@@ -119,9 +127,9 @@ class JsonMetadataAttributes(Struct, frozen=True, omit_defaults=True):
119
127
  ) -> "JsonMetadataAttributes":
120
128
  """Converts a pysdmx list of metadata attributes to SDMX-JSON."""
121
129
  return JsonMetadataAttributes(
122
- metadataAttributes=[
123
- JsonMetadataAttribute.from_model(a) for a in attributes
124
- ]
130
+ metadataAttributes=tuple(
131
+ [JsonMetadataAttribute.from_model(a) for a in attributes]
132
+ )
125
133
  )
126
134
 
127
135
 
@@ -21,7 +21,7 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
21
21
  dataStructures: Sequence[JsonDataStructure]
22
22
  valuelists: Sequence[JsonValuelist] = ()
23
23
  codelists: Sequence[JsonCodelist] = ()
24
- contentConstraints: Sequence[JsonDataConstraint] = ()
24
+ dataConstraints: Sequence[JsonDataConstraint] = ()
25
25
 
26
26
  def to_model(
27
27
  self,
@@ -32,7 +32,7 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
32
32
  self.conceptSchemes,
33
33
  self.codelists,
34
34
  self.valuelists,
35
- self.contentConstraints,
35
+ self.dataConstraints,
36
36
  )
37
37
  return comps, grps # type: ignore[return-value]
38
38