datacontract-cli 0.10.35__py3-none-any.whl → 0.10.36__py3-none-any.whl

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.

Potentially problematic release.


This version of datacontract-cli might be problematic. Click here for more details.

Files changed (34) hide show
  1. datacontract/api.py +1 -1
  2. datacontract/cli.py +1 -1
  3. datacontract/data_contract.py +18 -51
  4. datacontract/engines/data_contract_checks.py +280 -19
  5. datacontract/export/dbt_converter.py +30 -4
  6. datacontract/export/dqx_converter.py +12 -7
  7. datacontract/export/excel_exporter.py +3 -3
  8. datacontract/export/markdown_converter.py +35 -16
  9. datacontract/export/rdf_converter.py +2 -2
  10. datacontract/export/sql_type_converter.py +6 -4
  11. datacontract/imports/odcs_v3_importer.py +71 -18
  12. datacontract/imports/unity_importer.py +16 -11
  13. datacontract/init/init_template.py +1 -1
  14. datacontract/lint/resolve.py +1 -1
  15. datacontract/lint/schema.py +1 -1
  16. datacontract/schemas/datacontract-1.1.0.init.yaml +1 -1
  17. datacontract/schemas/datacontract-1.2.0.init.yaml +1 -1
  18. datacontract/schemas/datacontract-1.2.1.init.yaml +91 -0
  19. datacontract/schemas/datacontract-1.2.1.schema.json +2058 -0
  20. datacontract/schemas/odcs-3.0.2.schema.json +2382 -0
  21. datacontract/templates/datacontract_odcs.html +60 -41
  22. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/METADATA +27 -24
  23. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/RECORD +27 -31
  24. datacontract/lint/lint.py +0 -142
  25. datacontract/lint/linters/__init__.py +0 -0
  26. datacontract/lint/linters/description_linter.py +0 -33
  27. datacontract/lint/linters/field_pattern_linter.py +0 -34
  28. datacontract/lint/linters/field_reference_linter.py +0 -47
  29. datacontract/lint/linters/notice_period_linter.py +0 -55
  30. datacontract/lint/linters/valid_constraints_linter.py +0 -100
  31. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/WHEEL +0 -0
  32. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/entry_points.txt +0 -0
  33. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/licenses/LICENSE +0 -0
  34. {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,61 @@
1
+ {% macro render_nested_properties(properties, level) %}
2
+ <tr>
3
+ {% for property in properties %}
4
+ <td class="px-4 py-4 whitespace-nowrap">
5
+
6
+ <div class="text-sm font-medium text-gray-900">
7
+ {% for i in range(0,level)%}
8
+ &nbsp;
9
+ {% endfor %}
10
+ {% if level > 0 %}
11
+
12
+ {% endif %}
13
+ {{ property.name or "items" }}</div>
14
+ {% if property.primaryKey %}
15
+ <span
16
+ class="inline-flex items-center rounded-md bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">Primary Key</span>
17
+ {% endif %}
18
+ {% if property.partitioned %}
19
+ <span
20
+ class="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-800 ring-1 ring-inset ring-blue-600/20">Partitioned</span>
21
+ {% endif %}
22
+ {% if property.criticalDataElement %}
23
+ <span
24
+ class="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-800 ring-1 ring-inset ring-red-600/20">Critical</span>
25
+ {% endif %}
26
+ </td>
27
+ <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">{{
28
+ property.businessName or "-" }}
29
+ </td>
30
+ <td class="px-4 py-4 whitespace-nowrap">
31
+ <div class="text-sm text-gray-900">{{ property.logicalType }}</div>
32
+ {% if property.physicalType and property.physicalType !=
33
+ property.logicalType %}
34
+ <div class="text-xs text-gray-500">{{ property.physicalType }}</div>
35
+ {% endif %}
36
+ </td>
37
+ <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
38
+ {% if property.required %}
39
+ <span class="text-green-600">Yes</span>
40
+ {% else %}
41
+ <span class="text-gray-400">No</span>
42
+ {% endif %}
43
+ </td>
44
+ <td class="px-4 py-4 text-sm text-gray-500">{{ property.description or "-"
45
+ }}
46
+ </td>
47
+ </tr>
48
+ {% if property.properties %}
49
+ {{render_nested_properties(property.properties, level+1)}}
50
+ {% endif %}
51
+ {% if property.items %}
52
+ {{render_nested_properties([property.items], level+1)}}
53
+ {% endif %}
54
+ {% endfor %}
55
+ <!-- Mark the end of the contained fields -->
56
+ <tr style="--tw-divide-y-reverse: 2"></tr>
57
+ {% endmacro %}
58
+
1
59
  <!doctype html>
2
60
  <html class="h-full bg-gray-100" lang="en">
3
61
  <head>
@@ -12,7 +70,6 @@
12
70
  </style>
13
71
  </head>
14
72
  <body class="h-full">
15
-
16
73
  <div class="min-h-full flex flex-col">
17
74
 
18
75
  <nav class="bg-white shadow-sm">
@@ -265,45 +322,7 @@
265
322
  </tr>
266
323
  </thead>
267
324
  <tbody class="divide-y divide-gray-200 bg-white">
