datacontract-cli 1.2.3__tar.gz → 1.2.4__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 (476) hide show
  1. {datacontract_cli-1.2.3/datacontract_cli.egg-info → datacontract_cli-1.2.4}/PKG-INFO +7 -7
  2. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/README.md +1 -1
  3. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/api.py +8 -1
  4. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/cli.py +20 -1
  5. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_ci.py +5 -2
  6. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_test.py +4 -1
  7. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/config/settings.py +5 -0
  8. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/config/variables.py +38 -8
  9. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/check_spec.py +6 -1
  10. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/create_checks.py +178 -22
  11. datacontract_cli-1.2.4/datacontract/engines/checks/custom_quality_check.py +552 -0
  12. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/dimensions.py +2 -0
  13. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/sql_guard.py +4 -0
  14. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/data_contract_test.py +7 -1
  15. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/check_hana_execute.py +2 -0
  16. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/hana_quality_check.py +126 -16
  17. datacontract_cli-1.2.4/datacontract/engines/ibis/__init__.py +16 -0
  18. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/connect.py +6 -6
  19. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/duckdb_connection.py +13 -0
  20. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/ibis_check_execute.py +32 -0
  21. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/mysql_importer.py +2 -3
  22. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/test_results_writer.py +2 -2
  23. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4/datacontract_cli.egg-info}/PKG-INFO +7 -7
  24. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract_cli.egg-info/SOURCES.txt +3 -0
  25. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract_cli.egg-info/requires.txt +5 -5
  26. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/pyproject.toml +6 -7
  27. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_api.py +14 -0
  28. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_mysql.py +15 -4
  29. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_sql_placeholders.py +22 -17
  30. datacontract_cli-1.2.4/tests/test_custom_quality_checks.py +531 -0
  31. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_hana_quality_check.py +39 -0
  32. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_mysql.py +30 -0
  33. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_lint.py +1 -1
  34. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_quality_sql_dialects.py +4 -0
  35. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_postgres.py +13 -0
  36. datacontract_cli-1.2.4/tests/test_test_relationships.py +270 -0
  37. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_results_writer.py +6 -0
  38. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_untrusted_contract.py +1 -1
  39. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_variables.py +19 -0
  40. datacontract_cli-1.2.3/datacontract/output/__init__.py +0 -0
  41. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/LICENSE +0 -0
  42. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/MANIFEST.in +0 -0
  43. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/__init__.py +0 -0
  44. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/breaking/__init__.py +0 -0
  45. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/breaking/detector.py +0 -0
  46. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/breaking/rules.py +0 -0
  47. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/catalog/catalog.py +0 -0
  48. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/changelog/__init__.py +0 -0
  49. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/changelog/changelog.py +0 -0
  50. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/changelog/normalize.py +0 -0
  51. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_api.py +0 -0
  52. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_breaking.py +0 -0
  53. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_catalog.py +0 -0
  54. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_changelog.py +0 -0
  55. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_dbt.py +0 -0
  56. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_edit.py +0 -0
  57. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_export.py +0 -0
  58. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_import.py +0 -0
  59. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_init.py +0 -0
  60. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_lint.py +0 -0
  61. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/command_publish.py +0 -0
  62. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/config/__init__.py +0 -0
  63. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/config/cli_context.py +0 -0
  64. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/data_contract.py +0 -0
  65. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/VERSION +0 -0
  66. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
  67. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
  68. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/datacontract-editor.css +0 -0
  69. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
  70. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/index.html +0 -0
  71. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
  72. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
  73. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/odcs.svg +0 -0
  74. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/robots.txt +0 -0
  75. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/rolldown-runtime-DY7j01NX.js +0 -0
  76. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/editor_assets/sitemap.xml +0 -0
  77. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/__init__.py +0 -0
  78. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/__init__.py +0 -0
  79. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/check_filter.py +0 -0
  80. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/physical_type_match.py +0 -0
  81. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/severity.py +0 -0
  82. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/checks/type_normalize.py +0 -0
  83. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
  84. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
  85. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
  86. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
  87. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
  88. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/__init__.py +0 -0
  89. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/hana_check_selection.py +0 -0
  90. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/hana_connection.py +0 -0
  91. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/hana_schema_check.py +0 -0
  92. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/hana/hana_type_mapping.py +0 -0
  93. {datacontract_cli-1.2.3/datacontract/engines/ibis → datacontract_cli-1.2.4/datacontract/engines/ibis/connections}/__init__.py +0 -0
  94. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
  95. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
  96. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/exasol_patch.py +0 -0
  97. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/hive_patch.py +0 -0
  98. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/iceberg.py +0 -0
  99. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/kafka.py +0 -0
  100. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
  101. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/redshift_credentials.py +0 -0
  102. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
  103. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/dtype_category.py +0 -0
  104. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/native_type.py +0 -0
  105. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/engines/ibis/snowflake_structured_types.py +0 -0
  106. {datacontract_cli-1.2.3/datacontract/engines/ibis/connections → datacontract_cli-1.2.4/datacontract/export}/__init__.py +0 -0
  107. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/avro_exporter.py +0 -0
  108. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/avro_idl_exporter.py +0 -0
  109. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/bigquery_exporter.py +0 -0
  110. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/custom_exporter.py +0 -0
  111. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/data_caterer_exporter.py +0 -0
  112. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/dbml_exporter.py +0 -0
  113. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/dbt_exporter.py +0 -0
  114. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/dcs_exporter.py +0 -0
  115. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/dqx_exporter.py +0 -0
  116. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/duckdb_type_converter.py +0 -0
  117. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/excel_exporter.py +0 -0
  118. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/exporter.py +0 -0
  119. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/exporter_factory.py +0 -0
  120. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/go_exporter.py +0 -0
  121. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/great_expectations_exporter.py +0 -0
  122. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/html_exporter.py +0 -0
  123. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/iceberg_exporter.py +0 -0
  124. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/jsonschema_exporter.py +0 -0
  125. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/markdown_exporter.py +0 -0
  126. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/mermaid_exporter.py +0 -0
  127. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/odcs_v3_exporter.py +0 -0
  128. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/pandas_type_converter.py +0 -0
  129. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/protobuf_exporter.py +0 -0
  130. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/pydantic_exporter.py +0 -0
  131. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/rdf_exporter.py +0 -0
  132. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/sodacl_check_builder.py +0 -0
  133. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/sodacl_exporter.py +0 -0
  134. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/spark_exporter.py +0 -0
  135. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/sql_exporter.py +0 -0
  136. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/sql_type_converter.py +0 -0
  137. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/sqlalchemy_exporter.py +0 -0
  138. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/export/xsd_exporter.py +0 -0
  139. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/athena_importer.py +0 -0
  140. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/avro_importer.py +0 -0
  141. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/bigquery_importer.py +0 -0
  142. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/clickhouse_importer.py +0 -0
  143. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/csv_importer.py +0 -0
  144. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/dbml_importer.py +0 -0
  145. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/dbt_importer.py +0 -0
  146. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/dcs_importer.py +0 -0
  147. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/excel_importer.py +0 -0
  148. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/glue_importer.py +0 -0
  149. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/hive_importer.py +0 -0
  150. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/iceberg_importer.py +0 -0
  151. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/importer.py +0 -0
  152. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/importer_factory.py +0 -0
  153. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/json_importer.py +0 -0
  154. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/jsonschema_importer.py +0 -0
  155. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/object_storage_importer.py +0 -0
  156. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/odata_importer.py +0 -0
  157. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/odcs_helper.py +0 -0
  158. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/odcs_importer.py +0 -0
  159. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/openapi_importer.py +0 -0
  160. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/oracle_importer.py +0 -0
  161. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/parquet_importer.py +0 -0
  162. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/postgres_importer.py +0 -0
  163. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/powerbi_importer.py +0 -0
  164. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/protobuf_importer.py +0 -0
  165. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/pydantic_importer.py +0 -0
  166. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/redshift_importer.py +0 -0
  167. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/snowflake_importer.py +0 -0
  168. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/spark_importer.py +0 -0
  169. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/spark_type_json.py +0 -0
  170. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/sql_importer.py +0 -0
  171. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/sqlserver_importer.py +0 -0
  172. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/trino_importer.py +0 -0
  173. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/unity_importer.py +0 -0
  174. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/imports/xsd_importer.py +0 -0
  175. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/init/init_template.py +0 -0
  176. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/integration/dbt_sync.py +0 -0
  177. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/integration/dbt_test_mapping.py +0 -0
  178. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/integration/entropy_data.py +0 -0
  179. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/constraints.py +0 -0
  180. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/files.py +0 -0
  181. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/resolve.py +0 -0
  182. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/resources.py +0 -0
  183. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/s3.py +0 -0
  184. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/schema.py +0 -0
  185. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/lint/urls.py +0 -0
  186. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/breaking.py +0 -0
  187. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/changelog.py +0 -0
  188. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/enum_values.py +0 -0
  189. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/exceptions.py +0 -0
  190. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/map_type.py +0 -0
  191. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/natural_keys.py +0 -0
  192. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/odcs.py +0 -0
  193. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/run.py +0 -0
  194. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/server.py +0 -0
  195. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/vector_type.py +0 -0
  196. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/model/workbook.py +0 -0
  197. {datacontract_cli-1.2.3/datacontract/export → datacontract_cli-1.2.4/datacontract/output}/__init__.py +0 -0
  198. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/ci_output.py +0 -0
  199. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/json_test_results.py +0 -0
  200. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/junit_test_results.py +0 -0
  201. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/output_format.py +0 -0
  202. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/text_breaking_results.py +0 -0
  203. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/output/text_changelog_results.py +0 -0
  204. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/py.typed +0 -0
  205. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
  206. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
  207. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
  208. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
  209. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
  210. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
  211. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
  212. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
  213. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/odcs-3.2.0.init.yaml +0 -0
  214. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/schemas/odcs-3.2.0.schema.json +0 -0
  215. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/datacontract.html +0 -0
  216. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/datacontract_odcs.html +0 -0
  217. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/excel/odcs-template-v3.0.xlsx +0 -0
  218. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/excel/odcs-template-v3.1.xlsx +0 -0
  219. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/excel/odcs-template-v3.2.xlsx +0 -0
  220. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/index.html +0 -0
  221. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/macros/authoritative_definitions.html +0 -0
  222. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/macros/context.html +0 -0
  223. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/macros/schema.html +0 -0
  224. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/macros/server.html +0 -0
  225. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/macros/support.html +0 -0
  226. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/datacontract_information.html +0 -0
  227. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
  228. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/datacontract_terms.html +0 -0
  229. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/definition.html +0 -0
  230. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/example.html +0 -0
  231. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/model_field.html +0 -0
  232. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/quality.html +0 -0
  233. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials/server.html +0 -0
  234. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/context.html +0 -0
  235. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/custom_properties.html +0 -0
  236. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/diagram.html +0 -0
  237. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
  238. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/footer.html +0 -0
  239. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
  240. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/header.html +0 -0
  241. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/pricing.html +0 -0
  242. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/roles.html +0 -0
  243. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/schema.html +0 -0
  244. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/servers.html +0 -0
  245. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/sla.html +0 -0
  246. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/support.html +0 -0
  247. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/team.html +0 -0
  248. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
  249. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/partials_odcs/title.html +0 -0
  250. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract/templates/style/output.css +0 -0
  251. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract_cli.egg-info/dependency_links.txt +0 -0
  252. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract_cli.egg-info/entry_points.txt +0 -0
  253. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/datacontract_cli.egg-info/top_level.txt +0 -0
  254. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/setup.cfg +0 -0
  255. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_api_openapi.py +0 -0
  256. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_breaking.py +0 -0
  257. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_breaking_odcs_3_2.py +0 -0
  258. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_catalog.py +0 -0
  259. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_changelog.py +0 -0
  260. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_changelog_engine.py +0 -0
  261. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_changelog_normalize.py +0 -0
  262. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_changelog_output_text.py +0 -0
  263. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_check_quality_provenance.py +0 -0
  264. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_ci_output.py +0 -0
  265. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_cli.py +0 -0
  266. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_command_api.py +0 -0
  267. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_command_api_options.py +0 -0
  268. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_config.py +0 -0
  269. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_config_file.py +0 -0
  270. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_config_require.py +0 -0
  271. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_config_threading.py +0 -0
  272. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_athena.py +0 -0
  273. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_bigquery.py +0 -0
  274. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_clickhouse.py +0 -0
  275. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_databricks.py +0 -0
  276. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_exasol.py +0 -0
  277. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_hive.py +0 -0
  278. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_impala.py +0 -0
  279. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_oracle.py +0 -0
  280. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_postgres.py +0 -0
  281. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_redshift.py +0 -0
  282. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_s3_credentials.py +0 -0
  283. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_snowflake.py +0 -0
  284. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_sqlserver.py +0 -0
  285. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_connect_trino.py +0 -0
  286. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_array_items.py +0 -0
  287. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_nested.py +0 -0
  288. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_nested_type.py +0 -0
  289. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_physical_type.py +0 -0
  290. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_primary_key.py +0 -0
  291. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_retention_period.py +0 -0
  292. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_create_checks_servicelevel.py +0 -0
  293. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_data_contract.py +0 -0
  294. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_data_contract_checks.py +0 -0
  295. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_data_contract_specification.py +0 -0
  296. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_dbt_sync.py +0 -0
  297. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_dbt_test_mapping.py +0 -0
  298. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_description_linter.py +0 -0
  299. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_docs_commands.py +0 -0
  300. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_docs_config_options.py +0 -0
  301. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_docs_ordering.py +0 -0
  302. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_download_datacontract_file.py +0 -0
  303. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_dtype_category.py +0 -0
  304. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_duckdb_json.py +0 -0
  305. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_edit.py +0 -0
  306. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_enum_values.py +0 -0
  307. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_avro.py +0 -0
  308. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_avro_idl.py +0 -0
  309. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_bigquery.py +0 -0
  310. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_complex_data_contract.py +0 -0
  311. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_custom.py +0 -0
  312. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_custom_exporter.py +0 -0
  313. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_custom_model.py +0 -0
  314. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_data_caterer.py +0 -0
  315. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dbml.py +0 -0
  316. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dbt_models.py +0 -0
  317. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dbt_sources.py +0 -0
  318. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dbt_staging_sql.py +0 -0
  319. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dcs.py +0 -0
  320. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_dqx.py +0 -0
  321. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_excel.py +0 -0
  322. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_go.py +0 -0
  323. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_great_expectations.py +0 -0
  324. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_html.py +0 -0
  325. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_iceberg.py +0 -0
  326. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_jsonschema.py +0 -0
  327. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_large_contract.py +0 -0
  328. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_markdown.py +0 -0
  329. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_mermaid.py +0 -0
  330. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_odcs_v3.py +0 -0
  331. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_protobuf.py +0 -0
  332. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_pydantic.py +0 -0
  333. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_rdf.py +0 -0
  334. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_sodacl.py +0 -0
  335. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_spark.py +0 -0
  336. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_sql.py +0 -0
  337. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_sql_clickhouse.py +0 -0
  338. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_sql_query.py +0 -0
  339. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_sqlalchemy.py +0 -0
  340. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_export_xsd.py +0 -0
  341. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_hana_connection.py +0 -0
  342. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_hana_schema_check.py +0 -0
  343. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_hana_type_mapping.py +0 -0
  344. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_ibis_check_execute.py +0 -0
  345. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_ibis_mssql_pattern.py +0 -0
  346. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_ibis_mssql_schema.py +0 -0
  347. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_ibis_oracle_schema.py +0 -0
  348. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_athena.py +0 -0
  349. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_avro.py +0 -0
  350. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_bigquery.py +0 -0
  351. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_clickhouse.py +0 -0
  352. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_csv.py +0 -0
  353. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_dbml.py +0 -0
  354. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_dbt.py +0 -0
  355. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_excel.py +0 -0
  356. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_glue.py +0 -0
  357. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_hive.py +0 -0
  358. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_iceberg.py +0 -0
  359. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_json.py +0 -0
  360. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_jsonschema.py +0 -0
  361. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_object_storage.py +0 -0
  362. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_odata.py +0 -0
  363. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_odcs_v3.py +0 -0
  364. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_openapi.py +0 -0
  365. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_oracle.py +0 -0
  366. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_parquet.py +0 -0
  367. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_postgres.py +0 -0
  368. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_powerbi.py +0 -0
  369. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_protobuf.py +0 -0
  370. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_pydantic_model.py +0 -0
  371. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_redshift.py +0 -0
  372. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_snowflake.py +0 -0
  373. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_spark_type_json.py +0 -0
  374. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_clickhouse.py +0 -0
  375. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_hive.py +0 -0
  376. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_oracle.py +0 -0
  377. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_postgres.py +0 -0
  378. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_snowflake.py +0 -0
  379. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_sqlserver.py +0 -0
  380. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sql_struct.py +0 -0
  381. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_sqlserver.py +0 -0
  382. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_trino.py +0 -0
  383. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_unity_file.py +0 -0
  384. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_unmapped_types.py +0 -0
  385. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_import_xsd.py +0 -0
  386. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_install_extras.py +0 -0
  387. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_integration_entropydata.py +0 -0
  388. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_lint_files.py +0 -0
  389. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_lint_odcs_3_2_constraints.py +0 -0
  390. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_lint_schema.py +0 -0
  391. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_lint_urls.py +0 -0
  392. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_map_type.py +0 -0
  393. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_metadata_fields.py +0 -0
  394. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_native_type_schema_scope.py +0 -0
  395. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_nested_path_checks.py +0 -0
  396. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_oracle_patch.py +0 -0
  397. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_physical_type_match.py +0 -0
  398. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_publish_test_results.py +0 -0
  399. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_publish_test_results_payload.py +0 -0
  400. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_quality_unrunnable_rules.py +0 -0
  401. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_redshift_credentials.py +0 -0
  402. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_redshift_patch.py +0 -0
  403. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_resolve.py +0 -0
  404. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_resolve_definitions.py +0 -0
  405. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_resolve_local_definitions.py +0 -0
  406. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_roundtrip_jsonschema.py +0 -0
  407. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_roundtrip_pydantic_model.py +0 -0
  408. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_roundtrip_xsd.py +0 -0
  409. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_row_filter_duplicate_count.py +0 -0
  410. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_run_check_deprecated_fields.py +0 -0
  411. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_run_check_engine.py +0 -0
  412. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_server_overrides.py +0 -0
  413. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_server_type_mssql.py +0 -0
  414. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_server_type_synonyms.py +0 -0
  415. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_servers_3_2_0.py +0 -0
  416. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_snowflake_structured_types.py +0 -0
  417. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_sql_type_converter_physicaltype.py +0 -0
  418. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_api.py +0 -0
  419. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_api_openapi_import.py +0 -0
  420. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_array_items.py +0 -0
  421. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_athena_iceberg.py +0 -0
  422. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_azure_blob_file.py +0 -0
  423. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_azure_remote.py +0 -0
  424. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_bigquery.py +0 -0
  425. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_checks_filter.py +0 -0
  426. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_clickhouse.py +0 -0
  427. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_databricks.py +0 -0
  428. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_delta.py +0 -0
  429. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_diagnostics.py +0 -0
  430. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_dimension_filter.py +0 -0
  431. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_dry_run.py +0 -0
  432. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_duckdb.py +0 -0
  433. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_failed_samples.py +0 -0
  434. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_gcs_csv_remote.py +0 -0
  435. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_gcs_json_remote.py +0 -0
  436. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_hana.py +0 -0
  437. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_hive.py +0 -0
  438. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_iceberg.py +0 -0
  439. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_iceberg_rest.py +0 -0
  440. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_iceberg_s3tables.py +0 -0
  441. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_kafka.py +0 -0
  442. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_kafka_decoding.py +0 -0
  443. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_kafka_remote.py +0 -0
  444. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_local_json.py +0 -0
  445. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_local_json_nd.py +0 -0
  446. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_local_xml.py +0 -0
  447. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_metadata_only.py +0 -0
  448. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_mysql.py +0 -0
  449. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_nested_checks.py +0 -0
  450. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_oracle.py +0 -0
  451. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_oracle_xe.py +0 -0
  452. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_output_json.py +0 -0
  453. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_output_junit.py +0 -0
  454. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_parquet.py +0 -0
  455. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_quality.py +0 -0
  456. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_quality_id_and_tag_filter.py +0 -0
  457. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_quality_local_fallback.py +0 -0
  458. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_quality_percent_severity.py +0 -0
  459. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_redshift.py +0 -0
  460. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_row_filter.py +0 -0
  461. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_csv.py +0 -0
  462. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_delta.py +0 -0
  463. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_json.py +0 -0
  464. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_json_complex.py +0 -0
  465. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_json_multiple_files.py +0 -0
  466. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_s3_json_multiple_models.py +0 -0
  467. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_schema_evolution.py +0 -0
  468. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_server_not_found.py +0 -0
  469. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_snowflake.py +0 -0
  470. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_soda_connection_error.py +0 -0
  471. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_sqlserver.py +0 -0
  472. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_trino.py +0 -0
  473. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_test_variables.py +0 -0
  474. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_type_normalize.py +0 -0
  475. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_untrusted_contract_variables.py +0 -0
  476. {datacontract_cli-1.2.3 → datacontract_cli-1.2.4}/tests/test_vector_type.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacontract-cli
