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.
- datacontract/api.py +1 -1
- datacontract/cli.py +1 -1
- datacontract/data_contract.py +18 -51
- datacontract/engines/data_contract_checks.py +280 -19
- datacontract/export/dbt_converter.py +30 -4
- datacontract/export/dqx_converter.py +12 -7
- datacontract/export/excel_exporter.py +3 -3
- datacontract/export/markdown_converter.py +35 -16
- datacontract/export/rdf_converter.py +2 -2
- datacontract/export/sql_type_converter.py +6 -4
- datacontract/imports/odcs_v3_importer.py +71 -18
- datacontract/imports/unity_importer.py +16 -11
- datacontract/init/init_template.py +1 -1
- datacontract/lint/resolve.py +1 -1
- datacontract/lint/schema.py +1 -1
- datacontract/schemas/datacontract-1.1.0.init.yaml +1 -1
- datacontract/schemas/datacontract-1.2.0.init.yaml +1 -1
- datacontract/schemas/datacontract-1.2.1.init.yaml +91 -0
- datacontract/schemas/datacontract-1.2.1.schema.json +2058 -0
- datacontract/schemas/odcs-3.0.2.schema.json +2382 -0
- datacontract/templates/datacontract_odcs.html +60 -41
- {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/METADATA +27 -24
- {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/RECORD +27 -31
- datacontract/lint/lint.py +0 -142
- datacontract/lint/linters/__init__.py +0 -0
- datacontract/lint/linters/description_linter.py +0 -33
- datacontract/lint/linters/field_pattern_linter.py +0 -34
- datacontract/lint/linters/field_reference_linter.py +0 -47
- datacontract/lint/linters/notice_period_linter.py +0 -55
- datacontract/lint/linters/valid_constraints_linter.py +0 -100
- {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/WHEEL +0 -0
- {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/entry_points.txt +0 -0
- {datacontract_cli-0.10.35.dist-info → datacontract_cli-0.10.36.dist-info}/licenses/LICENSE +0 -0
- {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
|
+
|
|
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
|
-
{
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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]<
|
|
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.
|
|
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 [
|
|
122
|
+
Let's use [uv](https://docs.astral.sh/uv/) to install the CLI (or use the [Docker image](#docker)),
|
|
123
123
|
```bash
|
|
124
|
-
$
|
|
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
|
|
1614
|
-
DataContract
|
|
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
|
|
1618
|
-
DataContract
|
|
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
|
|
1622
|
-
DataContract
|
|
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
|
|
1626
|
-
DataContract
|
|
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.
|
|
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.
|
|
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.
|
|
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=
|
|
3
|
-
datacontract/cli.py,sha256=
|
|
4
|
-
datacontract/data_contract.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
79
|
-
datacontract/init/init_template.py,sha256=
|
|
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/
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
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.
|
|
119
|
-
datacontract_cli-0.10.
|
|
120
|
-
datacontract_cli-0.10.
|
|
121
|
-
datacontract_cli-0.10.
|
|
122
|
-
datacontract_cli-0.10.
|
|
123
|
-
datacontract_cli-0.10.
|
|
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
|