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.

Files changed (261) hide show
  1. dvt/__init__.py +7 -0
  2. dvt/_pydantic_shim.py +26 -0
  3. dvt/adapters/__init__.py +16 -0
  4. dvt/adapters/multi_adapter_manager.py +268 -0
  5. dvt/artifacts/__init__.py +0 -0
  6. dvt/artifacts/exceptions/__init__.py +1 -0
  7. dvt/artifacts/exceptions/schemas.py +31 -0
  8. dvt/artifacts/resources/__init__.py +116 -0
  9. dvt/artifacts/resources/base.py +68 -0
  10. dvt/artifacts/resources/types.py +93 -0
  11. dvt/artifacts/resources/v1/analysis.py +10 -0
  12. dvt/artifacts/resources/v1/catalog.py +23 -0
  13. dvt/artifacts/resources/v1/components.py +275 -0
  14. dvt/artifacts/resources/v1/config.py +282 -0
  15. dvt/artifacts/resources/v1/documentation.py +11 -0
  16. dvt/artifacts/resources/v1/exposure.py +52 -0
  17. dvt/artifacts/resources/v1/function.py +53 -0
  18. dvt/artifacts/resources/v1/generic_test.py +32 -0
  19. dvt/artifacts/resources/v1/group.py +22 -0
  20. dvt/artifacts/resources/v1/hook.py +11 -0
  21. dvt/artifacts/resources/v1/macro.py +30 -0
  22. dvt/artifacts/resources/v1/metric.py +173 -0
  23. dvt/artifacts/resources/v1/model.py +146 -0
  24. dvt/artifacts/resources/v1/owner.py +10 -0
  25. dvt/artifacts/resources/v1/saved_query.py +112 -0
  26. dvt/artifacts/resources/v1/seed.py +42 -0
  27. dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  28. dvt/artifacts/resources/v1/semantic_model.py +315 -0
  29. dvt/artifacts/resources/v1/singular_test.py +14 -0
  30. dvt/artifacts/resources/v1/snapshot.py +92 -0
  31. dvt/artifacts/resources/v1/source_definition.py +85 -0
  32. dvt/artifacts/resources/v1/sql_operation.py +10 -0
  33. dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
  34. dvt/artifacts/schemas/__init__.py +0 -0
  35. dvt/artifacts/schemas/base.py +191 -0
  36. dvt/artifacts/schemas/batch_results.py +24 -0
  37. dvt/artifacts/schemas/catalog/__init__.py +12 -0
  38. dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  39. dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
  40. dvt/artifacts/schemas/freshness/__init__.py +1 -0
  41. dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  42. dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
  43. dvt/artifacts/schemas/manifest/__init__.py +2 -0
  44. dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  45. dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
  46. dvt/artifacts/schemas/results.py +148 -0
  47. dvt/artifacts/schemas/run/__init__.py +2 -0
  48. dvt/artifacts/schemas/run/v5/__init__.py +0 -0
  49. dvt/artifacts/schemas/run/v5/run.py +184 -0
  50. dvt/artifacts/schemas/upgrades/__init__.py +4 -0
  51. dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  52. dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  53. dvt/artifacts/utils/validation.py +153 -0
  54. dvt/cli/__init__.py +1 -0
  55. dvt/cli/context.py +16 -0
  56. dvt/cli/exceptions.py +56 -0
  57. dvt/cli/flags.py +558 -0
  58. dvt/cli/main.py +971 -0
  59. dvt/cli/option_types.py +121 -0
  60. dvt/cli/options.py +79 -0
  61. dvt/cli/params.py +803 -0
  62. dvt/cli/requires.py +478 -0
  63. dvt/cli/resolvers.py +32 -0
  64. dvt/cli/types.py +40 -0
  65. dvt/clients/__init__.py +0 -0
  66. dvt/clients/checked_load.py +82 -0
  67. dvt/clients/git.py +164 -0
  68. dvt/clients/jinja.py +206 -0
  69. dvt/clients/jinja_static.py +245 -0
  70. dvt/clients/registry.py +192 -0
  71. dvt/clients/yaml_helper.py +68 -0
  72. dvt/compilation.py +833 -0
  73. dvt/compute/__init__.py +26 -0
  74. dvt/compute/base.py +288 -0
  75. dvt/compute/engines/__init__.py +13 -0
  76. dvt/compute/engines/duckdb_engine.py +368 -0
  77. dvt/compute/engines/spark_engine.py +273 -0
  78. dvt/compute/query_analyzer.py +212 -0
  79. dvt/compute/router.py +483 -0
  80. dvt/config/__init__.py +4 -0
  81. dvt/config/catalogs.py +95 -0
  82. dvt/config/compute_config.py +406 -0
  83. dvt/config/profile.py +411 -0
  84. dvt/config/profiles_v2.py +464 -0
  85. dvt/config/project.py +893 -0
  86. dvt/config/renderer.py +232 -0
  87. dvt/config/runtime.py +491 -0
  88. dvt/config/selectors.py +209 -0
  89. dvt/config/utils.py +78 -0
  90. dvt/connectors/.gitignore +6 -0
  91. dvt/connectors/README.md +306 -0
  92. dvt/connectors/catalog.yml +217 -0
  93. dvt/connectors/download_connectors.py +300 -0
  94. dvt/constants.py +29 -0
  95. dvt/context/__init__.py +0 -0
  96. dvt/context/base.py +746 -0
  97. dvt/context/configured.py +136 -0
  98. dvt/context/context_config.py +350 -0
  99. dvt/context/docs.py +82 -0
  100. dvt/context/exceptions_jinja.py +179 -0
  101. dvt/context/macro_resolver.py +195 -0
  102. dvt/context/macros.py +171 -0
  103. dvt/context/manifest.py +73 -0
  104. dvt/context/providers.py +2198 -0
  105. dvt/context/query_header.py +14 -0
  106. dvt/context/secret.py +59 -0
  107. dvt/context/target.py +74 -0
  108. dvt/contracts/__init__.py +0 -0
  109. dvt/contracts/files.py +413 -0
  110. dvt/contracts/graph/__init__.py +0 -0
  111. dvt/contracts/graph/manifest.py +1904 -0
  112. dvt/contracts/graph/metrics.py +98 -0
  113. dvt/contracts/graph/model_config.py +71 -0
  114. dvt/contracts/graph/node_args.py +42 -0
  115. dvt/contracts/graph/nodes.py +1806 -0
  116. dvt/contracts/graph/semantic_manifest.py +233 -0
  117. dvt/contracts/graph/unparsed.py +812 -0
  118. dvt/contracts/project.py +417 -0
  119. dvt/contracts/results.py +53 -0
  120. dvt/contracts/selection.py +23 -0
  121. dvt/contracts/sql.py +86 -0
  122. dvt/contracts/state.py +69 -0
  123. dvt/contracts/util.py +46 -0
  124. dvt/deprecations.py +347 -0
  125. dvt/deps/__init__.py +0 -0
  126. dvt/deps/base.py +153 -0
  127. dvt/deps/git.py +196 -0
  128. dvt/deps/local.py +80 -0
  129. dvt/deps/registry.py +131 -0
  130. dvt/deps/resolver.py +149 -0
  131. dvt/deps/tarball.py +121 -0
  132. dvt/docs/source/_ext/dbt_click.py +118 -0
  133. dvt/docs/source/conf.py +32 -0
  134. dvt/env_vars.py +64 -0
  135. dvt/event_time/event_time.py +40 -0
  136. dvt/event_time/sample_window.py +60 -0
  137. dvt/events/__init__.py +16 -0
  138. dvt/events/base_types.py +37 -0
  139. dvt/events/core_types_pb2.py +2 -0
  140. dvt/events/logging.py +109 -0
  141. dvt/events/types.py +2534 -0
  142. dvt/exceptions.py +1487 -0
  143. dvt/flags.py +89 -0
  144. dvt/graph/__init__.py +11 -0
  145. dvt/graph/cli.py +248 -0
  146. dvt/graph/graph.py +172 -0
  147. dvt/graph/queue.py +213 -0
  148. dvt/graph/selector.py +375 -0
  149. dvt/graph/selector_methods.py +976 -0
  150. dvt/graph/selector_spec.py +223 -0
  151. dvt/graph/thread_pool.py +18 -0
  152. dvt/hooks.py +21 -0
  153. dvt/include/README.md +49 -0
  154. dvt/include/__init__.py +3 -0
  155. dvt/include/global_project.py +4 -0
  156. dvt/include/starter_project/.gitignore +4 -0
  157. dvt/include/starter_project/README.md +15 -0
  158. dvt/include/starter_project/__init__.py +3 -0
  159. dvt/include/starter_project/analyses/.gitkeep +0 -0
  160. dvt/include/starter_project/dvt_project.yml +36 -0
  161. dvt/include/starter_project/macros/.gitkeep +0 -0
  162. dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  163. dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  164. dvt/include/starter_project/models/example/schema.yml +21 -0
  165. dvt/include/starter_project/seeds/.gitkeep +0 -0
  166. dvt/include/starter_project/snapshots/.gitkeep +0 -0
  167. dvt/include/starter_project/tests/.gitkeep +0 -0
  168. dvt/internal_deprecations.py +27 -0
  169. dvt/jsonschemas/__init__.py +3 -0
  170. dvt/jsonschemas/jsonschemas.py +309 -0
  171. dvt/jsonschemas/project/0.0.110.json +4717 -0
  172. dvt/jsonschemas/project/0.0.85.json +2015 -0
  173. dvt/jsonschemas/resources/0.0.110.json +2636 -0
  174. dvt/jsonschemas/resources/0.0.85.json +2536 -0
  175. dvt/jsonschemas/resources/latest.json +6773 -0
  176. dvt/links.py +4 -0
  177. dvt/materializations/__init__.py +0 -0
  178. dvt/materializations/incremental/__init__.py +0 -0
  179. dvt/materializations/incremental/microbatch.py +235 -0
  180. dvt/mp_context.py +8 -0
  181. dvt/node_types.py +37 -0
  182. dvt/parser/__init__.py +23 -0
  183. dvt/parser/analysis.py +21 -0
  184. dvt/parser/base.py +549 -0
  185. dvt/parser/common.py +267 -0
  186. dvt/parser/docs.py +52 -0
  187. dvt/parser/fixtures.py +51 -0
  188. dvt/parser/functions.py +30 -0
  189. dvt/parser/generic_test.py +100 -0
  190. dvt/parser/generic_test_builders.py +334 -0
  191. dvt/parser/hooks.py +119 -0
  192. dvt/parser/macros.py +137 -0
  193. dvt/parser/manifest.py +2204 -0
  194. dvt/parser/models.py +574 -0
  195. dvt/parser/partial.py +1179 -0
  196. dvt/parser/read_files.py +445 -0
  197. dvt/parser/schema_generic_tests.py +423 -0
  198. dvt/parser/schema_renderer.py +111 -0
  199. dvt/parser/schema_yaml_readers.py +936 -0
  200. dvt/parser/schemas.py +1467 -0
  201. dvt/parser/search.py +149 -0
  202. dvt/parser/seeds.py +28 -0
  203. dvt/parser/singular_test.py +20 -0
  204. dvt/parser/snapshots.py +44 -0
  205. dvt/parser/sources.py +557 -0
  206. dvt/parser/sql.py +63 -0
  207. dvt/parser/unit_tests.py +622 -0
  208. dvt/plugins/__init__.py +20 -0
  209. dvt/plugins/contracts.py +10 -0
  210. dvt/plugins/exceptions.py +2 -0
  211. dvt/plugins/manager.py +164 -0
  212. dvt/plugins/manifest.py +21 -0
  213. dvt/profiler.py +20 -0
  214. dvt/py.typed +1 -0
  215. dvt/runners/__init__.py +2 -0
  216. dvt/runners/exposure_runner.py +7 -0
  217. dvt/runners/no_op_runner.py +46 -0
  218. dvt/runners/saved_query_runner.py +7 -0
  219. dvt/selected_resources.py +8 -0
  220. dvt/task/__init__.py +0 -0
  221. dvt/task/base.py +504 -0
  222. dvt/task/build.py +197 -0
  223. dvt/task/clean.py +57 -0
  224. dvt/task/clone.py +162 -0
  225. dvt/task/compile.py +151 -0
  226. dvt/task/compute.py +366 -0
  227. dvt/task/debug.py +650 -0
  228. dvt/task/deps.py +280 -0
  229. dvt/task/docs/__init__.py +3 -0
  230. dvt/task/docs/generate.py +408 -0
  231. dvt/task/docs/index.html +250 -0
  232. dvt/task/docs/serve.py +28 -0
  233. dvt/task/freshness.py +323 -0
  234. dvt/task/function.py +122 -0
  235. dvt/task/group_lookup.py +46 -0
  236. dvt/task/init.py +374 -0
  237. dvt/task/list.py +237 -0
  238. dvt/task/printer.py +176 -0
  239. dvt/task/profiles.py +256 -0
  240. dvt/task/retry.py +175 -0
  241. dvt/task/run.py +1146 -0
  242. dvt/task/run_operation.py +142 -0
  243. dvt/task/runnable.py +802 -0
  244. dvt/task/seed.py +104 -0
  245. dvt/task/show.py +150 -0
  246. dvt/task/snapshot.py +57 -0
  247. dvt/task/sql.py +111 -0
  248. dvt/task/test.py +464 -0
  249. dvt/tests/fixtures/__init__.py +1 -0
  250. dvt/tests/fixtures/project.py +620 -0
  251. dvt/tests/util.py +651 -0
  252. dvt/tracking.py +529 -0
  253. dvt/utils/__init__.py +3 -0
  254. dvt/utils/artifact_upload.py +151 -0
  255. dvt/utils/utils.py +408 -0
  256. dvt/version.py +249 -0
  257. dvt_core-1.11.0b4.dist-info/METADATA +252 -0
  258. dvt_core-1.11.0b4.dist-info/RECORD +261 -0
  259. dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
  260. dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
  261. 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
@@ -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)