3
- Version: 1.2.3
3
+ Version: 1.2.4
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@entropy-data.com>, Stefan Negele <stefan.negele@innoq.com>, Simon Harrer <simon.harrer@entropy-data.com>
6
6
  License-Expression: MIT
@@ -40,7 +40,7 @@ Requires-Dist: jsonschema<5.0.0,>=4.23.0
40
40
  Requires-Dist: pytz>=2024.1
41
41
  Requires-Dist: python-multipart<1.0.0,>=0.0.20
42
42
  Requires-Dist: rich<16.0,>=13.7
43
- Requires-Dist: sqlglot<30.18,>=26.6.0
43
+ Requires-Dist: sqlglot<31.0.0,>=26.6.0
44
44
  Requires-Dist: python-dotenv<2.0.0,>=1.0.0
45
45
  Requires-Dist: boto3<2.0.0,>=1.34.41
46
46
  Requires-Dist: Jinja2<4.0.0,>=3.1.5
@@ -104,7 +104,7 @@ Requires-Dist: duckdb-extension-azure<1.6.0,>=1.5.0; extra == "azure"
104
104
  Requires-Dist: azure-identity<1.26,>=1.17.1; extra == "azure"
105
105
  Requires-Dist: azure-storage-blob<12.31,>=12.24; extra == "azure"
