dvt-core 1.11.0b4__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 dvt-core might be problematic. Click here for more details.
- dvt/__init__.py +7 -0
- dvt/_pydantic_shim.py +26 -0
- dvt/adapters/__init__.py +16 -0
- dvt/adapters/multi_adapter_manager.py +268 -0
- dvt/artifacts/__init__.py +0 -0
- dvt/artifacts/exceptions/__init__.py +1 -0
- dvt/artifacts/exceptions/schemas.py +31 -0
- dvt/artifacts/resources/__init__.py +116 -0
- dvt/artifacts/resources/base.py +68 -0
- dvt/artifacts/resources/types.py +93 -0
- dvt/artifacts/resources/v1/analysis.py +10 -0
- dvt/artifacts/resources/v1/catalog.py +23 -0
- dvt/artifacts/resources/v1/components.py +275 -0
- dvt/artifacts/resources/v1/config.py +282 -0
- dvt/artifacts/resources/v1/documentation.py +11 -0
- dvt/artifacts/resources/v1/exposure.py +52 -0
- dvt/artifacts/resources/v1/function.py +53 -0
- dvt/artifacts/resources/v1/generic_test.py +32 -0
- dvt/artifacts/resources/v1/group.py +22 -0
- dvt/artifacts/resources/v1/hook.py +11 -0
- dvt/artifacts/resources/v1/macro.py +30 -0
- dvt/artifacts/resources/v1/metric.py +173 -0
- dvt/artifacts/resources/v1/model.py +146 -0
- dvt/artifacts/resources/v1/owner.py +10 -0
- dvt/artifacts/resources/v1/saved_query.py +112 -0
- dvt/artifacts/resources/v1/seed.py +42 -0
- dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dvt/artifacts/resources/v1/semantic_model.py +315 -0
- dvt/artifacts/resources/v1/singular_test.py +14 -0
- dvt/artifacts/resources/v1/snapshot.py +92 -0
- dvt/artifacts/resources/v1/source_definition.py +85 -0
- dvt/artifacts/resources/v1/sql_operation.py +10 -0
- dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
- dvt/artifacts/schemas/__init__.py +0 -0
- dvt/artifacts/schemas/base.py +191 -0
- dvt/artifacts/schemas/batch_results.py +24 -0
- dvt/artifacts/schemas/catalog/__init__.py +12 -0
- dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
- dvt/artifacts/schemas/freshness/__init__.py +1 -0
- dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
- dvt/artifacts/schemas/manifest/__init__.py +2 -0
- dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
- dvt/artifacts/schemas/results.py +148 -0
- dvt/artifacts/schemas/run/__init__.py +2 -0
- dvt/artifacts/schemas/run/v5/__init__.py +0 -0
- dvt/artifacts/schemas/run/v5/run.py +184 -0
- dvt/artifacts/schemas/upgrades/__init__.py +4 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dvt/artifacts/utils/validation.py +153 -0
- dvt/cli/__init__.py +1 -0
- dvt/cli/context.py +16 -0
- dvt/cli/exceptions.py +56 -0
- dvt/cli/flags.py +558 -0
- dvt/cli/main.py +971 -0
- dvt/cli/option_types.py +121 -0
- dvt/cli/options.py +79 -0
- dvt/cli/params.py +803 -0
- dvt/cli/requires.py +478 -0
- dvt/cli/resolvers.py +32 -0
- dvt/cli/types.py +40 -0
- dvt/clients/__init__.py +0 -0
- dvt/clients/checked_load.py +82 -0
- dvt/clients/git.py +164 -0
- dvt/clients/jinja.py +206 -0
- dvt/clients/jinja_static.py +245 -0
- dvt/clients/registry.py +192 -0
- dvt/clients/yaml_helper.py +68 -0
- dvt/compilation.py +833 -0
- dvt/compute/__init__.py +26 -0
- dvt/compute/base.py +288 -0
- dvt/compute/engines/__init__.py +13 -0
- dvt/compute/engines/duckdb_engine.py +368 -0
- dvt/compute/engines/spark_engine.py +273 -0
- dvt/compute/query_analyzer.py +212 -0
- dvt/compute/router.py +483 -0
- dvt/config/__init__.py +4 -0
- dvt/config/catalogs.py +95 -0
- dvt/config/compute_config.py +406 -0
- dvt/config/profile.py +411 -0
- dvt/config/profiles_v2.py +464 -0
- dvt/config/project.py +893 -0
- dvt/config/renderer.py +232 -0
- dvt/config/runtime.py +491 -0
- dvt/config/selectors.py +209 -0
- dvt/config/utils.py +78 -0
- dvt/connectors/.gitignore +6 -0
- dvt/connectors/README.md +306 -0
- dvt/connectors/catalog.yml +217 -0
- dvt/connectors/download_connectors.py +300 -0
- dvt/constants.py +29 -0
- dvt/context/__init__.py +0 -0
- dvt/context/base.py +746 -0
- dvt/context/configured.py +136 -0
- dvt/context/context_config.py +350 -0
- dvt/context/docs.py +82 -0
- dvt/context/exceptions_jinja.py +179 -0
- dvt/context/macro_resolver.py +195 -0
- dvt/context/macros.py +171 -0
- dvt/context/manifest.py +73 -0
- dvt/context/providers.py +2198 -0
- dvt/context/query_header.py +14 -0
- dvt/context/secret.py +59 -0
- dvt/context/target.py +74 -0
- dvt/contracts/__init__.py +0 -0
- dvt/contracts/files.py +413 -0
- dvt/contracts/graph/__init__.py +0 -0
- dvt/contracts/graph/manifest.py +1904 -0
- dvt/contracts/graph/metrics.py +98 -0
- dvt/contracts/graph/model_config.py +71 -0
- dvt/contracts/graph/node_args.py +42 -0
- dvt/contracts/graph/nodes.py +1806 -0
- dvt/contracts/graph/semantic_manifest.py +233 -0
- dvt/contracts/graph/unparsed.py +812 -0
- dvt/contracts/project.py +417 -0
- dvt/contracts/results.py +53 -0
- dvt/contracts/selection.py +23 -0
- dvt/contracts/sql.py +86 -0
- dvt/contracts/state.py +69 -0
- dvt/contracts/util.py +46 -0
- dvt/deprecations.py +347 -0
- dvt/deps/__init__.py +0 -0
- dvt/deps/base.py +153 -0
- dvt/deps/git.py +196 -0
- dvt/deps/local.py +80 -0
- dvt/deps/registry.py +131 -0
- dvt/deps/resolver.py +149 -0
- dvt/deps/tarball.py +121 -0
- dvt/docs/source/_ext/dbt_click.py +118 -0
- dvt/docs/source/conf.py +32 -0
- dvt/env_vars.py +64 -0
- dvt/event_time/event_time.py +40 -0
- dvt/event_time/sample_window.py +60 -0
- dvt/events/__init__.py +16 -0
- dvt/events/base_types.py +37 -0
- dvt/events/core_types_pb2.py +2 -0
- dvt/events/logging.py +109 -0
- dvt/events/types.py +2534 -0
- dvt/exceptions.py +1487 -0
- dvt/flags.py +89 -0
- dvt/graph/__init__.py +11 -0
- dvt/graph/cli.py +248 -0
- dvt/graph/graph.py +172 -0
- dvt/graph/queue.py +213 -0
- dvt/graph/selector.py +375 -0
- dvt/graph/selector_methods.py +976 -0
- dvt/graph/selector_spec.py +223 -0
- dvt/graph/thread_pool.py +18 -0
- dvt/hooks.py +21 -0
- dvt/include/README.md +49 -0
- dvt/include/__init__.py +3 -0
- dvt/include/global_project.py +4 -0
- dvt/include/starter_project/.gitignore +4 -0
- dvt/include/starter_project/README.md +15 -0
- dvt/include/starter_project/__init__.py +3 -0
- dvt/include/starter_project/analyses/.gitkeep +0 -0
- dvt/include/starter_project/dvt_project.yml +36 -0
- dvt/include/starter_project/macros/.gitkeep +0 -0
- dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dvt/include/starter_project/models/example/schema.yml +21 -0
- dvt/include/starter_project/seeds/.gitkeep +0 -0
- dvt/include/starter_project/snapshots/.gitkeep +0 -0
- dvt/include/starter_project/tests/.gitkeep +0 -0
- dvt/internal_deprecations.py +27 -0
- dvt/jsonschemas/__init__.py +3 -0
- dvt/jsonschemas/jsonschemas.py +309 -0
- dvt/jsonschemas/project/0.0.110.json +4717 -0
- dvt/jsonschemas/project/0.0.85.json +2015 -0
- dvt/jsonschemas/resources/0.0.110.json +2636 -0
- dvt/jsonschemas/resources/0.0.85.json +2536 -0
- dvt/jsonschemas/resources/latest.json +6773 -0
- dvt/links.py +4 -0
- dvt/materializations/__init__.py +0 -0
- dvt/materializations/incremental/__init__.py +0 -0
- dvt/materializations/incremental/microbatch.py +235 -0
- dvt/mp_context.py +8 -0
- dvt/node_types.py +37 -0
- dvt/parser/__init__.py +23 -0
- dvt/parser/analysis.py +21 -0
- dvt/parser/base.py +549 -0
- dvt/parser/common.py +267 -0
- dvt/parser/docs.py +52 -0
- dvt/parser/fixtures.py +51 -0
- dvt/parser/functions.py +30 -0
- dvt/parser/generic_test.py +100 -0
- dvt/parser/generic_test_builders.py +334 -0
- dvt/parser/hooks.py +119 -0
- dvt/parser/macros.py +137 -0
- dvt/parser/manifest.py +2204 -0
- dvt/parser/models.py +574 -0
- dvt/parser/partial.py +1179 -0
- dvt/parser/read_files.py +445 -0
- dvt/parser/schema_generic_tests.py +423 -0
- dvt/parser/schema_renderer.py +111 -0
- dvt/parser/schema_yaml_readers.py +936 -0
- dvt/parser/schemas.py +1467 -0
- dvt/parser/search.py +149 -0
- dvt/parser/seeds.py +28 -0
- dvt/parser/singular_test.py +20 -0
- dvt/parser/snapshots.py +44 -0
- dvt/parser/sources.py +557 -0
- dvt/parser/sql.py +63 -0
- dvt/parser/unit_tests.py +622 -0
- dvt/plugins/__init__.py +20 -0
- dvt/plugins/contracts.py +10 -0
- dvt/plugins/exceptions.py +2 -0
- dvt/plugins/manager.py +164 -0
- dvt/plugins/manifest.py +21 -0
- dvt/profiler.py +20 -0
- dvt/py.typed +1 -0
- dvt/runners/__init__.py +2 -0
- dvt/runners/exposure_runner.py +7 -0
- dvt/runners/no_op_runner.py +46 -0
- dvt/runners/saved_query_runner.py +7 -0
- dvt/selected_resources.py +8 -0
- dvt/task/__init__.py +0 -0
- dvt/task/base.py +504 -0
- dvt/task/build.py +197 -0
- dvt/task/clean.py +57 -0
- dvt/task/clone.py +162 -0
- dvt/task/compile.py +151 -0
- dvt/task/compute.py +366 -0
- dvt/task/debug.py +650 -0
- dvt/task/deps.py +280 -0
- dvt/task/docs/__init__.py +3 -0
- dvt/task/docs/generate.py +408 -0
- dvt/task/docs/index.html +250 -0
- dvt/task/docs/serve.py +28 -0
- dvt/task/freshness.py +323 -0
- dvt/task/function.py +122 -0
- dvt/task/group_lookup.py +46 -0
- dvt/task/init.py +374 -0
- dvt/task/list.py +237 -0
- dvt/task/printer.py +176 -0
- dvt/task/profiles.py +256 -0
- dvt/task/retry.py +175 -0
- dvt/task/run.py +1146 -0
- dvt/task/run_operation.py +142 -0
- dvt/task/runnable.py +802 -0
- dvt/task/seed.py +104 -0
- dvt/task/show.py +150 -0
- dvt/task/snapshot.py +57 -0
- dvt/task/sql.py +111 -0
- dvt/task/test.py +464 -0
- dvt/tests/fixtures/__init__.py +1 -0
- dvt/tests/fixtures/project.py +620 -0
- dvt/tests/util.py +651 -0
- dvt/tracking.py +529 -0
- dvt/utils/__init__.py +3 -0
- dvt/utils/artifact_upload.py +151 -0
- dvt/utils/utils.py +408 -0
- dvt/version.py +249 -0
- dvt_core-1.11.0b4.dist-info/METADATA +252 -0
- dvt_core-1.11.0b4.dist-info/RECORD +261 -0
- dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
- dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
- dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
dvt/parser/common.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
|
|
3
|
+
|
|
4
|
+
from dvt.artifacts.resources import ColumnConfig, ColumnInfo, NodeVersion
|
|
5
|
+
from dvt.contracts.graph.nodes import UnpatchedSourceDefinition
|
|
6
|
+
from dvt.contracts.graph.unparsed import (
|
|
7
|
+
HasColumnDocs,
|
|
8
|
+
HasColumnProps,
|
|
9
|
+
HasColumnTests,
|
|
10
|
+
UnparsedAnalysisUpdate,
|
|
11
|
+
UnparsedColumn,
|
|
12
|
+
UnparsedExposure,
|
|
13
|
+
UnparsedFunctionUpdate,
|
|
14
|
+
UnparsedMacroUpdate,
|
|
15
|
+
UnparsedModelUpdate,
|
|
16
|
+
UnparsedNodeUpdate,
|
|
17
|
+
UnparsedSingularTestUpdate,
|
|
18
|
+
)
|
|
19
|
+
from dvt.exceptions import ParsingError
|
|
20
|
+
from dvt.node_types import NodeType
|
|
21
|
+
from dvt.parser.search import FileBlock
|
|
22
|
+
|
|
23
|
+
from dbt_common.contracts.constraints import ColumnLevelConstraint, ConstraintType
|
|
24
|
+
from dbt_common.exceptions import DbtInternalError
|
|
25
|
+
from dbt_semantic_interfaces.type_enums import TimeGranularity
|
|
26
|
+
|
|
27
|
+
schema_file_keys_to_resource_types = {
|
|
28
|
+
"models": NodeType.Model,
|
|
29
|
+
"seeds": NodeType.Seed,
|
|
30
|
+
"snapshots": NodeType.Snapshot,
|
|
31
|
+
"sources": NodeType.Source,
|
|
32
|
+
"macros": NodeType.Macro,
|
|
33
|
+
"analyses": NodeType.Analysis,
|
|
34
|
+
"exposures": NodeType.Exposure,
|
|
35
|
+
"metrics": NodeType.Metric,
|
|
36
|
+
"semantic_models": NodeType.SemanticModel,
|
|
37
|
+
"saved_queries": NodeType.SavedQuery,
|
|
38
|
+
"functions": NodeType.Function,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
resource_types_to_schema_file_keys = {
|
|
42
|
+
v: k for (k, v) in schema_file_keys_to_resource_types.items()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
schema_file_keys = list(schema_file_keys_to_resource_types.keys())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def trimmed(inp: str) -> str:
|
|
49
|
+
if len(inp) < 50:
|
|
50
|
+
return inp
|
|
51
|
+
return inp[:44] + "..." + inp[-3:]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
TestDef = Union[str, Dict[str, Any]]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Target = TypeVar(
|
|
58
|
+
"Target",
|
|
59
|
+
UnparsedNodeUpdate,
|
|
60
|
+
UnparsedMacroUpdate,
|
|
61
|
+
UnparsedAnalysisUpdate,
|
|
62
|
+
UnpatchedSourceDefinition,
|
|
63
|
+
UnparsedExposure,
|
|
64
|
+
UnparsedModelUpdate,
|
|
65
|
+
UnparsedFunctionUpdate,
|
|
66
|
+
UnparsedSingularTestUpdate,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
ColumnTarget = TypeVar(
|
|
71
|
+
"ColumnTarget",
|
|
72
|
+
UnparsedModelUpdate,
|
|
73
|
+
UnparsedNodeUpdate,
|
|
74
|
+
UnparsedAnalysisUpdate,
|
|
75
|
+
UnpatchedSourceDefinition,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
Versioned = TypeVar("Versioned", bound=UnparsedModelUpdate)
|
|
79
|
+
|
|
80
|
+
Testable = TypeVar("Testable", UnparsedNodeUpdate, UnpatchedSourceDefinition, UnparsedModelUpdate)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class YamlBlock(FileBlock):
|
|
85
|
+
data: Dict[str, Any]
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_file_block(cls, src: FileBlock, data: Dict[str, Any]):
|
|
89
|
+
return cls(
|
|
90
|
+
file=src.file,
|
|
91
|
+
data=data,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class TargetBlock(YamlBlock, Generic[Target]):
|
|
97
|
+
target: Target
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def name(self):
|
|
101
|
+
return self.target.name
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def columns(self):
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def data_tests(self) -> List[TestDef]:
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def tests(self) -> List[TestDef]:
|
|
113
|
+
return []
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def from_yaml_block(cls, src: YamlBlock, target: Target) -> "TargetBlock[Target]":
|
|
117
|
+
return cls(
|
|
118
|
+
file=src.file,
|
|
119
|
+
data=src.data,
|
|
120
|
+
target=target,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class TargetColumnsBlock(TargetBlock[ColumnTarget], Generic[ColumnTarget]):
|
|
126
|
+
@property
|
|
127
|
+
def columns(self):
|
|
128
|
+
if self.target.columns is None:
|
|
129
|
+
return []
|
|
130
|
+
else:
|
|
131
|
+
return self.target.columns
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class TestBlock(TargetColumnsBlock[Testable], Generic[Testable]):
|
|
136
|
+
@property
|
|
137
|
+
def data_tests(self) -> List[TestDef]:
|
|
138
|
+
if self.target.data_tests is None:
|
|
139
|
+
return []
|
|
140
|
+
else:
|
|
141
|
+
return self.target.data_tests
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def quote_columns(self) -> Optional[bool]:
|
|
145
|
+
return self.target.quote_columns
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_yaml_block(cls, src: YamlBlock, target: Testable) -> "TestBlock[Testable]":
|
|
149
|
+
return cls(
|
|
150
|
+
file=src.file,
|
|
151
|
+
data=src.data,
|
|
152
|
+
target=target,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class VersionedTestBlock(TestBlock, Generic[Versioned]):
|
|
158
|
+
@property
|
|
159
|
+
def columns(self):
|
|
160
|
+
if not self.target.versions:
|
|
161
|
+
return super().columns
|
|
162
|
+
else:
|
|
163
|
+
raise DbtInternalError(".columns for VersionedTestBlock with versions")
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def data_tests(self) -> List[TestDef]:
|
|
167
|
+
if not self.target.versions:
|
|
168
|
+
return super().data_tests
|
|
169
|
+
else:
|
|
170
|
+
raise DbtInternalError(".data_tests for VersionedTestBlock with versions")
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def from_yaml_block(cls, src: YamlBlock, target: Versioned) -> "VersionedTestBlock[Versioned]":
|
|
174
|
+
return cls(
|
|
175
|
+
file=src.file,
|
|
176
|
+
data=src.data,
|
|
177
|
+
target=target,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass
|
|
182
|
+
class GenericTestBlock(TestBlock[Testable], Generic[Testable]):
|
|
183
|
+
data_test: Dict[str, Any]
|
|
184
|
+
column_name: Optional[str]
|
|
185
|
+
tags: List[str]
|
|
186
|
+
version: Optional[NodeVersion]
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def from_test_block(
|
|
190
|
+
cls,
|
|
191
|
+
src: TestBlock,
|
|
192
|
+
data_test: Dict[str, Any],
|
|
193
|
+
column_name: Optional[str],
|
|
194
|
+
tags: List[str],
|
|
195
|
+
version: Optional[NodeVersion],
|
|
196
|
+
) -> "GenericTestBlock":
|
|
197
|
+
return cls(
|
|
198
|
+
file=src.file,
|
|
199
|
+
data=src.data,
|
|
200
|
+
target=src.target,
|
|
201
|
+
data_test=data_test,
|
|
202
|
+
column_name=column_name,
|
|
203
|
+
tags=tags,
|
|
204
|
+
version=version,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ParserRef:
|
|
209
|
+
"""A helper object to hold parse-time references."""
|
|
210
|
+
|
|
211
|
+
def __init__(self) -> None:
|
|
212
|
+
self.column_info: Dict[str, ColumnInfo] = {}
|
|
213
|
+
|
|
214
|
+
def _add(self, column: HasColumnProps) -> None:
|
|
215
|
+
tags: List[str] = getattr(column, "tags", [])
|
|
216
|
+
quote: Optional[bool] = None
|
|
217
|
+
granularity: Optional[TimeGranularity] = None
|
|
218
|
+
if isinstance(column, UnparsedColumn):
|
|
219
|
+
quote = column.quote
|
|
220
|
+
granularity = TimeGranularity(column.granularity) if column.granularity else None
|
|
221
|
+
|
|
222
|
+
if any(
|
|
223
|
+
c
|
|
224
|
+
for c in column.constraints
|
|
225
|
+
if "type" not in c or not ConstraintType.is_valid(c["type"])
|
|
226
|
+
):
|
|
227
|
+
raise ParsingError(f"Invalid constraint type on column {column.name}")
|
|
228
|
+
|
|
229
|
+
# Merge meta and tags from column and config
|
|
230
|
+
column_config_meta = (
|
|
231
|
+
column.config["meta"] if isinstance(column.config.get("meta"), dict) else {}
|
|
232
|
+
)
|
|
233
|
+
column_config_tags = []
|
|
234
|
+
if "tags" in column.config:
|
|
235
|
+
if isinstance(column.config["tags"], list):
|
|
236
|
+
column_config_tags = column.config["tags"]
|
|
237
|
+
elif isinstance(column.config["tags"], str):
|
|
238
|
+
column_config_tags = [column.config["tags"]]
|
|
239
|
+
|
|
240
|
+
column_meta = {**column.meta, **column_config_meta}
|
|
241
|
+
column_tags = list(set(tags + column_config_tags))
|
|
242
|
+
self.column_info[column.name] = ColumnInfo(
|
|
243
|
+
name=column.name,
|
|
244
|
+
description=column.description,
|
|
245
|
+
data_type=column.data_type,
|
|
246
|
+
constraints=[ColumnLevelConstraint.from_dict(c) for c in column.constraints],
|
|
247
|
+
meta=column_meta,
|
|
248
|
+
tags=column_tags,
|
|
249
|
+
quote=quote,
|
|
250
|
+
_extra=column.extra,
|
|
251
|
+
granularity=granularity,
|
|
252
|
+
config=ColumnConfig(meta=column_meta, tags=column_tags),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def from_target(cls, target: Union[HasColumnDocs, HasColumnTests]) -> "ParserRef":
|
|
257
|
+
refs = cls()
|
|
258
|
+
for column in target.columns:
|
|
259
|
+
refs._add(column)
|
|
260
|
+
return refs
|
|
261
|
+
|
|
262
|
+
@classmethod
|
|
263
|
+
def from_versioned_target(cls, target: Versioned, version: NodeVersion) -> "ParserRef":
|
|
264
|
+
refs = cls()
|
|
265
|
+
for base_column in target.get_columns_for_version(version):
|
|
266
|
+
refs._add(base_column)
|
|
267
|
+
return refs
|
dvt/parser/docs.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Iterable, Optional
|
|
3
|
+
|
|
4
|
+
from dvt.clients.jinja import get_rendered
|
|
5
|
+
from dvt.contracts.files import SourceFile
|
|
6
|
+
from dvt.contracts.graph.nodes import Documentation
|
|
7
|
+
from dvt.node_types import NodeType
|
|
8
|
+
from dvt.parser.base import Parser
|
|
9
|
+
from dvt.parser.search import BlockContents, BlockSearcher, FileBlock
|
|
10
|
+
|
|
11
|
+
SHOULD_PARSE_RE = re.compile(r"{[{%]")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DocumentationParser(Parser[Documentation]):
|
|
15
|
+
@property
|
|
16
|
+
def resource_type(self) -> NodeType:
|
|
17
|
+
return NodeType.Documentation
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def get_compiled_path(cls, block: FileBlock):
|
|
21
|
+
return block.path.relative_path
|
|
22
|
+
|
|
23
|
+
def generate_unique_id(self, resource_name: str, _: Optional[str] = None) -> str:
|
|
24
|
+
# For consistency, use the same format for doc unique_ids
|
|
25
|
+
return f"doc.{self.project.project_name}.{resource_name}"
|
|
26
|
+
|
|
27
|
+
def parse_block(self, block: BlockContents) -> Iterable[Documentation]:
|
|
28
|
+
unique_id = self.generate_unique_id(block.name)
|
|
29
|
+
contents = get_rendered(block.contents, {}).strip()
|
|
30
|
+
|
|
31
|
+
doc = Documentation(
|
|
32
|
+
path=block.file.path.relative_path,
|
|
33
|
+
original_file_path=block.path.original_file_path,
|
|
34
|
+
package_name=self.project.project_name,
|
|
35
|
+
unique_id=unique_id,
|
|
36
|
+
name=block.name,
|
|
37
|
+
block_contents=contents,
|
|
38
|
+
resource_type=NodeType.Documentation,
|
|
39
|
+
)
|
|
40
|
+
return [doc]
|
|
41
|
+
|
|
42
|
+
def parse_file(self, file_block: FileBlock):
|
|
43
|
+
assert isinstance(file_block.file, SourceFile)
|
|
44
|
+
searcher: Iterable[BlockContents] = BlockSearcher(
|
|
45
|
+
source=[file_block],
|
|
46
|
+
allowed_blocks={"docs"},
|
|
47
|
+
source_tag_factory=BlockContents,
|
|
48
|
+
check_jinja=False,
|
|
49
|
+
)
|
|
50
|
+
for block in searcher:
|
|
51
|
+
for parsed in self.parse_block(block):
|
|
52
|
+
self.manifest.add_doc(file_block.file, parsed)
|
dvt/parser/fixtures.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
from io import StringIO
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from dvt.contracts.files import FixtureSourceFile
|
|
6
|
+
from dvt.contracts.graph.nodes import UnitTestFileFixture
|
|
7
|
+
from dvt.node_types import NodeType
|
|
8
|
+
from dvt.parser.base import Parser
|
|
9
|
+
from dvt.parser.search import FileBlock
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FixtureParser(Parser[UnitTestFileFixture]):
|
|
13
|
+
@property
|
|
14
|
+
def resource_type(self) -> NodeType:
|
|
15
|
+
return NodeType.Fixture
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def get_compiled_path(cls, block: FileBlock):
|
|
19
|
+
# Is this necessary?
|
|
20
|
+
return block.path.relative_path
|
|
21
|
+
|
|
22
|
+
def generate_unique_id(self, resource_name: str, _: Optional[str] = None) -> str:
|
|
23
|
+
return f"fixture.{self.project.project_name}.{resource_name}"
|
|
24
|
+
|
|
25
|
+
def parse_file(self, file_block: FileBlock):
|
|
26
|
+
assert isinstance(file_block.file, FixtureSourceFile)
|
|
27
|
+
unique_id = self.generate_unique_id(file_block.name)
|
|
28
|
+
|
|
29
|
+
if file_block.file.path.relative_path.endswith(".sql"):
|
|
30
|
+
rows = file_block.file.contents # type: ignore
|
|
31
|
+
else: # endswith('.csv')
|
|
32
|
+
rows = self.get_rows(file_block.file.contents) # type: ignore
|
|
33
|
+
|
|
34
|
+
fixture = UnitTestFileFixture(
|
|
35
|
+
name=file_block.name,
|
|
36
|
+
path=file_block.file.path.relative_path,
|
|
37
|
+
original_file_path=file_block.path.original_file_path,
|
|
38
|
+
package_name=self.project.project_name,
|
|
39
|
+
unique_id=unique_id,
|
|
40
|
+
resource_type=NodeType.Fixture,
|
|
41
|
+
rows=rows,
|
|
42
|
+
)
|
|
43
|
+
self.manifest.add_fixture(file_block.file, fixture)
|
|
44
|
+
|
|
45
|
+
def get_rows(self, contents) -> List[Dict[str, Any]]:
|
|
46
|
+
rows = []
|
|
47
|
+
dummy_file = StringIO(contents)
|
|
48
|
+
reader = csv.DictReader(dummy_file)
|
|
49
|
+
for row in reader:
|
|
50
|
+
rows.append(row)
|
|
51
|
+
return rows
|
dvt/parser/functions.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from dvt.artifacts.resources.types import NodeType
|
|
2
|
+
from dvt.contracts.graph.nodes import FunctionNode, ManifestNode
|
|
3
|
+
from dvt.parser.base import SimpleParser
|
|
4
|
+
from dvt.parser.search import FileBlock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FunctionParser(SimpleParser[FileBlock, FunctionNode]):
|
|
8
|
+
def parse_from_dict(self, dct, validate=True) -> FunctionNode:
|
|
9
|
+
if validate:
|
|
10
|
+
FunctionNode.validate(dct)
|
|
11
|
+
return FunctionNode.from_dict(dct)
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def resource_type(self) -> NodeType:
|
|
15
|
+
return NodeType.Function
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def get_compiled_path(cls, block: FileBlock):
|
|
19
|
+
return block.path.relative_path
|
|
20
|
+
|
|
21
|
+
# overrides SimpleSQLParser.add_result_node
|
|
22
|
+
def add_result_node(self, block: FileBlock, node: ManifestNode):
|
|
23
|
+
assert isinstance(node, FunctionNode), "Got non FunctionNode in FunctionParser"
|
|
24
|
+
if node.config.enabled:
|
|
25
|
+
self.manifest.add_function(node)
|
|
26
|
+
else:
|
|
27
|
+
self.manifest.add_disabled(block.file, node)
|
|
28
|
+
|
|
29
|
+
def parse_file(self, file_block: FileBlock) -> None:
|
|
30
|
+
self.parse_node(file_block)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Iterable, List
|
|
2
|
+
|
|
3
|
+
import jinja2
|
|
4
|
+
from dvt.contracts.files import SourceFile
|
|
5
|
+
from dvt.contracts.graph.nodes import GenericTestNode, Macro
|
|
6
|
+
from dvt.contracts.graph.unparsed import UnparsedMacro
|
|
7
|
+
from dvt.exceptions import ParsingError
|
|
8
|
+
from dvt.node_types import NodeType
|
|
9
|
+
from dvt.parser.base import BaseParser
|
|
10
|
+
from dvt.parser.search import FileBlock
|
|
11
|
+
|
|
12
|
+
from dbt_common.clients import jinja
|
|
13
|
+
from dbt_common.utils import MACRO_PREFIX
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GenericTestParser(BaseParser[GenericTestNode]):
|
|
17
|
+
@property
|
|
18
|
+
def resource_type(self) -> NodeType:
|
|
19
|
+
return NodeType.Macro
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def get_compiled_path(cls, block: FileBlock):
|
|
23
|
+
return block.path.relative_path
|
|
24
|
+
|
|
25
|
+
def create_generic_test_macro(
|
|
26
|
+
self, block: jinja.BlockTag, base_node: UnparsedMacro, name: str
|
|
27
|
+
) -> Macro:
|
|
28
|
+
unique_id = self.generate_unique_id(name)
|
|
29
|
+
macro_sql = block.full_block or ""
|
|
30
|
+
|
|
31
|
+
return Macro(
|
|
32
|
+
path=base_node.path,
|
|
33
|
+
macro_sql=macro_sql,
|
|
34
|
+
original_file_path=base_node.original_file_path,
|
|
35
|
+
package_name=base_node.package_name,
|
|
36
|
+
resource_type=base_node.resource_type,
|
|
37
|
+
name=name,
|
|
38
|
+
unique_id=unique_id,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def parse_unparsed_generic_test(self, base_node: UnparsedMacro) -> Iterable[Macro]:
|
|
42
|
+
try:
|
|
43
|
+
blocks: List[jinja.BlockTag] = [
|
|
44
|
+
t
|
|
45
|
+
for t in jinja.extract_toplevel_blocks(
|
|
46
|
+
base_node.raw_code,
|
|
47
|
+
allowed_blocks={"test", "data_test"},
|
|
48
|
+
collect_raw_data=False,
|
|
49
|
+
)
|
|
50
|
+
if isinstance(t, jinja.BlockTag)
|
|
51
|
+
]
|
|
52
|
+
except ParsingError as exc:
|
|
53
|
+
exc.add_node(base_node)
|
|
54
|
+
raise
|
|
55
|
+
|
|
56
|
+
for block in blocks:
|
|
57
|
+
try:
|
|
58
|
+
ast = jinja.parse(block.full_block)
|
|
59
|
+
except ParsingError as e:
|
|
60
|
+
e.add_node(base_node)
|
|
61
|
+
raise
|
|
62
|
+
|
|
63
|
+
# generic tests are structured as macros so we want to count the number of macro blocks
|
|
64
|
+
generic_test_nodes = list(ast.find_all(jinja2.nodes.Macro))
|
|
65
|
+
|
|
66
|
+
if len(generic_test_nodes) != 1:
|
|
67
|
+
# things have gone disastrously wrong, we thought we only
|
|
68
|
+
# parsed one block!
|
|
69
|
+
raise ParsingError(
|
|
70
|
+
f"Found multiple generic tests in {block.full_block}, expected 1",
|
|
71
|
+
node=base_node,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
generic_test_name = generic_test_nodes[0].name
|
|
75
|
+
|
|
76
|
+
if not generic_test_name.startswith(MACRO_PREFIX):
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
name: str = generic_test_name.replace(MACRO_PREFIX, "")
|
|
80
|
+
node = self.create_generic_test_macro(block, base_node, name)
|
|
81
|
+
yield node
|
|
82
|
+
|
|
83
|
+
def parse_file(self, block: FileBlock):
|
|
84
|
+
assert isinstance(block.file, SourceFile)
|
|
85
|
+
source_file = block.file
|
|
86
|
+
assert isinstance(source_file.contents, str)
|
|
87
|
+
original_file_path = source_file.path.original_file_path
|
|
88
|
+
|
|
89
|
+
# this is really only used for error messages
|
|
90
|
+
base_node = UnparsedMacro(
|
|
91
|
+
path=original_file_path,
|
|
92
|
+
original_file_path=original_file_path,
|
|
93
|
+
package_name=self.project.project_name,
|
|
94
|
+
raw_code=source_file.contents,
|
|
95
|
+
resource_type=NodeType.Macro,
|
|
96
|
+
language="sql",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
for node in self.parse_unparsed_generic_test(base_node):
|
|
100
|
+
self.manifest.add_macro(block.file, node)
|