268
- {% for property in schema.properties %}
269
- <tr>
270
- <td class="px-4 py-4 whitespace-nowrap">
271
- <div class="text-sm font-medium text-gray-900">{{ property.name }}</div>
272
- {% if property.primaryKey %}
273
- <span
274
- class="inline-flex items-center rounded-md bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">Primary Key</span>
275
- {% endif %}
276
- {% if property.partitioned %}
277
- <span
278
- class="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-800 ring-1 ring-inset ring-blue-600/20">Partitioned</span>
279
- {% endif %}
280
- {% if property.criticalDataElement %}
281
- <span
282
- class="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-800 ring-1 ring-inset ring-red-600/20">Critical</span>
283
- {% endif %}
284
- </td>
285
- <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">{{
286
- property.businessName or "-" }}
287
- </td>
288
- <td class="px-4 py-4 whitespace-nowrap">
289
- <div class="text-sm text-gray-900">{{ property.logicalType }}</div>
290
- {% if property.physicalType and property.physicalType !=
291
- property.logicalType %}
292
- <div class="text-xs text-gray-500">{{ property.physicalType }}</div>
293
- {% endif %}
294
- </td>
295
- <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
296
- {% if property.required %}
297
- <span class="text-green-600">Yes</span>
298
- {% else %}
299
- <span class="text-gray-400">No</span>
300
- {% endif %}
301
- </td>
302
- <td class="px-4 py-4 text-sm text-gray-500">{{ property.description or "-"
303
- }}
304
- </td>
305
- </tr>
306
- {% endfor %}
325
+ {{ render_nested_properties(schema.properties, 0) }}
307
326
  </tbody>
308
327
  {% if schema.quality %}
309
328
  <tfoot class="divide-y divide-gray-200 bg-white">
@@ -663,4 +682,4 @@
663
682
  </div>
664
683
 
665
684
  </body>
666
- </html>
685
+ </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacontract-cli
3
- Version: 0.10.35
3
+ Version: 0.10.36
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
@@ -11,7 +11,7 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: typer<0.17,>=0.15.1
14
+ Requires-Dist: typer<0.20,>=0.15.1
15
15
  Requires-Dist: pydantic<2.12.0,>=2.8.2
16
16
  Requires-Dist: pyyaml~=6.0.1
17
17
  Requires-Dist: requests<2.33,>=2.31
@@ -28,7 +28,7 @@ Requires-Dist: python-dotenv<2.0.0,>=1.0.0
28
28
  Requires-Dist: boto3<2.0.0,>=1.34.41
29
29
  Requires-Dist: Jinja2<4.0.0,>=3.1.5
30
30
  Requires-Dist: jinja_partials<1.0.0,>=0.2.1
31
- Requires-Dist: datacontract-specification<2.0.0,>=1.2.0
31
+ Requires-Dist: datacontract-specification<2.0.0,>=1.2.3
32
32
  Requires-Dist: open-data-contract-standard<4.0.0,>=3.0.4
33
33
  Provides-Extra: avro
34
34
  Requires-Dist: avro==1.12.0; extra == "avro"
@@ -42,7 +42,7 @@ Provides-Extra: databricks
42
42
  Requires-Dist: soda-core-spark-df<3.6.0,>=3.3.20; extra == "databricks"
43
43
  Requires-Dist: soda-core-spark[databricks]<3.6.0,>=3.3.20; extra == "databricks"
44
44
  Requires-Dist: databricks-sql-connector<4.1.0,>=3.7.0; extra == "databricks"
45
- Requires-Dist: databricks-sdk<0.64.0; extra == "databricks"
45
+ Requires-Dist: databricks-sdk<0.68.0; extra == "databricks"
46
46
  Requires-Dist: pyspark<4.0.0,>=3.5.5; extra == "databricks"
47
47
  Provides-Extra: iceberg
48
48
  Requires-Dist: pyiceberg==0.9.1; extra == "iceberg"
@@ -56,7 +56,7 @@ Provides-Extra: s3
56
56
  Requires-Dist: s3fs<2026.0.0,>=2025.2.0; extra == "s3"
57
57
  Requires-Dist: aiobotocore<2.25.0,>=2.17.0; extra == "s3"
58
58
  Provides-Extra: snowflake
59
- Requires-Dist: snowflake-connector-python[pandas]<3.17,>=3.6; extra == "snowflake"
59
+ Requires-Dist: snowflake-connector-python[pandas]<4.1,>=3.6; extra == "snowflake"
60
60
  Requires-Dist: soda-core-snowflake<3.6.0,>=3.3.20; extra == "snowflake"
61
61
  Provides-Extra: sqlserver
62
62
  Requires-Dist: soda-core-sqlserver<3.6.0,>=3.3.20; extra == "sqlserver"
@@ -83,7 +83,7 @@ Provides-Extra: dev
83
83
  Requires-Dist: datacontract-cli[all]; extra == "dev"
84
84
  Requires-Dist: httpx==0.28.1; extra == "dev"
85
85
  Requires-Dist: kafka-python; extra == "dev"
86
- Requires-Dist: moto==5.1.10; extra == "dev"
86
+ Requires-Dist: moto==5.1.13; extra == "dev"
87
87
  Requires-Dist: pandas>=2.1.0; extra == "dev"
88
88
  Requires-Dist: pre-commit<4.4.0,>=3.7.1; extra == "dev"
89
89
  Requires-Dist: pytest; extra == "dev"
@@ -119,9 +119,9 @@ We have a _servers_ section with endpoint details to the S3 bucket, _models_ for
119
119
 
120
120
  This data contract contains all information to connect to S3 and check that the actual data meets the defined schema and quality requirements. We can use this information to test if the actual data product in S3 is compliant to the data contract.
121
121
 
122
- Let's use [pip](https://pip.pypa.io/en/stable/getting-started/) to install the CLI (or use the [Docker image](#docker)),
122
+ Let's use [uv](https://docs.astral.sh/uv/) to install the CLI (or use the [Docker image](#docker)),
123
123
  ```bash
124
- $ python3 -m pip install 'datacontract-cli[all]'
124
+ $ uv tool install --python python3.11 'datacontract-cli[all]'
125
125
  ```