106
106
  Provides-Extra: snowflake
107
- Requires-Dist: snowflake-connector-python[pandas]<4.8,>=3.6; extra == "snowflake"
107
+ Requires-Dist: snowflake-connector-python[pandas]<4.9,>=3.6; extra == "snowflake"
108
108
  Requires-Dist: ibis-framework[snowflake]<13.0.0,>=10.0.0; extra == "snowflake"
109
109
  Provides-Extra: sqlserver
110
110
  Requires-Dist: ibis-framework[mssql]<13.0.0,>=10.0.0; extra == "sqlserver"
@@ -141,7 +141,7 @@ Requires-Dist: pyarrow>=18.1.0; extra == "parquet"
141
141
  Provides-Extra: rdf
142
142
  Requires-Dist: rdflib==7.6.0; extra == "rdf"
143
143
  Provides-Extra: api
144
- Requires-Dist: fastapi<0.142.0,>=0.115.0; extra == "api"
144
+ Requires-Dist: fastapi<0.143.0,>=0.115.0; extra == "api"
145
145
  Requires-Dist: uvicorn<0.55.0,>=0.44.0; extra == "api"
146
146
  Provides-Extra: protobuf
147
147
  Requires-Dist: proto-schema-parser<3.0,>=1.0; extra == "protobuf"
