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.
- dvt/__init__.py +7 -0
- dvt/_pydantic_shim.py +26 -0
- dvt/adapters/__init__.py +16 -0
- dvt/adapters/multi_adapter_manager.py +268 -0
- dvt/artifacts/__init__.py +0 -0
- dvt/artifacts/exceptions/__init__.py +1 -0
- dvt/artifacts/exceptions/schemas.py +31 -0
- dvt/artifacts/resources/__init__.py +116 -0
- dvt/artifacts/resources/base.py +68 -0
- dvt/artifacts/resources/types.py +93 -0
- dvt/artifacts/resources/v1/analysis.py +10 -0
- dvt/artifacts/resources/v1/catalog.py +23 -0
- dvt/artifacts/resources/v1/components.py +275 -0
- dvt/artifacts/resources/v1/config.py +282 -0
- dvt/artifacts/resources/v1/documentation.py +11 -0
- dvt/artifacts/resources/v1/exposure.py +52 -0
- dvt/artifacts/resources/v1/function.py +53 -0
- dvt/artifacts/resources/v1/generic_test.py +32 -0
- dvt/artifacts/resources/v1/group.py +22 -0
- dvt/artifacts/resources/v1/hook.py +11 -0
- dvt/artifacts/resources/v1/macro.py +30 -0
- dvt/artifacts/resources/v1/metric.py +173 -0
- dvt/artifacts/resources/v1/model.py +146 -0
- dvt/artifacts/resources/v1/owner.py +10 -0
- dvt/artifacts/resources/v1/saved_query.py +112 -0
- dvt/artifacts/resources/v1/seed.py +42 -0
- dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dvt/artifacts/resources/v1/semantic_model.py +315 -0
- dvt/artifacts/resources/v1/singular_test.py +14 -0
- dvt/artifacts/resources/v1/snapshot.py +92 -0
- dvt/artifacts/resources/v1/source_definition.py +85 -0
- dvt/artifacts/resources/v1/sql_operation.py +10 -0
- dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
- dvt/artifacts/schemas/__init__.py +0 -0
- dvt/artifacts/schemas/base.py +191 -0
- dvt/artifacts/schemas/batch_results.py +24 -0
- dvt/artifacts/schemas/catalog/__init__.py +12 -0
- dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
- dvt/artifacts/schemas/freshness/__init__.py +1 -0
- dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
- dvt/artifacts/schemas/manifest/__init__.py +2 -0
- dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
- dvt/artifacts/schemas/results.py +148 -0
- dvt/artifacts/schemas/run/__init__.py +2 -0
- dvt/artifacts/schemas/run/v5/__init__.py +0 -0
- dvt/artifacts/schemas/run/v5/run.py +184 -0
- dvt/artifacts/schemas/upgrades/__init__.py +4 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dvt/artifacts/utils/validation.py +153 -0
- dvt/cli/__init__.py +1 -0
- dvt/cli/context.py +16 -0
- dvt/cli/exceptions.py +56 -0
- dvt/cli/flags.py +558 -0
- dvt/cli/main.py +971 -0
- dvt/cli/option_types.py +121 -0
- dvt/cli/options.py +79 -0
- dvt/cli/params.py +803 -0
- dvt/cli/requires.py +478 -0
- dvt/cli/resolvers.py +32 -0
- dvt/cli/types.py +40 -0
- dvt/clients/__init__.py +0 -0
- dvt/clients/checked_load.py +82 -0
- dvt/clients/git.py +164 -0
- dvt/clients/jinja.py +206 -0
- dvt/clients/jinja_static.py +245 -0
- dvt/clients/registry.py +192 -0
- dvt/clients/yaml_helper.py +68 -0
- dvt/compilation.py +833 -0
- dvt/compute/__init__.py +26 -0
- dvt/compute/base.py +288 -0
- dvt/compute/engines/__init__.py +13 -0
- dvt/compute/engines/duckdb_engine.py +368 -0
- dvt/compute/engines/spark_engine.py +273 -0
- dvt/compute/query_analyzer.py +212 -0
- dvt/compute/router.py +483 -0
- dvt/config/__init__.py +4 -0
- dvt/config/catalogs.py +95 -0
- dvt/config/compute_config.py +406 -0
- dvt/config/profile.py +411 -0
- dvt/config/profiles_v2.py +464 -0
- dvt/config/project.py +893 -0
- dvt/config/renderer.py +232 -0
- dvt/config/runtime.py +491 -0
- dvt/config/selectors.py +209 -0
- dvt/config/utils.py +78 -0
- dvt/connectors/.gitignore +6 -0
- dvt/connectors/README.md +306 -0
- dvt/connectors/catalog.yml +217 -0
- dvt/connectors/download_connectors.py +300 -0
- dvt/constants.py +29 -0
- dvt/context/__init__.py +0 -0
- dvt/context/base.py +746 -0
- dvt/context/configured.py +136 -0
- dvt/context/context_config.py +350 -0
- dvt/context/docs.py +82 -0
- dvt/context/exceptions_jinja.py +179 -0
- dvt/context/macro_resolver.py +195 -0
- dvt/context/macros.py +171 -0
- dvt/context/manifest.py +73 -0
- dvt/context/providers.py +2198 -0
- dvt/context/query_header.py +14 -0
- dvt/context/secret.py +59 -0
- dvt/context/target.py +74 -0
- dvt/contracts/__init__.py +0 -0
- dvt/contracts/files.py +413 -0
- dvt/contracts/graph/__init__.py +0 -0
- dvt/contracts/graph/manifest.py +1904 -0
- dvt/contracts/graph/metrics.py +98 -0
- dvt/contracts/graph/model_config.py +71 -0
- dvt/contracts/graph/node_args.py +42 -0
- dvt/contracts/graph/nodes.py +1806 -0
- dvt/contracts/graph/semantic_manifest.py +233 -0
- dvt/contracts/graph/unparsed.py +812 -0
- dvt/contracts/project.py +417 -0
- dvt/contracts/results.py +53 -0
- dvt/contracts/selection.py +23 -0
- dvt/contracts/sql.py +86 -0
- dvt/contracts/state.py +69 -0
- dvt/contracts/util.py +46 -0
- dvt/deprecations.py +347 -0
- dvt/deps/__init__.py +0 -0
- dvt/deps/base.py +153 -0
- dvt/deps/git.py +196 -0
- dvt/deps/local.py +80 -0
- dvt/deps/registry.py +131 -0
- dvt/deps/resolver.py +149 -0
- dvt/deps/tarball.py +121 -0
- dvt/docs/source/_ext/dbt_click.py +118 -0
- dvt/docs/source/conf.py +32 -0
- dvt/env_vars.py +64 -0
- dvt/event_time/event_time.py +40 -0
- dvt/event_time/sample_window.py +60 -0
- dvt/events/__init__.py +16 -0
- dvt/events/base_types.py +37 -0
- dvt/events/core_types_pb2.py +2 -0
- dvt/events/logging.py +109 -0
- dvt/events/types.py +2534 -0
- dvt/exceptions.py +1487 -0
- dvt/flags.py +89 -0
- dvt/graph/__init__.py +11 -0
- dvt/graph/cli.py +248 -0
- dvt/graph/graph.py +172 -0
- dvt/graph/queue.py +213 -0
- dvt/graph/selector.py +375 -0
- dvt/graph/selector_methods.py +976 -0
- dvt/graph/selector_spec.py +223 -0
- dvt/graph/thread_pool.py +18 -0
- dvt/hooks.py +21 -0
- dvt/include/README.md +49 -0
- dvt/include/__init__.py +3 -0
- dvt/include/global_project.py +4 -0
- dvt/include/starter_project/.gitignore +4 -0
- dvt/include/starter_project/README.md +15 -0
- dvt/include/starter_project/__init__.py +3 -0
- dvt/include/starter_project/analyses/.gitkeep +0 -0
- dvt/include/starter_project/dvt_project.yml +36 -0
- dvt/include/starter_project/macros/.gitkeep +0 -0
- dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dvt/include/starter_project/models/example/schema.yml +21 -0
- dvt/include/starter_project/seeds/.gitkeep +0 -0
- dvt/include/starter_project/snapshots/.gitkeep +0 -0
- dvt/include/starter_project/tests/.gitkeep +0 -0
- dvt/internal_deprecations.py +27 -0
- dvt/jsonschemas/__init__.py +3 -0
- dvt/jsonschemas/jsonschemas.py +309 -0
- dvt/jsonschemas/project/0.0.110.json +4717 -0
- dvt/jsonschemas/project/0.0.85.json +2015 -0
- dvt/jsonschemas/resources/0.0.110.json +2636 -0
- dvt/jsonschemas/resources/0.0.85.json +2536 -0
- dvt/jsonschemas/resources/latest.json +6773 -0
- dvt/links.py +4 -0
- dvt/materializations/__init__.py +0 -0
- dvt/materializations/incremental/__init__.py +0 -0
- dvt/materializations/incremental/microbatch.py +235 -0
- dvt/mp_context.py +8 -0
- dvt/node_types.py +37 -0
- dvt/parser/__init__.py +23 -0
- dvt/parser/analysis.py +21 -0
- dvt/parser/base.py +549 -0
- dvt/parser/common.py +267 -0
- dvt/parser/docs.py +52 -0
- dvt/parser/fixtures.py +51 -0
- dvt/parser/functions.py +30 -0
- dvt/parser/generic_test.py +100 -0
- dvt/parser/generic_test_builders.py +334 -0
- dvt/parser/hooks.py +119 -0
- dvt/parser/macros.py +137 -0
- dvt/parser/manifest.py +2204 -0
- dvt/parser/models.py +574 -0
- dvt/parser/partial.py +1179 -0
- dvt/parser/read_files.py +445 -0
- dvt/parser/schema_generic_tests.py +423 -0
- dvt/parser/schema_renderer.py +111 -0
- dvt/parser/schema_yaml_readers.py +936 -0
- dvt/parser/schemas.py +1467 -0
- dvt/parser/search.py +149 -0
- dvt/parser/seeds.py +28 -0
- dvt/parser/singular_test.py +20 -0
- dvt/parser/snapshots.py +44 -0
- dvt/parser/sources.py +557 -0
- dvt/parser/sql.py +63 -0
- dvt/parser/unit_tests.py +622 -0
- dvt/plugins/__init__.py +20 -0
- dvt/plugins/contracts.py +10 -0
- dvt/plugins/exceptions.py +2 -0
- dvt/plugins/manager.py +164 -0
- dvt/plugins/manifest.py +21 -0
- dvt/profiler.py +20 -0
- dvt/py.typed +1 -0
- dvt/runners/__init__.py +2 -0
- dvt/runners/exposure_runner.py +7 -0
- dvt/runners/no_op_runner.py +46 -0
- dvt/runners/saved_query_runner.py +7 -0
- dvt/selected_resources.py +8 -0
- dvt/task/__init__.py +0 -0
- dvt/task/base.py +504 -0
- dvt/task/build.py +197 -0
- dvt/task/clean.py +57 -0
- dvt/task/clone.py +162 -0
- dvt/task/compile.py +151 -0
- dvt/task/compute.py +366 -0
- dvt/task/debug.py +650 -0
- dvt/task/deps.py +280 -0
- dvt/task/docs/__init__.py +3 -0
- dvt/task/docs/generate.py +408 -0
- dvt/task/docs/index.html +250 -0
- dvt/task/docs/serve.py +28 -0
- dvt/task/freshness.py +323 -0
- dvt/task/function.py +122 -0
- dvt/task/group_lookup.py +46 -0
- dvt/task/init.py +374 -0
- dvt/task/list.py +237 -0
- dvt/task/printer.py +176 -0
- dvt/task/profiles.py +256 -0
- dvt/task/retry.py +175 -0
- dvt/task/run.py +1146 -0
- dvt/task/run_operation.py +142 -0
- dvt/task/runnable.py +802 -0
- dvt/task/seed.py +104 -0
- dvt/task/show.py +150 -0
- dvt/task/snapshot.py +57 -0
- dvt/task/sql.py +111 -0
- dvt/task/test.py +464 -0
- dvt/tests/fixtures/__init__.py +1 -0
- dvt/tests/fixtures/project.py +620 -0
- dvt/tests/util.py +651 -0
- dvt/tracking.py +529 -0
- dvt/utils/__init__.py +3 -0
- dvt/utils/artifact_upload.py +151 -0
- dvt/utils/utils.py +408 -0
- dvt/version.py +249 -0
- dvt_core-1.11.0b4.dist-info/METADATA +252 -0
- dvt_core-1.11.0b4.dist-info/RECORD +261 -0
- dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
- dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
- 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()
|