126
126
 
127
127
 
@@ -1540,18 +1540,22 @@ datacontract import --format bigquery --bigquery-project <project_id> --bigquery
1540
1540
  ```
1541
1541
 
1542
1542
  #### Unity Catalog
1543
-
1544
1543
  ```bash
1545
1544
  # Example import from a Unity Catalog JSON file
1546
1545
  datacontract import --format unity --source my_unity_table.json
1547
1546
  ```
1548
1547
 
1549
1548
  ```bash
1550
- # Example import single table from Unity Catalog via HTTP endpoint
1549
+ # Example import single table from Unity Catalog via HTTP endpoint using PAT
1551
1550
  export DATACONTRACT_DATABRICKS_SERVER_HOSTNAME="https://xyz.cloud.databricks.com"
1552
- export DATACONTRACT_DATABRICKS_HTTP_PATH="/sql/1.0/warehouses/b053a331fa014fb4"
1553
1551
  export DATACONTRACT_DATABRICKS_TOKEN=<token>
1554
1552
  datacontract import --format unity --unity-table-full-name <table_full_name>
1553
+ ```
1554
+ Please Refer to [Databricks documentation](https://docs.databricks.com/aws/en/dev-tools/auth/unified-auth) on how to set up a profile
1555
+ ```bash
1556
+ # Example import single table from Unity Catalog via HTTP endpoint using Profile
1557
+ export DATACONTRACT_DATABRICKS_PROFILE="my-profile"
1558
+ datacontract import --format unity --unity-table-full-name <table_full_name>
1555
1559
  ```
1556
1560
 
1557
1561
  #### dbt
@@ -1610,20 +1614,20 @@ datacontract import --format spark --source "users,orders"
1610
1614
 
1611
1615
  ```bash
1612
1616
  # Example: Import Spark table
1613
- DataContract().import_from_source("spark", "users")
1614
- DataContract().import_from_source(format = "spark", source = "users")
1617
+ DataContract.import_from_source("spark", "users")
1618
+ DataContract.import_from_source(format = "spark", source = "users")
1615
1619
 
1616
1620
  # Example: Import Spark dataframe
1617
- DataContract().import_from_source("spark", "users", dataframe = df_user)
1618
- DataContract().import_from_source(format = "spark", source = "users", dataframe = df_user)
1621
+ DataContract.import_from_source("spark", "users", dataframe = df_user)
1622
+ DataContract.import_from_source(format = "spark", source = "users", dataframe = df_user)
1619
1623
 
1620
1624
  # Example: Import Spark table + table description
1621
- DataContract().import_from_source("spark", "users", description = "description")
1622
- DataContract().import_from_source(format = "spark", source = "users", description = "description")
1625
+ DataContract.import_from_source("spark", "users", description = "description")
1626
+ DataContract.import_from_source(format = "spark", source = "users", description = "description")
1623
1627
 
1624
1628
  # Example: Import Spark dataframe + table description
1625
- DataContract().import_from_source("spark", "users", dataframe = df_user, description = "description")
1626
- DataContract().import_from_source(format = "spark", source = "users", dataframe = df_user, description = "description")
1629
+ DataContract.import_from_source("spark", "users", dataframe = df_user, description = "description")
1630
+ DataContract.import_from_source(format = "spark", source = "users", dataframe = df_user, description = "description")
1627
1631
  ```
1628
1632
 
1629
1633
  #### DBML
@@ -1868,8 +1872,7 @@ Create a data contract based on the actual data. This is the fastest way to get
1868
1872
  $ datacontract test
1869
1873
  ```
1870
1874
 
1871
- 3. Make sure that all the best practices for a `datacontract.yaml` are met using the linter. You
1872
- probably forgot to document some fields and add the terms and conditions.
1875
+ 3. Validate that the `datacontract.yaml` is correctly formatted and adheres to the Data Contract Specification.
1873
1876
  ```bash
1874
1877
  $ datacontract lint
1875
1878
  ```
@@ -1890,8 +1893,7 @@ Create a data contract based on the requirements from use cases.
1890
1893
  ```
1891
1894
 
1892
1895
  2. Create the model and quality guarantees based on your business requirements. Fill in the terms,
1893
- descriptions, etc. Make sure you follow all best practices for a `datacontract.yaml` using the
1894
- linter.
1896
+ descriptions, etc. Validate that your `datacontract.yaml` is correctly formatted.
1895
1897
  ```bash
1896
1898
  $ datacontract lint
1897
1899
  ```
@@ -2085,7 +2087,7 @@ if __name__ == "__main__":
2085
2087
  Output
2086
2088
 
2087
2089
  ```yaml
2088
- dataContractSpecification: 1.2.0
2090
+ dataContractSpecification: 1.2.1
2089
2091
  id: uuid-custom
2090
2092
  info:
2091
2093
  title: my_custom_imported_data
