dvt-core 0.58.6__cp311-cp311-macosx_10_9_x86_64.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.
Files changed (324) hide show
  1. dbt/__init__.py +7 -0
  2. dbt/_pydantic_shim.py +26 -0
  3. dbt/artifacts/__init__.py +0 -0
  4. dbt/artifacts/exceptions/__init__.py +1 -0
  5. dbt/artifacts/exceptions/schemas.py +31 -0
  6. dbt/artifacts/resources/__init__.py +116 -0
  7. dbt/artifacts/resources/base.py +67 -0
  8. dbt/artifacts/resources/types.py +93 -0
  9. dbt/artifacts/resources/v1/analysis.py +10 -0
  10. dbt/artifacts/resources/v1/catalog.py +23 -0
  11. dbt/artifacts/resources/v1/components.py +274 -0
  12. dbt/artifacts/resources/v1/config.py +277 -0
  13. dbt/artifacts/resources/v1/documentation.py +11 -0
  14. dbt/artifacts/resources/v1/exposure.py +51 -0
  15. dbt/artifacts/resources/v1/function.py +52 -0
  16. dbt/artifacts/resources/v1/generic_test.py +31 -0
  17. dbt/artifacts/resources/v1/group.py +21 -0
  18. dbt/artifacts/resources/v1/hook.py +11 -0
  19. dbt/artifacts/resources/v1/macro.py +29 -0
  20. dbt/artifacts/resources/v1/metric.py +172 -0
  21. dbt/artifacts/resources/v1/model.py +145 -0
  22. dbt/artifacts/resources/v1/owner.py +10 -0
  23. dbt/artifacts/resources/v1/saved_query.py +111 -0
  24. dbt/artifacts/resources/v1/seed.py +41 -0
  25. dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  26. dbt/artifacts/resources/v1/semantic_model.py +314 -0
  27. dbt/artifacts/resources/v1/singular_test.py +14 -0
  28. dbt/artifacts/resources/v1/snapshot.py +91 -0
  29. dbt/artifacts/resources/v1/source_definition.py +84 -0
  30. dbt/artifacts/resources/v1/sql_operation.py +10 -0
  31. dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
  32. dbt/artifacts/schemas/__init__.py +0 -0
  33. dbt/artifacts/schemas/base.py +191 -0
  34. dbt/artifacts/schemas/batch_results.py +24 -0
  35. dbt/artifacts/schemas/catalog/__init__.py +11 -0
  36. dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  37. dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
  38. dbt/artifacts/schemas/freshness/__init__.py +1 -0
  39. dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  40. dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
  41. dbt/artifacts/schemas/manifest/__init__.py +2 -0
  42. dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  43. dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
  44. dbt/artifacts/schemas/results.py +147 -0
  45. dbt/artifacts/schemas/run/__init__.py +2 -0
  46. dbt/artifacts/schemas/run/v5/__init__.py +0 -0
  47. dbt/artifacts/schemas/run/v5/run.py +184 -0
  48. dbt/artifacts/schemas/upgrades/__init__.py +4 -0
  49. dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  50. dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  51. dbt/artifacts/utils/validation.py +153 -0
  52. dbt/cli/__init__.py +1 -0
  53. dbt/cli/context.py +17 -0
  54. dbt/cli/exceptions.py +57 -0
  55. dbt/cli/flags.py +560 -0
  56. dbt/cli/main.py +2403 -0
  57. dbt/cli/option_types.py +121 -0
  58. dbt/cli/options.py +80 -0
  59. dbt/cli/params.py +844 -0
  60. dbt/cli/requires.py +490 -0
  61. dbt/cli/resolvers.py +50 -0
  62. dbt/cli/types.py +40 -0
  63. dbt/clients/__init__.py +0 -0
  64. dbt/clients/checked_load.py +83 -0
  65. dbt/clients/git.py +164 -0
  66. dbt/clients/jinja.py +206 -0
  67. dbt/clients/jinja_static.py +245 -0
  68. dbt/clients/registry.py +192 -0
  69. dbt/clients/yaml_helper.py +68 -0
  70. dbt/compilation.py +876 -0
  71. dbt/compute/__init__.py +14 -0
  72. dbt/compute/engines/__init__.py +12 -0
  73. dbt/compute/engines/spark_engine.cpython-311-darwin.so +0 -0
  74. dbt/compute/engines/spark_engine.py +642 -0
  75. dbt/compute/federated_executor.cpython-311-darwin.so +0 -0
  76. dbt/compute/federated_executor.py +1080 -0
  77. dbt/compute/filter_pushdown.cpython-311-darwin.so +0 -0
  78. dbt/compute/filter_pushdown.py +273 -0
  79. dbt/compute/jar_provisioning.cpython-311-darwin.so +0 -0
  80. dbt/compute/jar_provisioning.py +255 -0
  81. dbt/compute/java_compat.cpython-311-darwin.so +0 -0
  82. dbt/compute/java_compat.py +689 -0
  83. dbt/compute/jdbc_utils.cpython-311-darwin.so +0 -0
  84. dbt/compute/jdbc_utils.py +678 -0
  85. dbt/compute/metadata/__init__.py +40 -0
  86. dbt/compute/metadata/adapters_registry.cpython-311-darwin.so +0 -0
  87. dbt/compute/metadata/adapters_registry.py +370 -0
  88. dbt/compute/metadata/registry.cpython-311-darwin.so +0 -0
  89. dbt/compute/metadata/registry.py +674 -0
  90. dbt/compute/metadata/store.cpython-311-darwin.so +0 -0
  91. dbt/compute/metadata/store.py +1499 -0
  92. dbt/compute/smart_selector.cpython-311-darwin.so +0 -0
  93. dbt/compute/smart_selector.py +377 -0
  94. dbt/compute/strategies/__init__.py +55 -0
  95. dbt/compute/strategies/base.cpython-311-darwin.so +0 -0
  96. dbt/compute/strategies/base.py +165 -0
  97. dbt/compute/strategies/dataproc.cpython-311-darwin.so +0 -0
  98. dbt/compute/strategies/dataproc.py +207 -0
  99. dbt/compute/strategies/emr.cpython-311-darwin.so +0 -0
  100. dbt/compute/strategies/emr.py +203 -0
  101. dbt/compute/strategies/local.cpython-311-darwin.so +0 -0
  102. dbt/compute/strategies/local.py +443 -0
  103. dbt/compute/strategies/standalone.cpython-311-darwin.so +0 -0
  104. dbt/compute/strategies/standalone.py +262 -0
  105. dbt/config/__init__.py +4 -0
  106. dbt/config/catalogs.py +94 -0
  107. dbt/config/compute.cpython-311-darwin.so +0 -0
  108. dbt/config/compute.py +513 -0
  109. dbt/config/dvt_profile.cpython-311-darwin.so +0 -0
  110. dbt/config/dvt_profile.py +342 -0
  111. dbt/config/profile.py +422 -0
  112. dbt/config/project.py +873 -0
  113. dbt/config/project_utils.py +28 -0
  114. dbt/config/renderer.py +231 -0
  115. dbt/config/runtime.py +553 -0
  116. dbt/config/selectors.py +208 -0
  117. dbt/config/utils.py +77 -0
  118. dbt/constants.py +28 -0
  119. dbt/context/__init__.py +0 -0
  120. dbt/context/base.py +745 -0
  121. dbt/context/configured.py +135 -0
  122. dbt/context/context_config.py +382 -0
  123. dbt/context/docs.py +82 -0
  124. dbt/context/exceptions_jinja.py +178 -0
  125. dbt/context/macro_resolver.py +195 -0
  126. dbt/context/macros.py +171 -0
  127. dbt/context/manifest.py +72 -0
  128. dbt/context/providers.py +2249 -0
  129. dbt/context/query_header.py +13 -0
  130. dbt/context/secret.py +58 -0
  131. dbt/context/target.py +74 -0
  132. dbt/contracts/__init__.py +0 -0
  133. dbt/contracts/files.py +413 -0
  134. dbt/contracts/graph/__init__.py +0 -0
  135. dbt/contracts/graph/manifest.py +1904 -0
  136. dbt/contracts/graph/metrics.py +97 -0
  137. dbt/contracts/graph/model_config.py +70 -0
  138. dbt/contracts/graph/node_args.py +42 -0
  139. dbt/contracts/graph/nodes.py +1806 -0
  140. dbt/contracts/graph/semantic_manifest.py +232 -0
  141. dbt/contracts/graph/unparsed.py +811 -0
  142. dbt/contracts/project.py +417 -0
  143. dbt/contracts/results.py +53 -0
  144. dbt/contracts/selection.py +23 -0
  145. dbt/contracts/sql.py +85 -0
  146. dbt/contracts/state.py +68 -0
  147. dbt/contracts/util.py +46 -0
  148. dbt/deprecations.py +348 -0
  149. dbt/deps/__init__.py +0 -0
  150. dbt/deps/base.py +152 -0
  151. dbt/deps/git.py +195 -0
  152. dbt/deps/local.py +79 -0
  153. dbt/deps/registry.py +130 -0
  154. dbt/deps/resolver.py +149 -0
  155. dbt/deps/tarball.py +120 -0
  156. dbt/docs/source/_ext/dbt_click.py +119 -0
  157. dbt/docs/source/conf.py +32 -0
  158. dbt/env_vars.py +64 -0
  159. dbt/event_time/event_time.py +40 -0
  160. dbt/event_time/sample_window.py +60 -0
  161. dbt/events/__init__.py +15 -0
  162. dbt/events/base_types.py +36 -0
  163. dbt/events/core_types_pb2.py +2 -0
  164. dbt/events/logging.py +108 -0
  165. dbt/events/types.py +2516 -0
  166. dbt/exceptions.py +1486 -0
  167. dbt/flags.py +89 -0
  168. dbt/graph/__init__.py +11 -0
  169. dbt/graph/cli.py +249 -0
  170. dbt/graph/graph.py +172 -0
  171. dbt/graph/queue.py +214 -0
  172. dbt/graph/selector.py +374 -0
  173. dbt/graph/selector_methods.py +975 -0
  174. dbt/graph/selector_spec.py +222 -0
  175. dbt/graph/thread_pool.py +18 -0
  176. dbt/hooks.py +21 -0
  177. dbt/include/README.md +49 -0
  178. dbt/include/__init__.py +3 -0
  179. dbt/include/data/adapters_registry.duckdb +0 -0
  180. dbt/include/data/build_registry.py +242 -0
  181. dbt/include/data/csv/adapter_queries.csv +33 -0
  182. dbt/include/data/csv/syntax_rules.csv +9 -0
  183. dbt/include/data/csv/type_mappings_bigquery.csv +28 -0
  184. dbt/include/data/csv/type_mappings_databricks.csv +30 -0
  185. dbt/include/data/csv/type_mappings_mysql.csv +40 -0
  186. dbt/include/data/csv/type_mappings_oracle.csv +30 -0
  187. dbt/include/data/csv/type_mappings_postgres.csv +56 -0
  188. dbt/include/data/csv/type_mappings_redshift.csv +33 -0
  189. dbt/include/data/csv/type_mappings_snowflake.csv +38 -0
  190. dbt/include/data/csv/type_mappings_sqlserver.csv +35 -0
  191. dbt/include/starter_project/.gitignore +4 -0
  192. dbt/include/starter_project/README.md +15 -0
  193. dbt/include/starter_project/__init__.py +3 -0
  194. dbt/include/starter_project/analyses/.gitkeep +0 -0
  195. dbt/include/starter_project/dbt_project.yml +36 -0
  196. dbt/include/starter_project/macros/.gitkeep +0 -0
  197. dbt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  198. dbt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  199. dbt/include/starter_project/models/example/schema.yml +21 -0
  200. dbt/include/starter_project/seeds/.gitkeep +0 -0
  201. dbt/include/starter_project/snapshots/.gitkeep +0 -0
  202. dbt/include/starter_project/tests/.gitkeep +0 -0
  203. dbt/internal_deprecations.py +26 -0
  204. dbt/jsonschemas/__init__.py +3 -0
  205. dbt/jsonschemas/jsonschemas.py +309 -0
  206. dbt/jsonschemas/project/0.0.110.json +4717 -0
  207. dbt/jsonschemas/project/0.0.85.json +2015 -0
  208. dbt/jsonschemas/resources/0.0.110.json +2636 -0
  209. dbt/jsonschemas/resources/0.0.85.json +2536 -0
  210. dbt/jsonschemas/resources/latest.json +6773 -0
  211. dbt/links.py +4 -0
  212. dbt/materializations/__init__.py +0 -0
  213. dbt/materializations/incremental/__init__.py +0 -0
  214. dbt/materializations/incremental/microbatch.py +236 -0
  215. dbt/mp_context.py +8 -0
  216. dbt/node_types.py +37 -0
  217. dbt/parser/__init__.py +23 -0
  218. dbt/parser/analysis.py +21 -0
  219. dbt/parser/base.py +548 -0
  220. dbt/parser/common.py +266 -0
  221. dbt/parser/docs.py +52 -0
  222. dbt/parser/fixtures.py +51 -0
  223. dbt/parser/functions.py +30 -0
  224. dbt/parser/generic_test.py +100 -0
  225. dbt/parser/generic_test_builders.py +333 -0
  226. dbt/parser/hooks.py +118 -0
  227. dbt/parser/macros.py +137 -0
  228. dbt/parser/manifest.py +2204 -0
  229. dbt/parser/models.py +573 -0
  230. dbt/parser/partial.py +1178 -0
  231. dbt/parser/read_files.py +445 -0
  232. dbt/parser/schema_generic_tests.py +422 -0
  233. dbt/parser/schema_renderer.py +111 -0
  234. dbt/parser/schema_yaml_readers.py +935 -0
  235. dbt/parser/schemas.py +1466 -0
  236. dbt/parser/search.py +149 -0
  237. dbt/parser/seeds.py +28 -0
  238. dbt/parser/singular_test.py +20 -0
  239. dbt/parser/snapshots.py +44 -0
  240. dbt/parser/sources.py +558 -0
  241. dbt/parser/sql.py +62 -0
  242. dbt/parser/unit_tests.py +621 -0
  243. dbt/plugins/__init__.py +20 -0
  244. dbt/plugins/contracts.py +9 -0
  245. dbt/plugins/exceptions.py +2 -0
  246. dbt/plugins/manager.py +163 -0
  247. dbt/plugins/manifest.py +21 -0
  248. dbt/profiler.py +20 -0
  249. dbt/py.typed +1 -0
  250. dbt/query_analyzer.cpython-311-darwin.so +0 -0
  251. dbt/query_analyzer.py +410 -0
  252. dbt/runners/__init__.py +2 -0
  253. dbt/runners/exposure_runner.py +7 -0
  254. dbt/runners/no_op_runner.py +45 -0
  255. dbt/runners/saved_query_runner.py +7 -0
  256. dbt/selected_resources.py +8 -0
  257. dbt/task/__init__.py +0 -0
  258. dbt/task/base.py +503 -0
  259. dbt/task/build.py +197 -0
  260. dbt/task/clean.py +56 -0
  261. dbt/task/clone.py +161 -0
  262. dbt/task/compile.py +150 -0
  263. dbt/task/compute.cpython-311-darwin.so +0 -0
  264. dbt/task/compute.py +458 -0
  265. dbt/task/debug.py +505 -0
  266. dbt/task/deps.py +280 -0
  267. dbt/task/docs/__init__.py +3 -0
  268. dbt/task/docs/api/__init__.py +23 -0
  269. dbt/task/docs/api/catalog.cpython-311-darwin.so +0 -0
  270. dbt/task/docs/api/catalog.py +204 -0
  271. dbt/task/docs/api/lineage.cpython-311-darwin.so +0 -0
  272. dbt/task/docs/api/lineage.py +234 -0
  273. dbt/task/docs/api/profile.cpython-311-darwin.so +0 -0
  274. dbt/task/docs/api/profile.py +204 -0
  275. dbt/task/docs/api/spark.cpython-311-darwin.so +0 -0
  276. dbt/task/docs/api/spark.py +186 -0
  277. dbt/task/docs/generate.py +947 -0
  278. dbt/task/docs/index.html +250 -0
  279. dbt/task/docs/serve.cpython-311-darwin.so +0 -0
  280. dbt/task/docs/serve.py +174 -0
  281. dbt/task/dvt_output.py +362 -0
  282. dbt/task/dvt_run.py +204 -0
  283. dbt/task/freshness.py +322 -0
  284. dbt/task/function.py +121 -0
  285. dbt/task/group_lookup.py +46 -0
  286. dbt/task/init.cpython-311-darwin.so +0 -0
  287. dbt/task/init.py +604 -0
  288. dbt/task/java.cpython-311-darwin.so +0 -0
  289. dbt/task/java.py +316 -0
  290. dbt/task/list.py +236 -0
  291. dbt/task/metadata.cpython-311-darwin.so +0 -0
  292. dbt/task/metadata.py +804 -0
  293. dbt/task/printer.py +175 -0
  294. dbt/task/profile.cpython-311-darwin.so +0 -0
  295. dbt/task/profile.py +1307 -0
  296. dbt/task/profile_serve.py +615 -0
  297. dbt/task/retract.py +438 -0
  298. dbt/task/retry.py +175 -0
  299. dbt/task/run.py +1387 -0
  300. dbt/task/run_operation.py +141 -0
  301. dbt/task/runnable.py +758 -0
  302. dbt/task/seed.py +103 -0
  303. dbt/task/show.py +149 -0
  304. dbt/task/snapshot.py +56 -0
  305. dbt/task/spark.cpython-311-darwin.so +0 -0
  306. dbt/task/spark.py +414 -0
  307. dbt/task/sql.py +110 -0
  308. dbt/task/target_sync.cpython-311-darwin.so +0 -0
  309. dbt/task/target_sync.py +766 -0
  310. dbt/task/test.py +464 -0
  311. dbt/tests/fixtures/__init__.py +1 -0
  312. dbt/tests/fixtures/project.py +620 -0
  313. dbt/tests/util.py +651 -0
  314. dbt/tracking.py +529 -0
  315. dbt/utils/__init__.py +3 -0
  316. dbt/utils/artifact_upload.py +151 -0
  317. dbt/utils/utils.py +408 -0
  318. dbt/version.py +270 -0
  319. dvt_cli/__init__.py +72 -0
  320. dvt_core-0.58.6.dist-info/METADATA +288 -0
  321. dvt_core-0.58.6.dist-info/RECORD +324 -0
  322. dvt_core-0.58.6.dist-info/WHEEL +5 -0
  323. dvt_core-0.58.6.dist-info/entry_points.txt +2 -0
  324. dvt_core-0.58.6.dist-info/top_level.txt +2 -0
