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
@@ -0,0 +1,136 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ from dvt.constants import DEFAULT_ENV_PLACEHOLDER
4
+ from dvt.context.base import Var, contextmember, contextproperty
5
+ from dvt.context.target import TargetContext
6
+ from dvt.exceptions import EnvVarMissingError, SecretEnvVarLocationError
7
+ from dvt.node_types import NodeType
8
+ from dvt.utils import MultiDict
9
+
10
+ from dbt.adapters.contracts.connection import AdapterRequiredConfig
11
+ from dbt_common.constants import SECRET_ENV_PREFIX
12
+ from dbt_common.context import get_invocation_context
13
+
14
+
15
+ class ConfiguredContext(TargetContext):
16
+ # subclasses are SchemaYamlContext, MacroResolvingContext, ManifestContext
17
+ config: AdapterRequiredConfig
18
+
19
+ def __init__(self, config: AdapterRequiredConfig) -> None:
20
+ super().__init__(config.to_target_dict(), config.cli_vars)
21
+ self.config = config
22
+
23
+ @contextproperty()
24
+ def project_name(self) -> str:
25
+ return self.config.project_name
26
+
27
+
28
+ class FQNLookup:
29
+ def __init__(self, package_name: str):
30
+ self.package_name = package_name
31
+ self.fqn = [package_name]
32
+ self.resource_type = NodeType.Model
33
+
34
+
35
+ class ConfiguredVar(Var):
36
+ def __init__(
37
+ self,
38
+ context: Dict[str, Any],
39
+ config: AdapterRequiredConfig,
40
+ project_name: str,
41
+ ):
42
+ super().__init__(context, config.cli_vars)
43
+ self._config = config
44
+ self._project_name = project_name
45
+
46
+ def __call__(self, var_name, default=Var._VAR_NOTSET):
47
+ my_config = self._config.load_dependencies()[self._project_name]
48
+
49
+ # cli vars > active project > local project
50
+ if var_name in self._config.cli_vars:
51
+ return self._config.cli_vars[var_name]
52
+
53
+ adapter_type = self._config.credentials.type
54
+ lookup = FQNLookup(self._project_name)
55
+ active_vars = self._config.vars.vars_for(lookup, adapter_type)
56
+
57
+ all_vars = MultiDict()
58
+ if self._config.project_name != my_config.project_name:
59
+ all_vars.add(my_config.vars.vars_for(lookup, adapter_type))
60
+ all_vars.add(active_vars)
61
+
62
+ if var_name in all_vars:
63
+ return all_vars[var_name]
64
+
65
+ if default is not Var._VAR_NOTSET:
66
+ return default
67
+
68
+ return self.get_missing_var(var_name)
69
+
70
+
71
+ class SchemaYamlVars:
72
+ def __init__(self):
73
+ self.env_vars = {}
74
+ self.vars = {}
75
+
76
+
77
+ class SchemaYamlContext(ConfiguredContext):
78
+ # subclass is DocsRuntimeContext
79
+ def __init__(self, config, project_name: str, schema_yaml_vars: Optional[SchemaYamlVars]):
80
+ super().__init__(config)
81
+ self._project_name = project_name
82
+ self.schema_yaml_vars = schema_yaml_vars
83
+
84
+ @contextproperty()
85
+ def var(self) -> ConfiguredVar:
86
+ return ConfiguredVar(self._ctx, self.config, self._project_name)
87
+
88
+ @contextmember()
89
+ def env_var(self, var: str, default: Optional[str] = None) -> str:
90
+ return_value = None
91
+ if var.startswith(SECRET_ENV_PREFIX):
92
+ raise SecretEnvVarLocationError(var)
93
+ env = get_invocation_context().env
94
+ if var in env:
95
+ return_value = env[var]
96
+ elif default is not None:
97
+ return_value = default
98
+
99
+ if return_value is not None:
100
+ if self.schema_yaml_vars:
101
+ # If the environment variable is set from a default, store a string indicating
102
+ # that so we can skip partial parsing. Otherwise the file will be scheduled for
103
+ # reparsing. If the default changes, the file will have been updated and therefore
104
+ # will be scheduled for reparsing anyways.
105
+ self.schema_yaml_vars.env_vars[var] = (
106
+ return_value if var in env else DEFAULT_ENV_PLACEHOLDER
107
+ )
108
+
109
+ return return_value
110
+ else:
111
+ raise EnvVarMissingError(var)
112
+
113
+
114
+ class MacroResolvingContext(ConfiguredContext):
115
+ def __init__(self, config):
116
+ super().__init__(config)
117
+
118
+ @contextproperty()
119
+ def var(self) -> ConfiguredVar:
120
+ return ConfiguredVar(self._ctx, self.config, self.config.project_name)
121
+
122
+
123
+ def generate_schema_yml_context(
124
+ config: AdapterRequiredConfig,
125
+ project_name: str,
126
+ schema_yaml_vars: Optional[SchemaYamlVars] = None,
127
+ ) -> Dict[str, Any]:
128
+ ctx = SchemaYamlContext(config, project_name, schema_yaml_vars)
129
+ return ctx.to_dict()
130
+
131
+
132
+ def generate_macro_context(
133
+ config: AdapterRequiredConfig,
134
+ ) -> Dict[str, Any]:
135
+ ctx = MacroResolvingContext(config)
136
+ return ctx.to_dict()
@@ -0,0 +1,350 @@
1
+ from abc import abstractmethod
2
+ from copy import deepcopy
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, Generic, Iterator, List, Optional, TypeVar
5
+
6
+ from dvt.config import IsFQNResource, Project, RuntimeConfig
7
+ from dvt.contracts.graph.model_config import get_config_for
8
+ from dvt.exceptions import SchemaConfigError
9
+ from dvt.flags import get_flags
10
+ from dvt.node_types import NodeType
11
+ from dvt.utils import fqn_search
12
+
13
+ from dbt.adapters.factory import get_config_class_by_name
14
+ from dbt_common.contracts.config.base import BaseConfig, merge_config_dicts
15
+ from dbt_common.dataclass_schema import ValidationError
16
+ from dbt_common.exceptions import DbtInternalError
17
+
18
+
19
+ @dataclass
20
+ class ModelParts(IsFQNResource):
21
+ fqn: List[str]
22
+ resource_type: NodeType
23
+ package_name: str
24
+
25
+
26
+ T = TypeVar("T") # any old type
27
+ C = TypeVar("C", bound=BaseConfig)
28
+
29
+
30
+ class ConfigSource:
31
+ def __init__(self, project):
32
+ self.project = project
33
+
34
+ def get_config_dict(self, resource_type: NodeType): ...
35
+
36
+
37
+ class UnrenderedConfig(ConfigSource):
38
+ def __init__(self, project: Project):
39
+ self.project = project
40
+
41
+ def get_config_dict(self, resource_type: NodeType) -> Dict[str, Any]:
42
+ unrendered = self.project.unrendered.project_dict
43
+ if resource_type == NodeType.Seed:
44
+ model_configs = unrendered.get("seeds")
45
+ elif resource_type == NodeType.Snapshot:
46
+ model_configs = unrendered.get("snapshots")
47
+ elif resource_type == NodeType.Source:
48
+ model_configs = unrendered.get("sources")
49
+ elif resource_type == NodeType.Test:
50
+ model_configs = unrendered.get("data_tests")
51
+ elif resource_type == NodeType.Metric:
52
+ model_configs = unrendered.get("metrics")
53
+ elif resource_type == NodeType.SemanticModel:
54
+ model_configs = unrendered.get("semantic_models")
55
+ elif resource_type == NodeType.SavedQuery:
56
+ model_configs = unrendered.get("saved_queries")
57
+ elif resource_type == NodeType.Exposure:
58
+ model_configs = unrendered.get("exposures")
59
+ elif resource_type == NodeType.Unit:
60
+ model_configs = unrendered.get("unit_tests")
61
+ else:
62
+ model_configs = unrendered.get("models")
63
+ if model_configs is None:
64
+ return {}
65
+ else:
66
+ return model_configs
67
+
68
+
69
+ class RenderedConfig(ConfigSource):
70
+ def __init__(self, project: Project):
71
+ self.project = project
72
+
73
+ def get_config_dict(self, resource_type: NodeType) -> Dict[str, Any]:
74
+ if resource_type == NodeType.Seed:
75
+ model_configs = self.project.seeds
76
+ elif resource_type == NodeType.Snapshot:
77
+ model_configs = self.project.snapshots
78
+ elif resource_type == NodeType.Source:
79
+ model_configs = self.project.sources
80
+ elif resource_type == NodeType.Test:
81
+ model_configs = self.project.data_tests
82
+ elif resource_type == NodeType.Metric:
83
+ model_configs = self.project.metrics
84
+ elif resource_type == NodeType.SemanticModel:
85
+ model_configs = self.project.semantic_models
86
+ elif resource_type == NodeType.SavedQuery:
87
+ model_configs = self.project.saved_queries
88
+ elif resource_type == NodeType.Exposure:
89
+ model_configs = self.project.exposures
90
+ elif resource_type == NodeType.Unit:
91
+ model_configs = self.project.unit_tests
92
+ elif resource_type == NodeType.Function:
93
+ model_configs = self.project.functions
94
+ else:
95
+ model_configs = self.project.models
96
+ return model_configs
97
+
98
+
99
+ class BaseContextConfigGenerator(Generic[T]):
100
+ def __init__(self, active_project: RuntimeConfig):
101
+ self._active_project = active_project
102
+
103
+ def get_config_source(self, project: Project) -> ConfigSource:
104
+ return RenderedConfig(project)
105
+
106
+ def get_node_project(self, project_name: str):
107
+ if project_name == self._active_project.project_name:
108
+ return self._active_project
109
+ dependencies = self._active_project.load_dependencies()
110
+ if project_name not in dependencies:
111
+ raise DbtInternalError(
112
+ f"Project name {project_name} not found in dependencies "
113
+ f"(found {list(dependencies)})"
114
+ )
115
+ return dependencies[project_name]
116
+
117
+ def _project_configs(
118
+ self, project: Project, fqn: List[str], resource_type: NodeType
119
+ ) -> Iterator[Dict[str, Any]]:
120
+ src = self.get_config_source(project)
121
+ model_configs = src.get_config_dict(resource_type)
122
+ for level_config in fqn_search(model_configs, fqn):
123
+ result = {}
124
+ for key, value in level_config.items():
125
+ if key.startswith("+"):
126
+ result[key[1:].strip()] = deepcopy(value)
127
+ elif not isinstance(value, dict):
128
+ result[key] = deepcopy(value)
129
+
130
+ yield result
131
+
132
+ def _active_project_configs(
133
+ self, fqn: List[str], resource_type: NodeType
134
+ ) -> Iterator[Dict[str, Any]]:
135
+ return self._project_configs(self._active_project, fqn, resource_type)
136
+
137
+ @abstractmethod
138
+ def _update_from_config(
139
+ self, result: T, partial: Dict[str, Any], validate: bool = False
140
+ ) -> T: ...
141
+
142
+ @abstractmethod
143
+ def initial_result(self, resource_type: NodeType, base: bool) -> T: ...
144
+
145
+ def calculate_node_config(
146
+ self,
147
+ # this is the config from the sql file
148
+ config_call_dict: Dict[str, Any],
149
+ fqn: List[str],
150
+ resource_type: NodeType,
151
+ project_name: str,
152
+ base: bool,
153
+ # this is the config from the schema file
154
+ patch_config_dict: Optional[Dict[str, Any]] = None,
155
+ ) -> BaseConfig:
156
+ own_config = self.get_node_project(project_name)
157
+
158
+ result = self.initial_result(resource_type=resource_type, base=base)
159
+
160
+ # builds the config from what was specified in the runtime_config, which generally
161
+ # comes from the project's dbt_project.yml file.
162
+ project_configs = self._project_configs(own_config, fqn, resource_type)
163
+ for fqn_config in project_configs:
164
+ result = self._update_from_config(result, fqn_config)
165
+
166
+ # When schema files patch config, it has lower precedence than
167
+ # config in the models (config_call_dict), so we add the patch_config_dict
168
+ # before the config_call_dict
169
+ if patch_config_dict:
170
+ result = self._update_from_config(result, patch_config_dict)
171
+
172
+ # config_calls are created in the 'experimental' model parser and
173
+ # the ParseConfigObject (via add_config_call)
174
+ result = self._update_from_config(result, config_call_dict)
175
+
176
+ if own_config.project_name != self._active_project.project_name:
177
+ for fqn_config in self._active_project_configs(fqn, resource_type):
178
+ result = self._update_from_config(result, fqn_config)
179
+
180
+ # this is mostly impactful in the snapshot config case
181
+ # TODO CT-211
182
+ return result # type: ignore[return-value]
183
+
184
+ @abstractmethod
185
+ def calculate_node_config_dict(
186
+ self,
187
+ config_call_dict: Dict[str, Any],
188
+ fqn: List[str],
189
+ resource_type: NodeType,
190
+ project_name: str,
191
+ base: bool,
192
+ patch_config_dict: Optional[Dict[str, Any]] = None,
193
+ ) -> Dict[str, Any]: ...
194
+
195
+
196
+ class ContextConfigGenerator(BaseContextConfigGenerator[C]):
197
+ def __init__(self, active_project: RuntimeConfig):
198
+ self._active_project = active_project
199
+
200
+ def get_config_source(self, project: Project) -> ConfigSource:
201
+ return RenderedConfig(project)
202
+
203
+ def initial_result(self, resource_type: NodeType, base: bool) -> C:
204
+ # defaults, own_config, config calls, active_config (if != own_config)
205
+ config_cls = get_config_for(resource_type, base=base)
206
+ # Calculate the defaults. We don't want to validate the defaults,
207
+ # because it might be invalid in the case of required config members
208
+ # (such as on snapshots!)
209
+ result = config_cls.from_dict({})
210
+ return result
211
+
212
+ def _update_from_config(self, result: C, partial: Dict[str, Any], validate: bool = False) -> C:
213
+ translated = self._active_project.credentials.translate_aliases(partial)
214
+ translated = self.translate_hook_names(translated)
215
+
216
+ adapter_type = self._active_project.credentials.type
217
+ adapter_config_cls = get_config_class_by_name(adapter_type)
218
+
219
+ updated = result.update_from(translated, adapter_config_cls, validate=validate)
220
+ return updated
221
+
222
+ def translate_hook_names(self, project_dict):
223
+ # This is a kind of kludge because the fix for #6411 specifically allowed misspelling
224
+ # the hook field names in dbt_project.yml, which only ever worked because we didn't
225
+ # run validate on the dbt_project configs.
226
+ if "pre_hook" in project_dict:
227
+ project_dict["pre-hook"] = project_dict.pop("pre_hook")
228
+ if "post_hook" in project_dict:
229
+ project_dict["post-hook"] = project_dict.pop("post_hook")
230
+ return project_dict
231
+
232
+ def calculate_node_config_dict(
233
+ self,
234
+ config_call_dict: Dict[str, Any],
235
+ fqn: List[str],
236
+ resource_type: NodeType,
237
+ project_name: str,
238
+ base: bool,
239
+ patch_config_dict: Optional[dict] = None,
240
+ ) -> Dict[str, Any]:
241
+ config = self.calculate_node_config(
242
+ config_call_dict=config_call_dict,
243
+ fqn=fqn,
244
+ resource_type=resource_type,
245
+ project_name=project_name,
246
+ base=base,
247
+ patch_config_dict=patch_config_dict,
248
+ )
249
+ try:
250
+ finalized = config.finalize_and_validate()
251
+ return finalized.to_dict(omit_none=True)
252
+ except ValidationError as exc:
253
+ # we got a ValidationError - probably bad types in config()
254
+ raise SchemaConfigError(exc, node=config) from exc
255
+
256
+
257
+ class UnrenderedConfigGenerator(BaseContextConfigGenerator[Dict[str, Any]]):
258
+ def get_config_source(self, project: Project) -> ConfigSource:
259
+ return UnrenderedConfig(project)
260
+
261
+ def calculate_node_config_dict(
262
+ self,
263
+ config_call_dict: Dict[str, Any],
264
+ fqn: List[str],
265
+ resource_type: NodeType,
266
+ project_name: str,
267
+ base: bool,
268
+ patch_config_dict: Optional[dict] = None,
269
+ ) -> Dict[str, Any]:
270
+ # TODO CT-211
271
+ return self.calculate_node_config(
272
+ config_call_dict=config_call_dict,
273
+ fqn=fqn,
274
+ resource_type=resource_type,
275
+ project_name=project_name,
276
+ base=base,
277
+ patch_config_dict=patch_config_dict,
278
+ ) # type: ignore[return-value]
279
+
280
+ def initial_result(self, resource_type: NodeType, base: bool) -> Dict[str, Any]:
281
+ return {}
282
+
283
+ def _update_from_config(
284
+ self,
285
+ result: Dict[str, Any],
286
+ partial: Dict[str, Any],
287
+ validate: bool = False,
288
+ ) -> Dict[str, Any]:
289
+ translated = self._active_project.credentials.translate_aliases(partial)
290
+ result.update(translated)
291
+ return result
292
+
293
+
294
+ class ContextConfig:
295
+ def __init__(
296
+ self,
297
+ active_project: RuntimeConfig,
298
+ fqn: List[str],
299
+ resource_type: NodeType,
300
+ project_name: str,
301
+ ) -> None:
302
+ self._config_call_dict: Dict[str, Any] = {}
303
+ self._unrendered_config_call_dict: Dict[str, Any] = {}
304
+ self._active_project = active_project
305
+ self._fqn = fqn
306
+ self._resource_type = resource_type
307
+ self._project_name = project_name
308
+
309
+ def add_config_call(self, opts: Dict[str, Any]) -> None:
310
+ dct = self._config_call_dict
311
+ merge_config_dicts(dct, opts)
312
+
313
+ def add_unrendered_config_call(self, opts: Dict[str, Any]) -> None:
314
+ # Cannot perform complex merge behaviours on unrendered configs as they may not be appropriate types.
315
+ self._unrendered_config_call_dict.update(opts)
316
+
317
+ def build_config_dict(
318
+ self,
319
+ base: bool = False,
320
+ *,
321
+ rendered: bool = True,
322
+ patch_config_dict: Optional[dict] = None,
323
+ ) -> Dict[str, Any]:
324
+ if rendered:
325
+ # TODO CT-211
326
+ src = ContextConfigGenerator(self._active_project) # type: ignore[var-annotated]
327
+ config_call_dict = self._config_call_dict
328
+ else:
329
+ # TODO CT-211
330
+ src = UnrenderedConfigGenerator(self._active_project) # type: ignore[assignment]
331
+
332
+ # preserve legacy behaviour - using unreliable (potentially rendered) _config_call_dict
333
+ if get_flags().state_modified_compare_more_unrendered_values is False:
334
+ config_call_dict = self._config_call_dict
335
+ else:
336
+ # Prefer _config_call_dict if it is available and _unrendered_config_call_dict is not,
337
+ # as _unrendered_config_call_dict is unreliable for non-sql nodes (e.g. no jinja config block rendered for python models, etc)
338
+ if self._config_call_dict and not self._unrendered_config_call_dict:
339
+ config_call_dict = self._config_call_dict
340
+ else:
341
+ config_call_dict = self._unrendered_config_call_dict
342
+
343
+ return src.calculate_node_config_dict(
344
+ config_call_dict=config_call_dict,
345
+ fqn=self._fqn,
346
+ resource_type=self._resource_type,
347
+ project_name=self._project_name,
348
+ base=base,
349
+ patch_config_dict=patch_config_dict,
350
+ )
dvt/context/docs.py ADDED
@@ -0,0 +1,82 @@
1
+ from typing import Any, Dict, Union
2
+
3
+ from dvt.config.runtime import RuntimeConfig
4
+ from dvt.context.base import contextmember
5
+ from dvt.context.configured import SchemaYamlContext
6
+ from dvt.contracts.graph.manifest import Manifest
7
+ from dvt.contracts.graph.nodes import Macro, ResultNode
8
+ from dvt.exceptions import DocArgsError, DocTargetNotFoundError
9
+
10
+
11
+ class DocsRuntimeContext(SchemaYamlContext):
12
+ def __init__(
13
+ self,
14
+ config: RuntimeConfig,
15
+ node: Union[Macro, ResultNode],
16
+ manifest: Manifest,
17
+ current_project: str,
18
+ ) -> None:
19
+ super().__init__(config, current_project, None)
20
+ self.node = node
21
+ self.manifest = manifest
22
+
23
+ @contextmember()
24
+ def doc(self, *args: str) -> str:
25
+ """The `doc` function is used to reference docs blocks in schema.yml
26
+ files. It is analogous to the `ref` function. For more information,
27
+ consult the Documentation guide.
28
+
29
+ > orders.md:
30
+
31
+ {% docs orders %}
32
+ # docs
33
+ - go
34
+ - here
35
+ {% enddocs %}
36
+
37
+ > schema.yml
38
+
39
+ version: 2
40
+ models:
41
+ - name: orders
42
+ description: "{{ doc('orders') }}"
43
+ """
44
+ # when you call doc(), this is what happens at runtime
45
+ if len(args) == 1:
46
+ doc_package_name = None
47
+ doc_name = args[0]
48
+ elif len(args) == 2:
49
+ doc_package_name, doc_name = args
50
+ else:
51
+ raise DocArgsError(self.node, args)
52
+
53
+ # Documentation
54
+ target_doc = self.manifest.resolve_doc(
55
+ doc_name,
56
+ doc_package_name,
57
+ self._project_name,
58
+ self.node.package_name,
59
+ )
60
+ if target_doc:
61
+ file_id = target_doc.file_id
62
+ if file_id in self.manifest.files:
63
+ source_file = self.manifest.files[file_id]
64
+ # TODO CT-211
65
+ source_file.add_node(self.node.unique_id) # type: ignore[union-attr]
66
+ else:
67
+ raise DocTargetNotFoundError(
68
+ node=self.node, target_doc_name=doc_name, target_doc_package=doc_package_name
69
+ )
70
+
71
+ return target_doc.block_contents
72
+
73
+
74
+ def generate_runtime_docs_context(
75
+ config: RuntimeConfig,
76
+ target: Any,
77
+ manifest: Manifest,
78
+ current_project: str,
79
+ ) -> Dict[str, Any]:
80
+ ctx = DocsRuntimeContext(config, target, manifest, current_project)
81
+ # This is not a Mashumaro to_dict call
82
+ return ctx.to_dict()