@@ -2111,6 +2113,7 @@ models:
2111
2113
  ```bash
2112
2114
  # make sure uv is installed
2113
2115
  uv python pin 3.11
2116
+ uv venv
2114
2117
  uv pip install -e '.[dev]'
2115
2118
  uv run ruff check
2116
2119
  uv run pytest
@@ -1,14 +1,14 @@
1
1
  datacontract/__init__.py,sha256=ThDdxDJsd7qNErLoh628nK5M7RzhJNYCmN-C6BAJFoo,405
2
- datacontract/api.py,sha256=kU-xnHgYUORK8W34IMPtSMNZ482MDnxBzY8AjujHEf4,8578
3
- datacontract/cli.py,sha256=jlFJionOL6Q78eyIKoO8RuZt2wx3D1I4-yOZEmA43iI,19401
4
- datacontract/data_contract.py,sha256=Jlgkbzj6UN8RtFDK5VFcqm7v8oitVs-q10msU8W3Uo8,15183
2
+ datacontract/api.py,sha256=nFmrJOhC5AygY9YS1VXsbvKNtW92B8AF-lXdhuCvcPE,8578
3
+ datacontract/cli.py,sha256=fdO2n9NaJPJCx_IgNDjquMyOisOaFokBF4q8RXnQ_Bo,19399
4
+ datacontract/data_contract.py,sha256=24QE2ym5dfwTP6vJ0OmW37GNfGCCV6y4x4-J5Ouvfjk,13248
5
5
  datacontract/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  datacontract/breaking/breaking.py,sha256=DnqgxUjD-EAZcg5RBizOP9a2WxsFTaQBik0AB_m3K00,20431
7
7
  datacontract/breaking/breaking_change.py,sha256=BIDEUo1U2CQLVT2-I5PyFttxAj6zQPI1UUkEoOOQXMY,2249
8
8
  datacontract/breaking/breaking_rules.py,sha256=M9IdzVJSA7oOr1fvLQl0y9MoBKeItPz42Db2U2cjH2Y,4063
9
9
  datacontract/catalog/catalog.py,sha256=HyFmUPsN3pWJ2WTdbs0DmOf0qmwAzc2-ijWse9_dhBc,2729
10
10
  datacontract/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- datacontract/engines/data_contract_checks.py,sha256=NbYz7p9ljnia2XiF6PeWR4UNiZVpCAj2ufKgpir-Ve4,28234
11
+ datacontract/engines/data_contract_checks.py,sha256=LNI0OZPrzrf7sn6lPgTL4uAHSqMH2VBrmaBo8etuSE0,37453
12
12
  datacontract/engines/data_contract_test.py,sha256=8qg0SkwtTmayfzNL2U_0xgx5Hi_DUePaMt2q_JiCqX8,4543
13
13
  datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py,sha256=zrDn-_EJJ5kv0kZWAA-toeEPuBd3YQ0-U7Jb8euNUS8,1558
14
14
  datacontract/engines/datacontract/check_that_datacontract_file_exists.py,sha256=Vw-7U0GmQT2127tybxggZfpRFiZVgoIh6ndkTGM0FP4,665
@@ -32,11 +32,11 @@ datacontract/export/bigquery_converter.py,sha256=9mm-XP3klu1C5J87L9EL5ZyMCJhLBws
32
32
  datacontract/export/custom_converter.py,sha256=xb8KbkRRgHmT4ewwC7XxtnKpe_ZMSJWBjYOaKjmO_KQ,1216
33
33
  datacontract/export/data_caterer_converter.py,sha256=eSEuy3TbqUIG_lHYEBOydAgp_CJNoGArXrcJvh81wcw,5984
34
34
  datacontract/export/dbml_converter.py,sha256=f_OZEFwRUyL-Kg2yn_G58I8iz1VfFrZh8Nbw3Wq0JDo,4777
35
- datacontract/export/dbt_converter.py,sha256=U2x7rtEnq1s3pHhM0L2B6D6OQtKdCdm4PBSqNCHczHk,10577
35
+ datacontract/export/dbt_converter.py,sha256=58bub8n22dfL8w6bKdFe28BLF0e4PHbrxO_H3rZ9wfk,11840
36
36
  datacontract/export/dcs_exporter.py,sha256=RALQ7bLAjak7EsoFFL2GFX2Oju7pnCDPCdRN_wo9wHM,210
37
- datacontract/export/dqx_converter.py,sha256=BWMK_U-_c0XHb_OI0p3q31RAAbOj9X4erD_oUtMz0Hg,4013
37
+ datacontract/export/dqx_converter.py,sha256=5UevFPE8RdFIeu4CgeVnXMNDfWU7DhR34DW7O1aVIFs,4105
38
38
  datacontract/export/duckdb_type_converter.py,sha256=hUAAbImhJUMJOXEG-UoOKQqYGrJM6UILpn2YjUuAUOw,2216
39
- datacontract/export/excel_exporter.py,sha256=ySZL93oaENIjaLyctwoXOiT3yWf311YG3vYtLttjImI,38274
39
+ datacontract/export/excel_exporter.py,sha256=qn1GZBHYMmEIE9AWTwUskOQjmXtRcFapPO7XCTNcEMw,38344
40
40
  datacontract/export/exporter.py,sha256=DfvMHDWmdqhJswLkQ5oMNojgYDblXDuRgFJRHuFSawM,3085
41
41
  datacontract/export/exporter_factory.py,sha256=UvP3_U7xj-GEjaifi__Jri6eYKx9SFXtmSrnkSbWuP0,6318
42
42
  datacontract/export/go_converter.py,sha256=Ttvbfu3YU-3GBwRD6nwCsFyZuc_hiIvJD-Jg2sT5WLw,3331
@@ -44,17 +44,17 @@ datacontract/export/great_expectations_converter.py,sha256=Wx0mESRy4xAf8y7HjESsG
44
44
  datacontract/export/html_exporter.py,sha256=EyTMj25_Df3irZiYw1hxVZeLYWp6YSG6z3IuFUviP14,3066
45
45
  datacontract/export/iceberg_converter.py,sha256=ArcQ_Y3z_W4_kGDU_8jPRx2-pHpP3Nhx1zYoETOL3c4,6804
46
46
  datacontract/export/jsonschema_converter.py,sha256=2MT82MurcQQbrVDRj1kFsxnmFd9scNSfYI1upQSecl4,5631
47
- datacontract/export/markdown_converter.py,sha256=sV74JBGEfvhteNYPwBR-78ucq0Avp9oaPdFpu3Ckd0w,9935
47
+ datacontract/export/markdown_converter.py,sha256=J6QEGuopR9AUyEhux1GpjmJaQa1iihsbNMAmGRQ63BQ,10430
48
48
  datacontract/export/mermaid_exporter.py,sha256=Hg2yc5DYDTEZ7etoIhB1LU6rob_sGlouDtkPxUtf6kQ,4008
49
49
  datacontract/export/odcs_v3_exporter.py,sha256=b__AiPAnCUuFQE5DPHsvXBrMeEl1t_mJ1vzTx84TMlI,13931
50
50
  datacontract/export/pandas_type_converter.py,sha256=464pQ3JQKFQa1TO0HBNcEoZvQye_yUbY6jQtiBaphSc,1117
51
51
  datacontract/export/protobuf_converter.py,sha256=DHLl8BW26xqltBsd7Qhz0RhTl9YZQKCbkmjNpECgubg,7928
52
52
  datacontract/export/pydantic_converter.py,sha256=1Lt9F8i6zyQYb44MyQtsXwCWWXYxZ47SmzArr_uPqsU,5579
53
- datacontract/export/rdf_converter.py,sha256=1aTe_fwBRBnYUrJZzhEQ8eVnl0mQ1hcF45aKVdgvSIc,6435
53
+ datacontract/export/rdf_converter.py,sha256=zY2BZrRxM0J6C2cgf5zA8c7FxDRImFjZUrJ4ksmvSTw,6435
54
54
  datacontract/export/sodacl_converter.py,sha256=75vQ2TnoLfjiDtWT2x8opumvotXVRs1YaIu1NLYz05M,1473
55
55
  datacontract/export/spark_converter.py,sha256=aol9ygEq29mjrZNiaK3Vdm8kEZhCgFFphuFiFDX-pOE,7953
56
56
  datacontract/export/sql_converter.py,sha256=vyLbDqzt_J3LRXpPv2W2HqUIyAtQx_S-jviBiSxh14A,5087
57
- datacontract/export/sql_type_converter.py,sha256=6-FKC4GTTSftXntIesiptQ51WVtS-mYgJpKrzhVDi1M,13694
57
+ datacontract/export/sql_type_converter.py,sha256=eWHRHJNeg6oOT2uUPjmcVjEf6H_qXZvDhvSCk-_iBAM,13890
58
58
  datacontract/export/sqlalchemy_converter.py,sha256=0DMncvA811lTtd5q4ZORREQ9YH1vQm1lJeqMWsFvloE,6463
59
59
  datacontract/export/terraform_converter.py,sha256=ExFoEvErVk-gBnWJiqC38SxDUmUEydpACWc917l5RyM,2163
60
60
  datacontract/imports/avro_importer.py,sha256=isfAnMq9bk-Yo5zSyTnqMegu7JIujn_sTGSTOYAc8-0,11847
@@ -70,26 +70,19 @@ datacontract/imports/importer_factory.py,sha256=RS7uwkkT7rIKGeMKgPmZhE3GVC9IfZxZ
70
70
  datacontract/imports/json_importer.py,sha256=JeGbqAC_wAO0u8HeMA5H-KJBfs6gpp1oGIpxt6nxSZI,12641
71
71
  datacontract/imports/jsonschema_importer.py,sha256=67H__XLugV4vguHrIqzW02dtx27zYTWnOms4D1ma3bk,4961
72
72
  datacontract/imports/odcs_importer.py,sha256=ZP2u3kJsgULANTbbqkP3joOlU9cUneZOPy6Ak3oTMgs,2140
73
- datacontract/imports/odcs_v3_importer.py,sha256=kuPqTBsUid3n9x0oNexFhTPIqFxH3bzQBWFTc617Cfc,18115
73
+ datacontract/imports/odcs_v3_importer.py,sha256=ov1_PVoxZvzCd0a-cP5Ft2YJ_8vFDSxhqFqJBdMPLRY,20474
74
74
  datacontract/imports/parquet_importer.py,sha256=W_0_16mX4stwDUt4GM2L7dnGmTpAySab5k13-OlTCCc,3095
75
75
  datacontract/imports/protobuf_importer.py,sha256=rlUIskv9PNi5rFQ4Hobt9zlnKpahGsb4dy5G5UJoVAw,10840
76
76
  datacontract/imports/spark_importer.py,sha256=OxX9hJhi8e1o1pZGOKh5zWsK96SX13r0WV04kKDD61M,8964
77
77
  datacontract/imports/sql_importer.py,sha256=CfHap1zpyy-NVol_i21bDai3l4PD8OLuJQwaVlihbqg,9543
78
- datacontract/imports/unity_importer.py,sha256=iW4CJNbHNoQR5fqVlBJfdHnbbDasPgkwHHmyV9FKGkI,8687
79
- datacontract/init/init_template.py,sha256=nc-B2ZkwDQ3GNFqxNmSDcStQMDbBTxysgTZDLw15izo,721
78
+ datacontract/imports/unity_importer.py,sha256=F3pGI-cpkHmyqn83JK9tlxdgj0dfcex1g4zGP8_BhPg,9087
79
+ datacontract/init/init_template.py,sha256=sLCxvXHqoeW-Qes9W8GSVPfDmmu7pfnVOm-puI1-wsQ,721
80
80
  datacontract/integration/datamesh_manager.py,sha256=FT9eadzFz181lg54b49_c_x2caGJT7mR3drlZBSBJLo,3375
81
81
  datacontract/lint/files.py,sha256=tg0vq_w4LQsEr_8A5qr4hUJmHeGalUpsXJXC1t-OGC0,471
82
- datacontract/lint/lint.py,sha256=Ew0n3ooXxmCVnUxJ_cDoacsD82QdMZYnKrxnG9J0sWQ,5077
83
- datacontract/lint/resolve.py,sha256=cXYAEfmy1mhK-RwnAOOQ7PzsDI0iRdKjQHDWHozq5DY,14507
82
+ datacontract/lint/resolve.py,sha256=wDD6NRT1d_6Kh_CRKRwPO_q10VrnlKECWCYgyxsI1Ks,14507
84
83
  datacontract/lint/resources.py,sha256=nfeZmORh1aP7EKpMKCmfbS04Te8pQ0nz64vJVkHOq3c,647
85
- datacontract/lint/schema.py,sha256=xRHPu6K-kepw1jSnUXY-s5IikjIrH7fvDtsGqQeupYw,1813
84
+ datacontract/lint/schema.py,sha256=GtjyWJvG_Q1O0kHkXSRaIzQh7a9kdZ64u-hKkbZpm8A,1813
86
85
  datacontract/lint/urls.py,sha256=giac0eAYa6hha8exleL3KsiPtiFlOq8l53axtAmCilw,2529
87
- datacontract/lint/linters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- datacontract/lint/linters/description_linter.py,sha256=kQi38TKhqiEL3fwQDs6SdQQ9hXBHgfAi6Q6ZFNuLw1o,1505
89
- datacontract/lint/linters/field_pattern_linter.py,sha256=lreGvOW3v_Glah_SriVe9ejZ7EuR6_gJsdr2tEORB_8,1084
90
- datacontract/lint/linters/field_reference_linter.py,sha256=klLsQleJwW2qSJT0haS_V63oo7uYt2-SXbJh-rRkfVI,1989
91
- datacontract/lint/linters/notice_period_linter.py,sha256=_0ZrwlHWA8my0IWAY3upQNKfeigj5N0ulT7Me9kd2PE,2124
92
- datacontract/lint/linters/valid_constraints_linter.py,sha256=-IUyFFoEbnoNbjjJntlJv1POajh2mRU-4mJQVPL5r9s,4904
93
86
  datacontract/model/exceptions.py,sha256=5BMuEH2qWuckNP4FTfpUEeEu6rjgGcLOD0GQugKRQ1U,1242
94
87
  datacontract/model/odcs.py,sha256=9PXwm72FASjNwteF1Jn591iP3-St0aq16Cpsk0PkEW8,389
95
88
  datacontract/model/run.py,sha256=4UdEUaJl5RxEpN9S3swSu1vGJUVyNhOpRkdfbBZhh90,3146
@@ -98,13 +91,16 @@ datacontract/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
98
91
  datacontract/output/junit_test_results.py,sha256=ZjevRMTxNSiR0HMr3bEvqv4olozPw2zEutbuLloInww,4822
99
92
  datacontract/output/output_format.py,sha256=_ZokDBo7-HXBs6czUv7kLLf9cYft_q5QaKzthsVnc58,212
100
93
  datacontract/output/test_results_writer.py,sha256=PWNLs3R_LQMH4xp5WDxLkQgY3xvj8Eyzw1jnfgkQxlc,2713
101
- datacontract/schemas/datacontract-1.1.0.init.yaml,sha256=RXRqtMiRsm1oqxXrKAzqQ_eScIr9D8td8-NWWmpYvM0,1828
94
+ datacontract/schemas/datacontract-1.1.0.init.yaml,sha256=ij_-ZEJP4A7ekeJfoqGpRbiCys7_YjkClNluVVo4C6E,1828
102
95
  datacontract/schemas/datacontract-1.1.0.schema.json,sha256=3Bu2rxEjkF6dNLcqi1GF4KoXBnEIopaJ87Qb8S4zUvg,62872
103
- datacontract/schemas/datacontract-1.2.0.init.yaml,sha256=RXRqtMiRsm1oqxXrKAzqQ_eScIr9D8td8-NWWmpYvM0,1828
96
+ datacontract/schemas/datacontract-1.2.0.init.yaml,sha256=ij_-ZEJP4A7ekeJfoqGpRbiCys7_YjkClNluVVo4C6E,1828
104
97
  datacontract/schemas/datacontract-1.2.0.schema.json,sha256=sk7oL06cug9-WozCrLH8v8MuR3a8MaV1Ztkm1P-7UFk,64226
98
+ datacontract/schemas/datacontract-1.2.1.init.yaml,sha256=ij_-ZEJP4A7ekeJfoqGpRbiCys7_YjkClNluVVo4C6E,1828
99
+ datacontract/schemas/datacontract-1.2.1.schema.json,sha256=Ha6F8i2jaL3BKOV5kjWgaxzykAiaSLqjIq-OEOojnx4,65233
105
100
  datacontract/schemas/odcs-3.0.1.schema.json,sha256=bRZsSXA0fV0EmV_8f1K68PlXu1m4K7JcuHpLnY3ESwQ,72933
101
+ datacontract/schemas/odcs-3.0.2.schema.json,sha256=_J13Tqc9E7RzpSho645meE86AxeU0dIt2U12-MnAfHk,69968
106
102
  datacontract/templates/datacontract.html,sha256=dksPEnY3c66jaaVS5r5vWfG6LzyXPjA4nO_yLUirJWg,17394
107
- datacontract/templates/datacontract_odcs.html,sha256=-k6v_ddMkQ6HPbj0_c6SmP3LfesRVsjvSVt4EpOfzpg,32849
103
+ datacontract/templates/datacontract_odcs.html,sha256=u4bpcgQVqwGmR0QjijJqecOClV2ZhpDvnNMAMzj4Ezc,32659
108
104
  datacontract/templates/index.html,sha256=8AAJbizpW6UxQBnHjW4Ft7L7nTn7XJtV43gVBwQxFxA,12711
109
105
  datacontract/templates/partials/datacontract_information.html,sha256=7ZBxgEgi2XndKBypeOpe03oCSRPOujC6NVlN7zexGNM,6221
110
106
  datacontract/templates/partials/datacontract_servicelevels.html,sha256=ed3QgB11B0Qq2h_NwaroGZ4pQMBPEhfeQaoS-qEipqY,11401
@@ -115,9 +111,9 @@ datacontract/templates/partials/model_field.html,sha256=2YBF95ypNCPFYuYKoeilRnDG
115
111
  datacontract/templates/partials/quality.html,sha256=ynEDWRn8I90Uje-xhGYgFcfwOgKI1R-CDki-EvTsauQ,1785
116
112
  datacontract/templates/partials/server.html,sha256=dHFJtonMjhiUHtT69RUgTpkoRwmNdTRzkCdH0LtGg_4,6279
117
113
  datacontract/templates/style/output.css,sha256=ioIo1f96VW7LHhDifj6QI8QbRChJl-LlQ59EwM8MEmA,28692
118
- datacontract_cli-0.10.35.dist-info/licenses/LICENSE,sha256=0hcS8X51AL0UvEsx1ZM6WQcxiy9d0j5iOfzdPYM6ONU,2205
119
- datacontract_cli-0.10.35.dist-info/METADATA,sha256=QkNoML7XM2Y2tlcv4in835nVni6yCoP9FbV-T0pJ0Pc,114486
120
- datacontract_cli-0.10.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
- datacontract_cli-0.10.35.dist-info/entry_points.txt,sha256=D3Eqy4q_Z6bHauGd4ppIyQglwbrm1AJnLau4Ppbw9Is,54
122
- datacontract_cli-0.10.35.dist-info/top_level.txt,sha256=VIRjd8EIUrBYWjEXJJjtdUgc0UAJdPZjmLiOR8BRBYM,13
123
- datacontract_cli-0.10.35.dist-info/RECORD,,
114
+ datacontract_cli-0.10.36.dist-info/licenses/LICENSE,sha256=0hcS8X51AL0UvEsx1ZM6WQcxiy9d0j5iOfzdPYM6ONU,2205
115
+ datacontract_cli-0.10.36.dist-info/METADATA,sha256=zk34Wgf2d8yAbM2bV4jw_ssSqyO9RGdMWTSSLMNH1vQ,114670
116
+ datacontract_cli-0.10.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
117
+ datacontract_cli-0.10.36.dist-info/entry_points.txt,sha256=D3Eqy4q_Z6bHauGd4ppIyQglwbrm1AJnLau4Ppbw9Is,54
118
+ datacontract_cli-0.10.36.dist-info/top_level.txt,sha256=VIRjd8EIUrBYWjEXJJjtdUgc0UAJdPZjmLiOR8BRBYM,13
119
+ datacontract_cli-0.10.36.dist-info/RECORD,,
datacontract/lint/lint.py DELETED
@@ -1,142 +0,0 @@
1
- import abc
2
- from dataclasses import dataclass, field
3
- from enum import Enum
4
- from typing import Any, Sequence, cast
5
-
6
- from datacontract.model.run import Check
7
-
8
- from ..model.data_contract_specification import DataContractSpecification
9
-
10
- """This module contains linter definitions for linting a data contract.
11
-
12
- Lints are quality checks that can succeed, fail, or warn. They are
13
- distinct from checks such as "valid yaml" or "file not found", which
14
- will cause the processing of the data contract to stop. Lints can be
15
- ignored, and are high-level requirements on the format of a data
16
- contract."""
17
-
18
-
19
- class LintSeverity(Enum):
20
- """The severity of a lint message. Generally, lint messages should be
21
- emitted with a severity of ERROR. WARNING should be used when the linter
22
- cannot determine a lint result, for example, when an unsupported model
23
- type is used.
24
- """
25
-
26
- ERROR = 2
27
- WARNING = 1
28
-
29
-
30
- @dataclass
31
- class LinterMessage:
32
- """A single linter message with attached severity and optional "model" that
33
- caused the message.
34
-
35
- Attributes:
36
- outcome: The outcome of the linting, either ERROR or WARNING. Linting outcomes with level WARNING are discarded for now.
37
- message: A message describing the error or warning in more detail.
38
- model: The model that caused the lint to fail. Is optional.
39
-
40
- """
41
-
42
- outcome: LintSeverity
43
- message: str
44
- model: Any = None
45
-
46
- @classmethod
47
- def error(cls, message: str, model=None):
48
- return LinterMessage(LintSeverity.ERROR, message, model)
49
-
50
- @classmethod
51
- def warning(cls, message: str, model=None):
52
- return LinterMessage(LintSeverity.WARNING, message, model)
53
-
54
-
55
- @dataclass
56
- class LinterResult:
57
- """Result of linting a contract. Contains multiple LinterResults from
58
- the same linter or lint phase.
59
-
60
- Attributes:
61
- linter: The linter that produced these results
62
- results: A list of linting results. Multiple identical linting
63
- results can be present in the list. An empty list means that
64
- the linter ran without producing warnings or errors.
65
- """
66
-
67
- results: Sequence[LinterMessage] = field(default_factory=list)
68
-
69
- @classmethod
70
- def erroneous(cls, message, model=None):
71
- return cls([LinterMessage.error(message, model)])
72
-
73
- @classmethod
74
- def cautious(cls, message, model=None):
75
- return cls([LinterMessage.warning(message, model)])
76
-
77
- def with_warning(self, message, model=None):
78
- result = LinterMessage.warning(message, model)
79
- return LinterResult(cast(list[LinterMessage], self.results) + [result])
80
-
81
- def with_error(self, message, model=None):
82
- result = LinterMessage.error(message, model)
83
- return LinterResult(cast(list[LinterMessage], self.results) + [result])
84
-
85
- def has_errors(self) -> bool:
86
- return any(map(lambda result: result.outcome == LintSeverity.ERROR, self.results))
87
-
88
- def has_warnings(self) -> bool:
89
- return any(map(lambda result: result.outcome == LintSeverity.WARNING, self.results))
90
-
91
- def error_results(self) -> Sequence[LinterMessage]:
92
- return [result for result in self.results if result.outcome == LintSeverity.ERROR]
93
-
94
- def warning_results(self) -> Sequence[LinterMessage]:
95
- return [result for result in self.results if result.outcome == LintSeverity.WARNING]
96
-
97
- def no_errors_or_warnings(self) -> bool:
98
- return len(self.results) == 0
99
-
100
- def combine(self, other: "LinterResult") -> "LinterResult":
101
- return LinterResult(cast(list[Any], self.results) + cast(list[Any], other.results))
102
-
103
-
104
- class Linter(abc.ABC):
105
- @property
106
- @abc.abstractmethod
107
- def name(self) -> str:
108
- """Human-readable name of the linter."""
109
- pass
110
-
111
- @property
112
- @abc.abstractmethod
113
- def id(self) -> str:
114
- """A linter ID for configuration (i.e. enabling and disabling)."""
115
- pass
116
-
117
- @abc.abstractmethod
118
- def lint_implementation(self, contract: DataContractSpecification) -> LinterResult:
119
- pass
120
-
121
- def lint(self, contract: DataContractSpecification) -> list[Check]:
122
- """Call with a data contract to get a list of check results from the linter."""
123
- result = self.lint_implementation(contract)
124
- checks = []
125
- if not result.error_results():
126
- checks.append(Check(type="lint", name=f"Linter '{self.name}'", result="passed", engine="datacontract"))
127
- else:
128
- # All linter messages are treated as warnings. Severity is
129
- # currently ignored, but could be used in filtering in the future
130
- # Linter messages with level WARNING are currently ignored, but might
131
- # be logged or printed in the future.
132
- for lint_error in result.error_results():
133
- checks.append(
134
- Check(
135
- type="lint",
136
- name=f"Linter '{self.name}'",
137
- result="warning",
138
- engine="datacontract",
139
- reason=lint_error.message,
140
- )
141
- )
142
- return checks
File without changes
@@ -1,33 +0,0 @@
1
- from datacontract.model.data_contract_specification import DataContractSpecification
2
-
3
- from ..lint import Linter, LinterResult
4
-
5
-
6
- class DescriptionLinter(Linter):
7
- """Check for a description on contracts, models, model fields, definitions and examples."""
8
-
9
- @property
10
- def name(self) -> str:
11
- return "Objects have descriptions"
12
-
13
- @property
14
- def id(self) -> str:
15
- return "description"
16
-
17
- def lint_implementation(self, contract: DataContractSpecification) -> LinterResult:
18
- result = LinterResult()
19
- if not contract.info or not contract.info.description:
20
- result = result.with_error("Contract has empty description.")
21
- for model_name, model in contract.models.items():
22
- if not model.description:
23
- result = result.with_error(f"Model '{model_name}' has empty description.")
24
- for field_name, field in model.fields.items():
25
- if not field.description:
26
- result = result.with_error(f"Field '{field_name}' in model '{model_name}' has empty description.")
27
- for definition_name, definition in contract.definitions.items():
28
- if not definition.description:
29
- result = result.with_error(f"Definition '{definition_name}' has empty description.")
30
- for index, example in enumerate(contract.examples):
31
- if not example.description:
32
- result = result.with_error(f"Example {index + 1} has empty description.")
33
- return result
@@ -1,34 +0,0 @@
1
- import re
2
-
3
- from datacontract.model.data_contract_specification import DataContractSpecification
4
-
5
- from ..lint import Linter, LinterResult
6
-
7
-
8
- class FieldPatternLinter(Linter):
9
- """Checks that all patterns defined for fields are correct Python regex
10
- syntax.
11
-
12
- """
13
-
14
- @property
15
- def name(self):
16
- return "Field pattern is correct regex"
17
-
18
- @property
19
- def id(self) -> str:
20
- return "field-pattern"
21
-
22
- def lint_implementation(self, contract: DataContractSpecification) -> LinterResult:
23
- result = LinterResult()
24
- for model_name, model in contract.models.items():
25
- for field_name, field in model.fields.items():
26
- if field.pattern:
27
- try:
28
- re.compile(field.pattern)
29
- except re.error as e:
30
- result = result.with_error(
31
- f"Failed to compile pattern regex '{field.pattern}' for "
32
- f"field '{field_name}' in model '{model_name}': {e.msg}"
33
- )
34
- return result