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.
- {datacontract_cli-1.0.11/datacontract_cli.egg-info → datacontract_cli-1.0.13}/PKG-INFO +4 -4
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/check_spec.py +1 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/create_checks.py +67 -3
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/physical_type_match.py +29 -2
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/type_normalize.py +88 -17
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/dtype_category.py +22 -14
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/ibis_check_execute.py +123 -11
- datacontract_cli-1.0.13/datacontract/engines/ibis/snowflake_structured_types.py +138 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13/datacontract_cli.egg-info}/PKG-INFO +4 -4
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/SOURCES.txt +3 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/requires.txt +3 -3
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/pyproject.toml +4 -4
- datacontract_cli-1.0.13/tests/test_create_checks_nested_type.py +438 -0
- datacontract_cli-1.0.13/tests/test_dtype_category.py +84 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_physical_type_match.py +35 -2
- datacontract_cli-1.0.13/tests/test_snowflake_structured_types.py +251 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_dataframe.py +2 -2
- datacontract_cli-1.0.13/tests/test_type_normalize.py +92 -0
- datacontract_cli-1.0.11/tests/test_dtype_category.py +0 -28
- datacontract_cli-1.0.11/tests/test_type_normalize.py +0 -15
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/LICENSE +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/MANIFEST.in +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/README.md +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/api.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/catalog/catalog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/changelog/normalize.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/cli.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_api.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_ci.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_dbt.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_export.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_import.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_init.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/command_test.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/data_contract.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/VERSION +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/apex-Dspwrl5m.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/azcli-k1BiOau1.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/bat-BIP72KWc.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/clojure-BAuDPsal.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/coffee-BV06qSnj.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/csp-KzlEHCyI.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/css-e4FKntZ3.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/dart-DdHeFWR0.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/datacontract-editor.css +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/elixir-C9X42b0T.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/flow9-Bff42pfB.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/go-DEr8zx9f.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/html-Cn4TGw_o.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/index.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ini-CZGtssYM.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/java-B260NeH3.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/javascript-B86Kac07.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/julia-kuNLM9c5.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/less-AZUtS89I.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/lua-Dz1RbATg.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/m3-CIMooLFn.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mips-BovsnfLQ.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/mysql-BwdpPE19.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pascal--j5-PYtz.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/perl-BzEWqUb5.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/php-A19QHzQV.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pla-COeKY1hA.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/pug-DLej6kn5.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/python-C8RW9B6A.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/r-D-F4P80w.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/razor-CLZqdY_r.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/redis-hwwIlMnV.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scheme-DeFRbslr.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/scss-RSTCiCmI.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/shell-BpF-iNu8.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/sql-CJwKPERn.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/st-DPlKG5DT.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/swift-YZBAbVHV.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/twig-C5TO7-kw.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/typescript-CJBeebKU.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/vb-ChHvIDrk.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/xml-vC-NMVX1.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/data_contract_test.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/connect.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/duckdb_connection.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/kafka.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/avro_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/avro_idl_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/bigquery_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/data_caterer_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dbml_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dcs_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/duckdb_type_converter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/go_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/great_expectations_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/html_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/iceberg_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/jsonschema_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/mermaid_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/odcs_export_helper.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/protobuf_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/pydantic_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/rdf_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sodacl_check_builder.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sodacl_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/spark_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sql_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sql_type_converter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/export/sqlalchemy_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/avro_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/bigquery_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dbml_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/dcs_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/excel_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/glue_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/iceberg_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/importer_factory.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/jsonschema_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/odcs_helper.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/odcs_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/parquet_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/powerbi_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/protobuf_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/snowflake_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/spark_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/sql_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/imports/unity_importer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/init/init_template.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/dbt_sync.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/dbt_test_mapping.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/integration/entropy_data.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/files.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/resolve.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/schema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/lint/urls.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/exceptions.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/odcs.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/run.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/model/server.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/test_results_writer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/output/text_changelog_results.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/py.typed +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.1.0.init.yaml +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/datacontract_odcs.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/setup.cfg +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_api.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_cli.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_create_checks_physical_type.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract_checks.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_dbt_sync.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_duckdb_json.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_edit.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_avro.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_great_expectations.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_html.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_jsonschema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_mermaid.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_odcs_v3.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_rdf.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sodacl.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_avro.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_csv.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_excel.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_iceberg.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_jsonschema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_parquet.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_powerbi.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_snowflake.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_spark.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_oracle.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_postgres.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_snowflake.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_sql_sqlserver.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_lint.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_require_env.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_resolve.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_resolve_definitions.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_azure_blob_file.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_checks_filter.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_diagnostics.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_kafka.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_parquet.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_postgres.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_quality_percent_severity.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_redshift.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_csv.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_delta.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_complex.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_multiple_models.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_s3_json_remote.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_schema_evolution.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_soda_connection_error.py +0 -0
- {datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/tests/test_test_sqlserver.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
{datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/check_spec.py
RENAMED
|
@@ -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"
|
{datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/create_checks.py
RENAMED
|
@@ -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=
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
140
|
-
|
|
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, ""
|
{datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/checks/type_normalize.py
RENAMED
|
@@ -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
|
-
|
|
169
|
-
|
|
170
|
-
actual
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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(
|
|
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
|
{datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/dtype_category.py
RENAMED
|
@@ -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
|
|
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.
|
|
57
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
|
93
|
-
|
|
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))
|
{datacontract_cli-1.0.11 → datacontract_cli-1.0.13}/datacontract/engines/ibis/ibis_check_execute.py
RENAMED
|
@@ -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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 '{
|
|
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
|
|