@@ -161,10 +161,10 @@ Requires-Dist: pre-commit<4.7.0,>=3.7.1; extra == "dev"
161
161
  Requires-Dist: pytest; extra == "dev"
162
162
  Requires-Dist: pytest-cov; extra == "dev"
163
163
  Requires-Dist: pytest-xdist; extra == "dev"
164
- Requires-Dist: pymssql==2.4.1; extra == "dev"
164
+ Requires-Dist: pymssql==2.4.2; extra == "dev"
165
165
  Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
166
166
  Requires-Dist: mysql-connector-python<26.8.0,>=8.0.30; extra == "dev"
167
- Requires-Dist: ruff==0.16.9; extra == "dev"
167
+ Requires-Dist: ruff==0.16.10; extra == "dev"
168
168
  Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.15.0; extra == "dev"
169
169
  Requires-Dist: trino==0.339.0; extra == "dev"
170
170
  Dynamic: license-file
@@ -256,7 +256,7 @@ Server: production (type=postgres, host=aws-1-eu-central-2.pooler.supabase.com,
256
256
  │ passed │ Check that field order_total has physical type INTEGER │ orders.order_total │ │
257
257
  │ passed │ Check that field order_total has no missing values │ orders.order_total │ │
258
258
  ╰────────┴───────────────────────────────────────────────────────────────────┴─────────────────────────┴─────────╯
259
- 🟢 Data contract is valid. Run 30 checks. Took 2.504495 seconds.
259
+ 🟢 Data contract is valid. Ran 30 checks. Took 2.504495 seconds.
260
260
  ```
261
261
 
262
262
  Voilà, the CLI tested that the YAML itself is valid, all records comply with the schema, and all quality attributes are met.
@@ -85,7 +85,7 @@ Server: production (type=postgres, host=aws-1-eu-central-2.pooler.supabase.com,
85
85
  │ passed │ Check that field order_total has physical type INTEGER │ orders.order_total │ │
86
86
  │ passed │ Check that field order_total has no missing values │ orders.order_total │ │
87
87
  ╰────────┴───────────────────────────────────────────────────────────────────┴─────────────────────────┴─────────╯
88
- 🟢 Data contract is valid. Run 30 checks. Took 2.504495 seconds.
88
+ 🟢 Data contract is valid. Ran 30 checks. Took 2.504495 seconds.
89
89
  ```
90
90
 
91
91
  Voilà, the CLI tested that the YAML itself is valid, all records comply with the schema, and all quality attributes are met.
@@ -449,7 +449,8 @@ def config_from_headers(headers) -> "Config | None":
449
449
  → ``DATACONTRACT_SNOWFLAKE_PASSWORD``, ``entropy-data-api-key`` →
450
450
  ``ENTROPY_DATA_API_KEY``. Returns None when no config headers are present,
451
451
  so env-var-configured deployments behave exactly as before. Unknown
452
- ``datacontract-*`` option names are rejected with a 400.
452
+ ``datacontract-*`` option names are rejected with a 400, and so is the
453
+ custom quality checks folder.
453
454
  """
454
455
  known = known_env_names()
455
456
  values = {}
@@ -457,6 +458,12 @@ def config_from_headers(headers) -> "Config | None":
457
458
  lowered = name.lower()
458
459
  env = lowered.upper().replace("-", "_")
459
460
  if lowered.startswith(_CONFIG_HEADER_PREFIX) or env in known:
461
+ if env == "DATACONTRACT_CUSTOM_QUALITY_CHECKS":
462
+ # A folder on the server's disk is the operator's to choose, never the caller's.
463
+ raise HTTPException(
464
+ status_code=status.HTTP_400_BAD_REQUEST,
465
+ detail="The custom quality checks folder is configured by the API server, not per request.",
466
+ )
460
467
  values[env] = value
461
468
  if not values:
462
469
  return None
@@ -15,7 +15,7 @@ from rich.markup import escape
15
15
  from typer.core import TyperGroup
16
16
  from typing_extensions import Annotated
17
17
 
18
- from datacontract.config import Config, set_cli_config
18
+ from datacontract.config import Config, cli_config, set_cli_config
19
19
  from datacontract.output.output_format import OutputFormat
20
20
 
21
21
  console = Console()
@@ -31,6 +31,23 @@ inline_references_option = Annotated[
31
31
  ),
32
32
  ]
33
33
 
34
+ custom_quality_checks_option = Annotated[
35
+ Optional[Path],
36
+ typer.Option(
37
+ metavar="DIR",
38
+ help="Folder with the custom quality checks the contract's quality rules use, one YAML file per check. "
39
+ "Defaults to DATACONTRACT_CUSTOM_QUALITY_CHECKS.",
40
+ ),
41
+ ]
42
+
43
+
44
+ def config_with_custom_quality_checks(custom_quality_checks: Optional[Path]) -> Config | None:
45
+ """The --config-file config, with the --custom-quality-checks folder if one was given."""
46
+ config = cli_config()
47
+ if custom_quality_checks is None:
48
+ return config
49
+ return Config.resolve(config).model_copy(update={"custom_quality_checks": str(custom_quality_checks)})
50
+
34
51
 
35
52
  # Order in which top-level commands appear in `datacontract --help` (cf. README.md)
36
53
  COMMAND_ORDER = [
@@ -130,6 +147,8 @@ app = typer.Typer(
130
147
  cls=OrderedCommandsWithMigrationHints,
131
148
  no_args_is_help=True,
132
149
  add_completion=False,
150
+ # the locals of a --debug traceback can hold credentials
151
+ pretty_exceptions_show_locals=False,
133
152
  help="CLI to manage data contracts. Documentation: https://docs.datacontract.com",
134
153
  epilog="Read the full documentation at https://docs.datacontract.com",
135
154
  )
@@ -10,14 +10,15 @@ from datacontract.cli import (
10
10
  _print_logs,
11
11
  _print_publish_failure,
12
12
  app,
13
+ config_with_custom_quality_checks,
13
14
  console,
15
+ custom_quality_checks_option,
14
16
  debug_option,
15
17
  enable_debug_logging,
16
18
  inline_references_option,
17
19
  resolve_output_format,
18
20
  validate_publish_url,
19
21
  )
20
- from datacontract.config import cli_config
21
22
  from datacontract.data_contract import DataContract
22
23
  from datacontract.output.ci_output import write_ci_output, write_ci_summary, write_json_results
23
24
  from datacontract.output.output_format import OutputFormat
@@ -80,6 +81,7 @@ def ci(
80
81
  bool,
81
82
  typer.Option(help="SSL verification when publishing the data contract."),
82
83
  ] = True,
84
+ custom_quality_checks: custom_quality_checks_option = None,
83
85
  inline_references: inline_references_option = True,
84
86
  debug: debug_option = None,
85
87
  ):
@@ -104,6 +106,7 @@ def ci(
104
106
  # Plain text output for CI logs; --json sends human output to stderr.
105
107
  out = Console(stderr=True, no_color=True) if json_output else Console(no_color=True)
106
108
 
109
+ config = config_with_custom_quality_checks(custom_quality_checks)
107
110
  results = []
108
111
  fail_results = {
109
112
  "warning": {"warning", "failed", "error"},
@@ -115,7 +118,7 @@ def ci(
115
118
  for location in locations:
116
119
  out.print(f"Testing {location}")
117
120
  run = DataContract(
118
- config=cli_config(),
121
+ config=config,
119
122
  data_contract_file=location,
120
123
  schema_location=schema,
121
124
  publish_url=publish,
@@ -10,7 +10,9 @@ from datacontract.cli import (
10
10
  _print_logs,
11
11
  _print_publish_failure,
12
12
  app,
13
+ config_with_custom_quality_checks,
13
14
  console,
15
+ custom_quality_checks_option,
14
16
  debug_option,
15
17
  enable_debug_logging,
16
18
  inline_references_option,
@@ -200,6 +202,7 @@ def test(
200
202
  bool,
201
203
  typer.Option(help="SSL verification when publishing the data contract."),
202
204
  ] = True,
205
+ custom_quality_checks: custom_quality_checks_option = None,
203
206
  inline_references: inline_references_option = True,
204
207
  debug: debug_option = None,
205
208
  ):
@@ -231,7 +234,7 @@ def test(
231
234
  if server == "all":
232
235
  server = None
233
236
  run = DataContract(
234
- config=cli_config(),
237
+ config=config_with_custom_quality_checks(custom_quality_checks),
235
238
  data_contract_file=location,
236
239
  schema_location=schema,
237
240
  publish_test_results=publish_test_results,
@@ -110,6 +110,7 @@ class Config(BaseSettings):
110
110
 
111
111
  # general
112
112
  api_header_authorization: SecretStr | None = None
113
+ custom_quality_checks: str | None = None
113
114
  max_errors: int | None = None
114
115
 
115
116
  # athena (credentials come from the s3_* options)
@@ -521,6 +522,10 @@ class Config(BaseSettings):
521
522
  def get_api_header_authorization(self, required: bool = False) -> str | None:
522
523
  return self._str_option("api_header_authorization", required)
523
524
 
525
+ # --- custom quality checks ---
526
+ def get_custom_quality_checks(self) -> str | None:
527
+ return self._str_option("custom_quality_checks")
528
+
524
529
  # --- max ---
525
530
  def get_max_errors(self) -> int | None:
526
531
  return self._int_option("max_errors")
@@ -231,26 +231,56 @@ _DEFERRED_FIELDS = {
231
231
  }
232
232
 
233
233
 
234
- def resolve_runtime_variables(value, source: str = "contract", variables: Optional[Mapping[str, str]] = None):
234
+ # Compared with the value a query returns, so a whole reference keeps the type it reads as.
235
+ _TYPED_FIELDS = {("DataQuality", "mustBe"), ("DataQuality", "mustNotBe")}
236
+ _PLAIN_NUMBER = re.compile(r"-?(0|[1-9][0-9]*)(\.[0-9]+)?")
237
+
238
+
239
+ def resolve_runtime_variables(
240
+ value, source: str = "contract", variables: Optional[Mapping[str, str]] = None, typed: bool = False
241
+ ):
235
242
  """Copy test inputs with variables resolved, recursively, without altering the contract.
236
243
 
237
244
  Handles property names/types, enum values, logical type options, library
238
245
  quality arguments, SLA values, and nested array/map definitions. Dictionary
239
- keys are structural identifiers and are never interpolated.
246
+ keys are structural identifiers and are never interpolated. With ``typed``, a
247
+ value that is one whole reference becomes a number or boolean if its resolved
248
+ text is a plain one (``1000``, ``-2.5``, ``true``); ``007`` stays a string.
240
249
  """
241
250
  if isinstance(value, BaseModel):
242
251
  owner = type(value).__name__
243
252
  updates = {}
244
253
  for field in type(value).model_fields:
245
- if field in _DEFERRED_FIELDS:
246
- continue
247
254
  original = getattr(value, field)
248
255
  field_source = f"{source}.{_field_name(field)}"
249
- resolved = resolve_runtime_variables(original, field_source, variables)
256
+ # A custom quality check's arguments are test inputs, like a library metric's.
257
+ if (
258
+ field == "implementation"
259
+ and getattr(value, "engine", None) == "datacontract-cli"
260
+ and isinstance(original, dict)
261
+ and "arguments" in original
262
+ ):
263
+ arguments = resolve_runtime_variables(original["arguments"], f"{field_source}.arguments", variables)
264
+ updates[field] = {**original, "arguments": arguments}
265
+ continue
266
+ if field in _DEFERRED_FIELDS:
267
+ continue
268
+ resolved = resolve_runtime_variables(original, field_source, variables, (owner, field) in _TYPED_FIELDS)
250
269
  updates[field] = _checked(original, resolved, owner, field, field_source)
251
270
  return value.model_copy(update=updates)
252
271
  if isinstance(value, dict):
253
- return {key: resolve_runtime_variables(item, f"{source}.{key}", variables) for key, item in value.items()}
272
+ return {
273
+ key: resolve_runtime_variables(item, f"{source}.{key}", variables, typed) for key, item in value.items()
274
+ }
254
275
  if isinstance(value, list):
255
- return [resolve_runtime_variables(item, f"{source}[{index}]", variables) for index, item in enumerate(value)]
256
- return resolve_variables(value, source=source, variables=variables)
276
+ return [
277
+ resolve_runtime_variables(item, f"{source}[{index}]", variables, typed) for index, item in enumerate(value)
278
+ ]
279
+ resolved = resolve_variables(value, source=source, variables=variables)
280
+ if not typed or not isinstance(value, str) or not _VARIABLE.fullmatch(value):
281
+ return resolved
282
+ if _PLAIN_NUMBER.fullmatch(resolved):
283
+ return float(resolved) if "." in resolved else int(resolved)
284
+ if resolved in ("true", "false"):
285
+ return resolved == "true"
286
+ return resolved
@@ -27,6 +27,7 @@ class MetricType(str, Enum):
27
27
  MISSING_COUNT = "missing_count"
28
28
  DUPLICATE_COUNT = "duplicate_count"
29
29
  INVALID_COUNT = "invalid_count"
30
+ MISSING_REFERENCE_COUNT = "missing_reference_count"
30
31
  FIELD_PRESENT = "field_present"
31
32
  FIELD_TYPE = "field_type"
32
33
  FIELD_PHYSICAL_TYPE = "field_physical_type"
@@ -158,10 +159,14 @@ class CheckSpec:
158
159
  expected_schema_property: Optional["SchemaProperty"] = None # FIELD_TYPE: structural comparison
159
160
  expected_physical_type: Optional[str] = None # FIELD_PHYSICAL_TYPE: contract physicalType
160
161
 
161
- columns: Optional[List[str]] = None # DUPLICATE_COUNT across multiple columns
162
+ columns: Optional[List[str]] = None # DUPLICATE_COUNT across multiple columns; MISSING_REFERENCE_COUNT keys
163
+
164
+ referenced_model: Optional[str] = None # MISSING_REFERENCE_COUNT
165
+ referenced_columns: Optional[List[str]] = None # MISSING_REFERENCE_COUNT, pairwise with `columns`
162
166
 
163
167
  query: Optional[str] = None # CUSTOM_SQL (placeholders already substituted)
164
168
  dialect: Optional[str] = None # CUSTOM_SQL input SQL dialect
169
+ diagnostics: Optional[dict] = None # CUSTOM_SQL: reported alongside the measured value
165
170
 
166
171
  seconds: Optional[int] = None # FRESHNESS / RETENTION threshold in seconds
167
172
 
@@ -25,6 +25,14 @@ from sqlglot import Dialect, exp
25
25
 
26
26
  from datacontract.config.variables import VariableError, contains_variables, resolve_variables
27
27
  from datacontract.engines.checks.check_spec import METADATA_METRICS, CheckSpec, MetricType, Op, Threshold
28
+ from datacontract.engines.checks.custom_quality_check import (
29
+ COLUMN_PLACEHOLDERS,
30
+ ENGINE,
31
+ CustomQualityCheckUnusable,
32
+ Instance,
33
+ instantiate,
34
+ placeholder_pattern,
35
+ )
28
36
  from datacontract.engines.checks.dimensions import default_dimension
29
37
  from datacontract.engines.checks.sql_guard import dialect_for_server_type, refusal_reason, sqlglot_dialect_by_name
30
38
  from datacontract.engines.checks.type_normalize import normalize_type_name
@@ -191,7 +199,11 @@ def to_threshold(quality: DataQuality) -> Optional[Threshold]:
191
199
 
192
200
 
193
201
  def prepare_query(
194
- quality: DataQuality, model_name: str, field_name: Optional[str], server: Optional[Server]
202
+ query: Optional[str],
203
+ model_name: str,
204
+ field_name: Optional[str],
205
+ server: Optional[Server],
206
+ instance: Optional[Instance] = None,
195
207
  ) -> Optional[str]:
196
208
  """Substitute placeholders in a user SQL query.
197
209
 
@@ -199,8 +211,9 @@ def prepare_query(
199
211
  cannot read it bare, so a plain name keeps the backend's case-insensitive
200
212
  resolution. Quotes the author wrote around a placeholder are dropped, except
201
213
  that backticks force the name to be quoted (e.g. a reserved word on Spark).
214
+ For a custom quality check ``instance``, its arguments are substituted too.
202
215
  """
203
- if not quality.query:
216
+ if not query:
204
217
  return None
205
218
 
206
219
  dialect = sqlglot_dialect_by_name(dialect_for_server_type(get_server_type(server)))
@@ -213,17 +226,21 @@ def prepare_query(
213
226
  for placeholder in ("dataset", "project", "catalog", "database"):
214
227
  names[placeholder] = (getattr(server, placeholder, None) if server else None) or model_name
215
228
  if field_name is not None:
216
- names |= dict.fromkeys(("field", "column", "property"), field_name)
229
+ names |= dict.fromkeys(COLUMN_PLACEHOLDERS, field_name)
217
230
 
218
- def identifier(match: re.Match) -> str:
219
- forced = "`" in match.group(0)
231
+ def identifier(name: str, forced: bool) -> str:
220
232
  return ".".join(
221
233
  exp.to_identifier(part, quoted=forced or not bare.fullmatch(part)).sql(dialect=dialect)
222
- for part in names[match.group(1)].split(".")
234
+ for part in name.split(".")
223
235
  )
224
236
 
237
+ if instance is not None:
238
+ return instance.render(dialect, names, identifier)
239
+
225
240
  # one pass, so a substituted name is not searched for placeholders again
226
- return re.sub(rf"[\"'`]?\$?\{{({'|'.join(names)})}}[\"'`]?", identifier, quality.query)
241
+ return placeholder_pattern(list(names)).sub(
242
+ lambda match: identifier(names[match.group(1)], "`" in match.group(0)), query
243
+ )
227
244
 
228
245
 
229
246
  # ---------------------------------------------------------------------------
@@ -234,6 +251,7 @@ def create_checks(
234
251
  server: Optional[Server],
235
252
  schema_name: str = "all",
236
253
  variables: Optional[Mapping[str, str]] = None,
254
+ custom_quality_checks: Optional[str] = None,
237
255
  ) -> List[CheckSpec]:
238
256
  checks: List[CheckSpec] = []
239
257
  if data_contract.schema_ is None:
@@ -244,7 +262,8 @@ def create_checks(
244
262
  if _is_azure_blob_schema(schema_obj, server):
245
263
  # File-metadata checks are emitted by check_azure_blob_file
246
264
  continue
247
- checks.extend(_to_schema_checks(schema_obj, server, variables))
265
+ checks.extend(_to_schema_checks(schema_obj, server, variables, custom_quality_checks))
266
+ checks.extend(_relationship_checks(schema_obj, data_contract.schema_, server))
248
267
  checks.extend(_to_servicelevel_checks(data_contract, server))
249
268
  checks = [c for c in checks if c is not None]
250
269
  # Schema and service level checks cannot declare an ODCS dimension, so fill
@@ -260,7 +279,10 @@ def _is_azure_blob_schema(schema_object: SchemaObject, server: Optional[Server])
260
279
 
261
280
 
262
281
  def _to_schema_checks(
263
- schema_object: SchemaObject, server: Optional[Server], variables: Optional[Mapping[str, str]] = None
282
+ schema_object: SchemaObject,
283
+ server: Optional[Server],
284
+ variables: Optional[Mapping[str, str]] = None,
285
+ custom_quality_checks: Optional[str] = None,
264
286
  ) -> List[CheckSpec]:
265
287
  checks: List[CheckSpec] = []
266
288
  server_type = get_server_type(server) if server is not None else None
@@ -575,7 +597,7 @@ def _to_schema_checks(
575
597
  )
576
598
 
577
599
  if prop.quality:
578
- checks.extend(_quality_checks(model, field, prop.quality, server, variables))
600
+ checks.extend(_quality_checks(model, field, prop.quality, server, variables, custom_quality_checks))
579
601
 
580
602
  # A check the server cannot run is reported, not dropped.
581
603
  unenforced = []
@@ -613,11 +635,100 @@ def _to_schema_checks(
613
635
  )
614
636
 
615
637
  if schema_object.quality:
616
- checks.extend(_quality_checks(model, None, schema_object.quality, server, variables))
638
+ checks.extend(_quality_checks(model, None, schema_object.quality, server, variables, custom_quality_checks))
617
639
 
618
640
  return checks
619
641
 
620
642
 
643
+ def _relationship_checks(
644
+ schema_object: SchemaObject, schemas: List[SchemaObject], server: Optional[Server]
645
+ ) -> List[CheckSpec]:
646
+ """A foreign key holds when every non-null key in this model exists in the referenced model."""
647
+ server_type = get_server_type(server) if server is not None else None
648
+ model = to_schema_name(schema_object, server_type)
649
+ properties = schema_object.properties or []
650
+ declared = [(prop.name, rel) for prop in properties for rel in prop.relationships or []]
651
+ declared += [(None, rel) for rel in schema_object.relationships or []]
652
+
653
+ checks: List[CheckSpec] = []
654
+ for source, rel in declared:
655
+ if (rel.type or "foreignKey") != "foreignKey":
656
+ continue
657
+ sources = [source] if source is not None else rel.from_ if isinstance(rel.from_, list) else [rel.from_]
658
+ targets = rel.to if isinstance(rel.to, list) else [rel.to]
659
+ # `from` may repeat this schema's name; any other prefix is kept, so it matches no property.
660
+ source_names = [str(s).removeprefix(f"{schema_object.name}.") for s in sources if s]
661
+ parts = [str(t).rsplit(".", 1) for t in targets if t]
662
+ target_schema = next((s for s in schemas if parts and len(parts[0]) == 2 and s.name == parts[0][0]), None)
663
+
664
+ def physical(props, name):
665
+ prop = next((p for p in props or [] if p.name == name), None)
666
+ return None if prop is None else prop.physicalName or prop.name
667
+
668
+ columns = [physical(properties, name) for name in source_names]
669
+ referenced = [
670
+ physical(target_schema.properties, part[-1])
671
+ if target_schema is not None and part[0] == target_schema.name
672
+ else None
673
+ for part in parts
674
+ ]
675
+ label = ", ".join(source_names) or "?"
676
+ if not columns or None in columns + referenced or len(referenced) != len(columns):
677
+ target_label = ", ".join(str(t) for t in targets)
678
+ if not columns or len(parts) != len(columns):
679
+ cause = "its from and to lists differ in length"
680
+ elif None in columns:
681
+ cause = f"{schema_object.name} has no property {source_names[columns.index(None)]}"
682
+ elif any(len(part) == 1 for part in parts):
683
+ cause = f"{next(part[0] for part in parts if len(part) == 1)} is not a schema.property reference"
684
+ elif target_schema is None:
685
+ cause = f"the contract has no schema {parts[0][0]}"
686
+ elif any(part[0] != target_schema.name for part in parts):
687
+ cause = "a composite key must reference a single schema"
688
+ else:
689
+ cause = f"{target_schema.name} has no property {parts[referenced.index(None)][-1]}"
690
+ checks.append(
691
+ CheckSpec(
692
+ key=f"{model}__{'__'.join(source_names)}__{'__'.join('__'.join(p) for p in parts)}"
693
+ "__field_relationships",
694
+ category="schema",
695
+ type="field_relationships",
696
+ name=f"Check that {label} has no values missing from {target_label}",
697
+ model=model,
698
+ field=source_names[0] if len(source_names) == 1 else None,
699
+ metric=MetricType.UNSUPPORTED,
700
+ preset_result="warning",
701
+ preset_reason=f"The relationship from {label} to {target_label} is not checked: {cause}.",
702
+ )
703
+ )
704
+ continue
705
+
706
+ referenced_model = to_schema_name(target_schema, server_type)
707
+ if len(columns) == 1:
708
+ name = f"Check that field {columns[0]} has no values missing from {referenced_model}.{referenced[0]}"
709
+ else:
710
+ name = (
711
+ f"Check that ({', '.join(columns)}) has no values missing from "
712
+ f"{referenced_model} ({', '.join(referenced)})"
713
+ )
714
+ checks.append(
715
+ CheckSpec(
716
+ key=f"{model}__{'__'.join(columns)}__{referenced_model}__{'__'.join(referenced)}__field_relationships",
717
+ category="schema",
718
+ type="field_relationships",
719
+ name=name,
720
+ model=model,
721
+ field=columns[0] if len(columns) == 1 else None,
722
+ metric=MetricType.MISSING_REFERENCE_COUNT,
723
+ threshold=Threshold(Op.EQ, 0),
724
+ columns=columns,
725
+ referenced_model=referenced_model,
726
+ referenced_columns=referenced,
727
+ )
728
+ )
729
+ return checks
730
+
731
+
621
732
  # ---------------------------------------------------------------------------
622
733
  # metric-check builders (preserve legacy keys / types)
623
734
  # ---------------------------------------------------------------------------
@@ -719,10 +830,11 @@ def _quality_checks(
719
830
  quality_list: List[DataQuality],
720
831
  server: Optional[Server],
721
832
  variables: Optional[Mapping[str, str]] = None,
833
+ custom_quality_checks: Optional[str] = None,
722
834
  ) -> List[CheckSpec]:
723
835
  checks: List[CheckSpec] = []
724
836
  for count, quality in enumerate(quality_list):
725
- rule_checks = _quality_rule_checks(model, field, quality, count, server, variables)
837
+ rule_checks = _quality_rule_checks(model, field, quality, count, server, variables, custom_quality_checks)
726
838
  # Every check keeps a link back to the rule that declared it, so that
727
839
  # `test --quality-id` / `test --tag` can select it.
728
840
  for check in rule_checks:
@@ -782,6 +894,8 @@ def _unexecuted_check(
782
894
  quality: DataQuality,
783
895
  reason: str,
784
896
  result: str = "warning",
897
+ name: Optional[str] = None,
898
+ dimension: Optional[str] = None,
785
899
  ) -> List[CheckSpec]:
786
900
  """A rule the engine cannot run, reported rather than dropped."""
787
901
  return [
@@ -789,11 +903,11 @@ def _unexecuted_check(
789
903
  key=check_key,
790
904
  category="quality",
791
905
  type=check_type,
792
- name=quality.description or unexecuted_check_name(model, field),
906
+ name=name or quality.description or unexecuted_check_name(model, field),
793
907
  model=model,
794
908
  field=field,
795
909
  metric=MetricType.UNSUPPORTED,
796
- dimension=quality.dimension,
910
+ dimension=dimension or quality.dimension,
797
911
  severity=quality.severity,
798
912
  preset_result=result,
799
913
  preset_reason=reason,
@@ -801,6 +915,14 @@ def _unexecuted_check(
801
915
  ]
802
916
 
803
917
 
918
+ def _array_item_reason(field: str) -> str:
919
+ return (
920
+ f"'{field}' is an array item, not a column, so it cannot be substituted into a query. "
921
+ f"Declare the rule on '{field.split('[]')[0]}' instead and match the elements with array "
922
+ f"functions, for example size(filter(...)) > 0."
923
+ )
924
+
925
+
804
926
  def _quality_rule_checks(
805
927
  model: str,
806
928
  field: Optional[str],
@@ -808,6 +930,7 @@ def _quality_rule_checks(
808
930
  count: int,
809
931
  server: Optional[Server],
810
932
  variables: Optional[Mapping[str, str]] = None,
933
+ custom_quality_checks: Optional[str] = None,
811
934
  ) -> List[CheckSpec]:
812
935
  """The checks of a single ODCS quality rule (``count`` is its index in the list)."""
813
936
  if quality.type == "custom" and quality.engine == "soda" and quality.implementation:
@@ -828,6 +951,45 @@ def _quality_rule_checks(
828
951
  ),
829
952
  )
830
953
  ]
954
+ if quality.type == "custom" and quality.engine == ENGINE:
955
+ if field is None:
956
+ check_key = f"{model}__quality_custom_check_{count}"
957
+ check_type = "model_quality_custom"
958
+ else:
959
+ check_key = f"{model}__{field}__quality_custom_check_{count}"
960
+ check_type = "field_quality_custom"
961
+ if field is not None and "[]" in field:
962
+ return _unexecuted_check(check_key, check_type, model, field, quality, _array_item_reason(field))
963
+ dialect = dialect_for_server_type(get_server_type(server))
964
+ try:
965
+ instance = instantiate(quality, model, field, dialect, custom_quality_checks)
966
+ query = prepare_query(instance.query, model, field, server, instance)
967
+ except CustomQualityCheckUnusable as e:
968
+ return _unexecuted_check(
969
+ check_key, check_type, model, field, quality, e.reason, e.result, e.name, e.dimension
970
+ )
971
+ rule = instance.quality
972
+ refusal = refusal_reason(query, dialect)
973
+ if refusal is not None:
974
+ return _unexecuted_check(
975
+ check_key, check_type, model, field, rule, refusal, "failed", instance.name, rule.dimension
976
+ )
977
+ return [
978
+ CheckSpec(
979
+ key=check_key,
980
+ category="quality",
981
+ type=check_type,
982
+ name=instance.name,
983
+ model=model,
984
+ field=field,
985
+ metric=MetricType.CUSTOM_SQL,
986
+ threshold=to_threshold(rule),
987
+ query=query,
988
+ severity=rule.severity,
989
+ dimension=rule.dimension,
990
+ diagnostics=instance.diagnostics,
991
+ )
992
+ ]
831
993
  if quality.type == "sql":
832
994
  if field is None:
833
995
  check_key = f"{model}__quality_sql_{count}"
@@ -836,8 +998,6 @@ def _quality_rule_checks(
836
998
  check_key = f"{model}__{field}__quality_sql_{count}"
837
999
  check_type = "field_quality_sql"
838
1000
  if field is not None and "[]" in field:
839
- # An array item is not a column, so substituting it into the query
840
- # would produce SQL no backend can parse.
841
1001
  return [
842
1002
  CheckSpec(
843
1003
  key=check_key,
@@ -850,15 +1010,11 @@ def _quality_rule_checks(
850
1010
  dimension=quality.dimension,
851
1011
  severity=quality.severity,
852
1012
  preset_result="warning",
853
- preset_reason=(
854
- f"'{field}' is an array item, not a column, so it cannot be substituted into a query. "
855
- f"Declare the rule on '{field.split('[]')[0]}' instead and match the elements with array "
856
- f"functions, for example size(filter(...)) > 0."
857
- ),
1013
+ preset_reason=_array_item_reason(field),
858
1014
  )
859
1015
  ]
860
1016
  threshold = to_threshold(quality)
861
- query = prepare_query(quality, model, field, server)
1017
+ query = prepare_query(quality.query, model, field, server)
862
1018
  if query is None:
863
1019
  return _unexecuted_check(check_key, check_type, model, field, quality, "The rule has no query.")
864
1020
  if threshold is None: