datacontract-cli 1.0.11__tar.gz → 1.0.13__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 (379) hide show
  1. {datacontract_cli-1.0.11/datacontract_cli.egg-info → datacontract_cli-1.0.13}/PKG-INFO +4 -4
  2. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/check_spec.py +1 -0
  3. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/create_checks.py +67 -3
  4. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/physical_type_match.py +29 -2
  5. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/type_normalize.py +88 -17
  6. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/dtype_category.py +22 -14
  7. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/ibis_check_execute.py +123 -11
  8. datacontract_cli-1.0.13/datacontract/engines/ibis/snowflake_structured_types.py +138 -0
  9. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13/datacontract_cli.egg-info}/PKG-INFO +4 -4
  10. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/SOURCES.txt +3 -0
  11. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/requires.txt +3 -3
  12. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/pyproject.toml +4 -4
  13. datacontract_cli-1.0.13/tests/test_create_checks_nested_type.py +438 -0
  14. datacontract_cli-1.0.13/tests/test_dtype_category.py +84 -0
  15. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_physical_type_match.py +35 -2
  16. datacontract_cli-1.0.13/tests/test_snowflake_structured_types.py +251 -0
  17. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_dataframe.py +2 -2
  18. datacontract_cli-1.0.13/tests/test_type_normalize.py +92 -0
  19. datacontract_cli-1.0.11/tests/test_dtype_category.py +0 -28
  20. datacontract_cli-1.0.11/tests/test_type_normalize.py +0 -15
  21. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/LICENSE +0 -0
  22. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/MANIFEST.in +0 -0
  23. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/README.md +0 -0
  24. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/__init__.py +0 -0
  25. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/api.py +0 -0
  26. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/catalog/catalog.py +0 -0
  27. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/__init__.py +0 -0
  28. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/changelog.py +0 -0
  29. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/normalize.py +0 -0
  30. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/cli.py +0 -0
  31. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_api.py +0 -0
  32. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_catalog.py +0 -0
  33. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_changelog.py +0 -0
  34. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_ci.py +0 -0
  35. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_dbt.py +0 -0
  36. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_edit.py +0 -0
  37. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_export.py +0 -0
  38. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_import.py +0 -0
  39. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_init.py +0 -0
  40. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_lint.py +0 -0
  41. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_publish.py +0 -0
  42. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_test.py +0 -0
  43. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/data_contract.py +0 -0
  44. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/VERSION +0 -0
  45. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -0
  46. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/apex-Dspwrl5m.js +0 -0
  47. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
  48. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
  49. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/azcli-k1BiOau1.js +0 -0
  50. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/bat-BIP72KWc.js +0 -0
  51. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -0
  52. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -0
  53. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/clojure-BAuDPsal.js +0 -0
  54. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/coffee-BV06qSnj.js +0 -0
  55. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -0
  56. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -0
  57. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/csp-KzlEHCyI.js +0 -0
  58. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/css-e4FKntZ3.js +0 -0
  59. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -0
  60. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -0
  61. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/dart-DdHeFWR0.js +0 -0
  62. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/datacontract-editor.css +0 -0
  63. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
  64. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -0
  65. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -0
  66. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/elixir-C9X42b0T.js +0 -0
  67. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/flow9-Bff42pfB.js +0 -0
  68. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -0
  69. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -0
  70. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/go-DEr8zx9f.js +0 -0
  71. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -0
  72. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -0
  73. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -0
  74. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/html-Cn4TGw_o.js +0 -0
  75. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -0
  76. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/index.html +0 -0
  77. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ini-CZGtssYM.js +0 -0
  78. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/java-B260NeH3.js +0 -0
  79. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/javascript-B86Kac07.js +0 -0
  80. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -0
  81. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/julia-kuNLM9c5.js +0 -0
  82. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -0
  83. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/less-AZUtS89I.js +0 -0
  84. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -0
  85. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -0
  86. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
  87. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/lua-Dz1RbATg.js +0 -0
  88. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/m3-CIMooLFn.js +0 -0
  89. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -0
  90. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -0
  91. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mips-BovsnfLQ.js +0 -0
  92. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
  93. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -0
  94. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mysql-BwdpPE19.js +0 -0
  95. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -0
  96. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/odcs.svg +0 -0
  97. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pascal--j5-PYtz.js +0 -0
  98. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -0
  99. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/perl-BzEWqUb5.js +0 -0
  100. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -0
  101. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/php-A19QHzQV.js +0 -0
  102. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pla-COeKY1hA.js +0 -0
  103. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -0
  104. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -0
  105. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -0
  106. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -0
  107. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pug-DLej6kn5.js +0 -0
  108. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/python-C8RW9B6A.js +0 -0
  109. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -0
  110. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/r-D-F4P80w.js +0 -0
  111. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/razor-CLZqdY_r.js +0 -0
  112. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/redis-hwwIlMnV.js +0 -0
  113. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -0
  114. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -0
  115. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -0
  116. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -0
  117. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -0
  118. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -0
  119. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scheme-DeFRbslr.js +0 -0
  120. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scss-RSTCiCmI.js +0 -0
  121. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/shell-BpF-iNu8.js +0 -0
  122. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -0
  123. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -0
  124. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -0
  125. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sql-CJwKPERn.js +0 -0
  126. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/st-DPlKG5DT.js +0 -0
  127. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/swift-YZBAbVHV.js +0 -0
  128. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -0
  129. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -0
  130. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -0
  131. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/twig-C5TO7-kw.js +0 -0
  132. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/typescript-CJBeebKU.js +0 -0
  133. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -0
  134. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/vb-ChHvIDrk.js +0 -0
  135. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -0
  136. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/xml-vC-NMVX1.js +0 -0
  137. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -0
  138. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/__init__.py +0 -0
  139. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/__init__.py +0 -0
  140. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/data_contract_test.py +0 -0
  141. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
  142. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
  143. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
  144. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
  145. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
  146. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/__init__.py +0 -0
  147. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/__init__.py +0 -0
  148. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/connect.py +0 -0
  149. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/duckdb_connection.py +0 -0
  150. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/kafka.py +0 -0
  151. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
  152. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
  153. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/native_type.py +0 -0
  154. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/__init__.py +0 -0
  155. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/avro_exporter.py +0 -0
  156. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/avro_idl_exporter.py +0 -0
  157. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/bigquery_exporter.py +0 -0
  158. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/custom_exporter.py +0 -0
  159. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/data_caterer_exporter.py +0 -0
  160. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dbml_exporter.py +0 -0
  161. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dbt_exporter.py +0 -0
  162. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dcs_exporter.py +0 -0
  163. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dqx_exporter.py +0 -0
  164. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/duckdb_type_converter.py +0 -0
  165. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/excel_exporter.py +0 -0
  166. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/exporter.py +0 -0
  167. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/exporter_factory.py +0 -0
  168. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/go_exporter.py +0 -0
  169. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/great_expectations_exporter.py +0 -0
  170. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/html_exporter.py +0 -0
  171. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/iceberg_exporter.py +0 -0
  172. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/jsonschema_exporter.py +0 -0
  173. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/markdown_exporter.py +0 -0
  174. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/mermaid_exporter.py +0 -0
  175. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/odcs_export_helper.py +0 -0
  176. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/odcs_v3_exporter.py +0 -0
  177. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/pandas_type_converter.py +0 -0
  178. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/protobuf_exporter.py +0 -0
  179. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/pydantic_exporter.py +0 -0
  180. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/rdf_exporter.py +0 -0
  181. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sodacl_check_builder.py +0 -0
  182. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sodacl_exporter.py +0 -0
  183. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/spark_exporter.py +0 -0
  184. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sql_exporter.py +0 -0
  185. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sql_type_converter.py +0 -0
  186. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sqlalchemy_exporter.py +0 -0
  187. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/avro_importer.py +0 -0
  188. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/bigquery_importer.py +0 -0
  189. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/csv_importer.py +0 -0
  190. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dbml_importer.py +0 -0
  191. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dbt_importer.py +0 -0
  192. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dcs_importer.py +0 -0
  193. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/excel_importer.py +0 -0
  194. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/glue_importer.py +0 -0
  195. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/iceberg_importer.py +0 -0
  196. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/importer.py +0 -0
  197. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/importer_factory.py +0 -0
  198. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/json_importer.py +0 -0
  199. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/jsonschema_importer.py +0 -0
  200. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/odcs_helper.py +0 -0
  201. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/odcs_importer.py +0 -0
  202. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/parquet_importer.py +0 -0
  203. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/powerbi_importer.py +0 -0
  204. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/protobuf_importer.py +0 -0
  205. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/snowflake_importer.py +0 -0
  206. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/spark_importer.py +0 -0
  207. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/sql_importer.py +0 -0
  208. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/unity_importer.py +0 -0
  209. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/init/init_template.py +0 -0
  210. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/dbt_sync.py +0 -0
  211. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/dbt_test_mapping.py +0 -0
  212. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/entropy_data.py +0 -0
  213. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/files.py +0 -0
  214. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/resolve.py +0 -0
  215. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/resources.py +0 -0
  216. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/schema.py +0 -0
  217. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/urls.py +0 -0
  218. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/changelog.py +0 -0
  219. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/exceptions.py +0 -0
  220. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/odcs.py +0 -0
  221. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/run.py +0 -0
  222. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/server.py +0 -0
  223. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/__init__.py +0 -0
  224. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/ci_output.py +0 -0
  225. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/json_test_results.py +0 -0
  226. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/junit_test_results.py +0 -0
  227. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/output_format.py +0 -0
  228. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/test_results_writer.py +0 -0
  229. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/text_changelog_results.py +0 -0
  230. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/py.typed +0 -0
  231. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
  232. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
  233. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
  234. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
  235. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
  236. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
  237. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
  238. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
  239. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.1.0.init.yaml +0 -0
  240. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
  241. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/datacontract.html +0 -0
  242. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/datacontract_odcs.html +0 -0
  243. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/index.html +0 -0
  244. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_information.html +0 -0
  245. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
  246. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_terms.html +0 -0
  247. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/definition.html +0 -0
  248. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/example.html +0 -0
  249. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/model_field.html +0 -0
  250. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/quality.html +0 -0
  251. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/server.html +0 -0
  252. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/style/output.css +0 -0
  253. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/dependency_links.txt +0 -0
  254. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/entry_points.txt +0 -0
  255. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/top_level.txt +0 -0
  256. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/setup.cfg +0 -0
  257. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_api.py +0 -0
  258. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_catalog.py +0 -0
  259. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog.py +0 -0
  260. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_engine.py +0 -0
  261. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_normalize.py +0 -0
  262. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_output_text.py +0 -0
  263. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ci_output.py +0 -0
  264. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_cli.py +0 -0
  265. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_databricks.py +0 -0
  266. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_sqlserver.py +0 -0
  267. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_trino.py +0 -0
  268. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_create_checks_physical_type.py +0 -0
  269. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract.py +0 -0
  270. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract_checks.py +0 -0
  271. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract_specification.py +0 -0
  272. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_dbt_sync.py +0 -0
  273. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_dbt_test_mapping.py +0 -0
  274. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_description_linter.py +0 -0
  275. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_download_datacontract_file.py +0 -0
  276. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_duckdb_json.py +0 -0
  277. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_edit.py +0 -0
  278. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_avro.py +0 -0
  279. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_avro_idl.py +0 -0
  280. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_bigquery.py +0 -0
  281. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_complex_data_contract.py +0 -0
  282. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom.py +0 -0
  283. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom_exporter.py +0 -0
  284. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom_model.py +0 -0
  285. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_data_caterer.py +0 -0
  286. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbml.py +0 -0
  287. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_models.py +0 -0
  288. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_sources.py +0 -0
  289. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_staging_sql.py +0 -0
  290. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dqx.py +0 -0
  291. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_excel.py +0 -0
  292. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_go.py +0 -0
  293. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_great_expectations.py +0 -0
  294. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_html.py +0 -0
  295. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_iceberg.py +0 -0
  296. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_jsonschema.py +0 -0
  297. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_markdown.py +0 -0
  298. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_mermaid.py +0 -0
  299. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_odcs_v3.py +0 -0
  300. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_protobuf.py +0 -0
  301. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_pydantic.py +0 -0
  302. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_rdf.py +0 -0
  303. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sodacl.py +0 -0
  304. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_spark.py +0 -0
  305. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql.py +0 -0
  306. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql_clickhouse.py +0 -0
  307. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql_query.py +0 -0
  308. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sqlalchemy.py +0 -0
  309. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ibis_mssql_pattern.py +0 -0
  310. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ibis_oracle_schema.py +0 -0
  311. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_avro.py +0 -0
  312. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_bigquery.py +0 -0
  313. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_csv.py +0 -0
  314. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_dbml.py +0 -0
  315. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_dbt.py +0 -0
  316. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_excel.py +0 -0
  317. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_glue.py +0 -0
  318. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_iceberg.py +0 -0
  319. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_json.py +0 -0
  320. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_jsonschema.py +0 -0
  321. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_odcs_v3.py +0 -0
  322. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_parquet.py +0 -0
  323. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_powerbi.py +0 -0
  324. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_protobuf.py +0 -0
  325. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_snowflake.py +0 -0
  326. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_spark.py +0 -0
  327. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_oracle.py +0 -0
  328. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_postgres.py +0 -0
  329. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_snowflake.py +0 -0
  330. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_sqlserver.py +0 -0
  331. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_unity_file.py +0 -0
  332. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_integration_entropydata.py +0 -0
  333. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_lint.py +0 -0
  334. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_oracle_patch.py +0 -0
  335. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_redshift_patch.py +0 -0
  336. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_require_env.py +0 -0
  337. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_resolve.py +0 -0
  338. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_resolve_definitions.py +0 -0
  339. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_roundtrip_jsonschema.py +0 -0
  340. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_sql_type_converter_physicaltype.py +0 -0
  341. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_api.py +0 -0
  342. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_athena_iceberg.py +0 -0
  343. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_azure_blob_file.py +0 -0
  344. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_azure_remote.py +0 -0
  345. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_bigquery.py +0 -0
  346. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_checks_filter.py +0 -0
  347. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_databricks.py +0 -0
  348. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_delta.py +0 -0
  349. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_diagnostics.py +0 -0
  350. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_failed_samples.py +0 -0
  351. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_gcs_csv_remote.py +0 -0
  352. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_gcs_json_remote.py +0 -0
  353. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_kafka.py +0 -0
  354. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_kafka_remote.py +0 -0
  355. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_local_json.py +0 -0
  356. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_local_json_nd.py +0 -0
  357. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_mysql.py +0 -0
  358. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_oracle.py +0 -0
  359. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_oracle_xe.py +0 -0
  360. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_output_json.py +0 -0
  361. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_output_junit.py +0 -0
  362. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_parquet.py +0 -0
  363. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_postgres.py +0 -0
  364. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality.py +0 -0
  365. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality_local_fallback.py +0 -0
  366. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality_percent_severity.py +0 -0
  367. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_redshift.py +0 -0
  368. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_csv.py +0 -0
  369. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_delta.py +0 -0
  370. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json.py +0 -0
  371. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_complex.py +0 -0
  372. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_multiple_models.py +0 -0
  373. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_remote.py +0 -0
  374. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_schema_evolution.py +0 -0
  375. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_server_not_found.py +0 -0
  376. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_snowflake.py +0 -0
  377. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_soda_connection_error.py +0 -0
  378. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_sqlserver.py +0 -0
  379. {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_trino.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacontract-cli
3
- Version: 1.0.11
3
+ Version: 1.0.13
4
4
  Summary: The datacontract CLI is an open source command-line tool for working with Data Contracts. It uses data contract YAML files to lint the data contract, connect to data sources and execute schema and quality tests, detect breaking changes, and export to different formats. The tool is written in Python. It can be used as a standalone CLI tool, in a CI/CD pipeline, or directly as a Python library.
5
5
  Author-email: Jochen Christ <jochen.christ@innoq.com>, Stefan Negele <stefan.negele@innoq.com>, Simon Harrer <simon.harrer@innoq.com>
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Requires-Dist: typer<0.27,>=0.18.0
15
15
  Requires-Dist: click<9.0.0,>=8.1.0
16
16
  Requires-Dist: pydantic<2.14.0,>=2.8.2
17
17
  Requires-Dist: pyyaml~=6.0.1
18
- Requires-Dist: ruamel.yaml<0.19,>=0.17
18
+ Requires-Dist: ruamel.yaml<0.20,>=0.17
19
19
  Requires-Dist: requests<2.35,>=2.31
20
20
  Requires-Dist: truststore<1.0,>=0.10
21
21
  Requires-Dist: fastjsonschema<2.22.0,>=2.19.1
@@ -94,7 +94,7 @@ Provides-Extra: rdf
94
94
  Requires-Dist: rdflib==7.6.0; extra == "rdf"
95
95
  Provides-Extra: api
96
96
  Requires-Dist: fastapi<0.140.0,>=0.115.0; extra == "api"
97
- Requires-Dist: uvicorn<0.51.0,>=0.44.0; extra == "api"
97
+ Requires-Dist: uvicorn<0.52.0,>=0.44.0; extra == "api"
98
98
  Provides-Extra: protobuf
99
99
  Requires-Dist: proto-schema-parser<3.0,>=1.0; extra == "protobuf"
100
100
  Provides-Extra: all
@@ -112,7 +112,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
112
112
  Requires-Dist: pymssql==2.3.13; extra == "dev"
113
113
  Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
114
114
  Requires-Dist: mysql-connector-python<9.7.0,>=8.0.30; extra == "dev"
115
- Requires-Dist: ruff==0.15.20; extra == "dev"
115
+ Requires-Dist: ruff==0.15.21; extra == "dev"
116
116
  Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.14.2; extra == "dev"
117
117
  Requires-Dist: trino==0.338.0; extra == "dev"
118
118
  Dynamic: license-file
@@ -30,6 +30,7 @@ class MetricType(str, Enum):
30
30
  FIELD_PRESENT = "field_present"
31
31
  FIELD_TYPE = "field_type"
32
32
  FIELD_PHYSICAL_TYPE = "field_physical_type"
33
+ FIELD_NESTED_TYPE = "field_nested_type"
33
34
  FRESHNESS = "freshness"
34
35
  RETENTION = "retention"
35
36
  CUSTOM_SQL = "custom_sql"
@@ -22,6 +22,7 @@ from open_data_contract_standard.model import (
22
22
  )
23
23
 
24
24
  from datacontract.engines.checks.check_spec import CheckSpec, MetricType, Op, Threshold
25
+ from datacontract.engines.checks.type_normalize import normalize_type_name
25
26
  from datacontract.engines.ibis.native_type import supports_native_type_introspection
26
27
 
27
28
  logger = logging.getLogger(__name__)
@@ -54,6 +55,51 @@ def to_schema_name(schema_object: SchemaObject, server_type: Optional[str]) -> s
54
55
  return schema_object.name
55
56
 
56
57
 
58
+ def _scalar_element_type(prop: SchemaProperty, physical: bool) -> Optional[str]:
59
+ """The declared element type of an array of scalars, or None for anything else."""
60
+ if normalize_type_name(prop.logicalType or prop.physicalType) != "array" or prop.items is None:
61
+ return None
62
+ items = prop.items
63
+ if items.properties or items.items is not None:
64
+ return None
65
+ label = (items.physicalType or items.logicalType) if physical else (items.logicalType or items.physicalType)
66
+ if normalize_type_name(label) in ("object", "array"):
67
+ return None
68
+ return label
69
+
70
+
71
+ def _declared_type_label(prop: SchemaProperty, physical: bool) -> Optional[str]:
72
+ """Render the declared type with its children, e.g. ``OBJECT(code VARCHAR(10), description VARCHAR)``."""
73
+ label = (prop.physicalType or prop.logicalType) if physical else (prop.logicalType or prop.physicalType)
74
+ base = normalize_type_name(label)
75
+ if base == "array" and prop.items is not None:
76
+ return f"{label}({_declared_type_label(prop.items, physical)})"
77
+ if base == "object" and prop.properties:
78
+ children = ", ".join(f"{child.name} {_declared_type_label(child, physical)}" for child in prop.properties)
79
+ return f"{label}({children})"
80
+ return label
81
+
82
+
83
+ def _nested_type_check(model: str, field: str, prop: SchemaProperty, physical: bool) -> CheckSpec:
84
+ check_type = "field_nested_physical_type" if physical else "field_nested_type"
85
+ element = _scalar_element_type(prop, physical)
86
+ if element is not None:
87
+ name = f"Check that items of array {field} have {'physical type' if physical else 'type'} {element}"
88
+ else:
89
+ name = f"Check that nested {'physical types' if physical else 'types'} of {field} are correct"
90
+ return CheckSpec(
91
+ key=f"{model}__{field}__{check_type}",
92
+ category="schema",
93
+ type=check_type,
94
+ name=name,
95
+ model=model,
96
+ field=field,
97
+ metric=MetricType.FIELD_NESTED_TYPE,
98
+ expected_type_label=_declared_type_label(prop, physical),
99
+ expected_schema_property=prop,
100
+ )
101
+
102
+
57
103
  _PERCENT_UNITS = {"percent", "percentage", "%"}
58
104
 
59
105
 
@@ -172,6 +218,20 @@ def _to_schema_checks(schema_object: SchemaObject, server: Optional[Server]) ->
172
218
  )