@@ -0,0 +1,422 @@
1
+ import itertools
2
+ import os
3
+ import pathlib
4
+ from typing import Any, Dict, List, Optional, Union
5
+
6
+ from dbt.adapters.factory import get_adapter, get_adapter_package_names
7
+ from dbt.artifacts.resources import NodeVersion, RefArgs
8
+ from dbt.clients.jinja import add_rendered_test_kwargs, get_rendered
9
+ from dbt.context.configured import SchemaYamlVars, generate_schema_yml_context
10
+ from dbt.context.context_config import ContextConfig
11
+ from dbt.context.macro_resolver import MacroResolver
12
+ from dbt.context.providers import generate_test_context
13
+ from dbt.contracts.files import FileHash
14
+ from dbt.contracts.graph.nodes import (
15
+ GenericTestNode,
16
+ GraphMemberNode,
17
+ ManifestNode,
18
+ UnpatchedSourceDefinition,
19
+ )
20
+ from dbt.contracts.graph.unparsed import UnparsedColumn, UnparsedNodeUpdate
21
+ from dbt.exceptions import (
22
+ CompilationError,
23
+ ParsingError,
24
+ SchemaConfigError,
25
+ TestConfigError,
26
+ )
27
+ from dbt.node_types import NodeType
28
+ from dbt.parser.base import SimpleParser
29
+ from dbt.parser.common import (
30
+ GenericTestBlock,
31
+ Testable,
32
+ TestBlock,
33
+ TestDef,
34
+ VersionedTestBlock,
35
+ trimmed,
36
+ )
37
+ from dbt.parser.generic_test_builders import TestBuilder
38
+ from dbt.parser.search import FileBlock
39
+ from dbt.utils import get_pseudo_test_path, md5
40
+ from dbt_common.dataclass_schema import ValidationError
41
+
42
+
43
+ # This parser handles the tests that are defined in "schema" (yaml) files, on models,
44
+ # sources, etc. The base generic test is handled by the GenericTestParser
45
+ class SchemaGenericTestParser(SimpleParser):
46
+ def __init__(
47
+ self,
48
+ project,
49
+ manifest,
50
+ root_project,
51
+ ) -> None:
52
+ super().__init__(project, manifest, root_project)
53
+ self.schema_yaml_vars = SchemaYamlVars()
54
+ self.render_ctx = generate_schema_yml_context(
55
+ self.root_project, self.project.project_name, self.schema_yaml_vars
56
+ )
57
+ internal_package_names = get_adapter_package_names(self.root_project.credentials.type)
58
+ self.macro_resolver = MacroResolver(
59
+ self.manifest.macros, self.root_project.project_name, internal_package_names
60
+ )
61
+
62
+ @property
63
+ def resource_type(self) -> NodeType:
64
+ return NodeType.Test
65
+
66
+ @classmethod
67
+ def get_compiled_path(cls, block: FileBlock) -> str:
68
+ return block.path.relative_path
69
+
70
+ def parse_file(self, block: FileBlock, dct: Optional[Dict] = None) -> None:
71
+ pass
72
+
73
+ def parse_from_dict(self, dct, validate=True) -> GenericTestNode:
74
+ if validate:
75
+ GenericTestNode.validate(dct)
76
+ return GenericTestNode.from_dict(dct)
77
+
78
+ def parse_column_tests(
79
+ self, block: TestBlock, column: UnparsedColumn, version: Optional[NodeVersion]
80
+ ) -> None:
81
+ if not column.data_tests:
82
+ return
83
+
84
+ for data_test in column.data_tests:
85
+ self.parse_test(block, data_test, column, version)
86
+
87
+ def create_test_node(
88
+ self,
89
+ target: Union[UnpatchedSourceDefinition, UnparsedNodeUpdate],
90
+ path: str,
91
+ config: ContextConfig,
92
+ tags: List[str],
93
+ fqn: List[str],
94
+ name: str,
95
+ raw_code: str,
96
+ test_metadata: Dict[str, Any],
97
+ file_key_name: str,
98
+ column_name: Optional[str],
99
+ description: str,
100
+ ) -> GenericTestNode:
101
+
102
+ HASH_LENGTH = 10
103
+
104
+ # N.B: This function builds a hashable string from any given test_metadata dict.
105
+ # it's a bit fragile for general use (only supports str, int, float, List, Dict)
106
+ # but it gets the job done here without the overhead of complete ser(de).
107
+ def get_hashable_md(data: Union[str, int, float, List, Dict]) -> Union[str, List, Dict]:
108
+ if type(data) == dict:
109
+ return {k: get_hashable_md(data[k]) for k in sorted(data.keys())} # type: ignore
110
+ elif type(data) == list:
111
+ return [get_hashable_md(val) for val in data] # type: ignore
112
+ else:
113
+ return str(data)
114
+
115
+ hashable_metadata = repr(get_hashable_md(test_metadata))
116
+ hash_string = "".join([name, hashable_metadata])
117
+ test_hash = md5(hash_string)[-HASH_LENGTH:]
118
+
119
+ dct = {
120
+ "alias": name,
121
+ "schema": self.default_schema,
122
+ "database": self.default_database,
123
+ "fqn": fqn,
124
+ "name": name,
125
+ "resource_type": self.resource_type,
126
+ "tags": tags,
127
+ "path": path,
128
+ "original_file_path": target.original_file_path,
129
+ "package_name": self.project.project_name,
130
+ "raw_code": raw_code,
131
+ "language": "sql",
132
+ "unique_id": self.generate_unique_id(name, test_hash),
133
+ "config": self.config_dict(config),
134
+ "test_metadata": test_metadata,
135
+ "column_name": column_name,
136
+ "checksum": FileHash.empty().to_dict(omit_none=True),
137
+ "file_key_name": file_key_name,
138
+ "description": description,
139
+ }
140
+ try:
141
+ GenericTestNode.validate(dct)
142
+ return GenericTestNode.from_dict(dct)
143
+ except ValidationError as exc:
144
+ # this is a bit silly, but build an UnparsedNode just for error
145
+ # message reasons
146
+ node = self._create_error_node(
147
+ name=target.name,
148
+ path=path,
149
+ original_file_path=target.original_file_path,
150
+ raw_code=raw_code,
151
+ )
152
+ raise TestConfigError(exc, node)
153
+
154
+ # This is called directly in the SourcePatcher and by the "parse_node"
155
+ # command which is called by the SchemaParser.
156
+ def parse_generic_test(
157
+ self,
158
+ target: Testable,
159
+ data_test: Dict[str, Any],
160
+ tags: List[str],
161
+ column_name: Optional[str],
162
+ schema_file_id: str,
163
+ version: Optional[NodeVersion],
164
+ ) -> GenericTestNode:
165
+ try:
166
+ builder = TestBuilder(
167
+ data_test=data_test,
168
+ target=target,
169
+ column_name=column_name,
170
+ version=version,
171
+ package_name=target.package_name,
172
+ render_ctx=self.render_ctx,
173
+ )
174
+ if self.schema_yaml_vars.env_vars:
175
+ self.store_env_vars(target, schema_file_id, self.schema_yaml_vars.env_vars)
176
+ self.schema_yaml_vars.env_vars = {}
177
+
178
+ except ParsingError as exc:
179
+ context = trimmed(str(target))
180
+ msg = "Invalid test config given in {}:\n\t{}\n\t@: {}".format(
181
+ target.original_file_path, exc.msg, context
182
+ )
183
+ raise ParsingError(msg) from exc
184
+
185
+ except CompilationError as exc:
186
+ context = trimmed(str(target))
187
+ msg = (
188
+ "Invalid generic test configuration given in "
189
+ f"{target.original_file_path}: \n{exc.msg}\n\t@: {context}"
190
+ )
191
+ raise CompilationError(msg) from exc
192
+
193
+ original_name = os.path.basename(target.original_file_path)
194
+ compiled_path = get_pseudo_test_path(builder.compiled_name, original_name)
195
+
196
+ # fqn is the relative path of the yaml file where this generic test is defined,
197
+ # minus the project-level directory and the file name itself
198
+ # TODO pass a consistent path object from both UnparsedNode and UnpatchedSourceDefinition
199
+ path = pathlib.Path(target.original_file_path)
200
+ relative_path = str(path.relative_to(*path.parts[:1]))
201
+ fqn = self.get_fqn(relative_path, builder.fqn_name)
202
+
203
+ # this is the ContextConfig that is used in render_update
204
+ config: ContextConfig = self.initial_config(fqn)
205
+ # Adding the builder's config to the ContextConfig
206
+ # is needed to ensure the config makes it to the pre_model hook which dbt-snowflake needs
207
+ config.add_config_call(builder.config)
208
+ # builder.args contains keyword args for the test macro,
209
+ # not configs which have been separated out in the builder.
210
+ # The keyword args are not completely rendered until compilation.
211
+ metadata = {
212
+ "namespace": builder.namespace,
213
+ "name": builder.name,
214
+ "kwargs": builder.args,
215
+ }
216
+ tags = sorted(set(itertools.chain(tags, builder.tags())))
217
+
218
+ if isinstance(target, UnpatchedSourceDefinition):
219
+ file_key_name = f"{target.source.yaml_key}.{target.source.name}"
220
+ else:
221
+ file_key_name = f"{target.yaml_key}.{target.name}"
222
+
223
+ node = self.create_test_node(
224
+ target=target,
225
+ path=compiled_path,
226
+ config=config,
227
+ fqn=fqn,
228
+ tags=tags,
229
+ name=builder.fqn_name,
230
+ raw_code=builder.build_raw_code(),
231
+ column_name=column_name,
232
+ test_metadata=metadata,
233
+ file_key_name=file_key_name,
234
+ description=builder.description,
235
+ )
236
+ self.render_test_update(node, config, builder, schema_file_id)
237
+
238
+ return node
239
+
240
+ def _lookup_attached_node(
241
+ self, target: Testable, version: Optional[NodeVersion]
242
+ ) -> Optional[Union[ManifestNode, GraphMemberNode]]:
243
+ """Look up attached node for Testable target nodes other than sources. Can be None if generic test attached to SQL node with no corresponding .sql file."""
244
+ attached_node = None # type: Optional[Union[ManifestNode, GraphMemberNode]]
245
+ if not isinstance(target, UnpatchedSourceDefinition):
246
+ attached_node_unique_id = self.manifest.ref_lookup.get_unique_id(
247
+ target.name, target.package_name, version
248
+ )
249
+ if attached_node_unique_id:
250
+ attached_node = self.manifest.nodes[attached_node_unique_id]
251
+ else:
252
+ disabled_node = self.manifest.disabled_lookup.find(
253
+ target.name, None
254
+ ) or self.manifest.disabled_lookup.find(target.name.upper(), None)
255
+ if disabled_node:
256
+ attached_node = self.manifest.disabled[disabled_node[0].unique_id][0]
257
+ return attached_node
258
+
259
+ def store_env_vars(self, target, schema_file_id, env_vars):
260
+ self.manifest.env_vars.update(env_vars)
261
+ if schema_file_id in self.manifest.files:
262
+ schema_file = self.manifest.files[schema_file_id]
263
+ if isinstance(target, UnpatchedSourceDefinition):
264
+ search_name = target.source.name
265
+ yaml_key = target.source.yaml_key
266
+ if "." in search_name: # source file definitions
267
+ (search_name, _) = search_name.split(".")
268
+ else:
269
+ search_name = target.name
270
+ yaml_key = target.yaml_key
271
+ for var in env_vars.keys():
272
+ schema_file.add_env_var(var, yaml_key, search_name)
273
+
274
+ # This does special shortcut processing for the two
275
+ # most common internal macros, not_null and unique,
276
+ # which avoids the jinja rendering to resolve config
277
+ # and variables, etc, which might be in the macro.
278
+ # In the future we will look at generalizing this
279
+ # more to handle additional macros or to use static
280
+ # parsing to avoid jinja overhead.
281
+ def render_test_update(self, node, config, builder, schema_file_id):
282
+ macro_unique_id = self.macro_resolver.get_macro_id(
283
+ node.package_name, "test_" + builder.name
284
+ )
285
+ # Add the depends_on here so we can limit the macros added
286
+ # to the context in rendering processing
287
+ node.depends_on.add_macro(macro_unique_id)
288
+ if macro_unique_id in ["macro.dbt.test_not_null", "macro.dbt.test_unique"]:
289
+ config_call_dict = builder.config
290
+ config._config_call_dict = config_call_dict
291
+ # This sets the config from dbt_project
292
+ self.update_parsed_node_config(node, config)
293
+ # source node tests are processed at patch_source time
294
+ if isinstance(builder.target, UnpatchedSourceDefinition):
295
+ sources = [builder.target.fqn[-2], builder.target.fqn[-1]]
296
+ node.sources.append(sources)
297
+ else: # all other nodes
298
+ node.refs.append(RefArgs(name=builder.target.name, version=builder.version))
299
+ else:
300
+ try:
301
+ # make a base context that doesn't have the magic kwargs field
302
+ context = generate_test_context(
303
+ node,
304
+ self.root_project,
305
+ self.manifest,
306
+ config,
307
+ self.macro_resolver,
308
+ )
309
+ # update with rendered test kwargs (which collects any refs)
310
+ # Note: This does not actually update the kwargs with the rendered
311
+ # values. That happens in compilation.
312
+ add_rendered_test_kwargs(context, node, capture_macros=True)
313
+ # the parsed node is not rendered in the native context.
314
+ get_rendered(node.raw_code, context, node, capture_macros=True)
315
+ self.update_parsed_node_config(node, config)
316
+ # env_vars should have been updated in the context env_var method
317
+ except ValidationError as exc:
318
+ # we got a ValidationError - probably bad types in config()
319
+ raise SchemaConfigError(exc, node=node) from exc
320
+
321
+ # Set attached_node for generic test nodes, if available.
322
+ # Generic test node inherits attached node's group config value.
323
+ attached_node = self._lookup_attached_node(builder.target, builder.version)
324
+ if attached_node:
325
+ node.attached_node = attached_node.unique_id
326
+ node.group, node.group = attached_node.group, attached_node.group
327
+
328
+ def parse_node(self, block: GenericTestBlock) -> GenericTestNode:
329
+ """In schema parsing, we rewrite most of the part of parse_node that
330
+ builds the initial node to be parsed, but rendering is basically the
331
+ same
332
+ """
333
+ node = self.parse_generic_test(
334
+ target=block.target,
335
+ data_test=block.data_test,
336
+ tags=block.tags,
337
+ column_name=block.column_name,
338
+ schema_file_id=block.file.file_id,
339
+ version=block.version,
340
+ )
341
+ self.add_test_node(block, node)
342
+ return node
343
+
344
+ def add_test_node(self, block: GenericTestBlock, node: GenericTestNode):
345
+ test_from = {"key": block.target.yaml_key, "name": block.target.name}
346
+ if node.config.enabled:
347
+ self.manifest.add_node(block.file, node, test_from)
348
+ else:
349
+ self.manifest.add_disabled(block.file, node, test_from)
350
+
351
+ def render_with_context(
352
+ self,
353
+ node: GenericTestNode,
354
+ config: ContextConfig,
355
+ ) -> None:
356
+ """Given the parsed node and a ContextConfig to use during
357
+ parsing, collect all the refs that might be squirreled away in the test
358
+ arguments. This includes the implicit "model" argument.
359
+ """
360
+ # make a base context that doesn't have the magic kwargs field
361
+ context = self._context_for(node, config)
362
+ # update it with the rendered test kwargs (which collects any refs)
363
+ add_rendered_test_kwargs(context, node, capture_macros=True)
364
+
365
+ # the parsed node is not rendered in the native context.
366
+ get_rendered(node.raw_code, context, node, capture_macros=True)
367
+
368
+ def parse_test(
369
+ self,
370
+ target_block: TestBlock,
371
+ data_test: TestDef,
372
+ column: Optional[UnparsedColumn],
373
+ version: Optional[NodeVersion],
374
+ ) -> None:
375
+ if isinstance(data_test, str):
376
+ data_test = {data_test: {}}
377
+
378
+ if column is None:
379
+ column_name: Optional[str] = None
380
+ column_tags: List[str] = []
381
+ else:
382
+ column_name = column.name
383
+ should_quote = column.quote or (column.quote is None and target_block.quote_columns)
384
+ if should_quote:
385
+ column_name = get_adapter(self.root_project).quote(column_name)
386
+
387
+ column_config_tags = column.config.get("tags", [])
388
+ if isinstance(column_config_tags, str):
389
+ column_config_tags = [column_config_tags]
390
+ column_tags = list(set(column.tags + column_config_tags))
391
+
392
+ block = GenericTestBlock.from_test_block(
393
+ src=target_block,
394
+ data_test=data_test,
395
+ column_name=column_name,
396
+ tags=column_tags,
397
+ version=version,
398
+ )
399
+ self.parse_node(block)
400
+
401
+ def parse_tests(self, block: TestBlock) -> None:
402
+ for column in block.columns:
403
+ self.parse_column_tests(block, column, None)
404
+
405
+ for data_test in block.data_tests:
406
+ self.parse_test(block, data_test, None, None)
407
+
408
+ def parse_versioned_tests(self, block: VersionedTestBlock) -> None:
409
+ if not block.target.versions:
410
+ self.parse_tests(block)
411
+ else:
412
+ for version in block.target.versions:
413
+ for column in block.target.get_columns_for_version(version.v):
414
+ self.parse_column_tests(block, column, version.v)
415
+
416
+ for test in block.target.get_tests_for_version(version.v):
417
+ self.parse_test(block, test, None, version.v)
418
+
419
+ def generate_unique_id(self, resource_name: str, hash: Optional[str] = None) -> str:
420
+ return ".".join(
421
+ filter(None, [self.resource_type, self.project.project_name, resource_name, hash])
422
+ )
@@ -0,0 +1,111 @@
1
+ from typing import Any, Dict
2
+
3
+ from dbt.config.renderer import BaseRenderer, Keypath
4
+
5
+
6
+ # This class renders dictionaries derived from "schema" yaml files.
7
+ # It calls Jinja on strings (in deep_map_render), except for certain
8
+ # keys which are skipped because they need to be rendered later
9
+ # (tests and description). Test configs are rendered in the
10
+ # generic test builder code, but skips the keyword args. The test
11
+ # keyword args are rendered to capture refs in render_test_update.
12
+ # Keyword args are finally rendered at compilation time.
13
+ # Descriptions are not rendered until 'process_docs'.
14
+ # Pre- and post-hooks in configs are late-rendered.
15
+ class SchemaYamlRenderer(BaseRenderer):
16
+ def __init__(self, context: Dict[str, Any], key: str) -> None:
17
+ super().__init__(context)
18
+ self.key = key
19
+
20
+ @property
21
+ def name(self):
22
+ return "Rendering yaml"
23
+
24
+ def _is_norender_key(self, keypath: Keypath) -> bool:
25
+ """
26
+ models:
27
+ - name: blah
28
+ description: blah
29
+ data_tests: ...
30
+ columns:
31
+ - name:
32
+ description: blah
33
+ data_tests: ...
34
+
35
+ Return True if it's tests, data_tests or description - those aren't rendered now
36
+ because they're rendered later in parse_generic_tests or process_docs.
37
+ "tests" and "data_tests" are both currently supported but "tests" has been deprecated
38
+ """
39
+ # top level descriptions and data_tests
40
+ if len(keypath) >= 1 and keypath[0] in (
41
+ "tests",
42
+ "data_tests",
43
+ "description",
44
+ "loaded_at_query",
45
+ ):
46
+ return True
47
+
48
+ # columns descriptions and data_tests
49
+ if len(keypath) == 2 and keypath[1] in (
50
+ "tests",
51
+ "data_tests",
52
+ "description",
53
+ "loaded_at_query",
54
+ ):
55
+ return True
56
+
57
+ # config: pre- and post-hooks, and loaded_at_query
58
+ if (
59
+ len(keypath) >= 2
60
+ and keypath[0] == "config"
61
+ and keypath[1] in ("pre_hook", "post_hook", "loaded_at_query")
62
+ ):
63
+ return True
64
+
65
+ # versions
66
+ if len(keypath) == 5 and keypath[4] == "description":
67
+ return True
68
+
69
+ if (
70
+ len(keypath) >= 3
71
+ and keypath[0] in ("columns", "dimensions", "measures", "entities")
72
+ and keypath[2] in ("tests", "data_tests", "description")
73
+ ):
74
+ return True
75
+
76
+ return False
77
+
78
+ # don't render descriptions or test keyword arguments
79
+ def should_render_keypath(self, keypath: Keypath) -> bool:
80
+ if len(keypath) < 1:
81
+ return True
82
+ if self.key == "sources":
83
+ if keypath[0] in ("description", "loaded_at_query"):
84
+ return False
85
+ if len(keypath) >= 2 and keypath[0] == "config" and keypath[1] == "loaded_at_query":
86
+ return False
87
+ if keypath[0] == "tables":
88
+ if self._is_norender_key(keypath[2:]):
89
+ return False
90
+ elif self.key == "macros":
91
+ if keypath[0] == "arguments":
92
+ if self._is_norender_key(keypath[1:]):
93
+ return False
94
+ elif self._is_norender_key(keypath[0:]):
95
+ return False
96
+ elif self.key == "metrics":
97
+ # This ensures that metric filters are skipped
98
+ if keypath[-1] == "filter" or len(keypath) > 1 and keypath[-2] == "filter":
99
+ return False
100
+ elif self._is_norender_key(keypath[0:]):
101
+ return False
102
+ elif self.key == "saved_queries":
103
+ # This ensures that saved query filters are skipped
104
+ if keypath[0] == "query_params" and len(keypath) > 1 and keypath[1] == "where":
105
+ return False
106
+ elif self._is_norender_key(keypath[0:]):
107
+ return False
108
+ else: # models, seeds, snapshots, analyses
109
+ if self._is_norender_key(keypath[0:]):
110
+ return False
111
+ return True