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/events/types.py ADDED
@@ -0,0 +1,2534 @@
1
+ import json
2
+ from typing import List
3
+
4
+ from dvt.constants import MAXIMUM_SEED_SIZE_NAME, PIN_PACKAGE_URL
5
+ from dvt.events.base_types import (
6
+ DebugLevel,
7
+ DynamicLevel,
8
+ ErrorLevel,
9
+ InfoLevel,
10
+ WarnLevel,
11
+ )
12
+ from dvt.flags import get_flags
13
+
14
+ from dbt_common.events.base_types import EventLevel
15
+ from dbt_common.events.format import (
16
+ format_fancy_output_line,
17
+ pluralize,
18
+ timestamp_to_datetime_string,
19
+ )
20
+ from dbt_common.ui import (
21
+ deprecation_tag,
22
+ error_tag,
23
+ green,
24
+ line_wrap_message,
25
+ red,
26
+ warning_tag,
27
+ yellow,
28
+ )
29
+
30
+ # Event codes have prefixes which follow this table
31
+ #
32
+ # | Code | Description |
33
+ # |:----:|:-------------------:|
34
+ # | A | Pre-project loading |
35
+ # | D | Deprecations |
36
+ # | E | DB adapter |
37
+ # | I | Project parsing |
38
+ # | M | Deps generation |
39
+ # | P | Artifacts |
40
+ # | Q | Node execution |
41
+ # | W | Node testing |
42
+ # | Z | Misc |
43
+ # | T | Test only |
44
+ #
45
+ # The basic idea is that event codes roughly translate to the natural order of running a dbt task
46
+
47
+ # =======================================================
48
+ # A - Pre-project loading
49
+ # =======================================================
50
+
51
+
52
+ class MainReportVersion(InfoLevel):
53
+ def code(self) -> str:
54
+ return "A001"
55
+
56
+ def message(self) -> str:
57
+ return f"Running with dbt{self.version}"
58
+
59
+
60
+ class MainReportArgs(DebugLevel):
61
+ def code(self) -> str:
62
+ return "A002"
63
+
64
+ def message(self) -> str:
65
+ return f"running dbt with arguments {str(self.args)}"
66
+
67
+
68
+ class MainTrackingUserState(DebugLevel):
69
+ def code(self) -> str:
70
+ return "A003"
71
+
72
+ def message(self) -> str:
73
+ return f"Tracking: {self.user_state}"
74
+
75
+
76
+ # Removed A004: MergedFromState
77
+
78
+
79
+ class MissingProfileTarget(InfoLevel):
80
+ def code(self) -> str:
81
+ return "A005"
82
+
83
+ def message(self) -> str:
84
+ return f"target not specified in profile '{self.profile_name}', using '{self.target_name}'"
85
+
86
+
87
+ # Skipped A006, A007
88
+
89
+
90
+ class InvalidOptionYAML(ErrorLevel):
91
+ def code(self) -> str:
92
+ return "A008"
93
+
94
+ def message(self) -> str:
95
+ return f"The YAML provided in the --{self.option_name} argument is not valid."
96
+
97
+
98
+ class LogDbtProjectError(ErrorLevel):
99
+ def code(self) -> str:
100
+ return "A009"
101
+
102
+ def message(self) -> str:
103
+ msg = "Encountered an error while reading the project:"
104
+ if self.exc:
105
+ msg += f" ERROR: {str(self.exc)}"
106
+ return msg
107
+
108
+
109
+ # Skipped A010
110
+
111
+
112
+ class LogDbtProfileError(ErrorLevel):
113
+ def code(self) -> str:
114
+ return "A011"
115
+
116
+ def message(self) -> str:
117
+ msg = "Encountered an error while reading profiles:\n" f" ERROR: {str(self.exc)}"
118
+ if self.profiles:
119
+ msg += "Defined profiles:\n"
120
+ for profile in self.profiles:
121
+ msg += f" - {profile}"
122
+ else:
123
+ msg += "There are no profiles defined in your profiles.yml file"
124
+
125
+ msg += """
126
+ For more information on configuring profiles, please consult the dbt docs:
127
+
128
+ https://docs.getdbt.com/docs/configure-your-profile
129
+ """
130
+ return msg
131
+
132
+
133
+ class StarterProjectPath(DebugLevel):
134
+ def code(self) -> str:
135
+ return "A017"
136
+
137
+ def message(self) -> str:
138
+ return f"Starter project path: {self.dir}"
139
+
140
+
141
+ class ConfigFolderDirectory(InfoLevel):
142
+ def code(self) -> str:
143
+ return "A018"
144
+
145
+ def message(self) -> str:
146
+ return f"Creating dbt configuration folder at {self.dir}"
147
+
148
+
149
+ class NoSampleProfileFound(InfoLevel):
150
+ def code(self) -> str:
151
+ return "A019"
152
+
153
+ def message(self) -> str:
154
+ return f"No sample profile found for {self.adapter}."
155
+
156
+
157
+ class ProfileWrittenWithSample(InfoLevel):
158
+ def code(self) -> str:
159
+ return "A020"
160
+
161
+ def message(self) -> str:
162
+ return (
163
+ f"Profile {self.name} written to {self.path} "
164
+ "using target's sample configuration. Once updated, you'll be able to "
165
+ "start developing with dbt."
166
+ )
167
+
168
+
169
+ class ProfileWrittenWithTargetTemplateYAML(InfoLevel):
170
+ def code(self) -> str:
171
+ return "A021"
172
+
173
+ def message(self) -> str:
174
+ return (
175
+ f"Profile {self.name} written to {self.path} using target's "
176
+ "profile_template.yml and your supplied values. Run 'dbt debug' to "
177
+ "validate the connection."
178
+ )
179
+
180
+
181
+ class ProfileWrittenWithProjectTemplateYAML(InfoLevel):
182
+ def code(self) -> str:
183
+ return "A022"
184
+
185
+ def message(self) -> str:
186
+ return (
187
+ f"Profile {self.name} written to {self.path} using project's "
188
+ "profile_template.yml and your supplied values. Run 'dbt debug' to "
189
+ "validate the connection."
190
+ )
191
+
192
+
193
+ class SettingUpProfile(InfoLevel):
194
+ def code(self) -> str:
195
+ return "A023"
196
+
197
+ def message(self) -> str:
198
+ return "Setting up your profile."
199
+
200
+
201
+ class InvalidProfileTemplateYAML(InfoLevel):
202
+ def code(self) -> str:
203
+ return "A024"
204
+
205
+ def message(self) -> str:
206
+ return "Invalid profile_template.yml in project."
207
+
208
+
209
+ class ProjectNameAlreadyExists(InfoLevel):
210
+ def code(self) -> str:
211
+ return "A025"
212
+
213
+ def message(self) -> str:
214
+ return f"A project called {self.name} already exists here."
215
+
216
+
217
+ class ProjectCreated(InfoLevel):
218
+ def code(self) -> str:
219
+ return "A026"
220
+
221
+ def message(self) -> str:
222
+ return f"""
223
+ Your new dbt project "{self.project_name}" was created!
224
+
225
+ For more information on how to configure the profiles.yml file,
226
+ please consult the dbt documentation here:
227
+
228
+ {self.docs_url}
229
+
230
+ One more thing:
231
+
232
+ Need help? Don't hesitate to reach out to us via GitHub issues or on Slack:
233
+
234
+ {self.slack_url}
235
+
236
+ Happy modeling!
237
+ """
238
+
239
+
240
+ # =======================================================
241
+ # D - Deprecations
242
+ # =======================================================
243
+
244
+
245
+ def require_event_names_in_deprecations():
246
+ # The require_event_names_in_deprecations flag isn't guaranteed to be set by the
247
+ # time some deprecations are fired. We could have done the following ever single deprecation
248
+ # that needs it, but this makes it simpler to flip the flag later.
249
+ return getattr(get_flags(), "require_event_names_in_deprecations", False)
250
+
251
+
252
+ class DeprecatedModel(WarnLevel):
253
+ def code(self) -> str:
254
+ return "I065"
255
+
256
+ def message(self) -> str:
257
+ version = ".v" + self.model_version if self.model_version else ""
258
+ msg = (
259
+ f"Model {self.model_name}{version} has passed its deprecation date of {self.deprecation_date}. "
260
+ "This model should be disabled or removed."
261
+ )
262
+
263
+ if require_event_names_in_deprecations():
264
+ return line_wrap_message(deprecation_tag(msg, self.__class__.__name__))
265
+ else:
266
+ return warning_tag(msg)
267
+
268
+
269
+ class PackageRedirectDeprecation(WarnLevel):
270
+ def code(self) -> str:
271
+ return "D001"
272
+
273
+ def message(self) -> str:
274
+ description = (
275
+ f"The `{self.old_name}` package is deprecated in favor of `{self.new_name}`. Please "
276
+ f"update your `packages.yml` configuration to use `{self.new_name}` instead."
277
+ )
278
+
279
+ if require_event_names_in_deprecations():
280
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
281
+ else:
282
+ return line_wrap_message(deprecation_tag(description))
283
+
284
+
285
+ class PackageInstallPathDeprecation(WarnLevel):
286
+ def code(self) -> str:
287
+ return "D002"
288
+
289
+ def message(self) -> str:
290
+ description = """\
291
+ The default package install path has changed from `dbt_modules` to `dbt_packages`.
292
+ Please update `clean-targets` in `dbt_project.yml` and check `.gitignore` as well.
293
+ Or, set `packages-install-path: dbt_modules` if you'd like to keep the current value.
294
+ """
295
+
296
+ if require_event_names_in_deprecations():
297
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
298
+ else:
299
+ return line_wrap_message(deprecation_tag(description))
300
+
301
+
302
+ class ConfigSourcePathDeprecation(WarnLevel):
303
+ def code(self) -> str:
304
+ return "D003"
305
+
306
+ def message(self) -> str:
307
+ description = (
308
+ f"The `{self.deprecated_path}` config has been renamed to `{self.exp_path}`. "
309
+ "Please update your `dbt_project.yml` configuration to reflect this change."
310
+ )
311
+
312
+ if require_event_names_in_deprecations():
313
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
314
+ else:
315
+ return line_wrap_message(deprecation_tag(description))
316
+
317
+
318
+ class ConfigDataPathDeprecation(WarnLevel):
319
+ def code(self) -> str:
320
+ return "D004"
321
+
322
+ def message(self) -> str:
323
+ description = (
324
+ f"The `{self.deprecated_path}` config has been renamed to `{self.exp_path}`. "
325
+ "Please update your `dbt_project.yml` configuration to reflect this change."
326
+ )
327
+
328
+ if require_event_names_in_deprecations():
329
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
330
+ else:
331
+ return line_wrap_message(deprecation_tag(description))
332
+
333
+
334
+ class MetricAttributesRenamed(WarnLevel):
335
+ def code(self) -> str:
336
+ return "D006"
337
+
338
+ def message(self) -> str:
339
+ description = (
340
+ "dbt-core v1.3 renamed attributes for metrics:"
341
+ "\n 'sql' -> 'expression'"
342
+ "\n 'type' -> 'calculation_method'"
343
+ "\n 'type: expression' -> 'calculation_method: derived'"
344
+ f"\nPlease remove them from the metric definition of metric '{self.metric_name}'"
345
+ "\nRelevant issue here: https://github.com/dbt-labs/dbt-core/issues/5849"
346
+ )
347
+
348
+ if require_event_names_in_deprecations():
349
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
350
+ else:
351
+ return deprecation_tag(description)
352
+
353
+
354
+ class ExposureNameDeprecation(WarnLevel):
355
+ def code(self) -> str:
356
+ return "D007"
357
+
358
+ def message(self) -> str:
359
+ description = (
360
+ "Starting in v1.3, the 'name' of an exposure should contain only letters, "
361
+ "numbers, and underscores. Exposures support a new property, 'label', which may "
362
+ f"contain spaces, capital letters, and special characters. {self.exposure} does not "
363
+ "follow this pattern. Please update the 'name', and use the 'label' property for a "
364
+ "human-friendly title. This will raise an error in a future version of dbt-core."
365
+ )
366
+
367
+ if require_event_names_in_deprecations():
368
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
369
+ else:
370
+ return line_wrap_message(deprecation_tag(description))
371
+
372
+
373
+ class InternalDeprecation(WarnLevel):
374
+ def code(self) -> str:
375
+ return "D008"
376
+
377
+ def message(self) -> str:
378
+ extra_reason = ""
379
+ if self.reason:
380
+ extra_reason = f"\n{self.reason}"
381
+ msg = (
382
+ f"`{self.name}` is deprecated and will be removed in dbt-core version {self.version}\n\n"
383
+ f"Adapter maintainers can resolve this deprecation by {self.suggested_action}. {extra_reason}"
384
+ )
385
+
386
+ if require_event_names_in_deprecations():
387
+ return deprecation_tag(msg, self.__class__.__name__)
388
+ else:
389
+ return warning_tag(msg)
390
+
391
+
392
+ class EnvironmentVariableRenamed(WarnLevel):
393
+ def code(self) -> str:
394
+ return "D009"
395
+
396
+ def message(self) -> str:
397
+ description = (
398
+ f"The environment variable `{self.old_name}` has been renamed as `{self.new_name}`.\n"
399
+ f"If `{self.old_name}` is currently set, its value will be used instead of `{self.new_name}`.\n"
400
+ f"Set `{self.new_name}` and unset `{self.old_name}` to avoid this deprecation warning and "
401
+ "ensure it works properly in a future release."
402
+ )
403
+
404
+ if require_event_names_in_deprecations():
405
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
406
+ else:
407
+ return line_wrap_message(deprecation_tag(description))
408
+
409
+
410
+ class ConfigLogPathDeprecation(WarnLevel):
411
+ def code(self) -> str:
412
+ return "D010"
413
+
414
+ def message(self) -> str:
415
+ output = "logs"
416
+ cli_flag = "--log-path"
417
+ env_var = "DBT_LOG_PATH"
418
+ description = (
419
+ f"The `{self.deprecated_path}` config in `dbt_project.yml` has been deprecated, "
420
+ f"and will no longer be supported in a future version of dbt-core. "
421
+ f"If you wish to write dbt {output} to a custom directory, please use "
422
+ f"the {cli_flag} CLI flag or {env_var} env var instead."
423
+ )
424
+
425
+ if require_event_names_in_deprecations():
426
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
427
+ else:
428
+ return line_wrap_message(deprecation_tag(description))
429
+
430
+
431
+ class DbtProjectFileDeprecated(WarnLevel):
432
+ def code(self) -> str:
433
+ return "D011"
434
+
435
+ def message(self) -> str:
436
+ description = (
437
+ "The `dbt_project.yml` filename is deprecated and will be removed in a future version. "
438
+ "Please rename your project file to `dvt_project.yml`. "
439
+ "DVT will continue to read `dbt_project.yml` for now, but this support will be removed. "
440
+ "You can rename the file manually or use `dvt migrate-project` (future feature)."
441
+ )
442
+ if require_event_names_in_deprecations():
443
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
444
+ else:
445
+ return line_wrap_message(deprecation_tag(description))
446
+
447
+
448
+ class ConfigTargetPathDeprecation(WarnLevel):
449
+ def code(self) -> str:
450
+ return "D012"
451
+
452
+ def message(self) -> str:
453
+ output = "artifacts"
454
+ cli_flag = "--target-path"
455
+ env_var = "DBT_TARGET_PATH"
456
+ description = (
457
+ f"The `{self.deprecated_path}` config in `dbt_project.yml` has been deprecated, "
458
+ f"and will no longer be supported in a future version of dbt-core. "
459
+ f"If you wish to write dbt {output} to a custom directory, please use "
460
+ f"the {cli_flag} CLI flag or {env_var} env var instead."
461
+ )
462
+
463
+ if require_event_names_in_deprecations():
464
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
465
+ else:
466
+ return line_wrap_message(deprecation_tag(description))
467
+
468
+
469
+ # Note: this deprecation has been removed, but we are leaving
470
+ # the event class here, because users may have specified it in
471
+ # warn_error_options.
472
+ class TestsConfigDeprecation(WarnLevel):
473
+ def code(self) -> str:
474
+ return "D012"
475
+
476
+ def message(self) -> str:
477
+ description = (
478
+ f"The `{self.deprecated_path}` config has been renamed to `{self.exp_path}`. "
479
+ "Please see https://docs.getdbt.com/docs/build/data-tests#new-data_tests-syntax for more information."
480
+ )
481
+
482
+ if require_event_names_in_deprecations():
483
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
484
+ else:
485
+ return line_wrap_message(deprecation_tag(description))
486
+
487
+
488
+ class ProjectFlagsMovedDeprecation(WarnLevel):
489
+ def code(self) -> str:
490
+ return "D013"
491
+
492
+ def message(self) -> str:
493
+ description = (
494
+ "User config should be moved from the 'config' key in profiles.yml to the 'flags' "
495
+ "key in dbt_project.yml."
496
+ )
497
+ # Can't use line_wrap_message here because flags.printer_width isn't available yet
498
+ if require_event_names_in_deprecations():
499
+ return deprecation_tag(description, self.__class__.__name__)
500
+ else:
501
+ return deprecation_tag(description)
502
+
503
+
504
+ class SpacesInResourceNameDeprecation(DynamicLevel):
505
+ def code(self) -> str:
506
+ return "D014"
507
+
508
+ def message(self) -> str:
509
+ description = f"Found spaces in the name of `{self.unique_id}`"
510
+
511
+ if self.level == EventLevel.ERROR.value:
512
+ description = error_tag(description)
513
+ elif self.level == EventLevel.WARN.value:
514
+ description = warning_tag(description)
515
+
516
+ if require_event_names_in_deprecations():
517
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
518
+ else:
519
+ return line_wrap_message(description)
520
+
521
+
522
+ class ResourceNamesWithSpacesDeprecation(WarnLevel):
523
+ def code(self) -> str:
524
+ return "D015"
525
+
526
+ def message(self) -> str:
527
+ description = f"Spaces found in {self.count_invalid_names} resource name(s). This is deprecated, and may lead to errors when using dbt."
528
+
529
+ if self.show_debug_hint:
530
+ description += " Run again with `--debug` to see them all."
531
+
532
+ description += " For more information: https://docs.getdbt.com/reference/global-configs/legacy-behaviors"
533
+
534
+ if require_event_names_in_deprecations():
535
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
536
+ else:
537
+ return line_wrap_message(warning_tag(description))
538
+
539
+
540
+ class PackageMaterializationOverrideDeprecation(WarnLevel):
541
+ def code(self) -> str:
542
+ return "D016"
543
+
544
+ def message(self) -> str:
545
+ description = f"Installed package '{self.package_name}' is overriding the built-in materialization '{self.materialization_name}'. Overrides of built-in materializations from installed packages will be deprecated in future versions of dbt. For more information: https://docs.getdbt.com/reference/global-configs/legacy-behaviors"
546
+
547
+ if require_event_names_in_deprecations():
548
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
549
+ else:
550
+ return line_wrap_message(warning_tag(description))
551
+
552
+
553
+ class SourceFreshnessProjectHooksNotRun(WarnLevel):
554
+ def code(self) -> str:
555
+ return "D017"
556
+
557
+ def message(self) -> str:
558
+ description = "In a future version of dbt, the `source freshness` command will start running `on-run-start` and `on-run-end` hooks by default. For more information: https://docs.getdbt.com/reference/global-configs/legacy-behaviors"
559
+
560
+ if require_event_names_in_deprecations():
561
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
562
+ else:
563
+ return line_wrap_message(warning_tag(description))
564
+
565
+
566
+ class MFTimespineWithoutYamlConfigurationDeprecation(WarnLevel):
567
+ def code(self) -> str:
568
+ return "D018"
569
+
570
+ def message(self) -> str:
571
+ description = "Time spines without YAML configuration are in the process of deprecation. Please add YAML configuration for your 'metricflow_time_spine' model. See documentation on MetricFlow time spines: https://docs.getdbt.com/docs/build/metricflow-time-spine and behavior change documentation: https://docs.getdbt.com/reference/global-configs/behavior-changes."
572
+
573
+ if require_event_names_in_deprecations():
574
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
575
+ else:
576
+ return line_wrap_message(warning_tag(description))
577
+
578
+
579
+ class MFCumulativeTypeParamsDeprecation(WarnLevel):
580
+ def code(self) -> str:
581
+ return "D019"
582
+
583
+ def message(self) -> str:
584
+ description = "Cumulative fields `type_params.window` and `type_params.grain_to_date` have been moved and will soon be deprecated. Please nest those values under `type_params.cumulative_type_params.window` and `type_params.cumulative_type_params.grain_to_date`. See documentation on behavior changes: https://docs.getdbt.com/reference/global-configs/behavior-changes."
585
+
586
+ if require_event_names_in_deprecations():
587
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
588
+ else:
589
+ return line_wrap_message(warning_tag(description))
590
+
591
+
592
+ class MicrobatchMacroOutsideOfBatchesDeprecation(WarnLevel):
593
+ def code(self) -> str:
594
+ return "D020"
595
+
596
+ def message(self) -> str:
597
+ description = "The use of a custom microbatch macro outside of batched execution is deprecated. To use it with batched execution, set `flags.require_batched_execution_for_custom_microbatch_strategy` to `True` in `dbt_project.yml`. In the future this will be the default behavior."
598
+
599
+ if require_event_names_in_deprecations():
600
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
601
+ else:
602
+ return line_wrap_message(warning_tag(description))
603
+
604
+
605
+ # Skipping D021. It belonged to the now deleted PackageRedirectDeprecationSummary event.
606
+
607
+
608
+ class GenericJSONSchemaValidationDeprecation(WarnLevel):
609
+ def code(self) -> str:
610
+ return "D022"
611
+
612
+ def message(self) -> str:
613
+ possible_causes = "This generally means that either we failed to catch this as a more specific deprecation type OR our JSONSchema had a regression (and this deprecation was erroneous)."
614
+
615
+ if self.key_path == "":
616
+ description = f"{self.violation} at top level in file `{self.file}` is possibly a deprecation. {possible_causes}"
617
+ else:
618
+ description = f"{self.violation} in file `{self.file}` at path `{self.key_path}` is possibly a deprecation. {possible_causes}"
619
+
620
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
621
+
622
+
623
+ class UnexpectedJinjaBlockDeprecation(WarnLevel):
624
+ def code(self) -> str:
625
+ return "D023"
626
+
627
+ def message(self) -> str:
628
+ description = f"{self.msg} in file `{self.file}`"
629
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
630
+
631
+
632
+ class DuplicateYAMLKeysDeprecation(WarnLevel):
633
+ def code(self) -> str:
634
+ return "D024"
635
+
636
+ def message(self) -> str:
637
+ description = f"{self.duplicate_description} in file `{self.file}`"
638
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
639
+
640
+
641
+ class CustomTopLevelKeyDeprecation(WarnLevel):
642
+ def code(self) -> str:
643
+ return "D025"
644
+
645
+ def message(self) -> str:
646
+ description = f"{self.msg} in file `{self.file}`"
647
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
648
+
649
+
650
+ class CustomKeyInConfigDeprecation(WarnLevel):
651
+ def code(self) -> str:
652
+ return "D026"
653
+
654
+ def message(self) -> str:
655
+ path_specification = ""
656
+ if self.key_path != "":
657
+ path_specification = f" at path `{self.key_path}`"
658
+
659
+ description = f"Custom key `{self.key}` found in `config`{path_specification} in file `{self.file}`. Custom config keys should move into the `config.meta`."
660
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
661
+
662
+
663
+ class CustomKeyInObjectDeprecation(WarnLevel):
664
+ def code(self) -> str:
665
+ return "D027"
666
+
667
+ def message(self) -> str:
668
+ description = f"Custom key `{self.key}` found at `{self.key_path}` in file `{self.file}`. This may mean the key is a typo, or is simply not a key supported by the object."
669
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
670
+
671
+
672
+ class DeprecationsSummary(WarnLevel):
673
+ def code(self) -> str:
674
+ return "D028"
675
+
676
+ def message(self) -> str:
677
+ description = "Summary of encountered deprecations:"
678
+ for summary in self.summaries:
679
+ description += (
680
+ f"\n\n- {summary.event_name}: {pluralize(summary.occurrences, 'occurrence')}"
681
+ )
682
+
683
+ if self.show_all_hint:
684
+ description += "\n\nTo see all deprecation instances instead of just the first occurrence of each, run command again with the `--show-all-deprecations` flag. You may also need to run with `--no-partial-parse` as some deprecations are only encountered during parsing."
685
+
686
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
687
+
688
+
689
+ class CustomOutputPathInSourceFreshnessDeprecation(WarnLevel):
690
+ def code(self) -> str:
691
+ return "D029"
692
+
693
+ def message(self) -> str:
694
+ description = f"Custom output path usage `--output {self.path}` usage detected in `dbt source freshness` command."
695
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
696
+
697
+
698
+ class PropertyMovedToConfigDeprecation(WarnLevel):
699
+ def code(self) -> str:
700
+ return "D030"
701
+
702
+ def message(self) -> str:
703
+ description = f"Found `{self.key}` as a top-level property of `{self.key_path}` in file `{self.file}`. The `{self.key}` top-level property should be moved into the `config` of `{self.key_path}`."
704
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
705
+
706
+
707
+ class WEOIncludeExcludeDeprecation(WarnLevel):
708
+ def code(self) -> str:
709
+ return "D031"
710
+
711
+ def message(self) -> str:
712
+ found_keys: List[str] = []
713
+ if self.found_include:
714
+ found_keys.append("`include`")
715
+ if self.found_exclude:
716
+ found_keys.append("`exclude`")
717
+
718
+ description = f"Found {' and '.join(found_keys)} in `warn_error_options` specification."
719
+ if self.found_include:
720
+ description += " Please use `error` instead of `include`."
721
+ if self.found_exclude:
722
+ description += " Please use `warn` instead of `exclude`."
723
+
724
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
725
+
726
+
727
+ class ModelParamUsageDeprecation(WarnLevel):
728
+ def code(self) -> str:
729
+ return "D032"
730
+
731
+ def message(self) -> str:
732
+ description = "Usage of `--models`, `--model`, and `-m` is deprecated in favor of `--select` or `-s`."
733
+ return line_wrap_message(deprecation_tag(description))
734
+
735
+
736
+ class ModulesItertoolsUsageDeprecation(WarnLevel):
737
+ def code(self) -> str:
738
+ return "D034"
739
+
740
+ def message(self) -> str:
741
+ description = (
742
+ "Usage of itertools modules is deprecated. Please use the built-in functions instead."
743
+ )
744
+ return line_wrap_message(deprecation_tag(description))
745
+
746
+
747
+ class SourceOverrideDeprecation(WarnLevel):
748
+ def code(self) -> str:
749
+ return "D035"
750
+
751
+ def message(self) -> str:
752
+ description = f"The source property `overrides` is deprecated but was found on source `{self.source_name}` in file `{self.file}`. Instead, `enabled` should be used to disable the unwanted source."
753
+ return line_wrap_message(deprecation_tag(description))
754
+
755
+
756
+ class EnvironmentVariableNamespaceDeprecation(WarnLevel):
757
+ def code(self) -> str:
758
+ return "D036"
759
+
760
+ def message(self) -> str:
761
+ description = f"Found custom environment variable `{self.env_var}` in the environment. The prefix `{self.reserved_prefix}` is reserved for dbt engine environment variables. Custom environment variables with the prefix `{self.reserved_prefix}` may cause collisions and runtime errors."
762
+ return line_wrap_message(deprecation_tag(description))
763
+
764
+
765
+ class MissingPlusPrefixDeprecation(WarnLevel):
766
+ def code(self) -> str:
767
+ return "D037"
768
+
769
+ def message(self) -> str:
770
+ description = f"Missing '+' prefix on `{self.key}` found at `{self.key_path}` in file `{self.file}`. Hierarchical config values without a '+' prefix are deprecated in dbt_project.yml."
771
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
772
+
773
+
774
+ class ArgumentsPropertyInGenericTestDeprecation(WarnLevel):
775
+ def code(self) -> str:
776
+ return "D038"
777
+
778
+ def message(self) -> str:
779
+ description = f"Found `arguments` property in test definition of {self.test_name} without usage of `require_generic_test_arguments_property` behavior change flag. The `arguments` property is deprecated for custom usage and will be used to nest keyword arguments in future versions of dbt."
780
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
781
+
782
+
783
+ class MissingArgumentsPropertyInGenericTestDeprecation(WarnLevel):
784
+ def code(self) -> str:
785
+ return "D039"
786
+
787
+ def message(self) -> str:
788
+ description = f"Found top-level arguments to test {self.test_name}. Arguments to generic tests should be nested under the `arguments` property."
789
+ return line_wrap_message(deprecation_tag(description, self.__class__.__name__))
790
+
791
+
792
+ # =======================================================
793
+ # I - Project parsing
794
+ # =======================================================
795
+
796
+
797
+ class InputFileDiffError(DebugLevel):
798
+ def code(self) -> str:
799
+ return "I001"
800
+
801
+ def message(self) -> str:
802
+ return f"Error processing file diff: {self.category}, {self.file_id}"
803
+
804
+
805
+ # Skipping I003, I004, I005, I006, I007
806
+
807
+
808
+ class InvalidValueForField(WarnLevel):
809
+ def code(self) -> str:
810
+ return "I008"
811
+
812
+ def message(self) -> str:
813
+ return f"Invalid value ({self.field_value}) for field {self.field_name}"
814
+
815
+
816
+ class ValidationWarning(WarnLevel):
817
+ def code(self) -> str:
818
+ return "I009"
819
+
820
+ def message(self) -> str:
821
+ return warning_tag(
822
+ f"Field {self.field_name} is not valid for {self.resource_type} ({self.node_name})"
823
+ )
824
+
825
+
826
+ class ParsePerfInfoPath(InfoLevel):
827
+ def code(self) -> str:
828
+ return "I010"
829
+
830
+ def message(self) -> str:
831
+ return f"Performance info: {self.path}"
832
+
833
+
834
+ # Removed I011: GenericTestFileParse
835
+
836
+
837
+ # Removed I012: MacroFileParse
838
+
839
+
840
+ # Skipping I013
841
+
842
+
843
+ class PartialParsingErrorProcessingFile(DebugLevel):
844
+ def code(self) -> str:
845
+ return "I014"
846
+
847
+ def message(self) -> str:
848
+ return f"Partial parsing exception processing file {self.file}"
849
+
850
+
851
+ # Skipped I015
852
+
853
+
854
+ class PartialParsingError(DebugLevel):
855
+ def code(self) -> str:
856
+ return "I016"
857
+
858
+ def message(self) -> str:
859
+ return f"PP exception info: {self.exc_info}"
860
+
861
+
862
+ class PartialParsingSkipParsing(DebugLevel):
863
+ def code(self) -> str:
864
+ return "I017"
865
+
866
+ def message(self) -> str:
867
+ return "Partial parsing enabled, no changes found, skipping parsing"
868
+
869
+
870
+ # Skipped I018, I019, I020, I021, I022, I023
871
+
872
+
873
+ class UnableToPartialParse(InfoLevel):
874
+ def code(self) -> str:
875
+ return "I024"
876
+
877
+ def message(self) -> str:
878
+ return f"Unable to do partial parsing because {self.reason}"
879
+
880
+
881
+ class StateCheckVarsHash(DebugLevel):
882
+ def code(self) -> str:
883
+ return "I025"
884
+
885
+ def message(self) -> str:
886
+ return f"checksum: {self.checksum}, vars: {self.vars}, profile: {self.profile}, target: {self.target}, version: {self.version}"
887
+
888
+
889
+ # Skipped I025, I026, I026, I027
890
+
891
+
892
+ class PartialParsingNotEnabled(DebugLevel):
893
+ def code(self) -> str:
894
+ return "I028"
895
+
896
+ def message(self) -> str:
897
+ return "Partial parsing not enabled"
898
+
899
+
900
+ class ParsedFileLoadFailed(DebugLevel):
901
+ def code(self) -> str:
902
+ return "I029"
903
+
904
+ def message(self) -> str:
905
+ return f"Failed to load parsed file from disk at {self.path}: {self.exc}"
906
+
907
+
908
+ # Skipped I030-I039
909
+
910
+
911
+ class PartialParsingEnabled(DebugLevel):
912
+ def code(self) -> str:
913
+ return "I040"
914
+
915
+ def message(self) -> str:
916
+ return (
917
+ f"Partial parsing enabled: "
918
+ f"{self.deleted} files deleted, "
919
+ f"{self.added} files added, "
920
+ f"{self.changed} files changed."
921
+ )
922
+
923
+
924
+ class PartialParsingFile(DebugLevel):
925
+ def code(self) -> str:
926
+ return "I041"
927
+
928
+ def message(self) -> str:
929
+ return f"Partial parsing: {self.operation} file: {self.file_id}"
930
+
931
+
932
+ # Skipped I042, I043, I044, I045, I046, I047, I048, I049
933
+
934
+
935
+ class InvalidDisabledTargetInTestNode(DebugLevel):
936
+ def code(self) -> str:
937
+ return "I050"
938
+
939
+ def message(self) -> str:
940
+ target_package_string = ""
941
+
942
+ if self.target_package != target_package_string:
943
+ target_package_string = f"in package '{self.target_package}' "
944
+
945
+ msg = (
946
+ f"{self.resource_type_title} '{self.unique_id}' "
947
+ f"({self.original_file_path}) depends on a {self.target_kind} "
948
+ f"named '{self.target_name}' {target_package_string}which is disabled"
949
+ )
950
+
951
+ return warning_tag(msg)
952
+
953
+
954
+ class UnusedResourceConfigPath(WarnLevel):
955
+ def code(self) -> str:
956
+ return "I051"
957
+
958
+ def message(self) -> str:
959
+ path_list = "\n".join(f"- {u}" for u in self.unused_config_paths)
960
+ msg = (
961
+ "Configuration paths exist in your dbt_project.yml file which do not "
962
+ "apply to any resources.\n"
963
+ f"There are {len(self.unused_config_paths)} unused configuration paths:\n{path_list}"
964
+ )
965
+ return warning_tag(msg)
966
+
967
+
968
+ class SeedIncreased(WarnLevel):
969
+ def code(self) -> str:
970
+ return "I052"
971
+
972
+ def message(self) -> str:
973
+ msg = (
974
+ f"Found a seed ({self.package_name}.{self.name}) "
975
+ f">{MAXIMUM_SEED_SIZE_NAME} in size. The previous file was "
976
+ f"<={MAXIMUM_SEED_SIZE_NAME}, so it has changed"
977
+ )
978
+ return msg
979
+
980
+
981
+ class SeedExceedsLimitSamePath(WarnLevel):
982
+ def code(self) -> str:
983
+ return "I053"
984
+
985
+ def message(self) -> str:
986
+ msg = (
987
+ f"Found a seed ({self.package_name}.{self.name}) "
988
+ f">{MAXIMUM_SEED_SIZE_NAME} in size at the same path, dbt "
989
+ f"cannot tell if it has changed: assuming they are the same"
990
+ )
991
+ return msg
992
+
993
+
994
+ class SeedExceedsLimitAndPathChanged(WarnLevel):
995
+ def code(self) -> str:
996
+ return "I054"
997
+
998
+ def message(self) -> str:
999
+ msg = (
1000
+ f"Found a seed ({self.package_name}.{self.name}) "
1001
+ f">{MAXIMUM_SEED_SIZE_NAME} in size. The previous file was in "
1002
+ f"a different location, assuming it has changed"
1003
+ )
1004
+ return msg
1005
+
1006
+
1007
+ class SeedExceedsLimitChecksumChanged(WarnLevel):
1008
+ def code(self) -> str:
1009
+ return "I055"
1010
+
1011
+ def message(self) -> str:
1012
+ msg = (
1013
+ f"Found a seed ({self.package_name}.{self.name}) "
1014
+ f">{MAXIMUM_SEED_SIZE_NAME} in size. The previous file had a "
1015
+ f"checksum type of {self.checksum_name}, so it has changed"
1016
+ )
1017
+ return msg
1018
+
1019
+
1020
+ class UnusedTables(WarnLevel):
1021
+ def code(self) -> str:
1022
+ return "I056"
1023
+
1024
+ def message(self) -> str:
1025
+ msg = [
1026
+ "During parsing, dbt encountered source overrides that had no target:",
1027
+ ]
1028
+ msg += self.unused_tables
1029
+ msg.append("")
1030
+ return warning_tag("\n".join(msg))
1031
+
1032
+
1033
+ class WrongResourceSchemaFile(WarnLevel):
1034
+ def code(self) -> str:
1035
+ return "I057"
1036
+
1037
+ def message(self) -> str:
1038
+ msg = line_wrap_message(
1039
+ f"""\
1040
+ '{self.patch_name}' is a {self.resource_type} node, but it is
1041
+ specified in the {self.yaml_key} section of
1042
+ {self.file_path}.
1043
+ To fix this error, place the `{self.patch_name}`
1044
+ specification under the {self.plural_resource_type} key instead.
1045
+ """
1046
+ )
1047
+ return warning_tag(msg)
1048
+
1049
+
1050
+ class NoNodeForYamlKey(WarnLevel):
1051
+ def code(self) -> str:
1052
+ return "I058"
1053
+
1054
+ def message(self) -> str:
1055
+ msg = (
1056
+ f"Did not find matching node for patch with name '{self.patch_name}' "
1057
+ f"in the '{self.yaml_key}' section of "
1058
+ f"file '{self.file_path}'"
1059
+ )
1060
+ return warning_tag(msg)
1061
+
1062
+
1063
+ class MacroNotFoundForPatch(WarnLevel):
1064
+ def code(self) -> str:
1065
+ return "I059"
1066
+
1067
+ def message(self) -> str:
1068
+ msg = f'Found patch for macro "{self.patch_name}" which was not found'
1069
+ return warning_tag(msg)
1070
+
1071
+
1072
+ class NodeNotFoundOrDisabled(WarnLevel):
1073
+ def code(self) -> str:
1074
+ return "I060"
1075
+
1076
+ def message(self) -> str:
1077
+ # this is duplicated logic from exceptions.get_not_found_or_disabled_msg
1078
+ # when we convert exceptions to be structured maybe it can be combined?
1079
+ # converting the bool to a string since None is also valid
1080
+ if self.disabled == "None":
1081
+ reason = "was not found or is disabled"
1082
+ elif self.disabled == "True":
1083
+ reason = "is disabled"
1084
+ else:
1085
+ reason = "was not found"
1086
+
1087
+ target_package_string = ""
1088
+
1089
+ if self.target_package is not None:
1090
+ target_package_string = f"in package '{self.target_package}' "
1091
+
1092
+ msg = (
1093
+ f"{self.resource_type_title} '{self.unique_id}' "
1094
+ f"({self.original_file_path}) depends on a {self.target_kind} "
1095
+ f"named '{self.target_name}' {target_package_string}which {reason}"
1096
+ )
1097
+
1098
+ return warning_tag(msg)
1099
+
1100
+
1101
+ class JinjaLogWarning(WarnLevel):
1102
+ def code(self) -> str:
1103
+ return "I061"
1104
+
1105
+ def message(self) -> str:
1106
+ return self.msg
1107
+
1108
+
1109
+ class JinjaLogInfo(InfoLevel):
1110
+ def code(self) -> str:
1111
+ return "I062"
1112
+
1113
+ def message(self) -> str:
1114
+ # This is for the log method used in macros so msg cannot be built here
1115
+ return self.msg
1116
+
1117
+
1118
+ class JinjaLogDebug(DebugLevel):
1119
+ def code(self) -> str:
1120
+ return "I063"
1121
+
1122
+ def message(self) -> str:
1123
+ # This is for the log method used in macros so msg cannot be built here
1124
+ return self.msg
1125
+
1126
+
1127
+ class UnpinnedRefNewVersionAvailable(InfoLevel):
1128
+ def code(self) -> str:
1129
+ return "I064"
1130
+
1131
+ def message(self) -> str:
1132
+ msg = (
1133
+ f"While compiling '{self.node_info.node_name}':\n"
1134
+ f"Found an unpinned reference to versioned model '{self.ref_node_name}' in project '{self.ref_node_package}'.\n"
1135
+ f"Resolving to latest version: {self.ref_node_name}.v{self.ref_node_version}\n"
1136
+ f"A prerelease version {self.ref_max_version} is available. It has not yet been marked 'latest' by its maintainer.\n"
1137
+ f"When that happens, this reference will resolve to {self.ref_node_name}.v{self.ref_max_version} instead.\n\n"
1138
+ f" Try out v{self.ref_max_version}: {{{{ ref('{self.ref_node_package}', '{self.ref_node_name}', v='{self.ref_max_version}') }}}}\n"
1139
+ f" Pin to v{self.ref_node_version}: {{{{ ref('{self.ref_node_package}', '{self.ref_node_name}', v='{self.ref_node_version}') }}}}\n"
1140
+ )
1141
+ return msg
1142
+
1143
+
1144
+ class UpcomingReferenceDeprecation(WarnLevel):
1145
+ def code(self) -> str:
1146
+ return "I066"
1147
+
1148
+ def message(self) -> str:
1149
+ ref_model_version = ".v" + self.ref_model_version if self.ref_model_version else ""
1150
+ msg = (
1151
+ f"While compiling '{self.model_name}': Found a reference to {self.ref_model_name}{ref_model_version}, "
1152
+ f"which is slated for deprecation on '{self.ref_model_deprecation_date}'. "
1153
+ )
1154
+
1155
+ if self.ref_model_version and self.ref_model_version != self.ref_model_latest_version:
1156
+ coda = (
1157
+ f"A new version of '{self.ref_model_name}' is available. Try it out: "
1158
+ f"{{{{ ref('{self.ref_model_package}', '{self.ref_model_name}', "
1159
+ f"v='{self.ref_model_latest_version}') }}}}."
1160
+ )
1161
+ msg = msg + coda
1162
+
1163
+ return warning_tag(msg)
1164
+
1165
+
1166
+ class DeprecatedReference(WarnLevel):
1167
+ def code(self) -> str:
1168
+ return "I067"
1169
+
1170
+ def message(self) -> str:
1171
+ ref_model_version = ".v" + self.ref_model_version if self.ref_model_version else ""
1172
+ msg = (
1173
+ f"While compiling '{self.model_name}': Found a reference to {self.ref_model_name}{ref_model_version}, "
1174
+ f"which was deprecated on '{self.ref_model_deprecation_date}'. "
1175
+ )
1176
+
1177
+ if self.ref_model_version and self.ref_model_version != self.ref_model_latest_version:
1178
+ coda = (
1179
+ f"A new version of '{self.ref_model_name}' is available. Migrate now: "
1180
+ f"{{{{ ref('{self.ref_model_package}', '{self.ref_model_name}', "
1181
+ f"v='{self.ref_model_latest_version}') }}}}."
1182
+ )
1183
+ msg = msg + coda
1184
+
1185
+ return warning_tag(msg)
1186
+
1187
+
1188
+ class UnsupportedConstraintMaterialization(WarnLevel):
1189
+ def code(self) -> str:
1190
+ return "I068"
1191
+
1192
+ def message(self) -> str:
1193
+ msg = (
1194
+ f"Constraint types are not supported for {self.materialized} materializations and will "
1195
+ "be ignored. Set 'warn_unsupported: false' on this constraint to ignore this warning."
1196
+ )
1197
+
1198
+ return line_wrap_message(warning_tag(msg))
1199
+
1200
+
1201
+ class ParseInlineNodeError(ErrorLevel):
1202
+ def code(self) -> str:
1203
+ return "I069"
1204
+
1205
+ def message(self) -> str:
1206
+ return "Error while parsing node: " + self.node_info.node_name + "\n" + self.exc
1207
+
1208
+
1209
+ class SemanticValidationFailure(WarnLevel):
1210
+ def code(self) -> str:
1211
+ return "I070"
1212
+
1213
+ def message(self) -> str:
1214
+ return self.msg
1215
+
1216
+
1217
+ class UnversionedBreakingChange(WarnLevel):
1218
+ def code(self) -> str:
1219
+ return "I071"
1220
+
1221
+ def message(self) -> str:
1222
+ reasons = "\n - ".join(self.breaking_changes)
1223
+
1224
+ msg = (
1225
+ f"Breaking change to contracted, unversioned model {self.model_name} ({self.model_file_path})"
1226
+ "\nWhile comparing to previous project state, dbt detected a breaking change to an unversioned model."
1227
+ f"\n - {reasons}\n"
1228
+ )
1229
+
1230
+ return warning_tag(msg)
1231
+
1232
+
1233
+ class WarnStateTargetEqual(WarnLevel):
1234
+ def code(self) -> str:
1235
+ return "I072"
1236
+
1237
+ def message(self) -> str:
1238
+ return yellow(
1239
+ f"Warning: The state and target directories are the same: '{self.state_path}'. "
1240
+ f"This could lead to missing changes due to overwritten state including non-idempotent retries."
1241
+ )
1242
+
1243
+
1244
+ class FreshnessConfigProblem(WarnLevel):
1245
+ def code(self) -> str:
1246
+ return "I073"
1247
+
1248
+ def message(self) -> str:
1249
+ return self.msg
1250
+
1251
+
1252
+ class MicrobatchModelNoEventTimeInputs(WarnLevel):
1253
+ def code(self) -> str:
1254
+ return "I074"
1255
+
1256
+ def message(self) -> str:
1257
+ msg = (
1258
+ f"The microbatch model '{self.model_name}' has no 'ref' or 'source' input with an 'event_time' configuration. "
1259
+ "\nThis means no filtering can be applied and can result in unexpected duplicate records in the resulting microbatch model."
1260
+ )
1261
+
1262
+ return warning_tag(msg)
1263
+
1264
+
1265
+ class InvalidConcurrentBatchesConfig(WarnLevel):
1266
+ def code(self) -> str:
1267
+ return "I075"
1268
+
1269
+ def message(self) -> str:
1270
+ maybe_plural_count_of_models = pluralize(self.num_models, "microbatch model")
1271
+ description = f"Found {maybe_plural_count_of_models} with the `concurrent_batches` config set to true, but the {self.adapter_type} adapter does not support running batches concurrently. Batches will be run sequentially."
1272
+ return line_wrap_message(warning_tag(description))
1273
+
1274
+
1275
+ class InvalidMacroAnnotation(WarnLevel):
1276
+ def code(self) -> str:
1277
+ return "I076"
1278
+
1279
+ def message(self) -> str:
1280
+ return self.msg
1281
+
1282
+
1283
+ # =======================================================
1284
+ # M - Deps generation
1285
+ # =======================================================
1286
+
1287
+
1288
+ class GitSparseCheckoutSubdirectory(DebugLevel):
1289
+ def code(self) -> str:
1290
+ return "M001"
1291
+
1292
+ def message(self) -> str:
1293
+ return f"Subdirectory specified: {self.subdir}, using sparse checkout."
1294
+
1295
+
1296
+ class GitProgressCheckoutRevision(DebugLevel):
1297
+ def code(self) -> str:
1298
+ return "M002"
1299
+
1300
+ def message(self) -> str:
1301
+ return f"Checking out revision {self.revision}."
1302
+
1303
+
1304
+ class GitProgressUpdatingExistingDependency(DebugLevel):
1305
+ def code(self) -> str:
1306
+ return "M003"
1307
+
1308
+ def message(self) -> str:
1309
+ return f"Updating existing dependency {self.dir}."
1310
+
1311
+
1312
+ class GitProgressPullingNewDependency(DebugLevel):
1313
+ def code(self) -> str:
1314
+ return "M004"
1315
+
1316
+ def message(self) -> str:
1317
+ return f"Pulling new dependency {self.dir}."
1318
+
1319
+
1320
+ class GitNothingToDo(DebugLevel):
1321
+ def code(self) -> str:
1322
+ return "M005"
1323
+
1324
+ def message(self) -> str:
1325
+ return f"Already at {self.sha}, nothing to do."
1326
+
1327
+
1328
+ class GitProgressUpdatedCheckoutRange(DebugLevel):
1329
+ def code(self) -> str:
1330
+ return "M006"
1331
+
1332
+ def message(self) -> str:
1333
+ return f"Updated checkout from {self.start_sha} to {self.end_sha}."
1334
+
1335
+
1336
+ class GitProgressCheckedOutAt(DebugLevel):
1337
+ def code(self) -> str:
1338
+ return "M007"
1339
+
1340
+ def message(self) -> str:
1341
+ return f"Checked out at {self.end_sha}."
1342
+
1343
+
1344
+ class RegistryProgressGETRequest(DebugLevel):
1345
+ def code(self) -> str:
1346
+ return "M008"
1347
+
1348
+ def message(self) -> str:
1349
+ return f"Making package registry request: GET {self.url}"
1350
+
1351
+
1352
+ class RegistryProgressGETResponse(DebugLevel):
1353
+ def code(self) -> str:
1354
+ return "M009"
1355
+
1356
+ def message(self) -> str:
1357
+ return f"Response from registry: GET {self.url} {self.resp_code}"
1358
+
1359
+
1360
+ class SelectorReportInvalidSelector(InfoLevel):
1361
+ def code(self) -> str:
1362
+ return "M010"
1363
+
1364
+ def message(self) -> str:
1365
+ return (
1366
+ f"The '{self.spec_method}' selector specified in {self.raw_spec} is "
1367
+ f"invalid. Must be one of [{self.valid_selectors}]"
1368
+ )
1369
+
1370
+
1371
+ class DepsNoPackagesFound(InfoLevel):
1372
+ def code(self) -> str:
1373
+ return "M013"
1374
+
1375
+ def message(self) -> str:
1376
+ return "Warning: No packages were found in packages.yml"
1377
+
1378
+
1379
+ class DepsStartPackageInstall(InfoLevel):
1380
+ def code(self) -> str:
1381
+ return "M014"
1382
+
1383
+ def message(self) -> str:
1384
+ return f"Installing {self.package_name}"
1385
+
1386
+
1387
+ class DepsInstallInfo(InfoLevel):
1388
+ def code(self) -> str:
1389
+ return "M015"
1390
+
1391
+ def message(self) -> str:
1392
+ return f"Installed from {self.version_name}"
1393
+
1394
+
1395
+ class DepsUpdateAvailable(InfoLevel):
1396
+ def code(self) -> str:
1397
+ return "M016"
1398
+
1399
+ def message(self) -> str:
1400
+ return f"Updated version available: {self.version_latest}"
1401
+
1402
+
1403
+ class DepsUpToDate(InfoLevel):
1404
+ def code(self) -> str:
1405
+ return "M017"
1406
+
1407
+ def message(self) -> str:
1408
+ return "Up to date!"
1409
+
1410
+
1411
+ class DepsListSubdirectory(InfoLevel):
1412
+ def code(self) -> str:
1413
+ return "M018"
1414
+
1415
+ def message(self) -> str:
1416
+ return f"and subdirectory {self.subdirectory}"
1417
+
1418
+
1419
+ class DepsNotifyUpdatesAvailable(InfoLevel):
1420
+ def code(self) -> str:
1421
+ return "M019"
1422
+
1423
+ def message(self) -> str:
1424
+ return f"Updates available for packages: {self.packages} \
1425
+ \nUpdate your versions in packages.yml, then run dbt deps"
1426
+
1427
+
1428
+ class RegistryIndexProgressGETRequest(DebugLevel):
1429
+ def code(self) -> str:
1430
+ return "M022"
1431
+
1432
+ def message(self) -> str:
1433
+ return f"Making package index registry request: GET {self.url}"
1434
+
1435
+
1436
+ class RegistryIndexProgressGETResponse(DebugLevel):
1437
+ def code(self) -> str:
1438
+ return "M023"
1439
+
1440
+ def message(self) -> str:
1441
+ return f"Response from registry index: GET {self.url} {self.resp_code}"
1442
+
1443
+
1444
+ class RegistryResponseUnexpectedType(DebugLevel):
1445
+ def code(self) -> str:
1446
+ return "M024"
1447
+
1448
+ def message(self) -> str:
1449
+ return f"Response was None: {self.response}"
1450
+
1451
+
1452
+ class RegistryResponseMissingTopKeys(DebugLevel):
1453
+ def code(self) -> str:
1454
+ return "M025"
1455
+
1456
+ def message(self) -> str:
1457
+ # expected/actual keys logged in exception
1458
+ return f"Response missing top level keys: {self.response}"
1459
+
1460
+
1461
+ class RegistryResponseMissingNestedKeys(DebugLevel):
1462
+ def code(self) -> str:
1463
+ return "M026"
1464
+
1465
+ def message(self) -> str:
1466
+ # expected/actual keys logged in exception
1467
+ return f"Response missing nested keys: {self.response}"
1468
+
1469
+
1470
+ class RegistryResponseExtraNestedKeys(DebugLevel):
1471
+ def code(self) -> str:
1472
+ return "M027"
1473
+
1474
+ def message(self) -> str:
1475
+ # expected/actual keys logged in exception
1476
+ return f"Response contained inconsistent keys: {self.response}"
1477
+
1478
+
1479
+ class DepsSetDownloadDirectory(DebugLevel):
1480
+ def code(self) -> str:
1481
+ return "M028"
1482
+
1483
+ def message(self) -> str:
1484
+ return f"Set downloads directory='{self.path}'"
1485
+
1486
+
1487
+ class DepsUnpinned(WarnLevel):
1488
+ def code(self) -> str:
1489
+ return "M029"
1490
+
1491
+ def message(self) -> str:
1492
+ if self.revision == "HEAD":
1493
+ unpinned_msg = "not pinned, using HEAD (default branch)"
1494
+ elif self.revision in ("main", "master"):
1495
+ unpinned_msg = f'pinned to the "{self.revision}" branch'
1496
+ else:
1497
+ unpinned_msg = None
1498
+
1499
+ msg = (
1500
+ f'The git package "{self.git}" \n\tis {unpinned_msg}.\n\tThis can introduce '
1501
+ f"breaking changes into your project without warning!\n\nSee {PIN_PACKAGE_URL}"
1502
+ )
1503
+ return yellow(f"WARNING: {msg}")
1504
+
1505
+
1506
+ class NoNodesForSelectionCriteria(WarnLevel):
1507
+ def code(self) -> str:
1508
+ return "M030"
1509
+
1510
+ def message(self) -> str:
1511
+ return f"The selection criterion '{self.spec_raw}' does not match any enabled nodes"
1512
+
1513
+
1514
+ class DepsLockUpdating(InfoLevel):
1515
+ def code(self):
1516
+ return "M031"
1517
+
1518
+ def message(self) -> str:
1519
+ return f"Updating lock file in file path: {self.lock_filepath}"
1520
+
1521
+
1522
+ class DepsAddPackage(InfoLevel):
1523
+ def code(self):
1524
+ return "M032"
1525
+
1526
+ def message(self) -> str:
1527
+ return f"Added new package {self.package_name}@{self.version} to {self.packages_filepath}"
1528
+
1529
+
1530
+ class DepsFoundDuplicatePackage(InfoLevel):
1531
+ def code(self):
1532
+ return "M033"
1533
+
1534
+ def message(self) -> str:
1535
+ return f"Found duplicate package in packages.yml, removing: {self.removed_package}"
1536
+
1537
+
1538
+ class DepsScrubbedPackageName(WarnLevel):
1539
+ def code(self):
1540
+ return "M035"
1541
+
1542
+ def message(self) -> str:
1543
+ return f"Detected secret env var in {self.package_name}. dbt will write a scrubbed representation to the lock file. This will cause issues with subsequent 'dbt deps' using the lock file, requiring 'dbt deps --upgrade'"
1544
+
1545
+
1546
+ # =======================================================
1547
+ # P - Artifacts
1548
+ # =======================================================
1549
+
1550
+
1551
+ class ArtifactWritten(DebugLevel):
1552
+ def code(self):
1553
+ return "P001"
1554
+
1555
+ def message(self) -> str:
1556
+ return f"Wrote artifact {self.artifact_type} to {self.artifact_path}"
1557
+
1558
+
1559
+ # =======================================================
1560
+ # Q - Node execution
1561
+ # =======================================================
1562
+
1563
+
1564
+ class RunningOperationCaughtError(ErrorLevel):
1565
+ def code(self) -> str:
1566
+ return "Q001"
1567
+
1568
+ def message(self) -> str:
1569
+ return f"Encountered an error while running operation: {self.exc}"
1570
+
1571
+
1572
+ class CompileComplete(InfoLevel):
1573
+ def code(self) -> str:
1574
+ return "Q002"
1575
+
1576
+ def message(self) -> str:
1577
+ return "Done."
1578
+
1579
+
1580
+ class FreshnessCheckComplete(InfoLevel):
1581
+ def code(self) -> str:
1582
+ return "Q003"
1583
+
1584
+ def message(self) -> str:
1585
+ return "Done."
1586
+
1587
+
1588
+ class SeedHeader(InfoLevel):
1589
+ def code(self) -> str:
1590
+ return "Q004"
1591
+
1592
+ def message(self) -> str:
1593
+ return self.header
1594
+
1595
+
1596
+ class SQLRunnerException(DebugLevel):
1597
+ def code(self) -> str:
1598
+ return "Q006"
1599
+
1600
+ def message(self) -> str:
1601
+ return f"Got an exception: {self.exc}"
1602
+
1603
+
1604
+ class LogTestResult(DynamicLevel):
1605
+ def code(self) -> str:
1606
+ return "Q007"
1607
+
1608
+ def message(self) -> str:
1609
+ if self.status == "error":
1610
+ info = "ERROR"
1611
+ status = red(
1612
+ info,
1613
+ )
1614
+ elif self.status == "pass":
1615
+ info = "PASS"
1616
+ status = green(info)
1617
+ elif self.status == "warn":
1618
+ info = f"WARN {self.num_failures}"
1619
+ status = yellow(info)
1620
+ else: # self.status == "fail":
1621
+ info = f"FAIL {self.num_failures}"
1622
+ status = red(info)
1623
+ msg = f"{info} {self.name}"
1624
+
1625
+ return format_fancy_output_line(
1626
+ msg=msg,
1627
+ status=status,
1628
+ index=self.index,
1629
+ total=self.num_models,
1630
+ execution_time=self.execution_time,
1631
+ )
1632
+
1633
+ @classmethod
1634
+ def status_to_level(cls, status):
1635
+ # The statuses come from TestStatus
1636
+ level_lookup = {
1637
+ "fail": EventLevel.ERROR,
1638
+ "pass": EventLevel.INFO,
1639
+ "warn": EventLevel.WARN,
1640
+ "error": EventLevel.ERROR,
1641
+ }
1642
+ if status in level_lookup:
1643
+ return level_lookup[status]
1644
+ else:
1645
+ return EventLevel.INFO
1646
+
1647
+
1648
+ class LogNodeResult(DynamicLevel):
1649
+ def code(self) -> str:
1650
+ return "Q008"
1651
+
1652
+ def message(self) -> str:
1653
+ return self.msg
1654
+
1655
+
1656
+ # Skipped Q009, Q010
1657
+
1658
+
1659
+ class LogStartLine(InfoLevel):
1660
+ def code(self) -> str:
1661
+ return "Q011"
1662
+
1663
+ def message(self) -> str:
1664
+ msg = f"START {self.description}"
1665
+ return format_fancy_output_line(msg=msg, status="RUN", index=self.index, total=self.total)
1666
+
1667
+
1668
+ class LogModelResult(DynamicLevel):
1669
+ def code(self) -> str:
1670
+ return "Q012"
1671
+
1672
+ def message(self) -> str:
1673
+ if self.status == "error":
1674
+ info = "ERROR creating"
1675
+ status = red(self.status.upper())
1676
+ elif "PARTIAL SUCCESS" in self.status:
1677
+ info = "PARTIALLY created"
1678
+ status = yellow(self.status.upper())
1679
+ else:
1680
+ info = "OK created"
1681
+ status = green(self.status)
1682
+
1683
+ msg = f"{info} {self.description}"
1684
+ return format_fancy_output_line(
1685
+ msg=msg,
1686
+ status=status,
1687
+ index=self.index,
1688
+ total=self.total,
1689
+ execution_time=self.execution_time,
1690
+ )
1691
+
1692
+
1693
+ # Skipped Q013, Q014
1694
+
1695
+
1696
+ class LogSnapshotResult(DynamicLevel):
1697
+ def code(self) -> str:
1698
+ return "Q015"
1699
+
1700
+ def message(self) -> str:
1701
+ if self.status == "error":
1702
+ info = "ERROR snapshotting"
1703
+ status = red(self.status.upper())
1704
+ else:
1705
+ info = "OK snapshotted"
1706
+ status = green(self.result_message)
1707
+
1708
+ msg = "{info} {description}".format(info=info, description=self.description, **self.cfg)
1709
+ return format_fancy_output_line(
1710
+ msg=msg,
1711
+ status=status,
1712
+ index=self.index,
1713
+ total=self.total,
1714
+ execution_time=self.execution_time,
1715
+ )
1716
+
1717
+
1718
+ class LogSeedResult(DynamicLevel):
1719
+ def code(self) -> str:
1720
+ return "Q016"
1721
+
1722
+ def message(self) -> str:
1723
+ if self.status == "error":
1724
+ info = "ERROR loading"
1725
+ status = red(self.status.upper())
1726
+ else:
1727
+ info = "OK loaded"
1728
+ status = green(self.result_message)
1729
+ msg = f"{info} seed file {self.schema}.{self.relation}"
1730
+ return format_fancy_output_line(
1731
+ msg=msg,
1732
+ status=status,
1733
+ index=self.index,
1734
+ total=self.total,
1735
+ execution_time=self.execution_time,
1736
+ )
1737
+
1738
+
1739
+ # Skipped Q017
1740
+
1741
+
1742
+ class LogFreshnessResult(DynamicLevel):
1743
+ def code(self) -> str:
1744
+ return "Q018"
1745
+
1746
+ def message(self) -> str:
1747
+ if self.status == "runtime error":
1748
+ info = "ERROR"
1749
+ status = red(info)
1750
+ elif self.status == "error":
1751
+ info = "ERROR STALE"
1752
+ status = red(info)
1753
+ elif self.status == "warn":
1754
+ info = "WARN"
1755
+ status = yellow(info)
1756
+ else:
1757
+ info = "PASS"
1758
+ status = green(info)
1759
+ msg = f"{info} freshness of {self.source_name}.{self.table_name}"
1760
+ return format_fancy_output_line(
1761
+ msg=msg,
1762
+ status=status,
1763
+ index=self.index,
1764
+ total=self.total,
1765
+ execution_time=self.execution_time,
1766
+ )
1767
+
1768
+ @classmethod
1769
+ def status_to_level(cls, status):
1770
+ # The statuses come from FreshnessStatus
1771
+ # TODO should this return EventLevel enum instead?
1772
+ level_lookup = {
1773
+ "runtime error": EventLevel.ERROR,
1774
+ "pass": EventLevel.INFO,
1775
+ "warn": EventLevel.WARN,
1776
+ "error": EventLevel.ERROR,
1777
+ }
1778
+ if status in level_lookup:
1779
+ return level_lookup[status]
1780
+ else:
1781
+ return EventLevel.INFO
1782
+
1783
+
1784
+ class LogNodeNoOpResult(InfoLevel):
1785
+ def code(self) -> str:
1786
+ return "Q019"
1787
+
1788
+ def message(self) -> str:
1789
+ msg = f"NO-OP {self.description}"
1790
+ return format_fancy_output_line(
1791
+ msg=msg,
1792
+ status=yellow("NO-OP"),
1793
+ index=self.index,
1794
+ total=self.total,
1795
+ execution_time=self.execution_time,
1796
+ )
1797
+
1798
+
1799
+ # Skipped Q020, Q021
1800
+
1801
+
1802
+ class LogCancelLine(ErrorLevel):
1803
+ def code(self) -> str:
1804
+ return "Q022"
1805
+
1806
+ def message(self) -> str:
1807
+ msg = f"CANCEL query {self.conn_name}"
1808
+ return format_fancy_output_line(msg=msg, status=red("CANCEL"), index=None, total=None)
1809
+
1810
+
1811
+ class DefaultSelector(InfoLevel):
1812
+ def code(self) -> str:
1813
+ return "Q023"
1814
+
1815
+ def message(self) -> str:
1816
+ return f"Using default selector {self.name}"
1817
+
1818
+
1819
+ class NodeStart(DebugLevel):
1820
+ def code(self) -> str:
1821
+ return "Q024"
1822
+
1823
+ def message(self) -> str:
1824
+ return f"Began running node {self.node_info.unique_id}"
1825
+
1826
+
1827
+ class NodeFinished(DebugLevel):
1828
+ def code(self) -> str:
1829
+ return "Q025"
1830
+
1831
+ def message(self) -> str:
1832
+ return f"Finished running node {self.node_info.unique_id}"
1833
+
1834
+
1835
+ class QueryCancelationUnsupported(InfoLevel):
1836
+ def code(self) -> str:
1837
+ return "Q026"
1838
+
1839
+ def message(self) -> str:
1840
+ msg = (
1841
+ f"The {self.type} adapter does not support query "
1842
+ "cancellation. Some queries may still be "
1843
+ "running!"
1844
+ )
1845
+ return yellow(msg)
1846
+
1847
+
1848
+ class ConcurrencyLine(InfoLevel):
1849
+ def code(self) -> str:
1850
+ return "Q027"
1851
+
1852
+ def message(self) -> str:
1853
+ return f"Concurrency: {self.num_threads} threads (target='{self.target_name}')"
1854
+
1855
+
1856
+ class WritingInjectedSQLForNode(DebugLevel):
1857
+ def code(self) -> str:
1858
+ return "Q029"
1859
+
1860
+ def message(self) -> str:
1861
+ return f'Writing injected SQL for node "{self.node_info.unique_id}"'
1862
+
1863
+
1864
+ class NodeCompiling(DebugLevel):
1865
+ def code(self) -> str:
1866
+ return "Q030"
1867
+
1868
+ def message(self) -> str:
1869
+ return f"Began compiling node {self.node_info.unique_id}"
1870
+
1871
+
1872
+ class NodeExecuting(DebugLevel):
1873
+ def code(self) -> str:
1874
+ return "Q031"
1875
+
1876
+ def message(self) -> str:
1877
+ return f"Began executing node {self.node_info.unique_id}"
1878
+
1879
+
1880
+ class LogHookStartLine(InfoLevel):
1881
+ def code(self) -> str:
1882
+ return "Q032"
1883
+
1884
+ def message(self) -> str:
1885
+ msg = f"START hook: {self.statement}"
1886
+ return format_fancy_output_line(
1887
+ msg=msg, status="RUN", index=self.index, total=self.total, truncate=True
1888
+ )
1889
+
1890
+
1891
+ class LogHookEndLine(InfoLevel):
1892
+ def code(self) -> str:
1893
+ return "Q033"
1894
+
1895
+ def message(self) -> str:
1896
+ if self.status == "success":
1897
+ info = "OK"
1898
+ status = green(info)
1899
+ elif self.status == "skipped":
1900
+ info = "SKIP"
1901
+ status = yellow(info)
1902
+ else:
1903
+ info = "ERROR"
1904
+ status = red(info)
1905
+ msg = f"{info} hook: {self.statement}"
1906
+
1907
+ return format_fancy_output_line(
1908
+ msg=msg,
1909
+ status=status,
1910
+ index=self.index,
1911
+ total=self.total,
1912
+ execution_time=self.execution_time,
1913
+ truncate=True,
1914
+ )
1915
+
1916
+
1917
+ class SkippingDetails(InfoLevel):
1918
+ def code(self) -> str:
1919
+ return "Q034"
1920
+
1921
+ def message(self) -> str:
1922
+ # ToDo: move to core or figure out NodeType
1923
+ if self.resource_type in ["model", "seed", "snapshot"]:
1924
+ msg = f"SKIP relation {self.schema}.{self.node_name}"
1925
+ else:
1926
+ msg = f"SKIP {self.resource_type} {self.node_name}"
1927
+ return format_fancy_output_line(
1928
+ msg=msg, status=yellow("SKIP"), index=self.index, total=self.total
1929
+ )
1930
+
1931
+
1932
+ class NothingToDo(WarnLevel):
1933
+ def code(self) -> str:
1934
+ return "Q035"
1935
+
1936
+ def message(self) -> str:
1937
+ return "Nothing to do. Try checking your model configs and model specification args"
1938
+
1939
+
1940
+ class RunningOperationUncaughtError(ErrorLevel):
1941
+ def code(self) -> str:
1942
+ return "Q036"
1943
+
1944
+ def message(self) -> str:
1945
+ return f"Encountered an error while running operation: {self.exc}"
1946
+
1947
+
1948
+ class EndRunResult(DebugLevel):
1949
+ def code(self) -> str:
1950
+ return "Q037"
1951
+
1952
+ def message(self) -> str:
1953
+ return "Command end result"
1954
+
1955
+
1956
+ class NoNodesSelected(WarnLevel):
1957
+ def code(self) -> str:
1958
+ return "Q038"
1959
+
1960
+ def message(self) -> str:
1961
+ return "No nodes selected!"
1962
+
1963
+
1964
+ class CommandCompleted(DebugLevel):
1965
+ def code(self) -> str:
1966
+ return "Q039"
1967
+
1968
+ def message(self) -> str:
1969
+ status = "succeeded" if self.success else "failed"
1970
+ completed_at = timestamp_to_datetime_string(self.completed_at)
1971
+ return f"Command `{self.command}` {status} at {completed_at} after {self.elapsed:0.2f} seconds"
1972
+
1973
+
1974
+ class ShowNode(InfoLevel):
1975
+ def code(self) -> str:
1976
+ return "Q041"
1977
+
1978
+ def message(self) -> str:
1979
+ if self.output_format == "json":
1980
+ if self.is_inline:
1981
+ return json.dumps({"show": json.loads(self.preview)}, indent=2)
1982
+ else:
1983
+ return json.dumps(
1984
+ {"node": self.node_name, "show": json.loads(self.preview)}, indent=2
1985
+ )
1986
+ else:
1987
+ if self.quiet:
1988
+ return self.preview
1989
+ elif self.is_inline:
1990
+ return f"Previewing inline node:\n{self.preview}"
1991
+ else:
1992
+ return f"Previewing node '{self.node_name}':\n{self.preview}"
1993
+
1994
+
1995
+ class CompiledNode(InfoLevel):
1996
+ def code(self) -> str:
1997
+ return "Q042"
1998
+
1999
+ def message(self) -> str:
2000
+ if self.output_format == "json":
2001
+ if self.is_inline:
2002
+ return json.dumps({"compiled": self.compiled}, indent=2)
2003
+ else:
2004
+ return json.dumps({"node": self.node_name, "compiled": self.compiled}, indent=2)
2005
+ else:
2006
+ if self.quiet:
2007
+ return self.compiled
2008
+ elif self.is_inline:
2009
+ return f"Compiled inline node is:\n{self.compiled}"
2010
+ else:
2011
+ return f"Compiled node '{self.node_name}' is:\n{self.compiled}"
2012
+
2013
+
2014
+ class SnapshotTimestampWarning(WarnLevel):
2015
+ def code(self) -> str:
2016
+ return "Q043"
2017
+
2018
+ def message(self) -> str:
2019
+ return (
2020
+ f"Data type of snapshot table timestamp columns ({self.snapshot_time_data_type}) "
2021
+ f"doesn't match derived column 'updated_at' ({self.updated_at_data_type}). "
2022
+ "Please update snapshot config 'updated_at'."
2023
+ )
2024
+
2025
+
2026
+ class MicrobatchExecutionDebug(DebugLevel):
2027
+ def code(self) -> str:
2028
+ return "Q044"
2029
+
2030
+ def message(self) -> str:
2031
+ return self.msg
2032
+
2033
+
2034
+ class LogStartBatch(InfoLevel):
2035
+ def code(self) -> str:
2036
+ return "Q045"
2037
+
2038
+ def message(self) -> str:
2039
+ msg = f"START {self.description}"
2040
+
2041
+ # TODO update common so that we can append "batch" in `format_fancy_output_line`
2042
+ formatted = format_fancy_output_line(
2043
+ msg=msg,
2044
+ status="RUN",
2045
+ index=self.batch_index,
2046
+ total=self.total_batches,
2047
+ )
2048
+ return f"Batch {formatted}"
2049
+
2050
+
2051
+ class LogBatchResult(DynamicLevel):
2052
+ def code(self) -> str:
2053
+ return "Q046"
2054
+
2055
+ def message(self) -> str:
2056
+ if self.status == "error":
2057
+ info = "ERROR creating"
2058
+ status = red(self.status.upper())
2059
+ elif self.status == "skipped":
2060
+ info = "SKIP"
2061
+ status = yellow(self.status.upper())
2062
+ else:
2063
+ info = "OK created"
2064
+ status = green(self.status)
2065
+
2066
+ msg = f"{info} {self.description}"
2067
+
2068
+ # TODO update common so that we can append "batch" in `format_fancy_output_line`
2069
+ formatted = format_fancy_output_line(
2070
+ msg=msg,
2071
+ status=status,
2072
+ index=self.batch_index,
2073
+ total=self.total_batches,
2074
+ execution_time=self.execution_time,
2075
+ )
2076
+ return f"Batch {formatted}"
2077
+
2078
+
2079
+ class LogFunctionResult(DynamicLevel):
2080
+ def code(self) -> str:
2081
+ return "Q047"
2082
+
2083
+ def message(self) -> str:
2084
+ if self.status == "error":
2085
+ info = "ERROR creating"
2086
+ status = red(self.status.upper())
2087
+ elif self.status == "skipped":
2088
+ info = "SKIP"
2089
+ status = yellow(self.status.upper())
2090
+ else:
2091
+ info = "OK created"
2092
+ status = green(self.status.upper())
2093
+
2094
+ msg = f"{info} {self.description}"
2095
+ return format_fancy_output_line(
2096
+ msg=msg,
2097
+ status=status,
2098
+ index=self.index,
2099
+ total=self.total,
2100
+ execution_time=self.execution_time,
2101
+ )
2102
+
2103
+
2104
+ # =======================================================
2105
+ # W - Node testing
2106
+ # =======================================================
2107
+
2108
+ # Skipped W001
2109
+
2110
+
2111
+ class CatchableExceptionOnRun(DebugLevel):
2112
+ def code(self) -> str:
2113
+ return "W002"
2114
+
2115
+ def message(self) -> str:
2116
+ return str(self.exc)
2117
+
2118
+
2119
+ class InternalErrorOnRun(DebugLevel):
2120
+ def code(self) -> str:
2121
+ return "W003"
2122
+
2123
+ def message(self) -> str:
2124
+ prefix = f"Internal error executing {self.build_path}"
2125
+
2126
+ internal_error_string = """This is an error in dbt. Please try again. If \
2127
+ the error persists, open an issue at https://github.com/dbt-labs/dbt-core
2128
+ """.strip()
2129
+
2130
+ return f"{red(prefix)}\n" f"{str(self.exc).strip()}\n\n" f"{internal_error_string}"
2131
+
2132
+
2133
+ class GenericExceptionOnRun(ErrorLevel):
2134
+ def code(self) -> str:
2135
+ return "W004"
2136
+
2137
+ def message(self) -> str:
2138
+ node_description = self.build_path
2139
+ if node_description is None:
2140
+ node_description = self.unique_id
2141
+ prefix = f"Unhandled error while executing {node_description}"
2142
+ return f"{red(prefix)}\n{str(self.exc).strip()}"
2143
+
2144
+
2145
+ class NodeConnectionReleaseError(DebugLevel):
2146
+ def code(self) -> str:
2147
+ return "W005"
2148
+
2149
+ def message(self) -> str:
2150
+ return f"Error releasing connection for node {self.node_name}: {str(self.exc)}"
2151
+
2152
+
2153
+ class FoundStats(InfoLevel):
2154
+ def code(self) -> str:
2155
+ return "W006"
2156
+
2157
+ def message(self) -> str:
2158
+ return f"Found {self.stat_line}"
2159
+
2160
+
2161
+ # =======================================================
2162
+ # Z - Misc
2163
+ # =======================================================
2164
+
2165
+
2166
+ class MainKeyboardInterrupt(InfoLevel):
2167
+ def code(self) -> str:
2168
+ return "Z001"
2169
+
2170
+ def message(self) -> str:
2171
+ return "ctrl-c"
2172
+
2173
+
2174
+ class MainEncounteredError(ErrorLevel):
2175
+ def code(self) -> str:
2176
+ return "Z002"
2177
+
2178
+ def message(self) -> str:
2179
+ return f"Encountered an error:\n{self.exc}"
2180
+
2181
+
2182
+ class MainStackTrace(ErrorLevel):
2183
+ def code(self) -> str:
2184
+ return "Z003"
2185
+
2186
+ def message(self) -> str:
2187
+ return self.stack_trace
2188
+
2189
+
2190
+ # Skipped Z004
2191
+
2192
+
2193
+ class TimingInfoCollected(DebugLevel):
2194
+ def code(self) -> str:
2195
+ return "Z010"
2196
+
2197
+ def message(self) -> str:
2198
+ started_at = timestamp_to_datetime_string(self.timing_info.started_at)
2199
+ completed_at = timestamp_to_datetime_string(self.timing_info.completed_at)
2200
+ return f"Timing info for {self.node_info.unique_id} ({self.timing_info.name}): {started_at} => {completed_at}"
2201
+
2202
+
2203
+ # This prints the stack trace at the debug level while allowing just the nice exception message
2204
+ # at the error level - or whatever other level chosen. Used in multiple places.
2205
+
2206
+
2207
+ class LogDebugStackTrace(DebugLevel):
2208
+ def code(self) -> str:
2209
+ return "Z011"
2210
+
2211
+ def message(self) -> str:
2212
+ return f"{self.exc_info}"
2213
+
2214
+
2215
+ # We don't write "clean" events to the log, because the clean command
2216
+ # may have removed the log directory.
2217
+
2218
+
2219
+ class CheckCleanPath(InfoLevel):
2220
+ def code(self) -> str:
2221
+ return "Z012"
2222
+
2223
+ def message(self) -> str:
2224
+ return f"Checking {self.path}/*"
2225
+
2226
+
2227
+ class ConfirmCleanPath(InfoLevel):
2228
+ def code(self) -> str:
2229
+ return "Z013"
2230
+
2231
+ def message(self) -> str:
2232
+ return f"Cleaned {self.path}/*"
2233
+
2234
+
2235
+ class ProtectedCleanPath(InfoLevel):
2236
+ def code(self) -> str:
2237
+ return "Z014"
2238
+
2239
+ def message(self) -> str:
2240
+ return f"ERROR: not cleaning {self.path}/* because it is protected"
2241
+
2242
+
2243
+ class FinishedCleanPaths(InfoLevel):
2244
+ def code(self) -> str:
2245
+ return "Z015"
2246
+
2247
+ def message(self) -> str:
2248
+ return "Finished cleaning all paths."
2249
+
2250
+
2251
+ class OpenCommand(InfoLevel):
2252
+ def code(self) -> str:
2253
+ return "Z016"
2254
+
2255
+ def message(self) -> str:
2256
+ msg = f"""To view your profiles.yml file, run:
2257
+
2258
+ {self.open_cmd} {self.profiles_dir}"""
2259
+
2260
+ return msg
2261
+
2262
+
2263
+ class RunResultWarning(WarnLevel):
2264
+ def code(self) -> str:
2265
+ return "Z021"
2266
+
2267
+ def message(self) -> str:
2268
+ info = "Warning"
2269
+ return yellow(f"{info} in {self.resource_type} {self.node_name} ({self.path})")
2270
+
2271
+
2272
+ class RunResultFailure(ErrorLevel):
2273
+ def code(self) -> str:
2274
+ return "Z022"
2275
+
2276
+ def message(self) -> str:
2277
+ info = "Failure"
2278
+ return red(f"{info} in {self.resource_type} {self.node_name} ({self.path})")
2279
+
2280
+
2281
+ class StatsLine(InfoLevel):
2282
+ def code(self) -> str:
2283
+ return "Z023"
2284
+
2285
+ def message(self) -> str:
2286
+ stats_line = (
2287
+ "Done. PASS={pass} WARN={warn} ERROR={error} SKIP={skip} NO-OP={noop} TOTAL={total}"
2288
+ )
2289
+ return stats_line.format(**self.stats)
2290
+
2291
+
2292
+ class RunResultError(ErrorLevel):
2293
+ def code(self) -> str:
2294
+ return "Z024"
2295
+
2296
+ def message(self) -> str:
2297
+ # This is the message on the result object, cannot be built here
2298
+ return f" {self.msg}"
2299
+
2300
+
2301
+ class RunResultErrorNoMessage(ErrorLevel):
2302
+ def code(self) -> str:
2303
+ return "Z025"
2304
+
2305
+ def message(self) -> str:
2306
+ return f" Status: {self.status}"
2307
+
2308
+
2309
+ class SQLCompiledPath(InfoLevel):
2310
+ def code(self) -> str:
2311
+ return "Z026"
2312
+
2313
+ def message(self) -> str:
2314
+ return f" compiled code at {self.path}"
2315
+
2316
+
2317
+ class CheckNodeTestFailure(InfoLevel):
2318
+ def code(self) -> str:
2319
+ return "Z027"
2320
+
2321
+ def message(self) -> str:
2322
+ msg = f"select * from {self.relation_name}"
2323
+ border = "-" * len(msg)
2324
+ return f" See test failures:\n {border}\n {msg}\n {border}"
2325
+
2326
+
2327
+ # Skipped Z028, Z029
2328
+
2329
+
2330
+ class EndOfRunSummary(InfoLevel):
2331
+ def code(self) -> str:
2332
+ return "Z030"
2333
+
2334
+ def message(self) -> str:
2335
+ error_plural = pluralize(self.num_errors, "error")
2336
+ warn_plural = pluralize(self.num_warnings, "warning")
2337
+ partial_success_plural = f"""{self.num_partial_success} partial {"success" if self.num_partial_success == 1 else "successes"}"""
2338
+
2339
+ if self.keyboard_interrupt:
2340
+ message = yellow("Exited because of keyboard interrupt")
2341
+ elif self.num_errors > 0:
2342
+ message = red(
2343
+ f"Completed with {error_plural}, {partial_success_plural}, and {warn_plural}:"
2344
+ )
2345
+ elif self.num_partial_success > 0:
2346
+ message = yellow(f"Completed with {partial_success_plural} and {warn_plural}")
2347
+ elif self.num_warnings > 0:
2348
+ message = yellow(f"Completed with {warn_plural}:")
2349
+ else:
2350
+ message = green("Completed successfully")
2351
+ return message
2352
+
2353
+
2354
+ # Skipped Z031, Z032
2355
+
2356
+
2357
+ class MarkSkippedChildren(DebugLevel):
2358
+ def code(self) -> str:
2359
+ return "Z033"
2360
+
2361
+ def message(self) -> str:
2362
+ msg = (
2363
+ f"Marking all children of '{self.unique_id}' to be skipped "
2364
+ f"because of status '{self.status}'. "
2365
+ )
2366
+ if self.run_result.message:
2367
+ msg = msg + f" Reason: {self.run_result.message}."
2368
+ return msg
2369
+
2370
+
2371
+ class LogSkipBecauseError(ErrorLevel):
2372
+ def code(self) -> str:
2373
+ return "Z034"
2374
+
2375
+ def message(self) -> str:
2376
+ msg = f"SKIP relation {self.schema}.{self.relation} due to ephemeral model status '{self.status}'"
2377
+ return format_fancy_output_line(
2378
+ msg=msg, status=red("ERROR SKIP"), index=self.index, total=self.total
2379
+ )
2380
+
2381
+
2382
+ # Skipped Z035
2383
+
2384
+
2385
+ class EnsureGitInstalled(ErrorLevel):
2386
+ def code(self) -> str:
2387
+ return "Z036"
2388
+
2389
+ def message(self) -> str:
2390
+ return (
2391
+ "Make sure git is installed on your machine. More "
2392
+ "information: "
2393
+ "https://docs.getdbt.com/docs/package-management"
2394
+ )
2395
+
2396
+
2397
+ class DepsCreatingLocalSymlink(DebugLevel):
2398
+ def code(self) -> str:
2399
+ return "Z037"
2400
+
2401
+ def message(self) -> str:
2402
+ return "Creating symlink to local dependency."
2403
+
2404
+
2405
+ class DepsSymlinkNotAvailable(DebugLevel):
2406
+ def code(self) -> str:
2407
+ return "Z038"
2408
+
2409
+ def message(self) -> str:
2410
+ return "Symlinks are not available on this OS, copying dependency."
2411
+
2412
+
2413
+ class DisableTracking(DebugLevel):
2414
+ def code(self) -> str:
2415
+ return "Z039"
2416
+
2417
+ def message(self) -> str:
2418
+ return (
2419
+ "Error sending anonymous usage statistics. Disabling tracking for this execution. "
2420
+ "If you wish to permanently disable tracking, see: "
2421
+ "https://docs.getdbt.com/reference/global-configs#send-anonymous-usage-stats."
2422
+ )
2423
+
2424
+
2425
+ class SendingEvent(DebugLevel):
2426
+ def code(self) -> str:
2427
+ return "Z040"
2428
+
2429
+ def message(self) -> str:
2430
+ return f"Sending event: {self.kwargs}"
2431
+
2432
+
2433
+ class SendEventFailure(DebugLevel):
2434
+ def code(self) -> str:
2435
+ return "Z041"
2436
+
2437
+ def message(self) -> str:
2438
+ return "An error was encountered while trying to send an event"
2439
+
2440
+
2441
+ class FlushEvents(DebugLevel):
2442
+ def code(self) -> str:
2443
+ return "Z042"
2444
+
2445
+ def message(self) -> str:
2446
+ return "Flushing usage events"
2447
+
2448
+
2449
+ class FlushEventsFailure(DebugLevel):
2450
+ def code(self) -> str:
2451
+ return "Z043"
2452
+
2453
+ def message(self) -> str:
2454
+ return "An error was encountered while trying to flush usage events"
2455
+
2456
+
2457
+ class TrackingInitializeFailure(DebugLevel):
2458
+ def code(self) -> str:
2459
+ return "Z044"
2460
+
2461
+ def message(self) -> str:
2462
+ return "Got an exception trying to initialize tracking"
2463
+
2464
+
2465
+ # this is the message from the result object
2466
+
2467
+
2468
+ class RunResultWarningMessage(WarnLevel):
2469
+ def code(self) -> str:
2470
+ return "Z046"
2471
+
2472
+ def message(self) -> str:
2473
+ # This is the message on the result object, cannot be formatted in event
2474
+ return self.msg
2475
+
2476
+
2477
+ class DebugCmdOut(InfoLevel):
2478
+ def code(self) -> str:
2479
+ return "Z047"
2480
+
2481
+ def message(self) -> str:
2482
+ return self.msg
2483
+
2484
+
2485
+ class DebugCmdResult(InfoLevel):
2486
+ def code(self) -> str:
2487
+ return "Z048"
2488
+
2489
+ def message(self) -> str:
2490
+ return self.msg
2491
+
2492
+
2493
+ class ListCmdOut(InfoLevel):
2494
+ # No longer in use, switching to Z051 PrintEvent in dbt-common
2495
+ def code(self) -> str:
2496
+ return "Z049"
2497
+
2498
+ def message(self) -> str:
2499
+ return self.msg
2500
+
2501
+
2502
+ class ResourceReport(DebugLevel):
2503
+ def code(self) -> str:
2504
+ return "Z051"
2505
+
2506
+ def message(self) -> str:
2507
+ return f"Resource report: {self.to_json()}"
2508
+
2509
+
2510
+ # Artifact Upload Events #
2511
+
2512
+
2513
+ class ArtifactUploadError(ErrorLevel):
2514
+ def code(self) -> str:
2515
+ return "Z061"
2516
+
2517
+ def message(self) -> str:
2518
+ return f"Error uploading artifacts to artifact ingestion API: {self.msg}"
2519
+
2520
+
2521
+ class ArtifactUploadSuccess(InfoLevel):
2522
+ def code(self) -> str:
2523
+ return "Z062"
2524
+
2525
+ def message(self) -> str:
2526
+ return f"Artifacts uploaded successfully to artifact ingestion API: {self.msg}"
2527
+
2528
+
2529
+ class ArtifactUploadSkipped(DebugLevel):
2530
+ def code(self) -> str:
2531
+ return "Z063"
2532
+
2533
+ def message(self) -> str:
2534
+ return f"Artifacts skipped for command : {self.msg}"