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/cli/exceptions.py ADDED
@@ -0,0 +1,56 @@
1
+ from typing import IO, List, Optional, Union
2
+
3
+ from click.exceptions import ClickException
4
+ from dvt.artifacts.schemas.catalog import CatalogArtifact
5
+ from dvt.contracts.graph.manifest import Manifest
6
+ from dvt.contracts.results import RunExecutionResult
7
+ from dvt.utils import ExitCodes
8
+
9
+
10
+ class DbtUsageException(Exception):
11
+ pass
12
+
13
+
14
+ class DbtInternalException(Exception):
15
+ pass
16
+
17
+
18
+ class CliException(ClickException):
19
+ """The base exception class for our implementation of the click CLI.
20
+ The exit_code attribute is used by click to determine which exit code to produce
21
+ after an invocation."""
22
+
23
+ def __init__(self, exit_code: ExitCodes) -> None:
24
+ self.exit_code = exit_code.value
25
+
26
+ # the typing of _file is to satisfy the signature of ClickException.show
27
+ # overriding this method prevents click from printing any exceptions to stdout
28
+ def show(self, _file: Optional[IO] = None) -> None: # type: ignore[type-arg]
29
+ pass
30
+
31
+
32
+ class ResultExit(CliException):
33
+ """This class wraps any exception that contains results while invoking dbt, or the
34
+ results of an invocation that did not succeed but did not throw any exceptions."""
35
+
36
+ def __init__(
37
+ self,
38
+ result: Union[
39
+ bool, # debug
40
+ CatalogArtifact, # docs generate
41
+ List[str], # list/ls
42
+ Manifest, # parse
43
+ None, # clean, deps, init, source
44
+ RunExecutionResult, # build, compile, run, seed, snapshot, test, run-operation
45
+ ] = None,
46
+ ) -> None:
47
+ super().__init__(ExitCodes.ModelError)
48
+ self.result = result
49
+
50
+
51
+ class ExceptionExit(CliException):
52
+ """This class wraps any exception that does not contain results thrown while invoking dbt."""
53
+
54
+ def __init__(self, exception: Exception) -> None:
55
+ super().__init__(ExitCodes.UnhandledError)
56
+ self.exception = exception
dvt/cli/flags.py ADDED
@@ -0,0 +1,558 @@
1
+ import os
2
+ import sys
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from importlib import import_module
6
+ from pathlib import Path
7
+ from pprint import pformat as pf
8
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
9
+
10
+ from click import Context, Parameter, get_current_context
11
+ from click.core import Command as ClickCommand
12
+ from click.core import Group, ParameterSource
13
+ from dvt.cli.exceptions import DbtUsageException
14
+ from dvt.cli.resolvers import default_log_path, default_project_dir
15
+ from dvt.cli.types import Command as CliCommand
16
+ from dvt.config.project import read_project_flags
17
+ from dvt.config.utils import normalize_warn_error_options
18
+ from dvt.contracts.project import ProjectFlags
19
+ from dvt.deprecations import fire_buffered_deprecations, renamed_env_var, warn
20
+ from dvt.events import ALL_EVENT_NAMES
21
+
22
+ from dbt_common import ui
23
+ from dbt_common.clients import jinja
24
+ from dbt_common.events import functions
25
+ from dbt_common.exceptions import DbtInternalError
26
+ from dbt_common.helper_types import WarnErrorOptionsV2
27
+
28
+ if os.name != "nt":
29
+ # https://bugs.python.org/issue41567
30
+ import multiprocessing.popen_spawn_posix # type: ignore # noqa: F401
31
+
32
+ FLAGS_DEFAULTS = {
33
+ "INDIRECT_SELECTION": "eager",
34
+ "TARGET_PATH": None,
35
+ "DEFER_STATE": None, # necessary because of retry construction of flags
36
+ "WARN_ERROR": None,
37
+ # Cli args without project_flags or env var option.
38
+ "FULL_REFRESH": False,
39
+ "STRICT_MODE": False,
40
+ "STORE_FAILURES": False,
41
+ "INTROSPECT": True,
42
+ "STATE_MODIFIED_COMPARE_VARS": False,
43
+ }
44
+
45
+ DEPRECATED_PARAMS = {
46
+ "deprecated_defer": "defer",
47
+ "deprecated_favor_state": "favor_state",
48
+ "deprecated_print": "print",
49
+ "deprecated_state": "state",
50
+ }
51
+
52
+
53
+ DEPRECATED_FLAGS_TO_WARNINGS = {("--models", "--model", "-m"): "model-param-usage-deprecation"}
54
+
55
+ WHICH_KEY = "which"
56
+
57
+
58
+ def convert_config(config_name, config_value):
59
+ """Convert the values from config and original set_from_args to the correct type."""
60
+ ret = config_value
61
+ if config_name.lower() == "warn_error_options" and type(config_value) == dict:
62
+ normalize_warn_error_options(ret)
63
+ ret = WarnErrorOptionsV2(
64
+ error=config_value.get("error", []),
65
+ warn=config_value.get("warn", []),
66
+ silence=config_value.get("silence", []),
67
+ valid_error_names=ALL_EVENT_NAMES,
68
+ )
69
+ return ret
70
+
71
+
72
+ def args_to_context(args: List[str]) -> Context:
73
+ """Convert a list of args to a click context with proper hierarchy for dbt commands"""
74
+ from dvt.cli.main import cli
75
+
76
+ cli_ctx = cli.make_context(cli.name, args)
77
+ # Split args if they're a comma separated string.
78
+ if len(args) == 1 and "," in args[0]:
79
+ args = args[0].split(",")
80
+ sub_command_name, sub_command, args = cli.resolve_command(cli_ctx, args)
81
+ # Handle source and docs group.
82
+ if isinstance(sub_command, Group):
83
+ sub_command_name, sub_command, args = sub_command.resolve_command(cli_ctx, args)
84
+
85
+ assert isinstance(sub_command, ClickCommand)
86
+ sub_command_ctx = sub_command.make_context(sub_command_name, args)
87
+ sub_command_ctx.parent = cli_ctx
88
+ return sub_command_ctx
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class Flags:
93
+ """Primary configuration artifact for running dbt"""
94
+
95
+ def __init__(
96
+ self, ctx: Optional[Context] = None, project_flags: Optional[ProjectFlags] = None
97
+ ) -> None:
98
+ # Set the default flags.
99
+ for key, value in FLAGS_DEFAULTS.items():
100
+ object.__setattr__(self, key, value)
101
+ # Use to handle duplicate params in _assign_params
102
+ flags_defaults_list = list(FLAGS_DEFAULTS.keys())
103
+
104
+ if ctx is None:
105
+ ctx = get_current_context()
106
+
107
+ def _get_params_by_source(ctx: Context, source_type: ParameterSource):
108
+ """Generates all params of a given source type."""
109
+ yield from [
110
+ name for name, source in ctx._parameter_source.items() if source is source_type
111
+ ]
112
+ if ctx.parent:
113
+ yield from _get_params_by_source(ctx.parent, source_type)
114
+
115
+ # Ensure that any params sourced from the commandline are not present more than once.
116
+ # Click handles this exclusivity, but only at a per-subcommand level.
117
+ seen_params = []
118
+ for param in _get_params_by_source(ctx, ParameterSource.COMMANDLINE):
119
+ if param in seen_params:
120
+ raise DbtUsageException(
121
+ f"{param.lower()} was provided both before and after the subcommand, it can only be set either before or after.",
122
+ )
123
+ seen_params.append(param)
124
+
125
+ def _assign_params(
126
+ ctx: Context,
127
+ params_assigned_from_default: set,
128
+ params_assigned_from_user: set,
129
+ deprecated_env_vars: Dict[str, Callable],
130
+ ):
131
+ """Recursively adds all click params to flag object"""
132
+ for param_name, param_value in ctx.params.items():
133
+ # N.B. You have to use the base MRO method (object.__setattr__) to set attributes
134
+ # when using frozen dataclasses.
135
+ # https://docs.python.org/3/library/dataclasses.html#frozen-instances
136
+
137
+ # Handle deprecated env vars while still respecting old values
138
+ # e.g. DBT_NO_PRINT -> DBT_PRINT if DBT_NO_PRINT is set, it is
139
+ # respected over DBT_PRINT or --print.
140
+ new_name: Union[str, None] = None
141
+ if param_name in DEPRECATED_PARAMS:
142
+ # Deprecated env vars can only be set via env var.
143
+ # We use the deprecated option in click to serialize the value
144
+ # from the env var string.
145
+ param_source = ctx.get_parameter_source(param_name)
146
+ if param_source == ParameterSource.DEFAULT:
147
+ continue
148
+ elif param_source != ParameterSource.ENVIRONMENT:
149
+ raise DbtUsageException(
150
+ "Deprecated parameters can only be set via environment variables",
151
+ )
152
+
153
+ # Rename for clarity.
154
+ dep_name = param_name
155
+ new_name = DEPRECATED_PARAMS.get(dep_name)
156
+ try:
157
+ assert isinstance(new_name, str)
158
+ except AssertionError:
159
+ raise Exception(
160
+ f"No deprecated param name match in DEPRECATED_PARAMS from {dep_name} to {new_name}"
161
+ )
162
+
163
+ # Find param objects for their envvar name.
164
+ try:
165
+ dep_param = [x for x in ctx.command.params if x.name == dep_name][0]
166
+ new_param = [x for x in ctx.command.params if x.name == new_name][0]
167
+ except IndexError:
168
+ raise Exception(
169
+ f"No deprecated param name match in context from {dep_name} to {new_name}"
170
+ )
171
+
172
+ # Remove param from defaulted set since the deprecated
173
+ # value is not set from default, but from an env var.
174
+ if new_name in params_assigned_from_default:
175
+ params_assigned_from_default.remove(new_name)
176
+
177
+ # Add the deprecation warning function to the set.
178
+ assert isinstance(dep_param.envvar, str)
179
+ assert isinstance(new_param.envvar, str)
180
+ deprecated_env_vars[new_name] = renamed_env_var(
181
+ old_name=dep_param.envvar,
182
+ new_name=new_param.envvar,
183
+ )
184
+ # end deprecated_params
185
+
186
+ # Set the flag value.
187
+ is_duplicate = (
188
+ hasattr(self, param_name.upper())
189
+ and param_name.upper() not in flags_defaults_list
190
+ )
191
+ # First time through, set as though FLAGS_DEFAULTS hasn't been set, so not a duplicate.
192
+ # Subsequent pass (to process "parent" params) should be treated as duplicates.
193
+ if param_name.upper() in flags_defaults_list:
194
+ flags_defaults_list.remove(param_name.upper())
195
+ # Note: the following determines whether parameter came from click default,
196
+ # not from FLAGS_DEFAULTS in __init__.
197
+ is_default = ctx.get_parameter_source(param_name) == ParameterSource.DEFAULT
198
+ is_envvar = ctx.get_parameter_source(param_name) == ParameterSource.ENVIRONMENT
199
+
200
+ flag_name = (new_name or param_name).upper()
201
+
202
+ # envvar flags are assigned in either parent or child context if there
203
+ # isn't an overriding cli command flag.
204
+ # If the flag has been encountered as a child cli flag, we don't
205
+ # want to overwrite with parent envvar, since the commandline flag takes precedence.
206
+ if (is_duplicate and not (is_default or is_envvar)) or not is_duplicate:
207
+ object.__setattr__(self, flag_name, param_value)
208
+
209
+ # Track default assigned params.
210
+ # For flags that are accepted at both 'parent' and 'child' levels,
211
+ # we need to track user-provided and default values across both,
212
+ # to support detection of mutually exclusive flags later on.
213
+ if not is_default:
214
+ params_assigned_from_user.add(param_name)
215
+ if param_name in params_assigned_from_default:
216
+ params_assigned_from_default.remove(param_name)
217
+ if is_default and param_name not in params_assigned_from_user:
218
+ params_assigned_from_default.add(param_name)
219
+
220
+ if ctx.parent:
221
+ _assign_params(
222
+ ctx.parent,
223
+ params_assigned_from_default,
224
+ params_assigned_from_user,
225
+ deprecated_env_vars,
226
+ )
227
+
228
+ params_assigned_from_user = set() # type: Set[str]
229
+ params_assigned_from_default = set() # type: Set[str]
230
+ deprecated_env_vars: Dict[str, Callable] = {}
231
+ _assign_params(
232
+ ctx, params_assigned_from_default, params_assigned_from_user, deprecated_env_vars
233
+ )
234
+
235
+ # Set deprecated_env_var_warnings to be fired later after events have been init.
236
+ object.__setattr__(
237
+ self, "deprecated_env_var_warnings", [x for x in deprecated_env_vars.values()]
238
+ )
239
+
240
+ # Get the invoked command flags.
241
+ invoked_subcommand_name = (
242
+ ctx.invoked_subcommand if hasattr(ctx, "invoked_subcommand") else None
243
+ )
244
+ if invoked_subcommand_name is not None:
245
+ invoked_subcommand = getattr(import_module("dbt.cli.main"), invoked_subcommand_name)
246
+ invoked_subcommand.allow_extra_args = True
247
+ invoked_subcommand.ignore_unknown_options = True
248
+ invoked_subcommand_ctx = invoked_subcommand.make_context(None, sys.argv)
249
+ _assign_params(
250
+ invoked_subcommand_ctx,
251
+ params_assigned_from_default,
252
+ params_assigned_from_user,
253
+ deprecated_env_vars,
254
+ )
255
+
256
+ if not project_flags:
257
+ project_dir = getattr(self, "PROJECT_DIR", str(default_project_dir()))
258
+ profiles_dir = getattr(self, "PROFILES_DIR", None)
259
+ if profiles_dir and project_dir:
260
+ project_flags = read_project_flags(project_dir, profiles_dir)
261
+ else:
262
+ project_flags = None
263
+
264
+ # Add entire invocation command to flags
265
+ object.__setattr__(self, "INVOCATION_COMMAND", "dbt " + " ".join(sys.argv[1:]))
266
+
267
+ if project_flags:
268
+ # Overwrite default assignments with project flags if available.
269
+ param_assigned_from_default_copy = params_assigned_from_default.copy()
270
+ for param_assigned_from_default in params_assigned_from_default:
271
+ project_flags_param_value = getattr(
272
+ project_flags, param_assigned_from_default, None
273
+ )
274
+ if project_flags_param_value is not None:
275
+ object.__setattr__(
276
+ self,
277
+ param_assigned_from_default.upper(),
278
+ convert_config(param_assigned_from_default, project_flags_param_value),
279
+ )
280
+ param_assigned_from_default_copy.remove(param_assigned_from_default)
281
+ params_assigned_from_default = param_assigned_from_default_copy
282
+
283
+ # Add project-level flags that are not available as CLI options / env vars
284
+ for (
285
+ project_level_flag_name,
286
+ project_level_flag_value,
287
+ ) in project_flags.project_only_flags.items():
288
+ object.__setattr__(self, project_level_flag_name.upper(), project_level_flag_value)
289
+
290
+ # Set hard coded flags.
291
+ object.__setattr__(self, "WHICH", invoked_subcommand_name or ctx.info_name)
292
+
293
+ # Apply the lead/follow relationship between some parameters.
294
+ self._override_if_set("USE_COLORS", "USE_COLORS_FILE", params_assigned_from_default)
295
+ self._override_if_set("LOG_LEVEL", "LOG_LEVEL_FILE", params_assigned_from_default)
296
+ self._override_if_set("LOG_FORMAT", "LOG_FORMAT_FILE", params_assigned_from_default)
297
+
298
+ # Set default LOG_PATH from PROJECT_DIR, if available.
299
+ # Starting in v1.5, if `log-path` is set in `dbt_project.yml`, it will raise a deprecation warning,
300
+ # with the possibility of removing it in a future release.
301
+ if getattr(self, "LOG_PATH", None) is None:
302
+ project_dir = getattr(self, "PROJECT_DIR", str(default_project_dir()))
303
+ version_check = getattr(self, "VERSION_CHECK", True)
304
+ object.__setattr__(
305
+ self, "LOG_PATH", default_log_path(Path(project_dir), version_check)
306
+ )
307
+
308
+ # Support console DO NOT TRACK initiative.
309
+ if os.getenv("DO_NOT_TRACK", "").lower() in ("1", "t", "true", "y", "yes"):
310
+ object.__setattr__(self, "SEND_ANONYMOUS_USAGE_STATS", False)
311
+
312
+ # Check mutual exclusivity once all flags are set.
313
+ self._assert_mutually_exclusive(
314
+ params_assigned_from_default, ["WARN_ERROR", "WARN_ERROR_OPTIONS"]
315
+ )
316
+
317
+ # Handle arguments mutually exclusive with INLINE
318
+ self._assert_mutually_exclusive(params_assigned_from_default, ["SELECT", "INLINE"])
319
+ self._assert_mutually_exclusive(params_assigned_from_default, ["SELECTOR", "INLINE"])
320
+
321
+ # Check event_time configs for validity
322
+ self._validate_event_time_configs()
323
+
324
+ # Support lower cased access for legacy code.
325
+ params = set(
326
+ x for x in dir(self) if not callable(getattr(self, x)) and not x.startswith("__")
327
+ )
328
+ for param in params:
329
+ object.__setattr__(self, param.lower(), getattr(self, param))
330
+
331
+ self.set_common_global_flags()
332
+
333
+ def __str__(self) -> str:
334
+ return str(pf(self.__dict__))
335
+
336
+ def _override_if_set(self, lead: str, follow: str, defaulted: Set[str]) -> None:
337
+ """If the value of the lead parameter was set explicitly, apply the value to follow, unless follow was also set explicitly."""
338
+ if lead.lower() not in defaulted and follow.lower() in defaulted:
339
+ object.__setattr__(self, follow.upper(), getattr(self, lead.upper(), None))
340
+
341
+ def _assert_mutually_exclusive(
342
+ self, params_assigned_from_default: Set[str], group: List[str]
343
+ ) -> None:
344
+ """
345
+ Ensure no elements from group are simultaneously provided by a user, as inferred from params_assigned_from_default.
346
+ Raises click.UsageError if any two elements from group are simultaneously provided by a user.
347
+ """
348
+ set_flag = None
349
+ for flag in group:
350
+ flag_set_by_user = (
351
+ hasattr(self, flag) and flag.lower() not in params_assigned_from_default
352
+ )
353
+ if flag_set_by_user and set_flag:
354
+ raise DbtUsageException(
355
+ f"{flag.lower()}: not allowed with argument {set_flag.lower()}"
356
+ )
357
+ elif flag_set_by_user:
358
+ set_flag = flag
359
+
360
+ def _validate_event_time_configs(self) -> None:
361
+ event_time_start: datetime = (
362
+ getattr(self, "EVENT_TIME_START") if hasattr(self, "EVENT_TIME_START") else None
363
+ )
364
+ event_time_end: datetime = (
365
+ getattr(self, "EVENT_TIME_END") if hasattr(self, "EVENT_TIME_END") else None
366
+ )
367
+
368
+ # only do validations if at least one of `event_time_start` or `event_time_end` are specified
369
+ if event_time_start is not None or event_time_end is not None:
370
+
371
+ # These `ifs`, combined with the parent `if` make it so that `event_time_start` and
372
+ # `event_time_end` are mutually required
373
+ if event_time_start is None:
374
+ raise DbtUsageException(
375
+ "The flag `--event-time-end` was specified, but `--event-time-start` was not. "
376
+ "When specifying `--event-time-end`, `--event-time-start` must also be present."
377
+ )
378
+ if event_time_end is None:
379
+ raise DbtUsageException(
380
+ "The flag `--event-time-start` was specified, but `--event-time-end` was not. "
381
+ "When specifying `--event-time-start`, `--event-time-end` must also be present."
382
+ )
383
+
384
+ # This `if` just is a sanity check that `event_time_start` is before `event_time_end`
385
+ if event_time_start >= event_time_end:
386
+ raise DbtUsageException(
387
+ "Value for `--event-time-start` must be less than `--event-time-end`"
388
+ )
389
+
390
+ def fire_deprecations(self, ctx: Optional[Context] = None):
391
+ """Fires events for deprecated env_var usage."""
392
+ [dep_fn() for dep_fn in self.deprecated_env_var_warnings]
393
+ # It is necessary to remove this attr from the class so it does
394
+ # not get pickled when written to disk as json.
395
+ object.__delattr__(self, "deprecated_env_var_warnings")
396
+
397
+ fire_buffered_deprecations()
398
+
399
+ # Handle firing deprecations of CLI aliases separately using argv or dbtRunner args
400
+ # because click doesn't make it possible to disambiguite which literal CLI option was used
401
+ # and only preserves the 'canonical' representation.
402
+ original_command_args = (
403
+ ctx.obj["dbt_runner_command_args"]
404
+ if (ctx and ctx.obj and "dbt_runner_command_args" in ctx.obj)
405
+ else sys.argv
406
+ )
407
+ for deprecated_flags, warning in DEPRECATED_FLAGS_TO_WARNINGS.items():
408
+ for deprecated_flag in deprecated_flags:
409
+ if deprecated_flag in original_command_args:
410
+ warn(warning)
411
+
412
+ @classmethod
413
+ def from_dict(cls, command: CliCommand, args_dict: Dict[str, Any]) -> "Flags":
414
+ command_arg_list = command_params(command, args_dict)
415
+ ctx = args_to_context(command_arg_list)
416
+ flags = cls(ctx=ctx)
417
+ flags.fire_deprecations(ctx=ctx)
418
+ return flags
419
+
420
+ def set_common_global_flags(self):
421
+ # Set globals for common.ui
422
+ if getattr(self, "PRINTER_WIDTH", None) is not None:
423
+ ui.PRINTER_WIDTH = getattr(self, "PRINTER_WIDTH")
424
+ if getattr(self, "USE_COLORS", None) is not None:
425
+ ui.USE_COLOR = getattr(self, "USE_COLORS")
426
+
427
+ # Set globals for common.events.functions
428
+ functions.WARN_ERROR = getattr(self, "WARN_ERROR", False)
429
+ if getattr(self, "WARN_ERROR_OPTIONS", None) is not None:
430
+ functions.WARN_ERROR_OPTIONS = getattr(self, "WARN_ERROR_OPTIONS")
431
+
432
+ # Set globals for common.jinja
433
+ if getattr(self, "MACRO_DEBUGGING", None) is not None:
434
+ jinja.MACRO_DEBUGGING = getattr(self, "MACRO_DEBUGGING")
435
+
436
+ # This is here to prevent mypy from complaining about all of the
437
+ # attributes which we added dynamically.
438
+ def __getattr__(self, name: str) -> Any:
439
+ return super().__getattribute__(name) # type: ignore
440
+
441
+
442
+ CommandParams = List[str]
443
+
444
+
445
+ def command_params(command: CliCommand, args_dict: Dict[str, Any]) -> CommandParams:
446
+ """Given a command and a dict, returns a list of strings representing
447
+ the CLI params for that command. The order of this list is consistent with
448
+ which flags are expected at the parent level vs the command level.
449
+
450
+ e.g. fn("run", {"defer": True, "print": False}) -> ["--no-print", "run", "--defer"]
451
+
452
+ The result of this function can be passed in to the args_to_context function
453
+ to produce a click context to instantiate Flags with.
454
+ """
455
+
456
+ cmd_args = set(command_args(command))
457
+ prnt_args = set(parent_args())
458
+ default_args = set([x.lower() for x in FLAGS_DEFAULTS.keys()])
459
+
460
+ res = command.to_list()
461
+ for k, v in args_dict.items():
462
+ k = k.lower()
463
+ # if a "which" value exists in the args dict, it should match the command provided
464
+ if k == WHICH_KEY:
465
+ if v != command.value:
466
+ raise DbtInternalError(
467
+ f"Command '{command.value}' does not match value of which: '{v}'"
468
+ )
469
+ continue
470
+
471
+ # param was assigned from defaults and should not be included
472
+ if k not in (cmd_args | prnt_args) or (
473
+ k in default_args and v == FLAGS_DEFAULTS[k.upper()]
474
+ ):
475
+ continue
476
+
477
+ # if the param is in parent args, it should come before the arg name
478
+ # e.g. ["--print", "run"] vs ["run", "--print"]
479
+ add_fn = res.append
480
+ if k in prnt_args:
481
+
482
+ def add_fn(x):
483
+ res.insert(0, x)
484
+
485
+ spinal_cased = k.replace("_", "-")
486
+
487
+ # MultiOption flags come back as lists, but we want to pass them as space separated strings
488
+ if isinstance(v, list):
489
+ if len(v) > 0:
490
+ v = " ".join(v)
491
+ else:
492
+ continue
493
+
494
+ if k == "macro" and command == CliCommand.RUN_OPERATION:
495
+ add_fn(v)
496
+ # None is a Singleton, False is a Flyweight, only one instance of each.
497
+ elif (v is None or v is False) and k not in (
498
+ # These are None by default but they do not support --no-{flag}
499
+ "defer_state",
500
+ "log_format",
501
+ ):
502
+ add_fn(f"--no-{spinal_cased}")
503
+ elif v is True:
504
+ add_fn(f"--{spinal_cased}")
505
+ else:
506
+ add_fn(f"--{spinal_cased}={v}")
507
+
508
+ return res
509
+
510
+
511
+ ArgsList = List[str]
512
+
513
+
514
+ def parent_args() -> ArgsList:
515
+ """Return a list representing the params the base click command takes."""
516
+ from dvt.cli.main import cli
517
+
518
+ return format_params(cli.params)
519
+
520
+
521
+ def command_args(command: CliCommand) -> ArgsList:
522
+ """Given a command, return a list of strings representing the params
523
+ that command takes. This function only returns params assigned to a
524
+ specific command, not those of its parent command.
525
+
526
+ e.g. fn("run") -> ["defer", "favor_state", "exclude", ...]
527
+ """
528
+ import dvt.cli.main as cli
529
+
530
+ CMD_DICT: Dict[CliCommand, ClickCommand] = {
531
+ CliCommand.BUILD: cli.build,
532
+ CliCommand.CLEAN: cli.clean,
533
+ CliCommand.CLONE: cli.clone,
534
+ CliCommand.COMPILE: cli.compile,
535
+ CliCommand.DOCS_GENERATE: cli.docs_generate,
536
+ CliCommand.DOCS_SERVE: cli.docs_serve,
537
+ CliCommand.DEBUG: cli.debug,
538
+ CliCommand.DEPS: cli.deps,
539
+ CliCommand.INIT: cli.init,
540
+ CliCommand.LIST: cli.list,
541
+ CliCommand.PARSE: cli.parse,
542
+ CliCommand.RUN: cli.run,
543
+ CliCommand.RUN_OPERATION: cli.run_operation,
544
+ CliCommand.SEED: cli.seed,
545
+ CliCommand.SHOW: cli.show,
546
+ CliCommand.SNAPSHOT: cli.snapshot,
547
+ CliCommand.SOURCE_FRESHNESS: cli.freshness,
548
+ CliCommand.TEST: cli.test,
549
+ CliCommand.RETRY: cli.retry,
550
+ }
551
+ click_cmd: Optional[ClickCommand] = CMD_DICT.get(command, None)
552
+ if click_cmd is None:
553
+ raise DbtInternalError(f"No command found for name '{command.name}'")
554
+ return format_params(click_cmd.params)
555
+
556
+
557
+ def format_params(params: List[Parameter]) -> ArgsList:
558
+ return [str(x.name) for x in params if not str(x.name).lower().startswith("deprecated_")]