173
219
  )
174
220
 
221
+ # The raw view cannot provide nested type checks
222
+ declared_base = normalize_type_name(prop.logicalType or prop.physicalType)
223
+ nested_checks_possible = (
224
+ check_types
225
+ and not uses_raw_view
226
+ and declared_base in ("object", "array")
227
+ and (bool(prop.properties) or prop.items is not None)
228
+ )
229
+ base_prop = (
230
+ SchemaProperty(name=prop.name, logicalType=prop.logicalType, physicalType=prop.physicalType)
231
+ if nested_checks_possible
232
+ else prop
233
+ )
234
+
175
235
  # A declared physicalType is checked against the column's real native
176
236
  # type in the platform catalog and takes precedence over logicalType,
177
237
  # but only on backends that expose a meaningful native type. Elsewhere
@@ -191,11 +251,13 @@ def _to_schema_checks(schema_object: SchemaObject, server: Optional[Server]) ->
191
251
  expected_physical_type=prop.physicalType,
192
252
  # Carried for the logicalType fallback when the native type
193
253
  # cannot be read or the physicalType is cross-dialect.
194
- expected_schema_property=prop if prop.logicalType is not None else None,
254
+ expected_schema_property=base_prop if prop.logicalType is not None else None,
195
255
  )
196
256
  )
