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/exceptions.py ADDED
@@ -0,0 +1,1487 @@
1
+ import io
2
+ import json
3
+ import re
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Union
5
+
6
+ from dvt.node_types import REFABLE_NODE_TYPES, AccessType, NodeType
7
+
8
+ from dbt_common.constants import SECRET_ENV_PREFIX
9
+ from dbt_common.dataclass_schema import ValidationError
10
+ from dbt_common.exceptions import (
11
+ CommandResultError,
12
+ CompilationError,
13
+ DbtConfigError,
14
+ DbtInternalError,
15
+ DbtRuntimeError,
16
+ DbtValidationError,
17
+ env_secrets,
18
+ scrub_secrets,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ import agate
23
+
24
+
25
+ class ContractBreakingChangeError(DbtRuntimeError):
26
+ CODE = 10016
27
+ MESSAGE = "Breaking Change to Contract"
28
+
29
+ def __init__(
30
+ self,
31
+ breaking_changes: List[str],
32
+ node=None,
33
+ ) -> None:
34
+ self.breaking_changes = breaking_changes
35
+ super().__init__(self.message(), node)
36
+
37
+ @property
38
+ def type(self):
39
+ return "Breaking change to contract"
40
+
41
+ def message(self):
42
+ reasons = "\n - ".join(self.breaking_changes)
43
+
44
+ return (
45
+ "While comparing to previous project state, dbt detected a breaking change to an enforced contract."
46
+ f"\n - {reasons}\n"
47
+ "Consider making an additive (non-breaking) change instead, if possible.\n"
48
+ "Otherwise, create a new model version: https://docs.getdbt.com/docs/collaborate/govern/model-versions"
49
+ )
50
+
51
+
52
+ class ParsingError(DbtRuntimeError):
53
+ CODE = 10015
54
+ MESSAGE = "Parsing Error"
55
+
56
+ @property
57
+ def type(self):
58
+ return "Parsing"
59
+
60
+
61
+ class dbtPluginError(DbtRuntimeError):
62
+ CODE = 10020
63
+ MESSAGE = "Plugin Error"
64
+
65
+
66
+ # TODO: this isn't raised in the core codebase. Is it raised elsewhere?
67
+ class JSONValidationError(DbtValidationError):
68
+ def __init__(self, typename, errors) -> None:
69
+ self.typename = typename
70
+ self.errors = errors
71
+ self.errors_message = ", ".join(errors)
72
+ msg = f'Invalid arguments passed to "{self.typename}" instance: {self.errors_message}'
73
+ super().__init__(msg)
74
+
75
+ def __reduce__(self):
76
+ # see https://stackoverflow.com/a/36342588 for why this is necessary
77
+ return (JSONValidationError, (self.typename, self.errors))
78
+
79
+
80
+ class AliasError(DbtValidationError):
81
+ pass
82
+
83
+
84
+ class DependencyError(Exception):
85
+ CODE = 10006
86
+ MESSAGE = "Dependency Error"
87
+
88
+
89
+ class FailFastError(DbtRuntimeError):
90
+ CODE = 10013
91
+ MESSAGE = "FailFast Error"
92
+
93
+ def __init__(self, msg: str, result=None, node=None) -> None:
94
+ super().__init__(msg=msg, node=node)
95
+ self.result = result
96
+
97
+ @property
98
+ def type(self):
99
+ return "FailFast"
100
+
101
+
102
+ class DbtProjectError(DbtConfigError):
103
+ pass
104
+
105
+
106
+ class DbtSelectorsError(DbtConfigError):
107
+ pass
108
+
109
+
110
+ class DbtProfileError(DbtConfigError):
111
+ pass
112
+
113
+
114
+ class DbtExclusivePropertyUseError(DbtConfigError):
115
+ pass
116
+
117
+
118
+ class InvalidSelectorError(DbtRuntimeError):
119
+ def __init__(self, name: str) -> None:
120
+ self.name = name
121
+ super().__init__(name)
122
+
123
+
124
+ class DuplicateYamlKeyError(CompilationError):
125
+ pass
126
+
127
+
128
+ # compilation level exceptions
129
+ class GraphDependencyNotFoundError(CompilationError):
130
+ def __init__(self, node, dependency: str) -> None:
131
+ self.node = node
132
+ self.dependency = dependency
133
+ super().__init__(msg=self.get_message())
134
+
135
+ def get_message(self) -> str:
136
+ msg = f"'{self.node.unique_id}' depends on '{self.dependency}' which is not in the graph!"
137
+ return msg
138
+
139
+
140
+ class ForeignKeyConstraintToSyntaxError(CompilationError):
141
+ def __init__(self, node, expression: str) -> None:
142
+ self.expression = expression
143
+ self.node = node
144
+ super().__init__(msg=self.get_message())
145
+
146
+ def get_message(self) -> str:
147
+ msg = f"'{self.node.unique_id}' defines a foreign key constraint 'to' expression which is not valid 'ref' or 'source' syntax: {self.expression}."
148
+
149
+ return msg
150
+
151
+
152
+ # client level exceptions
153
+
154
+
155
+ class NoSupportedLanguagesFoundError(CompilationError):
156
+ def __init__(self, node) -> None:
157
+ self.node = node
158
+ self.msg = f"No supported_languages found in materialization macro {self.node.name}"
159
+ super().__init__(msg=self.msg)
160
+
161
+
162
+ class MaterializtionMacroNotUsedError(CompilationError):
163
+ def __init__(self, node) -> None:
164
+ self.node = node
165
+ self.msg = "Only materialization macros can be used with this function"
166
+ super().__init__(msg=self.msg)
167
+
168
+
169
+ class MacroNamespaceNotStringError(CompilationError):
170
+ def __init__(self, kwarg_type: Any) -> None:
171
+ self.kwarg_type = kwarg_type
172
+ super().__init__(msg=self.get_message())
173
+
174
+ def get_message(self) -> str:
175
+ msg = (
176
+ "The macro_namespace parameter to adapter.dispatch "
177
+ f"is a {self.kwarg_type}, not a string"
178
+ )
179
+ return msg
180
+
181
+
182
+ class UnknownGitCloningProblemError(DbtRuntimeError):
183
+ def __init__(self, repo: str) -> None:
184
+ self.repo = scrub_secrets(repo, env_secrets())
185
+ super().__init__(msg=self.get_message())
186
+
187
+ def get_message(self) -> str:
188
+ msg = f"""\
189
+ Something went wrong while cloning {self.repo}
190
+ Check the debug logs for more information
191
+ """
192
+ return msg
193
+
194
+
195
+ class NoAdaptersAvailableError(DbtRuntimeError):
196
+ def __init__(self) -> None:
197
+ super().__init__(msg=self.get_message())
198
+
199
+ def get_message(self) -> str:
200
+ msg = "No adapters available. Learn how to install an adapter by going to https://docs.getdbt.com/docs/connect-adapters#install-using-the-cli"
201
+ return msg
202
+
203
+
204
+ class BadSpecError(DbtInternalError):
205
+ def __init__(self, repo, revision, error) -> None:
206
+ self.repo = repo
207
+ self.revision = revision
208
+ self.stderr = scrub_secrets(error.stderr.strip(), env_secrets())
209
+ super().__init__(msg=self.get_message())
210
+
211
+ def get_message(self) -> str:
212
+ msg = f"Error checking out spec='{self.revision}' for repo {self.repo}\n{self.stderr}"
213
+ return msg
214
+
215
+
216
+ class GitCloningError(DbtInternalError):
217
+ def __init__(self, repo: str, revision: str, error: CommandResultError) -> None:
218
+ self.repo = repo
219
+ self.revision = revision
220
+ self.error = error
221
+ super().__init__(msg=self.get_message())
222
+
223
+ def get_message(self) -> str:
224
+ stderr = self.error.stderr.strip()
225
+ if "usage: git" in stderr:
226
+ stderr = stderr.split("\nusage: git")[0]
227
+ if re.match("fatal: destination path '(.+)' already exists", stderr):
228
+ self.error.cmd = list(scrub_secrets(str(self.error.cmd), env_secrets()))
229
+ raise self.error
230
+
231
+ msg = f"Error checking out spec='{self.revision}' for repo {self.repo}\n{stderr}"
232
+ return scrub_secrets(msg, env_secrets())
233
+
234
+
235
+ class GitCheckoutError(BadSpecError):
236
+ pass
237
+
238
+
239
+ class OperationError(CompilationError):
240
+ def __init__(self, operation_name) -> None:
241
+ self.operation_name = operation_name
242
+ super().__init__(msg=self.get_message())
243
+
244
+ def get_message(self) -> str:
245
+ msg = (
246
+ f"dbt encountered an error when attempting to create a {self.operation_name}. "
247
+ "If this error persists, please create an issue at: \n\n"
248
+ "https://github.com/dbt-labs/dbt-core"
249
+ )
250
+
251
+ return msg
252
+
253
+
254
+ # context level exceptions
255
+ class ZipStrictWrongTypeError(CompilationError):
256
+ def __init__(self, exc) -> None:
257
+ self.exc = exc
258
+ msg = str(self.exc)
259
+ super().__init__(msg=msg)
260
+
261
+
262
+ class SetStrictWrongTypeError(CompilationError):
263
+ def __init__(self, exc) -> None:
264
+ self.exc = exc
265
+ msg = str(self.exc)
266
+ super().__init__(msg=msg)
267
+
268
+
269
+ class LoadAgateTableValueError(CompilationError):
270
+ def __init__(self, exc: ValueError, node) -> None:
271
+ self.exc = exc
272
+ self.node = node
273
+ msg = str(self.exc)
274
+ super().__init__(msg=msg)
275
+
276
+
277
+ class LoadAgateTableNotSeedError(CompilationError):
278
+ def __init__(self, resource_type, node) -> None:
279
+ self.resource_type = resource_type
280
+ self.node = node
281
+ msg = f"can only load_agate_table for seeds (got a {self.resource_type})"
282
+ super().__init__(msg=msg)
283
+
284
+
285
+ class PackageNotInDepsError(CompilationError):
286
+ def __init__(self, package_name: str, node) -> None:
287
+ self.package_name = package_name
288
+ self.node = node
289
+ msg = f"Node package named {self.package_name} not found!"
290
+ super().__init__(msg=msg)
291
+
292
+
293
+ class OperationsCannotRefEphemeralNodesError(CompilationError):
294
+ def __init__(self, target_name: str, node) -> None:
295
+ self.target_name = target_name
296
+ self.node = node
297
+ msg = f"Operations can not ref() ephemeral nodes, but {target_name} is ephemeral"
298
+ super().__init__(msg=msg)
299
+
300
+
301
+ class PersistDocsValueTypeError(CompilationError):
302
+ def __init__(self, persist_docs: Any) -> None:
303
+ self.persist_docs = persist_docs
304
+ msg = (
305
+ "Invalid value provided for 'persist_docs'. Expected dict "
306
+ f"but received {type(self.persist_docs)}"
307
+ )
308
+ super().__init__(msg=msg)
309
+
310
+
311
+ class InlineModelConfigError(CompilationError):
312
+ def __init__(self, node) -> None:
313
+ self.node = node
314
+ msg = "Invalid inline model config"
315
+ super().__init__(msg=msg)
316
+
317
+
318
+ class ConflictingConfigKeysError(CompilationError):
319
+ def __init__(self, oldkey: str, newkey: str, node) -> None:
320
+ self.oldkey = oldkey
321
+ self.newkey = newkey
322
+ self.node = node
323
+ msg = f'Invalid config, has conflicting keys "{self.oldkey}" and "{self.newkey}"'
324
+ super().__init__(msg=msg)
325
+
326
+
327
+ class NumberSourceArgsError(CompilationError):
328
+ def __init__(self, args, node) -> None:
329
+ self.args = args
330
+ self.node = node
331
+ msg = f"source() takes exactly two arguments ({len(self.args)} given)"
332
+ super().__init__(msg=msg)
333
+
334
+
335
+ class RequiredVarNotFoundError(CompilationError):
336
+ def __init__(self, var_name: str, merged: Dict, node) -> None:
337
+ self.var_name = var_name
338
+ self.merged = merged
339
+ self.node = node
340
+ super().__init__(msg=self.get_message())
341
+
342
+ def get_message(self) -> str:
343
+ if self.node is not None:
344
+ node_name = self.node.name
345
+ else:
346
+ node_name = "<Configuration>"
347
+
348
+ dct = {k: self.merged[k] for k in self.merged}
349
+ pretty_vars = json.dumps(dct, sort_keys=True, indent=4)
350
+
351
+ msg = f"Required var '{self.var_name}' not found in config:\nVars supplied to {node_name} = {pretty_vars}"
352
+ return scrub_secrets(msg, self.var_secrets())
353
+
354
+ def var_secrets(self) -> List[str]:
355
+ return [v for k, v in self.merged.items() if k.startswith(SECRET_ENV_PREFIX) and v.strip()]
356
+
357
+
358
+ class PackageNotFoundForMacroError(CompilationError):
359
+ def __init__(self, package_name: str) -> None:
360
+ self.package_name = package_name
361
+ msg = f"Could not find package '{self.package_name}'"
362
+ super().__init__(msg=msg)
363
+
364
+
365
+ class SecretEnvVarLocationError(ParsingError):
366
+ def __init__(self, env_var_name: str) -> None:
367
+ self.env_var_name = env_var_name
368
+ super().__init__(msg=self.get_message())
369
+
370
+ def get_message(self) -> str:
371
+ msg = (
372
+ "Secret env vars are allowed only in profiles.yml or packages.yml. "
373
+ f"Found '{self.env_var_name}' referenced elsewhere."
374
+ )
375
+ return msg
376
+
377
+
378
+ class BooleanError(CompilationError):
379
+ def __init__(self, return_value: Any, macro_name: str) -> None:
380
+ self.return_value = return_value
381
+ self.macro_name = macro_name
382
+ super().__init__(msg=self.get_message())
383
+
384
+ def get_message(self) -> str:
385
+ msg = (
386
+ f"Macro '{self.macro_name}' returns '{self.return_value}'. It is not type 'bool' "
387
+ "and cannot not be converted reliably to a bool."
388
+ )
389
+ return msg
390
+
391
+
392
+ class RefArgsError(CompilationError):
393
+ def __init__(self, node, args) -> None:
394
+ self.node = node
395
+ self.args = args
396
+ super().__init__(msg=self.get_message())
397
+
398
+ def get_message(self) -> str:
399
+ msg = f"ref() takes at most two arguments ({len(self.args)} given)"
400
+ return msg
401
+
402
+
403
+ class MetricArgsError(CompilationError):
404
+ def __init__(self, node, args) -> None:
405
+ self.node = node
406
+ self.args = args
407
+ super().__init__(msg=self.get_message())
408
+
409
+ def get_message(self) -> str:
410
+ msg = f"metric() takes at most two arguments ({len(self.args)} given)"
411
+ return msg
412
+
413
+
414
+ class RefBadContextError(CompilationError):
415
+ def __init__(self, node, args) -> None:
416
+ self.node = node
417
+ self.args = args.positional_args # type: ignore
418
+ self.kwargs = args.keyword_args # type: ignore
419
+ super().__init__(msg=self.get_message())
420
+
421
+ def get_message(self) -> str:
422
+ # This explicitly references model['name'], instead of model['alias'], for
423
+ # better error messages. Ex. If models foo_users and bar_users are aliased
424
+ # to 'users', in their respective schemas, then you would want to see
425
+ # 'bar_users' in your error messge instead of just 'users'.
426
+ if isinstance(self.node, dict):
427
+ model_name = self.node["name"]
428
+ else:
429
+ model_name = self.node.name
430
+
431
+ ref_args = ", ".join("'{}'".format(a) for a in self.args)
432
+
433
+ keyword_args = ""
434
+ if self.kwargs:
435
+ keyword_args = ", ".join(
436
+ "{}='{}'".format(k, v) for k, v in self.kwargs.items() # type: ignore
437
+ )
438
+ keyword_args = "," + keyword_args
439
+
440
+ ref_string = f"{{{{ ref({ref_args}{keyword_args}) }}}}"
441
+
442
+ msg = f"""dbt was unable to infer all dependencies for the model "{model_name}".
443
+ This typically happens when ref() is placed within a conditional block.
444
+
445
+ To fix this, add the following hint to the top of the model "{model_name}":
446
+
447
+ -- depends_on: {ref_string}"""
448
+
449
+ return msg
450
+
451
+
452
+ class DocArgsError(CompilationError):
453
+ def __init__(self, node, args) -> None:
454
+ self.node = node
455
+ self.args = args
456
+ super().__init__(msg=self.get_message())
457
+
458
+ def get_message(self) -> str:
459
+ msg = f"doc() takes at most two arguments ({len(self.args)} given)"
460
+ return msg
461
+
462
+
463
+ class DocTargetNotFoundError(CompilationError):
464
+ def __init__(
465
+ self, node, target_doc_name: str, target_doc_package: Optional[str] = None
466
+ ) -> None:
467
+ self.node = node
468
+ self.target_doc_name = target_doc_name
469
+ self.target_doc_package = target_doc_package
470
+ super().__init__(msg=self.get_message())
471
+
472
+ def get_message(self) -> str:
473
+ target_package_string = ""
474
+ if self.target_doc_package is not None:
475
+ target_package_string = f"in package '{self.target_doc_package}' "
476
+ msg = f"Documentation for '{self.node.unique_id}' depends on doc '{self.target_doc_name}' {target_package_string} which was not found"
477
+ return msg
478
+
479
+
480
+ class MacroDispatchArgError(CompilationError):
481
+ def __init__(self, macro_name: str) -> None:
482
+ self.macro_name = macro_name
483
+ super().__init__(msg=self.get_message())
484
+
485
+ def get_message(self) -> str:
486
+ msg = f"""\
487
+ The "packages" argument of adapter.dispatch() has been deprecated.
488
+ Use the "macro_namespace" argument instead.
489
+
490
+ Raised during dispatch for: {self.macro_name}
491
+
492
+ For more information, see:
493
+
494
+ https://docs.getdbt.com/reference/dbt-jinja-functions/dispatch
495
+ """
496
+ return msg
497
+
498
+
499
+ class DuplicateMacroNameError(CompilationError):
500
+ def __init__(self, node_1, node_2, namespace: str) -> None:
501
+ self.node_1 = node_1
502
+ self.node_2 = node_2
503
+ self.namespace = namespace
504
+ super().__init__(msg=self.get_message())
505
+
506
+ def get_message(self) -> str:
507
+ duped_name = self.node_1.name
508
+ if self.node_1.package_name != self.node_2.package_name:
509
+ extra = f' ("{self.node_1.package_name}" and "{self.node_2.package_name}" are both in the "{self.namespace}" namespace)'
510
+ else:
511
+ extra = ""
512
+
513
+ msg = (
514
+ f'dbt found two macros with the name "{duped_name}" in the namespace "{self.namespace}"{extra}. '
515
+ "Since these macros have the same name and exist in the same "
516
+ "namespace, dbt will be unable to decide which to call. To fix this, "
517
+ f"change the name of one of these macros:\n- {self.node_1.unique_id} "
518
+ f"({self.node_1.original_file_path})\n- {self.node_2.unique_id} ({self.node_2.original_file_path})"
519
+ )
520
+
521
+ return msg
522
+
523
+
524
+ class MacroResultAlreadyLoadedError(CompilationError):
525
+ def __init__(self, result_name) -> None:
526
+ self.result_name = result_name
527
+ super().__init__(msg=self.get_message())
528
+
529
+ def get_message(self) -> str:
530
+ msg = f"The 'statement' result named '{self.result_name}' has already been loaded into a variable"
531
+
532
+ return msg
533
+
534
+
535
+ # parser level exceptions
536
+ class DictParseError(ParsingError):
537
+ def __init__(self, exc: ValidationError, node) -> None:
538
+ self.exc = exc
539
+ self.node = node
540
+ msg = self.validator_error_message(exc)
541
+ super().__init__(msg=msg)
542
+
543
+
544
+ class ConfigUpdateError(ParsingError):
545
+ def __init__(self, exc: ValidationError, node) -> None:
546
+ self.exc = exc
547
+ self.node = node
548
+ msg = self.validator_error_message(exc)
549
+ super().__init__(msg=msg)
550
+
551
+
552
+ class PythonParsingError(ParsingError):
553
+ def __init__(self, exc: SyntaxError, node) -> None:
554
+ self.exc = exc
555
+ self.node = node
556
+ super().__init__(msg=self.get_message())
557
+
558
+ def get_message(self) -> str:
559
+ validated_exc = self.validator_error_message(self.exc)
560
+ msg = f"{validated_exc}\n{self.exc.text}"
561
+ return msg
562
+
563
+
564
+ class PythonLiteralEvalError(ParsingError):
565
+ def __init__(self, exc: Exception, node) -> None:
566
+ self.exc = exc
567
+ self.node = node
568
+ super().__init__(msg=self.get_message())
569
+
570
+ def get_message(self) -> str:
571
+ msg = (
572
+ f"Error when trying to literal_eval an arg to dbt.ref(), dbt.source(), dbt.config() or dbt.config.get() \n{self.exc}\n"
573
+ "https://docs.python.org/3/library/ast.html#ast.literal_eval\n"
574
+ "In dbt python model, `dbt.ref`, `dbt.source`, `dbt.config`, `dbt.config.get` function args only support Python literal structures"
575
+ )
576
+
577
+ return msg
578
+
579
+
580
+ class ModelConfigError(ParsingError):
581
+ def __init__(self, exc: ValidationError, node) -> None:
582
+ self.msg = self.validator_error_message(exc)
583
+ self.node = node
584
+ super().__init__(msg=self.msg)
585
+
586
+
587
+ class YamlParseListError(ParsingError):
588
+ def __init__(
589
+ self,
590
+ path: str,
591
+ key: str,
592
+ yaml_data: List,
593
+ cause,
594
+ ) -> None:
595
+ self.path = path
596
+ self.key = key
597
+ self.yaml_data = yaml_data
598
+ self.cause = cause
599
+ super().__init__(msg=self.get_message())
600
+
601
+ def get_message(self) -> str:
602
+ if isinstance(self.cause, str):
603
+ reason = self.cause
604
+ elif isinstance(self.cause, ValidationError):
605
+ reason = self.validator_error_message(self.cause)
606
+ else:
607
+ reason = self.cause.msg
608
+ msg = f"Invalid {self.key} config given in {self.path} @ {self.key}: {self.yaml_data} - {reason}"
609
+ return msg
610
+
611
+
612
+ class YamlParseDictError(ParsingError):
613
+ def __init__(
614
+ self,
615
+ path: str,
616
+ key: str,
617
+ yaml_data: Dict[str, Any],
618
+ cause,
619
+ ) -> None:
620
+ self.path = path
621
+ self.key = key
622
+ self.yaml_data = yaml_data
623
+ self.cause = cause
624
+ super().__init__(msg=self.get_message())
625
+
626
+ def get_message(self) -> str:
627
+ if isinstance(self.cause, str):
628
+ reason = self.cause
629
+ elif isinstance(self.cause, ValidationError):
630
+ reason = self.validator_error_message(self.cause)
631
+ else:
632
+ reason = self.cause.msg
633
+ msg = f"Invalid {self.key} config given in {self.path} @ {self.key}: {self.yaml_data} - {reason}"
634
+ return msg
635
+
636
+
637
+ class YamlLoadError(ParsingError):
638
+ def __init__(
639
+ self,
640
+ path: str,
641
+ exc: DbtValidationError,
642
+ project_name: Optional[str] = None,
643
+ ) -> None:
644
+ self.project_name = project_name
645
+ self.path = path
646
+ self.exc = exc
647
+ super().__init__(msg=self.get_message())
648
+
649
+ def get_message(self) -> str:
650
+ reason = self.validator_error_message(self.exc)
651
+
652
+ msg = f"Error reading {self.project_name}: {self.path} - {reason}"
653
+
654
+ return msg
655
+
656
+
657
+ class TestConfigError(ParsingError):
658
+ def __init__(self, exc: ValidationError, node) -> None:
659
+ self.msg = self.validator_error_message(exc)
660
+ self.node = node
661
+ super().__init__(msg=self.msg)
662
+
663
+
664
+ class SchemaConfigError(ParsingError):
665
+ def __init__(self, exc: ValidationError, node) -> None:
666
+ self.msg = self.validator_error_message(exc)
667
+ self.node = node
668
+ super().__init__(msg=self.msg)
669
+
670
+
671
+ class SnapshopConfigError(ParsingError):
672
+ def __init__(self, exc: ValidationError, node) -> None:
673
+ self.msg = self.validator_error_message(exc)
674
+ self.node = node
675
+ super().__init__(msg=self.msg)
676
+
677
+
678
+ class DbtReferenceError(ParsingError):
679
+ def __init__(self, unique_id: str, ref_unique_id: str, access: AccessType, scope: str) -> None:
680
+ self.unique_id = unique_id
681
+ self.ref_unique_id = ref_unique_id
682
+ self.access = access
683
+ self.scope = scope
684
+ self.scope_type = "group" if self.access == AccessType.Private else "package"
685
+ super().__init__(msg=self.get_message())
686
+
687
+ def get_message(self) -> str:
688
+ return (
689
+ f"Node {self.unique_id} attempted to reference node {self.ref_unique_id}, "
690
+ f"which is not allowed because the referenced node is {self.access} to the '{self.scope}' {self.scope_type}."
691
+ )
692
+
693
+
694
+ class InvalidAccessTypeError(ParsingError):
695
+ def __init__(
696
+ self, unique_id: str, field_value: str, materialization: Optional[str] = None
697
+ ) -> None:
698
+ self.unique_id = unique_id
699
+ self.field_value = field_value
700
+ self.materialization = materialization
701
+
702
+ with_materialization = (
703
+ f"with '{self.materialization}' materialization " if self.materialization else ""
704
+ )
705
+ msg = f"Node {self.unique_id} {with_materialization}has an invalid value ({self.field_value}) for the access field"
706
+ super().__init__(msg=msg)
707
+
708
+
709
+ class InvalidUnitTestGivenInput(ParsingError):
710
+ def __init__(self, input: str) -> None:
711
+ msg = f"Unit test given inputs must be either a 'ref', 'source' or 'this' call. Got: '{input}'."
712
+ super().__init__(msg=msg)
713
+
714
+
715
+ class SameKeyNestedError(CompilationError):
716
+ def __init__(self) -> None:
717
+ msg = "Test cannot have the same key at the top-level and in config"
718
+ super().__init__(msg=msg)
719
+
720
+
721
+ class TestArgIncludesModelError(CompilationError):
722
+ def __init__(self) -> None:
723
+ msg = 'Test arguments include "model", which is a reserved argument'
724
+ super().__init__(msg=msg)
725
+
726
+
727
+ class UnexpectedTestNamePatternError(CompilationError):
728
+ def __init__(self, test_name: str) -> None:
729
+ self.test_name = test_name
730
+ msg = f"Test name string did not match expected pattern: {self.test_name}"
731
+ super().__init__(msg=msg)
732
+
733
+
734
+ class CustomMacroPopulatingConfigValueError(CompilationError):
735
+ def __init__(
736
+ self,
737
+ target_name: str,
738
+ name: str,
739
+ key: str,
740
+ err_msg: str,
741
+ column_name: Optional[str] = None,
742
+ ) -> None:
743
+ self.target_name = target_name
744
+ self.column_name = column_name
745
+ self.name = name
746
+ self.key = key
747
+ self.err_msg = err_msg
748
+ super().__init__(msg=self.get_message())
749
+
750
+ def get_message(self) -> str:
751
+ # Generic tests do not include custom macros in the Jinja
752
+ # rendering context, so this will almost always fail. As it
753
+ # currently stands, the error message is inscrutable, which
754
+ # has caused issues for some projects migrating from
755
+ # pre-0.20.0 to post-0.20.0.
756
+ # See https://github.com/dbt-labs/dbt-core/issues/4103
757
+ # and https://github.com/dbt-labs/dbt-core/issues/5294
758
+
759
+ msg = (
760
+ f"The {self.target_name}.{self.column_name} column's "
761
+ f'"{self.name}" test references an undefined '
762
+ f"macro in its {self.key} configuration argument. "
763
+ f"The macro {self.err_msg}.\n"
764
+ "Please note that the generic test configuration parser "
765
+ "currently does not support using custom macros to "
766
+ "populate configuration values"
767
+ )
768
+ return msg
769
+
770
+
771
+ class TagsNotListOfStringsError(CompilationError):
772
+ def __init__(self, tags: Any) -> None:
773
+ self.tags = tags
774
+ msg = f"got {self.tags} ({type(self.tags)}) for tags, expected a list of strings"
775
+ super().__init__(msg=msg)
776
+
777
+
778
+ class TagNotStringError(CompilationError):
779
+ def __init__(self, tag: Any) -> None:
780
+ self.tag = tag
781
+ msg = f"got {self.tag} ({type(self.tag)}) for tag, expected a str"
782
+ super().__init__(msg=msg)
783
+
784
+
785
+ class TestNameNotStringError(ParsingError):
786
+ def __init__(self, test_name: Any) -> None:
787
+ self.test_name = test_name
788
+ super().__init__(msg=self.get_message())
789
+
790
+ def get_message(self) -> str:
791
+ msg = f"test name must be a str, got {type(self.test_name)} (value {self.test_name})"
792
+ return msg
793
+
794
+
795
+ class TestArgsNotDictError(ParsingError):
796
+ def __init__(self, test_args: Any) -> None:
797
+ self.test_args = test_args
798
+ super().__init__(msg=self.get_message())
799
+
800
+ def get_message(self) -> str:
801
+ msg = f"test arguments must be a dict, got {type(self.test_args)} (value {self.test_args})"
802
+ return msg
803
+
804
+
805
+ class TestDefinitionDictLengthError(ParsingError):
806
+ def __init__(self, test):
807
+ self.test = test
808
+ super().__init__(msg=self.get_message())
809
+
810
+ def get_message(self) -> str:
811
+ msg = (
812
+ "test definition dictionary must have exactly one key, got"
813
+ f" {self.test} instead ({len(self.test)} keys)"
814
+ )
815
+ return msg
816
+
817
+
818
+ class TestTypeError(ParsingError):
819
+ def __init__(self, test: Any):
820
+ self.test = test
821
+ super().__init__(msg=self.get_message())
822
+
823
+ def get_message(self) -> str:
824
+ msg = f"test must be dict or str, got {type(self.test)} (value {self.test})"
825
+ return msg
826
+
827
+
828
+ # This is triggered across multiple files
829
+ class EnvVarMissingError(ParsingError):
830
+ def __init__(self, var: str):
831
+ self.var = var
832
+ super().__init__(msg=self.get_message())
833
+
834
+ def get_message(self) -> str:
835
+ msg = f"Env var required but not provided: '{self.var}'"
836
+ return msg
837
+
838
+
839
+ class TargetNotFoundError(CompilationError):
840
+ def __init__(
841
+ self,
842
+ node,
843
+ target_name: str,
844
+ target_kind: str,
845
+ target_package: Optional[str] = None,
846
+ target_version: Optional[Union[str, float]] = None,
847
+ disabled: Optional[bool] = None,
848
+ ):
849
+ self.node = node
850
+ self.target_name = target_name
851
+ self.target_kind = target_kind
852
+ self.target_package = target_package
853
+ self.target_version = target_version
854
+ self.disabled = disabled
855
+ super().__init__(msg=self.get_message())
856
+
857
+ def get_message(self) -> str:
858
+ original_file_path = self.node.original_file_path
859
+ unique_id = self.node.unique_id
860
+ resource_type_title = self.node.resource_type.title()
861
+
862
+ if self.disabled is None:
863
+ reason = "was not found or is disabled"
864
+ elif self.disabled is True:
865
+ reason = "is disabled"
866
+ else:
867
+ reason = "was not found"
868
+
869
+ target_version_string = ""
870
+ if self.target_version is not None:
871
+ target_version_string = f"with version '{self.target_version}' "
872
+
873
+ target_package_string = ""
874
+ if self.target_package is not None:
875
+ target_package_string = f"in package or project '{self.target_package}' "
876
+
877
+ msg = (
878
+ f"{resource_type_title} '{unique_id}' ({original_file_path}) depends on a "
879
+ f"{self.target_kind} named '{self.target_name}' {target_version_string}{target_package_string}which {reason}"
880
+ )
881
+ return msg
882
+
883
+
884
+ class DuplicateSourcePatchNameError(CompilationError):
885
+ def __init__(self, patch_1, patch_2):
886
+ self.patch_1 = patch_1
887
+ self.patch_2 = patch_2
888
+ super().__init__(msg=self.get_message())
889
+
890
+ def get_message(self) -> str:
891
+ name = f"{self.patch_1.overrides}.{self.patch_1.name}"
892
+ fix = self._fix_dupe_msg(
893
+ self.patch_1.path,
894
+ self.patch_2.path,
895
+ name,
896
+ "sources",
897
+ )
898
+ msg = (
899
+ f"dbt found two schema.yml entries for the same source named "
900
+ f"{self.patch_1.name} in package {self.patch_1.overrides}. Sources may only be "
901
+ f"overridden a single time. To fix this, {fix}"
902
+ )
903
+ return msg
904
+
905
+
906
+ class DuplicateMacroPatchNameError(CompilationError):
907
+ def __init__(self, patch_1, existing_patch_path):
908
+ self.patch_1 = patch_1
909
+ self.existing_patch_path = existing_patch_path
910
+ super().__init__(msg=self.get_message())
911
+
912
+ def get_message(self) -> str:
913
+ package_name = self.patch_1.package_name
914
+ name = self.patch_1.name
915
+ fix = self._fix_dupe_msg(
916
+ self.patch_1.original_file_path, self.existing_patch_path, name, "macros"
917
+ )
918
+ msg = (
919
+ f"dbt found two schema.yml entries for the same macro in package "
920
+ f"{package_name} named {name}. Macros may only be described a single "
921
+ f"time. To fix this, {fix}"
922
+ )
923
+ return msg
924
+
925
+
926
+ # core level exceptions
927
+ class DuplicateAliasError(AliasError):
928
+ def __init__(self, kwargs: Mapping[str, Any], aliases: Mapping[str, str], canonical_key: str):
929
+ self.kwargs = kwargs
930
+ self.aliases = aliases
931
+ self.canonical_key = canonical_key
932
+ super().__init__(msg=self.get_message())
933
+
934
+ def get_message(self) -> str:
935
+ # dupe found: go through the dict so we can have a nice-ish error
936
+ key_names = ", ".join(
937
+ "{}".format(k) for k in self.kwargs if self.aliases.get(k) == self.canonical_key
938
+ )
939
+ msg = f'Got duplicate keys: ({key_names}) all map to "{self.canonical_key}"'
940
+ return msg
941
+
942
+
943
+ # deps exceptions
944
+ class MultipleVersionGitDepsError(DependencyError):
945
+ def __init__(self, git: str, requested):
946
+ self.git = git
947
+ self.requested = requested
948
+ msg = (
949
+ "git dependencies should contain exactly one version. "
950
+ f"{self.git} contains: {self.requested}"
951
+ )
952
+ super().__init__(msg)
953
+
954
+
955
+ class DuplicateProjectDependencyError(DependencyError):
956
+ def __init__(self, project_name: str):
957
+ self.project_name = project_name
958
+ msg = (
959
+ f'Found duplicate project "{self.project_name}". This occurs when '
960
+ "a dependency has the same project name as some other dependency."
961
+ )
962
+ super().__init__(msg)
963
+
964
+
965
+ class DuplicateDependencyToRootError(DependencyError):
966
+ def __init__(self, project_name: str):
967
+ self.project_name = project_name
968
+ msg = (
969
+ "Found a dependency with the same name as the root project "
970
+ f'"{self.project_name}". Package names must be unique in a project.'
971
+ " Please rename one of these packages."
972
+ )
973
+ super().__init__(msg)
974
+
975
+
976
+ class MismatchedDependencyTypeError(DependencyError):
977
+ def __init__(self, new, old):
978
+ self.new = new
979
+ self.old = old
980
+ msg = (
981
+ f"Cannot incorporate {self.new} ({self.new.__class__.__name__}) in {self.old} "
982
+ f"({self.old.__class__.__name__}): mismatched types"
983
+ )
984
+ super().__init__(msg)
985
+
986
+
987
+ class PackageVersionNotFoundError(DependencyError):
988
+ def __init__(
989
+ self,
990
+ package_name: str,
991
+ version_range,
992
+ available_versions: List[str],
993
+ should_version_check: bool,
994
+ ):
995
+ self.package_name = package_name
996
+ self.version_range = version_range
997
+ self.available_versions = available_versions
998
+ self.should_version_check = should_version_check
999
+ super().__init__(self.get_message())
1000
+
1001
+ def get_message(self) -> str:
1002
+ base_msg = (
1003
+ "Could not find a matching compatible version for package {}\n"
1004
+ " Requested range: {}\n"
1005
+ " Compatible versions: {}\n"
1006
+ )
1007
+ addendum = (
1008
+ (
1009
+ "\n"
1010
+ " Not shown: package versions incompatible with installed version of dbt-core\n"
1011
+ " To include them, run 'dbt --no-version-check deps'"
1012
+ )
1013
+ if self.should_version_check
1014
+ else ""
1015
+ )
1016
+ msg = (
1017
+ base_msg.format(self.package_name, self.version_range, self.available_versions)
1018
+ + addendum
1019
+ )
1020
+ return msg
1021
+
1022
+
1023
+ class PackageNotFoundError(DependencyError):
1024
+ def __init__(self, package_name: str):
1025
+ self.package_name = package_name
1026
+ msg = f"Package {self.package_name} was not found in the package index"
1027
+ super().__init__(msg)
1028
+
1029
+
1030
+ # config level exceptions
1031
+ class ProfileConfigError(DbtProfileError):
1032
+ def __init__(self, exc: ValidationError):
1033
+ self.exc = exc
1034
+ msg = self.validator_error_message(self.exc)
1035
+ super().__init__(msg=msg)
1036
+
1037
+
1038
+ class ProjectContractError(DbtProjectError):
1039
+ def __init__(self, exc: ValidationError):
1040
+ self.exc = exc
1041
+ msg = self.validator_error_message(self.exc)
1042
+ super().__init__(msg=msg)
1043
+
1044
+
1045
+ class ProjectContractBrokenError(DbtProjectError):
1046
+ def __init__(self, exc: ValidationError):
1047
+ self.exc = exc
1048
+ msg = self.validator_error_message(self.exc)
1049
+ super().__init__(msg=msg)
1050
+
1051
+
1052
+ class ConfigContractBrokenError(DbtProjectError):
1053
+ def __init__(self, exc: ValidationError):
1054
+ self.exc = exc
1055
+ msg = self.validator_error_message(self.exc)
1056
+ super().__init__(msg=msg)
1057
+
1058
+
1059
+ class NonUniquePackageNameError(CompilationError):
1060
+ def __init__(self, project_name: str):
1061
+ self.project_name = project_name
1062
+ super().__init__(msg=self.get_message())
1063
+
1064
+ def get_message(self) -> str:
1065
+ msg = (
1066
+ "dbt found more than one package with the name "
1067
+ f'"{self.project_name}" included in this project. Package '
1068
+ "names must be unique in a project. Please rename "
1069
+ "one of these packages."
1070
+ )
1071
+ return msg
1072
+
1073
+
1074
+ class UninstalledPackagesFoundError(CompilationError):
1075
+ def __init__(
1076
+ self,
1077
+ count_packages_specified: int,
1078
+ count_packages_installed: int,
1079
+ packages_specified_path: str,
1080
+ packages_install_path: str,
1081
+ ):
1082
+ self.count_packages_specified = count_packages_specified
1083
+ self.count_packages_installed = count_packages_installed
1084
+ self.packages_specified_path = packages_specified_path
1085
+ self.packages_install_path = packages_install_path
1086
+ super().__init__(msg=self.get_message())
1087
+
1088
+ def get_message(self) -> str:
1089
+ msg = (
1090
+ f"dbt found {self.count_packages_specified} package(s) "
1091
+ f"specified in {self.packages_specified_path}, but only "
1092
+ f"{self.count_packages_installed} package(s) installed "
1093
+ f'in {self.packages_install_path}. Run "dbt deps" to '
1094
+ "install package dependencies."
1095
+ )
1096
+ return msg
1097
+
1098
+
1099
+ class OptionNotYamlDictError(CompilationError):
1100
+ def __init__(self, var_type, option_name):
1101
+ self.var_type = var_type
1102
+ self.option_name = option_name
1103
+ super().__init__(msg=self.get_message())
1104
+
1105
+ def get_message(self) -> str:
1106
+ type_name = self.var_type.__name__
1107
+
1108
+ msg = f"The --{self.option_name} argument must be a YAML dictionary, but was of type '{type_name}'"
1109
+ return msg
1110
+
1111
+
1112
+ # contracts level
1113
+ class UnrecognizedCredentialTypeError(CompilationError):
1114
+ def __init__(self, typename: str, supported_types: List):
1115
+ self.typename = typename
1116
+ self.supported_types = supported_types
1117
+ super().__init__(msg=self.get_message())
1118
+
1119
+ def get_message(self) -> str:
1120
+ msg = 'Unrecognized credentials type "{}" - supported types are ({})'.format(
1121
+ self.typename, ", ".join('"{}"'.format(t) for t in self.supported_types)
1122
+ )
1123
+ return msg
1124
+
1125
+
1126
+ # jinja exceptions
1127
+
1128
+
1129
+ class PatchTargetNotFoundError(CompilationError):
1130
+ def __init__(self, patches: Dict):
1131
+ self.patches = patches
1132
+ super().__init__(msg=self.get_message())
1133
+
1134
+ def get_message(self) -> str:
1135
+ patch_list = "\n\t".join(
1136
+ f"model {p.name} (referenced in path {p.original_file_path})"
1137
+ for p in self.patches.values()
1138
+ )
1139
+ msg = f"dbt could not find models for the following patches:\n\t{patch_list}"
1140
+ return msg
1141
+
1142
+
1143
+ class MissingRelationError(CompilationError):
1144
+ def __init__(self, relation, model=None):
1145
+ self.relation = relation
1146
+ self.model = model
1147
+ msg = f"Relation {self.relation} not found!"
1148
+ super().__init__(msg=msg)
1149
+
1150
+
1151
+ class AmbiguousAliasError(CompilationError):
1152
+ def __init__(self, node_1, node_2, duped_name=None):
1153
+ self.node_1 = node_1
1154
+ self.node_2 = node_2
1155
+ if duped_name is None:
1156
+ self.duped_name = f"{self.node_1.database}.{self.node_1.schema}.{self.node_1.alias}"
1157
+ else:
1158
+ self.duped_name = duped_name
1159
+ super().__init__(msg=self.get_message())
1160
+
1161
+ def get_message(self) -> str:
1162
+
1163
+ msg = (
1164
+ f'dbt found two resources with the database representation "{self.duped_name}".\ndbt '
1165
+ "cannot create two resources with identical database representations. "
1166
+ "To fix this,\nchange the configuration of one of these resources:"
1167
+ f"\n- {self.node_1.unique_id} ({self.node_1.original_file_path})\n- {self.node_2.unique_id} ({self.node_2.original_file_path})"
1168
+ )
1169
+ return msg
1170
+
1171
+
1172
+ class AmbiguousResourceNameRefError(CompilationError):
1173
+ def __init__(self, duped_name, unique_ids, node=None):
1174
+ self.duped_name = duped_name
1175
+ self.unique_ids = unique_ids
1176
+ self.packages = [unique_id.split(".")[1] for unique_id in unique_ids]
1177
+ super().__init__(msg=self.get_message(), node=node)
1178
+
1179
+ def get_message(self) -> str:
1180
+ formatted_unique_ids = "'{0}'".format("', '".join(self.unique_ids))
1181
+ formatted_packages = "'{0}'".format("' or '".join(self.packages))
1182
+ msg = (
1183
+ f"When referencing '{self.duped_name}', dbt found nodes in multiple packages: {formatted_unique_ids}"
1184
+ f"\nTo fix this, use two-argument 'ref', with the package name first: {formatted_packages}"
1185
+ )
1186
+ return msg
1187
+
1188
+
1189
+ class AmbiguousCatalogMatchError(CompilationError):
1190
+ def __init__(self, unique_id: str, match_1, match_2):
1191
+ self.unique_id = unique_id
1192
+ self.match_1 = match_1
1193
+ self.match_2 = match_2
1194
+ super().__init__(msg=self.get_message())
1195
+
1196
+ def get_match_string(self, match):
1197
+ match_schema = match.get("metadata", {}).get("schema")
1198
+ match_name = match.get("metadata", {}).get("name")
1199
+ return f"{match_schema}.{match_name}"
1200
+
1201
+ def get_message(self) -> str:
1202
+ msg = (
1203
+ "dbt found two relations in your warehouse with similar database identifiers. "
1204
+ "dbt\nis unable to determine which of these relations was created by the model "
1205
+ f'"{self.unique_id}".\nIn order for dbt to correctly generate the catalog, one '
1206
+ "of the following relations must be deleted or renamed:\n\n - "
1207
+ f"{self.get_match_string(self.match_1)}\n - {self.get_match_string(self.match_2)}"
1208
+ )
1209
+
1210
+ return msg
1211
+
1212
+
1213
+ class DependencyNotFoundError(CompilationError):
1214
+ def __init__(self, node, node_description, required_pkg):
1215
+ self.node = node
1216
+ self.node_description = node_description
1217
+ self.required_pkg = required_pkg
1218
+ super().__init__(msg=self.get_message())
1219
+
1220
+ def get_message(self) -> str:
1221
+ msg = (
1222
+ f"Error while parsing {self.node_description}.\nThe required package "
1223
+ f'"{self.required_pkg}" was not found. Is the package installed?\n'
1224
+ "Hint: You may need to run `dbt deps`."
1225
+ )
1226
+
1227
+ return msg
1228
+
1229
+
1230
+ class DuplicatePatchPathError(CompilationError):
1231
+ def __init__(self, patch_1, existing_patch_path):
1232
+ self.patch_1 = patch_1
1233
+ self.existing_patch_path = existing_patch_path
1234
+ super().__init__(msg=self.get_message())
1235
+
1236
+ def get_message(self) -> str:
1237
+ name = self.patch_1.name
1238
+ fix = self._fix_dupe_msg(
1239
+ self.patch_1.original_file_path,
1240
+ self.existing_patch_path,
1241
+ name,
1242
+ "resource",
1243
+ )
1244
+ msg = (
1245
+ f"dbt found two schema.yml entries for the same resource named "
1246
+ f"{name}. Resources and their associated columns may only be "
1247
+ f"described a single time. To fix this, {fix}"
1248
+ )
1249
+ return msg
1250
+
1251
+
1252
+ # should this inherit ParsingError instead?
1253
+ class DuplicateResourceNameError(CompilationError):
1254
+ def __init__(self, node_1, node_2):
1255
+ self.node_1 = node_1
1256
+ self.node_2 = node_2
1257
+ super().__init__(msg=self.get_message())
1258
+
1259
+ def get_message(self) -> str:
1260
+ duped_name = self.node_1.name
1261
+ node_type = NodeType(self.node_1.resource_type)
1262
+ pluralized = (
1263
+ node_type.pluralize()
1264
+ if self.node_1.resource_type == self.node_2.resource_type
1265
+ else "resources" # still raise if ref() collision, e.g. model + seed
1266
+ )
1267
+
1268
+ action = "looking for"
1269
+ # duplicate 'ref' targets
1270
+ if node_type in REFABLE_NODE_TYPES:
1271
+ formatted_name = f'ref("{duped_name}")'
1272
+ # duplicate sources
1273
+ elif node_type == NodeType.Source:
1274
+ duped_name = self.node_1.get_full_source_name()
1275
+ formatted_name = self.node_1.get_source_representation()
1276
+ # duplicate docs blocks
1277
+ elif node_type == NodeType.Documentation:
1278
+ formatted_name = f'doc("{duped_name}")'
1279
+ # duplicate generic tests
1280
+ elif node_type == NodeType.Test and hasattr(self.node_1, "test_metadata"):
1281
+ column_name = (
1282
+ f'column "{self.node_1.column_name}" in ' if self.node_1.column_name else ""
1283
+ )
1284
+ model_name = self.node_1.file_key_name
1285
+ duped_name = f'{self.node_1.name}" defined on {column_name}"{model_name}'
1286
+ action = "running"
1287
+ formatted_name = "tests"
1288
+ # all other resource types
1289
+ else:
1290
+ formatted_name = duped_name
1291
+
1292
+ msg = f"""
1293
+ dbt found two {pluralized} with the name "{duped_name}".
1294
+
1295
+ Since these resources have the same name, dbt will be unable to find the correct resource
1296
+ when {action} {formatted_name}.
1297
+
1298
+ To fix this, change the name of one of these resources:
1299
+ - {self.node_1.unique_id} ({self.node_1.original_file_path})
1300
+ - {self.node_2.unique_id} ({self.node_2.original_file_path})
1301
+ """.strip()
1302
+ return msg
1303
+
1304
+
1305
+ class DuplicateVersionedUnversionedError(ParsingError):
1306
+ def __init__(self, versioned_node, unversioned_node):
1307
+ self.versioned_node = versioned_node
1308
+ self.unversioned_node = unversioned_node
1309
+ super().__init__(msg=self.get_message())
1310
+
1311
+ def get_message(self) -> str:
1312
+ msg = f"""
1313
+ dbt found versioned and unversioned models with the name "{self.versioned_node.name}".
1314
+
1315
+ Since these resources have the same name, dbt will be unable to find the correct resource
1316
+ when looking for ref('{self.versioned_node.name}').
1317
+
1318
+ To fix this, change the name of the unversioned resource
1319
+ {self.unversioned_node.unique_id} ({self.unversioned_node.original_file_path})
1320
+ or add the unversioned model to the versions in {self.versioned_node.patch_path}
1321
+ """.strip()
1322
+ return msg
1323
+
1324
+
1325
+ class PropertyYMLError(CompilationError):
1326
+ def __init__(self, path: str, issue: str):
1327
+ self.path = path
1328
+ self.issue = issue
1329
+ super().__init__(msg=self.get_message())
1330
+
1331
+ def get_message(self) -> str:
1332
+ msg = (
1333
+ f"The yml property file at {self.path} is invalid because {self.issue}. "
1334
+ "Please consult the documentation for more information on yml property file "
1335
+ "syntax:\n\nhttps://docs.getdbt.com/reference/configs-and-properties"
1336
+ )
1337
+ return msg
1338
+
1339
+
1340
+ class ContractError(CompilationError):
1341
+ def __init__(self, yaml_columns, sql_columns):
1342
+ self.yaml_columns = yaml_columns
1343
+ self.sql_columns = sql_columns
1344
+ super().__init__(msg=self.get_message())
1345
+
1346
+ def get_mismatches(self) -> "agate.Table":
1347
+ # avoid a circular import
1348
+ from dbt_common.clients.agate_helper import table_from_data_flat
1349
+
1350
+ column_names = ["column_name", "definition_type", "contract_type", "mismatch_reason"]
1351
+ # list of mismatches
1352
+ mismatches: List[Dict[str, str]] = []
1353
+ # track sql cols so we don't need another for loop later
1354
+ sql_col_set = set()
1355
+ # for each sql col list
1356
+ for sql_col in self.sql_columns:
1357
+ # add sql col to set
1358
+ sql_col_set.add(sql_col["name"])
1359
+ # for each yaml col list
1360
+ for i, yaml_col in enumerate(self.yaml_columns):
1361
+ # if name matches
1362
+ if sql_col["name"] == yaml_col["name"]:
1363
+ # if type matches
1364
+ if sql_col["data_type"] == yaml_col["data_type"]:
1365
+ # its a perfect match! don't include in mismatch table
1366
+ break
1367
+ else:
1368
+ # same name, diff type
1369
+ row = [
1370
+ sql_col["name"],
1371
+ sql_col["data_type"],
1372
+ yaml_col["data_type"],
1373
+ "data type mismatch",
1374
+ ]
1375
+ mismatches += [dict(zip(column_names, row))]
1376
+ break
1377
+ # if last loop, then no name match
1378
+ if i == len(self.yaml_columns) - 1:
1379
+ row = [sql_col["name"], sql_col["data_type"], "", "missing in contract"]
1380
+ mismatches += [dict(zip(column_names, row))]
1381
+
1382
+ # now add all yaml cols without a match
1383
+ for yaml_col in self.yaml_columns:
1384
+ if yaml_col["name"] not in sql_col_set:
1385
+ row = [yaml_col["name"], "", yaml_col["data_type"], "missing in definition"]
1386
+ mismatches += [dict(zip(column_names, row))]
1387
+
1388
+ mismatches_sorted = sorted(mismatches, key=lambda d: d["column_name"])
1389
+ return table_from_data_flat(mismatches_sorted, column_names)
1390
+
1391
+ def get_message(self) -> str:
1392
+ if not self.yaml_columns:
1393
+ return (
1394
+ "This model has an enforced contract, and its 'columns' specification is missing"
1395
+ )
1396
+
1397
+ table: "agate.Table" = self.get_mismatches()
1398
+ # Hack to get Agate table output as string
1399
+ output = io.StringIO()
1400
+ table.print_table(output=output, max_rows=None, max_column_width=50) # type: ignore
1401
+ mismatches = output.getvalue()
1402
+
1403
+ msg = (
1404
+ "This model has an enforced contract that failed.\n"
1405
+ "Please ensure the name, data_type, and number of columns in your contract "
1406
+ "match the columns in your model's definition.\n\n"
1407
+ f"{mismatches}"
1408
+ )
1409
+
1410
+ return msg
1411
+
1412
+
1413
+ # not modifying these since rpc should be deprecated soon
1414
+ class UnknownAsyncIDException(Exception):
1415
+ CODE = 10012
1416
+ MESSAGE = "RPC server got an unknown async ID"
1417
+
1418
+ def __init__(self, task_id):
1419
+ self.task_id = task_id
1420
+
1421
+ def __str__(self):
1422
+ return f"{self.MESSAGE}: {self.task_id}"
1423
+
1424
+
1425
+ class RPCFailureResult(DbtRuntimeError):
1426
+ CODE = 10002
1427
+ MESSAGE = "RPC execution error"
1428
+
1429
+
1430
+ class RPCTimeoutException(DbtRuntimeError):
1431
+ CODE = 10008
1432
+ MESSAGE = "RPC timeout error"
1433
+
1434
+ def __init__(self, timeout: Optional[float] = None):
1435
+ super().__init__(self.MESSAGE)
1436
+ self.timeout = timeout
1437
+
1438
+ def data(self):
1439
+ result = super().data()
1440
+ result.update(
1441
+ {
1442
+ "timeout": self.timeout,
1443
+ "message": f"RPC timed out after {self.timeout}s",
1444
+ }
1445
+ )
1446
+ return result
1447
+
1448
+
1449
+ class RPCKilledException(DbtRuntimeError):
1450
+ CODE = 10009
1451
+ MESSAGE = "RPC process killed"
1452
+
1453
+ def __init__(self, signum: int):
1454
+ self.signum = signum
1455
+ self.msg = f"RPC process killed by signal {self.signum}"
1456
+ super().__init__(self.msg)
1457
+
1458
+ def data(self):
1459
+ return {
1460
+ "signum": self.signum,
1461
+ "message": self.msg,
1462
+ }
1463
+
1464
+
1465
+ class RPCCompiling(DbtRuntimeError):
1466
+ CODE = 10010
1467
+ MESSAGE = 'RPC server is compiling the project, call the "status" method for' " compile status"
1468
+
1469
+ def __init__(self, msg: Optional[str] = None, node=None):
1470
+ if msg is None:
1471
+ msg = "compile in progress"
1472
+ super().__init__(msg, node)
1473
+
1474
+
1475
+ class RPCLoadException(DbtRuntimeError):
1476
+ CODE = 10011
1477
+ MESSAGE = (
1478
+ 'RPC server failed to compile project, call the "status" method for' " compile status"
1479
+ )
1480
+
1481
+ def __init__(self, cause: Dict[str, Any]):
1482
+ self.cause = cause
1483
+ self.msg = f'{self.MESSAGE}: {self.cause["message"]}'
1484
+ super().__init__(self.msg)
1485
+
1486
+ def data(self):
1487
+ return {"cause": self.cause, "message": self.msg}