257
+ if nested_checks_possible:
258
+ checks.append(_nested_type_check(model, field, prop, physical=True))
197
259
  elif check_types and prop.logicalType is not None:
198
- schema_prop, label = prop, prop.logicalType or ""
260
+ label = prop.logicalType or ""
199
261
  checks.append(
200
262
  CheckSpec(
201
263
  key=f"{model}__{field}__field_type",
@@ -207,9 +269,11 @@ def _to_schema_checks(schema_object: SchemaObject, server: Optional[Server]) ->
207
269
  metric=MetricType.FIELD_TYPE,
208
270
  expected_category=label,
209
271
  expected_type_label=label,
210
- expected_schema_property=schema_prop,
272
+ expected_schema_property=base_prop,
211
273
  )
212
274
  )
275
+ if nested_checks_possible:
276
+ checks.append(_nested_type_check(model, field, prop, physical=False))
213
277
 
214
278
  if prop.required:
215
279
  checks.append(
@@ -43,6 +43,31 @@ def _timestamp_family() -> set:
43
43
 
44
44
  _TIMESTAMP_FAMILY = _timestamp_family()
45
45
 
46
+ # Each group is a single Snowflake type under different names, which its catalog
47
+ # reports as the one canonical name (TEXT, NUMBER, FLOAT), so a contract declaring
48
+ # any of the aliases must match its own column. Dialect-specific because elsewhere
49
+ # (MySQL, SQL Server) TEXT and VARCHAR, or INT and DECIMAL, are distinct types.
50
+ _SNOWFLAKE_FAMILIES = (
51
+ {exp.DataType.Type.VARCHAR, exp.DataType.Type.TEXT, exp.DataType.Type.NVARCHAR},
52
+ # exact numerics: INT/INTEGER/BIGINT/SMALLINT/TINYINT/BYTEINT/NUMERIC are NUMBER(38,0)
53
+ {
54
+ exp.DataType.Type.DECIMAL,
55
+ exp.DataType.Type.INT,
56
+ exp.DataType.Type.BIGINT,
57
+ exp.DataType.Type.SMALLINT,
58
+ exp.DataType.Type.TINYINT,
59
+ },
60
+ # approximate numerics: FLOAT/FLOAT4/FLOAT8/DOUBLE/REAL are all 64-bit FLOAT
61
+ {exp.DataType.Type.DOUBLE, exp.DataType.Type.FLOAT},
62
+ )
63
+
64
+
65
+ def _is_snowflake(dialect) -> bool:
66
+ if isinstance(dialect, str):
67
+ return dialect.lower() == "snowflake"
68
+ name = getattr(dialect, "__name__", None) or type(dialect).__name__
69
+ return name.lower() == "snowflake"
70
+
46
71
 
47
72
  def _parse(type_str: str, dialect) -> Optional[exp.DataType]:
48
73
  """Parse a type string into an ``exp.DataType`` for ``dialect``, or ``None``."""
@@ -132,12 +157,14 @@ def physical_type_matches(
132
157
  exp_dt.this == act_dt.this
133
158
  or {exp_dt.this, act_dt.this} <= _TIMESTAMP_FAMILY
134
159
  or (both_numeric and _base_sql(exp_dt, dialect) == _base_sql(act_dt, dialect))
160
+ or (_is_snowflake(dialect) and any({exp_dt.this, act_dt.this} <= family for family in _SNOWFLAKE_FAMILIES))
135
161
  )
136
162
  if not same_base:
137
163
  return False, f"expected physical type '{expected}' but the column is '{actual}'"
138
164
 
139
- expected_params = _params(exp_dt)
140
- if expected_params and expected_params != _params(act_dt):
165
+ # sqlglot fills in a dialect's default precision (a bare NUMBER parses as
166
+ # DECIMAL(38,0)), so what the contract declares is read off the raw string.
167
+ if _split_base(_normalize_raw(expected))[1] and _params(exp_dt) != _params(act_dt):
141
168
  return False, f"expected physical type '{expected}' but the column is '{actual}'"
142
169
 
143
170
  return True, ""
@@ -11,9 +11,19 @@ the actual type is structurally compatible with the expected one.
11
11
  from __future__ import annotations
12
12
 
13
13
  import re
14
+ from typing import NamedTuple
14
15
 
15
16
  from open_data_contract_standard.model import SchemaProperty
16
17
 
18
+ from datacontract.engines.checks.physical_type_match import physical_type_matches
19
+
20
+ # logicalType marker for a field whose type the backend cannot describe: a
21
+ # dynamically-typed column (ibis json: Snowflake VARIANT, Postgres JSONB,
22
+ # BigQuery JSON) or an unsupported one (binary, geography). No declared type can
23
+ # be confirmed against it, so any expected type fails rather than silently passing.
24
+ # Distinct from a missing field, which is what dropping it would look like.
25
+ UNKNOWN_LOGICAL_TYPE = "__unknown__"
26
+
17
27
 
18
28
  def normalize_type_name(type_name: str | None) -> str | None:
19
29
  """Map a contract type name (logical or physical) to one of the 9 ODCS categories.
@@ -139,6 +149,8 @@ def schema_property_matches(expected: SchemaProperty | None, actual: SchemaPrope
139
149
  if expected_base is None:
140
150
  return True
141
151
  if actual_base is None:
152
+ # actual is unsupported or dynamically typed (json element/variant): a
153
+ # declared concrete type can't be confirmed against it, so it fails.
142
154
  return False
143
155
  if expected_base != actual_base and not (expected_base in _NUMERIC and actual_base in _NUMERIC):
144
156
  return False
@@ -151,6 +163,8 @@ def schema_property_matches(expected: SchemaProperty | None, actual: SchemaPrope
151
163
  if expected_base == "object":
152
164
  if not expected.properties:
153
165
  return True
166
+ # actual.properties is None for an untyped object (ibis map/variant): the
167
+ # empty lookup below fails every declared field, which is what we want.
154
168
  actual_by_name = {p.name.lower(): p for p in (actual.properties or []) if p.name}
155
169
  for exp_field in expected.properties:
156
170
  if not exp_field.name:
@@ -165,29 +179,42 @@ def schema_property_matches(expected: SchemaProperty | None, actual: SchemaPrope
165
179
  return True
166
180
 
167
181
 
168
- def schema_property_mismatch_reason(
169
- expected: SchemaProperty | None,
170
- actual: SchemaProperty | None,
171
- path: str = "",
172
- ) -> str:
173
- """Return a human-readable description of the first structural mismatch and a count of other type errors, or '' if none."""
174
- errors = schema_property_mismatch_reasons(expected, actual, path)
182
+ class TypeMismatch(NamedTuple):
183
+ message: str
184
+ # False when the actual structure cannot be read (json / variant / untyped
185
+ # object): the declared type is neither confirmed nor refuted.
186
+ verifiable: bool
187
+
188
+
189
+ def format_mismatch_reason(errors: list[TypeMismatch]) -> str:
190
+ """The first mismatch and a count of the other type errors, or '' if none."""
175
191
  num_errors = len(errors)
176
192
 
177
193
  if num_errors == 0:
178
194
  return ""
179
195
  elif num_errors == 1:
180
- return errors[0]
196
+ return errors[0].message
181
197
  else:
182
- return f"{errors[0]} (and {num_errors - 1} other error{'s' if num_errors > 2 else ''})"
198
+ return f"{errors[0].message} (and {num_errors - 1} other error{'s' if num_errors > 2 else ''})"
199
+
200
+
201
+ def schema_property_mismatch_reason(
202
+ expected: SchemaProperty | None,
203
+ actual: SchemaProperty | None,
204
+ path: str = "",
205
+ dialect=None,
206
+ ) -> str:
207
+ """Return a human-readable description of the first structural mismatch and a count of other type errors, or '' if none."""
208
+ return format_mismatch_reason(schema_property_mismatch_reasons(expected, actual, path, dialect))
183
209
 
184
210
 
185
211
  def schema_property_mismatch_reasons(
186
212
  expected: SchemaProperty | None,
187
213
  actual: SchemaProperty | None,
188
214
  path: str = "",
189
- ) -> list[str]:
190
- errors: list[str] = []
215
+ dialect=None,
216
+ ) -> list[TypeMismatch]:
217
+ errors: list[TypeMismatch] = []
191
218
 
192
219
  if expected is None:
193
220
  return errors
@@ -195,29 +222,73 @@ def schema_property_mismatch_reasons(
195
222
  field_label = f"field '{path}'" if path else "column"
196
223
  if actual is None:
197
224
  exp_str = expected.logicalType or expected.physicalType
198
- errors.append(f"{field_label}: expected type '{exp_str}' but the column type could not be determined")
225
+ errors.append(
226
+ TypeMismatch(
227
+ f"{field_label}: expected type '{exp_str}' but the column type could not be determined",
228
+ verifiable=False,
229
+ )
230
+ )
199
231
  return errors
200
-
201
232
  expected_base = normalize_type_name(expected.logicalType or expected.physicalType)
202
233
  actual_base = normalize_type_name(actual.logicalType or actual.physicalType)
203
234
 
204
235
  if expected_base is None:
205
236
  return errors
206
237
 
238
+ if actual_base is None:
239
+ exp_str = expected.logicalType or expected.physicalType
240
+ act_str = actual.physicalType or "unknown"
241
+ errors.append(
242
+ TypeMismatch(
243
+ f"{field_label}: has type '{act_str}', but the contract specifies '{exp_str}'. "
244
+ f"A '{act_str}' value has no verifiable logical type. "
245
+ f"If this is intentional, specify `physicalType: {act_str}`.",
246
+ verifiable=False,
247
+ )
248
+ )
249
+ return errors
250
+
251
+ # A leaf that declares a physicalType is compared against the column's real
252
+ # native type, where the backend reports one. The declared type wins over the
253
+ # logicalType, as it does for the column itself.
254
+ if expected_base not in ("object", "array") and expected.physicalType and actual.physicalType:
255
+ result, reason = physical_type_matches(expected.physicalType, actual.physicalType, dialect)
256
+ if result is True:
257
+ return errors
258
+ if result is False:
259
+ errors.append(TypeMismatch(f"{field_label}: {reason}", verifiable=True))
260
+ return errors
261
+ # The declared type names nothing in this dialect (a logical keyword such
262
+ # as `string`, or a type from another platform): fall back to the category
263
+ # comparison, and only give up when there is no logicalType to fall back on.
264
+ if expected.logicalType is None:
265
+ errors.append(TypeMismatch(f"{field_label}: {reason}", verifiable=False))
266
+ return errors
267
+
207
268
  if expected_base != actual_base and not (expected_base in _NUMERIC and actual_base in _NUMERIC):
208
269
  exp_str = expected.logicalType or expected.physicalType
209
270
  act_str = actual.logicalType or actual.physicalType
210
- errors.append(f"{field_label}: expected type '{exp_str}' but got '{act_str}'")
271
+ errors.append(TypeMismatch(f"{field_label}: expected type '{exp_str}' but got '{act_str}'", verifiable=True))
211
272
  return errors
212
273
 
213
274
  if expected_base == "array":
214
275
  if expected.items is not None:
215
276
  child_path = f"{path}[]" if path else "[]"
216
- errors.extend(schema_property_mismatch_reasons(expected.items, actual.items, child_path))
277
+ errors.extend(schema_property_mismatch_reasons(expected.items, actual.items, child_path, dialect))
217
278
 
218
279
  if expected_base == "object":
219
280
  if not expected.properties:
220
281
  return errors
282
+ if actual.properties is None:
283
+ errors.append(
284
+ TypeMismatch(
285
+ f"{field_label}: nested properties are declared but the column is an untyped object "
286
+ "whose structure can't be verified. Use a structured type for your data or specify "
287
+ "physicalType.",
288
+ verifiable=False,
289
+ )
290
+ )
291
+ return errors
221
292
 
222
293
  actual_by_name = {p.name.lower(): p for p in (actual.properties or []) if p.name}
223
294
  for exp_field in expected.properties:
@@ -228,9 +299,9 @@ def schema_property_mismatch_reasons(
228
299
  act_field = actual_by_name.get(exp_field.name.lower())
229
300
 
230
301
  if act_field is None:
231
- errors.append(f"field '{child_path}' is missing")
302
+ errors.append(TypeMismatch(f"field '{child_path}' is missing", verifiable=True))
232
303
  continue
233
304
 
234
- errors.extend(schema_property_mismatch_reasons(exp_field, act_field, child_path))
305
+ errors.extend(schema_property_mismatch_reasons(exp_field, act_field, child_path, dialect))
235
306
 
236
307
  return errors
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  from open_data_contract_standard.model import SchemaProperty
8
8
 
9
+ from datacontract.engines.checks.type_normalize import UNKNOWN_LOGICAL_TYPE
10
+
9
11
  if TYPE_CHECKING:
10
12
  from ibis.expr.datatypes import DataType
11
13
 
@@ -48,13 +50,14 @@ def ibis_dtype_category(dtype) -> str:
48
50
  return "other"
49
51
 
50
52
 
51
- def ibis_dtype_to_schema_property(dtype: DataType) -> SchemaProperty | None:
53
+ def ibis_dtype_to_schema_property(dtype: DataType) -> SchemaProperty:
52
54
  """Map an ibis DataType to a SchemaProperty for structural type comparison.
53
55
 
54
56
  Returns a ``SchemaProperty`` with ``logicalType`` set to one of the 9 ODCS
55
57
  categories, recursively populating ``properties`` for structs and ``items``
56
- for arrays. Returns ``None`` for unsupported types (binary, map, null, etc.)
57
- so the caller can skip comparison rather than raising a false failure.
58
+ for arrays. A type that carries no verifiable logical type (json, binary,
59
+ null, …) becomes ``UNKNOWN_LOGICAL_TYPE`` with the actual type kept in
60
+ ``physicalType``, so the failure message can name it.
58
61
  """
59
62
  try:
60
63
  if dtype.is_boolean():
@@ -74,20 +77,25 @@ def ibis_dtype_to_schema_property(dtype: DataType) -> SchemaProperty | None:
74
77
  if dtype.is_struct():
75
78
  properties = []
76
79
  for field_name, ftype in dtype.fields.items():
80
+ # an unverifiable field type is still a field: keep it, so it is not reported missing
77
81
  child = ibis_dtype_to_schema_property(ftype)
78
- if child is not None:
79
- properties.append(
80
- SchemaProperty(
81
- name=field_name,
82
- logicalType=child.logicalType,
83
- items=child.items,
84
- properties=child.properties,
85
- )
82
+ properties.append(
83
+ SchemaProperty(
84
+ name=field_name,
85
+ logicalType=child.logicalType,
86
+ physicalType=child.physicalType,
87
+ items=child.items,
88
+ properties=child.properties,
86
89
  )
87
- return SchemaProperty(logicalType="object", properties=properties if properties else None)
90
+ )
91
+ return SchemaProperty(logicalType="object", properties=properties)
88
92
  if dtype.is_array():
89
93
  element = ibis_dtype_to_schema_property(dtype.value_type)
90
94
  return SchemaProperty(logicalType="array", items=element)
95
+ if dtype.is_map():
96
+ # base is confirmable, inner values are not (yet)
97
+ return SchemaProperty(logicalType="object", properties=None)
91
98
  except AttributeError:
92
- return None
93
- return None
99
+ return SchemaProperty(logicalType=UNKNOWN_LOGICAL_TYPE)
100
+ # json holds a different type per row; binary and null have no ODCS category
101
+ return SchemaProperty(logicalType=UNKNOWN_LOGICAL_TYPE, physicalType=str(dtype))
@@ -14,14 +14,21 @@ import uuid
14
14
  from collections import defaultdict
15
15
  from typing import List, Optional
16
16
 
17
- from open_data_contract_standard.model import OpenDataContractStandard, Server
17
+ from open_data_contract_standard.model import OpenDataContractStandard, SchemaProperty, Server
18
18
 
19
19
  from datacontract.engines.checks.check_spec import CheckSpec, MetricType
20
20
  from datacontract.engines.checks.physical_type_match import physical_type_matches
21
- from datacontract.engines.checks.type_normalize import schema_property_matches, schema_property_mismatch_reason
21
+ from datacontract.engines.checks.type_normalize import (
22
+ format_mismatch_reason,
23
+ normalize_type_name,
24
+ schema_property_matches,
25
+ schema_property_mismatch_reason,
26
+ schema_property_mismatch_reasons,
27
+ )
22
28
  from datacontract.engines.ibis.connections.connect import connect_ibis
23
29
  from datacontract.engines.ibis.dtype_category import ibis_dtype_to_schema_property
24
30
  from datacontract.engines.ibis.native_type import fetch_native_types, sqlglot_dialect
31
+ from datacontract.engines.ibis.snowflake_structured_types import fetch_structured_types, has_nesting
25
32
  from datacontract.model.exceptions import DataContractException
26
33
  from datacontract.model.run import Check, ResultEnum, Run
27
34
  from datacontract.model.server import get_server_type
@@ -62,6 +69,8 @@ def _describe(spec: CheckSpec) -> str:
62
69
  return f"type({spec.field}) == {spec.expected_type_label}"
63
70
  if spec.metric == MetricType.FIELD_PHYSICAL_TYPE:
64
71
  return f"physical_type({spec.field}) == {spec.expected_physical_type}"
72
+ if spec.metric == MetricType.FIELD_NESTED_TYPE:
73
+ return f"nested_types({spec.field}) match the contract"
65
74
  if spec.metric == MetricType.FIELD_PRESENT:
66
75
  return f"present({spec.field})"
67
76
  if spec.threshold is not None:
@@ -196,6 +205,15 @@ def _run_model(
196
205
  if any(spec.metric == MetricType.FIELD_PHYSICAL_TYPE for spec in specs):
197
206
  native_types = fetch_native_types(con, server, model)
198
207
 
208
+ # Snowflake collapses structured OBJECT/ARRAY nesting in the ibis dtype; read
209
+ # the real nested types from SHOW COLUMNS so field_type checks can recurse.
210
+ structured_types = None
211
+ if get_server_type(server) == "snowflake" and any(
212
+ spec.metric in (MetricType.FIELD_TYPE, MetricType.FIELD_PHYSICAL_TYPE, MetricType.FIELD_NESTED_TYPE)
213
+ for spec in specs
214
+ ):
215
+ structured_types = fetch_structured_types(con, server, t.get_name())
216
+
199
217
  agg_exprs = [] # list[(spec, named_expr)]
200
218
  for spec in specs:
201
219
  try:
@@ -219,9 +237,11 @@ def _run_model(
219
237
  elif spec.metric == MetricType.FIELD_PRESENT:
220
238
  _run_present(run, con, model, columns, spec)
221
239
  elif spec.metric == MetricType.FIELD_TYPE:
222
- _run_type(run, schema, columns, spec)
240
+ _run_type(run, schema, columns, spec, structured_types)
223
241
  elif spec.metric == MetricType.FIELD_PHYSICAL_TYPE:
224
- _run_physical_type(run, con, server, schema, columns, native_types, spec)
242
+ _run_physical_type(run, con, server, schema, columns, native_types, spec, structured_types)
243
+ elif spec.metric == MetricType.FIELD_NESTED_TYPE:
244
+ _run_nested_type(run, schema, columns, spec, structured_types, sqlglot_dialect(con))
225
245
  elif spec.metric in (MetricType.FRESHNESS, MetricType.RETENTION):
226
246
  _run_freshness(run, t, columns, spec)
227
247
  elif spec.metric == MetricType.CUSTOM_SQL:
@@ -615,7 +635,7 @@ def _run_present(run: Run, con, model: str, columns, spec: CheckSpec):
615
635
  )
616
636
 
617
637
 
618
- def _run_type(run: Run, schema, columns, spec: CheckSpec):
638
+ def _run_type(run: Run, schema, columns, spec: CheckSpec, structured_types: dict[str, SchemaProperty] | None = None):
619
639
  _set_impl(
620
640
  run,
621
641
  spec.key,
@@ -628,11 +648,19 @@ def _run_type(run: Run, schema, columns, spec: CheckSpec):
628
648
  _set_result(run, spec.key, ResultEnum.failed, f"Column '{spec.field}' is missing")
629
649
  return
630
650
  dtype = schema[actual_col]
631
- actual_prop = ibis_dtype_to_schema_property(dtype)
651
+ # Snowflake structured types come back collapsed from ibis; prefer the nested
652
+ # tree recovered from SHOW COLUMNS when available.
653
+ structured_prop = structured_types.get(spec.field.lower()) if structured_types else None
654
+ actual_prop = structured_prop or ibis_dtype_to_schema_property(dtype)
632
655
  _set_diagnostics(
633
656
  run,
634
657
  spec.key,
635
- _diag(metric="field_type", field=spec.field, expected=spec.expected_type_label, actual=str(dtype)),
658
+ _diag(
659
+ metric="field_type",
660
+ field=spec.field,
661
+ expected=spec.expected_type_label,
662
+ actual=structured_prop.physicalType if structured_prop else str(dtype),
663
+ ),
636
664
  )
637
665
  if schema_property_matches(spec.expected_schema_property, actual_prop):
638
666
  _set_result(run, spec.key, ResultEnum.passed, None)
@@ -646,7 +674,16 @@ def _run_type(run: Run, schema, columns, spec: CheckSpec):
646
674
  )
647
675
 
648
676
 
649
- def _run_physical_type(run: Run, con, server, schema, columns, native_types, spec: CheckSpec):
677
+ def _run_physical_type(
678
+ run: Run,
679
+ con,
680
+ server,
681
+ schema,
682
+ columns,
683
+ native_types,
684
+ spec: CheckSpec,
685
+ structured_types: dict[str, SchemaProperty] | None = None,
686
+ ):
650
687
  """Compare a column's real native type against the contract's physicalType.
651
688
 
652
689
  When the native type cannot be read for this backend, or the declared
@@ -668,7 +705,13 @@ def _run_physical_type(run: Run, con, server, schema, columns, native_types, spe
668
705
  _set_result(run, spec.key, ResultEnum.failed, f"Column '{spec.field}' is missing")
669
706
  return
670
707
 
671
- actual_native = native_types.get(spec.field.lower()) if native_types else None
708
+ # The catalog reports a structured OBJECT/ARRAY as its bare token, dropping the
709
+ # nested types; the tree recovered from SHOW COLUMNS renders the real native type.
710
+ structured_prop = structured_types.get(spec.field.lower()) if structured_types else None
711
+ if structured_prop is not None and has_nesting(structured_prop):
712
+ actual_native = structured_prop.physicalType
713
+ else:
714
+ actual_native = native_types.get(spec.field.lower()) if native_types else None
672
715
  _set_diagnostics(
673
716
  run,
674
717
  spec.key,
@@ -697,22 +740,91 @@ def _run_physical_type(run: Run, con, server, schema, columns, native_types, spe
697
740
  # logicalType category check when the property declares one.
698
741
  fallback = spec.expected_schema_property
699
742
  if fallback is not None and fallback.logicalType is not None:
700
- actual_prop = ibis_dtype_to_schema_property(schema[actual_col])
743
+ actual_prop = structured_prop or ibis_dtype_to_schema_property(schema[actual_col])
701
744
  if schema_property_matches(fallback, actual_prop):
702
745
  _set_result(run, spec.key, ResultEnum.passed, None)
703
746
  else:
704
747
  mismatch = schema_property_mismatch_reason(fallback, actual_prop)
748
+ actual_label = actual_native or schema[actual_col]
705
749
  _set_result(
706
750
  run,
707
751
  spec.key,
708
752
  ResultEnum.failed,
709
- mismatch or f"Expected type '{fallback.logicalType}' but column is '{schema[actual_col]}'",
753
+ mismatch or f"Expected type '{fallback.logicalType}' but column is '{actual_label}'",
710
754
  )
711
755
  return
712
756
 
713
757
  _set_result(run, spec.key, ResultEnum.warning, f"{reason}; skipping the physical type check")
714
758
 
715
759
 
760
+ def _run_nested_type(
761
+ run: Run,
762
+ schema,
763
+ columns,
764
+ spec: CheckSpec,
765
+ structured_types: dict[str, SchemaProperty] | None = None,
766
+ dialect=None,
767
+ ):
768
+ """Compare the children a property declares (``properties:`` / ``items:``) against
769
+ the column's real nested structure. The column's own type is the base check's job.
770
+ """
771
+ metric = spec.type
772
+ _set_impl(run, spec.key, f"nested types of '{spec.field}' match the contract", "introspection")
773
+ actual_col = columns.get(spec.field.lower())
774
+ if actual_col is None:
775
+ _set_diagnostics(run, spec.key, _diag(metric=metric, field=spec.field, expected=spec.expected_type_label))
776
+ _set_result(run, spec.key, ResultEnum.failed, f"Column '{spec.field}' is missing")
777
+ return
778
+
779
+ dtype = schema[actual_col]
780
+ structured_prop = structured_types.get(spec.field.lower()) if structured_types else None
781
+ actual_prop = structured_prop or ibis_dtype_to_schema_property(dtype)
782
+ actual_label = (structured_prop.physicalType if structured_prop else None) or str(dtype)
783
+ _set_diagnostics(
784
+ run,
785
+ spec.key,
786
+ _diag(metric=metric, field=spec.field, expected=spec.expected_type_label, actual=actual_label),
787
+ )
788
+
789
+ expected = spec.expected_schema_property
790
+ expected_base = normalize_type_name(expected.logicalType or expected.physicalType)
791
+ actual_base = normalize_type_name(actual_prop.logicalType or actual_prop.physicalType)
792
+ if actual_base is None:
793
+ # A dynamically-typed column (json / variant / jsonb) holds a different
794
+ # structure per row, so there is nothing to compare the children against.
795
+ _set_result(
796
+ run,
797
+ spec.key,
798
+ ResultEnum.warning,
799
+ f"The structure of the '{actual_label}' column '{spec.field}' cannot be read; "
800
+ f"skipping the nested type check",
801
+ )
802
+ return
803
+ if expected_base != actual_base:
804
+ # The base type check names the actual type; repeating it here would print
805
+ # the column's whole rendered structure.
806
+ _set_result(
807
+ run,
808
+ spec.key,
809
+ ResultEnum.failed,
810
+ f"Cannot verify the nested types of '{spec.field}': the column is not an {expected_base}",
811
+ )
812
+ return
813
+
814
+ errors = schema_property_mismatch_reasons(expected, actual_prop, spec.field, dialect)
815
+ if not errors:
816
+ _set_result(run, spec.key, ResultEnum.passed, None)
817
+ return
818
+ _update_diagnostics(run, spec.key, {"errors": [error.message for error in errors]})
819
+ verified = any(error.verifiable for error in errors)
820
+ _set_result(
821
+ run,
822
+ spec.key,
823
+ ResultEnum.failed if verified else ResultEnum.warning,
824
+ format_mismatch_reason(errors),
825
+ )
826
+
827
+
716
828
  def _run_freshness(run: Run, t, columns, spec: CheckSpec):
717
829
  import pandas as pd
718
830