dvt-core 0.59.0a51__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.
- dbt/__init__.py +7 -0
- dbt/_pydantic_shim.py +26 -0
- dbt/artifacts/__init__.py +0 -0
- dbt/artifacts/exceptions/__init__.py +1 -0
- dbt/artifacts/exceptions/schemas.py +31 -0
- dbt/artifacts/resources/__init__.py +116 -0
- dbt/artifacts/resources/base.py +67 -0
- dbt/artifacts/resources/types.py +93 -0
- dbt/artifacts/resources/v1/analysis.py +10 -0
- dbt/artifacts/resources/v1/catalog.py +23 -0
- dbt/artifacts/resources/v1/components.py +274 -0
- dbt/artifacts/resources/v1/config.py +277 -0
- dbt/artifacts/resources/v1/documentation.py +11 -0
- dbt/artifacts/resources/v1/exposure.py +51 -0
- dbt/artifacts/resources/v1/function.py +52 -0
- dbt/artifacts/resources/v1/generic_test.py +31 -0
- dbt/artifacts/resources/v1/group.py +21 -0
- dbt/artifacts/resources/v1/hook.py +11 -0
- dbt/artifacts/resources/v1/macro.py +29 -0
- dbt/artifacts/resources/v1/metric.py +172 -0
- dbt/artifacts/resources/v1/model.py +145 -0
- dbt/artifacts/resources/v1/owner.py +10 -0
- dbt/artifacts/resources/v1/saved_query.py +111 -0
- dbt/artifacts/resources/v1/seed.py +41 -0
- dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dbt/artifacts/resources/v1/semantic_model.py +314 -0
- dbt/artifacts/resources/v1/singular_test.py +14 -0
- dbt/artifacts/resources/v1/snapshot.py +91 -0
- dbt/artifacts/resources/v1/source_definition.py +84 -0
- dbt/artifacts/resources/v1/sql_operation.py +10 -0
- dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
- dbt/artifacts/schemas/__init__.py +0 -0
- dbt/artifacts/schemas/base.py +191 -0
- dbt/artifacts/schemas/batch_results.py +24 -0
- dbt/artifacts/schemas/catalog/__init__.py +11 -0
- dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
- dbt/artifacts/schemas/freshness/__init__.py +1 -0
- dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
- dbt/artifacts/schemas/manifest/__init__.py +2 -0
- dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
- dbt/artifacts/schemas/results.py +147 -0
- dbt/artifacts/schemas/run/__init__.py +2 -0
- dbt/artifacts/schemas/run/v5/__init__.py +0 -0
- dbt/artifacts/schemas/run/v5/run.py +184 -0
- dbt/artifacts/schemas/upgrades/__init__.py +4 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dbt/artifacts/utils/validation.py +153 -0
- dbt/cli/__init__.py +1 -0
- dbt/cli/context.py +17 -0
- dbt/cli/exceptions.py +57 -0
- dbt/cli/flags.py +560 -0
- dbt/cli/main.py +2660 -0
- dbt/cli/option_types.py +121 -0
- dbt/cli/options.py +80 -0
- dbt/cli/params.py +844 -0
- dbt/cli/requires.py +490 -0
- dbt/cli/resolvers.py +60 -0
- dbt/cli/types.py +40 -0
- dbt/clients/__init__.py +0 -0
- dbt/clients/checked_load.py +83 -0
- dbt/clients/git.py +164 -0
- dbt/clients/jinja.py +206 -0
- dbt/clients/jinja_static.py +245 -0
- dbt/clients/registry.py +192 -0
- dbt/clients/yaml_helper.py +68 -0
- dbt/compilation.py +876 -0
- dbt/compute/__init__.py +14 -0
- dbt/compute/engines/__init__.py +12 -0
- dbt/compute/engines/spark_engine.py +642 -0
- dbt/compute/federated_executor.py +1080 -0
- dbt/compute/filter_pushdown.py +273 -0
- dbt/compute/jar_provisioning.py +273 -0
- dbt/compute/java_compat.py +689 -0
- dbt/compute/jdbc_utils.py +1252 -0
- dbt/compute/metadata/__init__.py +63 -0
- dbt/compute/metadata/adapters_registry.py +370 -0
- dbt/compute/metadata/catalog_store.py +1036 -0
- dbt/compute/metadata/registry.py +674 -0
- dbt/compute/metadata/store.py +1020 -0
- dbt/compute/smart_selector.py +377 -0
- dbt/compute/spark_logger.py +272 -0
- dbt/compute/strategies/__init__.py +55 -0
- dbt/compute/strategies/base.py +165 -0
- dbt/compute/strategies/dataproc.py +207 -0
- dbt/compute/strategies/emr.py +203 -0
- dbt/compute/strategies/local.py +472 -0
- dbt/compute/strategies/standalone.py +262 -0
- dbt/config/__init__.py +4 -0
- dbt/config/catalogs.py +94 -0
- dbt/config/compute.py +513 -0
- dbt/config/dvt_profile.py +408 -0
- dbt/config/profile.py +422 -0
- dbt/config/project.py +888 -0
- dbt/config/project_utils.py +48 -0
- dbt/config/renderer.py +231 -0
- dbt/config/runtime.py +564 -0
- dbt/config/selectors.py +208 -0
- dbt/config/utils.py +77 -0
- dbt/constants.py +28 -0
- dbt/context/__init__.py +0 -0
- dbt/context/base.py +745 -0
- dbt/context/configured.py +135 -0
- dbt/context/context_config.py +382 -0
- dbt/context/docs.py +82 -0
- dbt/context/exceptions_jinja.py +178 -0
- dbt/context/macro_resolver.py +195 -0
- dbt/context/macros.py +171 -0
- dbt/context/manifest.py +72 -0
- dbt/context/providers.py +2249 -0
- dbt/context/query_header.py +13 -0
- dbt/context/secret.py +58 -0
- dbt/context/target.py +74 -0
- dbt/contracts/__init__.py +0 -0
- dbt/contracts/files.py +413 -0
- dbt/contracts/graph/__init__.py +0 -0
- dbt/contracts/graph/manifest.py +1904 -0
- dbt/contracts/graph/metrics.py +97 -0
- dbt/contracts/graph/model_config.py +70 -0
- dbt/contracts/graph/node_args.py +42 -0
- dbt/contracts/graph/nodes.py +1806 -0
- dbt/contracts/graph/semantic_manifest.py +232 -0
- dbt/contracts/graph/unparsed.py +811 -0
- dbt/contracts/project.py +419 -0
- dbt/contracts/results.py +53 -0
- dbt/contracts/selection.py +23 -0
- dbt/contracts/sql.py +85 -0
- dbt/contracts/state.py +68 -0
- dbt/contracts/util.py +46 -0
- dbt/deprecations.py +348 -0
- dbt/deps/__init__.py +0 -0
- dbt/deps/base.py +152 -0
- dbt/deps/git.py +195 -0
- dbt/deps/local.py +79 -0
- dbt/deps/registry.py +130 -0
- dbt/deps/resolver.py +149 -0
- dbt/deps/tarball.py +120 -0
- dbt/docs/source/_ext/dbt_click.py +119 -0
- dbt/docs/source/conf.py +32 -0
- dbt/env_vars.py +64 -0
- dbt/event_time/event_time.py +40 -0
- dbt/event_time/sample_window.py +60 -0
- dbt/events/__init__.py +15 -0
- dbt/events/base_types.py +36 -0
- dbt/events/core_types_pb2.py +2 -0
- dbt/events/logging.py +108 -0
- dbt/events/types.py +2516 -0
- dbt/exceptions.py +1486 -0
- dbt/flags.py +89 -0
- dbt/graph/__init__.py +11 -0
- dbt/graph/cli.py +249 -0
- dbt/graph/graph.py +172 -0
- dbt/graph/queue.py +214 -0
- dbt/graph/selector.py +374 -0
- dbt/graph/selector_methods.py +975 -0
- dbt/graph/selector_spec.py +222 -0
- dbt/graph/thread_pool.py +18 -0
- dbt/hooks.py +21 -0
- dbt/include/README.md +49 -0
- dbt/include/__init__.py +3 -0
- dbt/include/data/adapters_registry.duckdb +0 -0
- dbt/include/data/build_comprehensive_registry.py +1254 -0
- dbt/include/data/build_registry.py +242 -0
- dbt/include/data/csv/adapter_queries.csv +33 -0
- dbt/include/data/csv/syntax_rules.csv +9 -0
- dbt/include/data/csv/type_mappings_bigquery.csv +28 -0
- dbt/include/data/csv/type_mappings_databricks.csv +30 -0
- dbt/include/data/csv/type_mappings_mysql.csv +40 -0
- dbt/include/data/csv/type_mappings_oracle.csv +30 -0
- dbt/include/data/csv/type_mappings_postgres.csv +56 -0
- dbt/include/data/csv/type_mappings_redshift.csv +33 -0
- dbt/include/data/csv/type_mappings_snowflake.csv +38 -0
- dbt/include/data/csv/type_mappings_sqlserver.csv +35 -0
- dbt/include/dvt_starter_project/README.md +15 -0
- dbt/include/dvt_starter_project/__init__.py +3 -0
- dbt/include/dvt_starter_project/analyses/PLACEHOLDER +0 -0
- dbt/include/dvt_starter_project/dvt_project.yml +39 -0
- dbt/include/dvt_starter_project/logs/PLACEHOLDER +0 -0
- dbt/include/dvt_starter_project/macros/PLACEHOLDER +0 -0
- dbt/include/dvt_starter_project/models/example/my_first_dbt_model.sql +27 -0
- dbt/include/dvt_starter_project/models/example/my_second_dbt_model.sql +6 -0
- dbt/include/dvt_starter_project/models/example/schema.yml +21 -0
- dbt/include/dvt_starter_project/seeds/PLACEHOLDER +0 -0
- dbt/include/dvt_starter_project/snapshots/PLACEHOLDER +0 -0
- dbt/include/dvt_starter_project/tests/PLACEHOLDER +0 -0
- dbt/internal_deprecations.py +26 -0
- dbt/jsonschemas/__init__.py +3 -0
- dbt/jsonschemas/jsonschemas.py +309 -0
- dbt/jsonschemas/project/0.0.110.json +4717 -0
- dbt/jsonschemas/project/0.0.85.json +2015 -0
- dbt/jsonschemas/resources/0.0.110.json +2636 -0
- dbt/jsonschemas/resources/0.0.85.json +2536 -0
- dbt/jsonschemas/resources/latest.json +6773 -0
- dbt/links.py +4 -0
- dbt/materializations/__init__.py +0 -0
- dbt/materializations/incremental/__init__.py +0 -0
- dbt/materializations/incremental/microbatch.py +236 -0
- dbt/mp_context.py +8 -0
- dbt/node_types.py +37 -0
- dbt/parser/__init__.py +23 -0
- dbt/parser/analysis.py +21 -0
- dbt/parser/base.py +548 -0
- dbt/parser/common.py +266 -0
- dbt/parser/docs.py +52 -0
- dbt/parser/fixtures.py +51 -0
- dbt/parser/functions.py +30 -0
- dbt/parser/generic_test.py +100 -0
- dbt/parser/generic_test_builders.py +333 -0
- dbt/parser/hooks.py +122 -0
- dbt/parser/macros.py +137 -0
- dbt/parser/manifest.py +2208 -0
- dbt/parser/models.py +573 -0
- dbt/parser/partial.py +1178 -0
- dbt/parser/read_files.py +445 -0
- dbt/parser/schema_generic_tests.py +422 -0
- dbt/parser/schema_renderer.py +111 -0
- dbt/parser/schema_yaml_readers.py +935 -0
- dbt/parser/schemas.py +1466 -0
- dbt/parser/search.py +149 -0
- dbt/parser/seeds.py +28 -0
- dbt/parser/singular_test.py +20 -0
- dbt/parser/snapshots.py +44 -0
- dbt/parser/sources.py +558 -0
- dbt/parser/sql.py +62 -0
- dbt/parser/unit_tests.py +621 -0
- dbt/plugins/__init__.py +20 -0
- dbt/plugins/contracts.py +9 -0
- dbt/plugins/exceptions.py +2 -0
- dbt/plugins/manager.py +163 -0
- dbt/plugins/manifest.py +21 -0
- dbt/profiler.py +20 -0
- dbt/py.typed +1 -0
- dbt/query_analyzer.py +410 -0
- dbt/runners/__init__.py +2 -0
- dbt/runners/exposure_runner.py +7 -0
- dbt/runners/no_op_runner.py +45 -0
- dbt/runners/saved_query_runner.py +7 -0
- dbt/selected_resources.py +8 -0
- dbt/task/__init__.py +0 -0
- dbt/task/base.py +506 -0
- dbt/task/build.py +197 -0
- dbt/task/clean.py +56 -0
- dbt/task/clone.py +161 -0
- dbt/task/compile.py +150 -0
- dbt/task/compute.py +458 -0
- dbt/task/debug.py +513 -0
- dbt/task/deps.py +280 -0
- dbt/task/docs/__init__.py +3 -0
- dbt/task/docs/api/__init__.py +23 -0
- dbt/task/docs/api/catalog.py +204 -0
- dbt/task/docs/api/lineage.py +234 -0
- dbt/task/docs/api/profile.py +204 -0
- dbt/task/docs/api/spark.py +186 -0
- dbt/task/docs/generate.py +1002 -0
- dbt/task/docs/index.html +250 -0
- dbt/task/docs/serve.py +174 -0
- dbt/task/dvt_output.py +509 -0
- dbt/task/dvt_run.py +282 -0
- dbt/task/dvt_seed.py +806 -0
- dbt/task/freshness.py +322 -0
- dbt/task/function.py +121 -0
- dbt/task/group_lookup.py +46 -0
- dbt/task/init.py +1022 -0
- dbt/task/java.py +316 -0
- dbt/task/list.py +236 -0
- dbt/task/metadata.py +804 -0
- dbt/task/migrate.py +714 -0
- dbt/task/printer.py +175 -0
- dbt/task/profile.py +1489 -0
- dbt/task/profile_serve.py +662 -0
- dbt/task/retract.py +441 -0
- dbt/task/retry.py +175 -0
- dbt/task/run.py +1647 -0
- dbt/task/run_operation.py +141 -0
- dbt/task/runnable.py +758 -0
- dbt/task/seed.py +103 -0
- dbt/task/show.py +149 -0
- dbt/task/snapshot.py +56 -0
- dbt/task/spark.py +414 -0
- dbt/task/sql.py +110 -0
- dbt/task/target_sync.py +814 -0
- dbt/task/test.py +464 -0
- dbt/tests/fixtures/__init__.py +1 -0
- dbt/tests/fixtures/project.py +620 -0
- dbt/tests/util.py +651 -0
- dbt/tracking.py +529 -0
- dbt/utils/__init__.py +3 -0
- dbt/utils/artifact_upload.py +151 -0
- dbt/utils/utils.py +408 -0
- dbt/version.py +271 -0
- dvt_cli/__init__.py +158 -0
- dvt_core-0.59.0a51.dist-info/METADATA +288 -0
- dvt_core-0.59.0a51.dist-info/RECORD +299 -0
- dvt_core-0.59.0a51.dist-info/WHEEL +5 -0
- dvt_core-0.59.0a51.dist-info/entry_points.txt +2 -0
- dvt_core-0.59.0a51.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Utility functions for working with dbt project files."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_project_profile_name(project_dir) -> Optional[str]:
|
|
9
|
+
"""
|
|
10
|
+
Read profile name from dvt_project.yml or dbt_project.yml in the given directory.
|
|
11
|
+
|
|
12
|
+
v0.59.0a17+: Check dvt_project.yml first (DVT projects), then fall back to
|
|
13
|
+
dbt_project.yml (legacy dbt projects). Used by ALL DVT commands for
|
|
14
|
+
project-aware profile detection.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
project_dir: Path to the project directory (Path or str)
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Profile name if found, None otherwise
|
|
21
|
+
"""
|
|
22
|
+
# v0.59.0a18: Handle both Path and string input
|
|
23
|
+
if project_dir is None:
|
|
24
|
+
project_dir = Path.cwd()
|
|
25
|
+
elif not isinstance(project_dir, Path):
|
|
26
|
+
project_dir = Path(project_dir)
|
|
27
|
+
|
|
28
|
+
# Check dvt_project.yml first, then dbt_project.yml
|
|
29
|
+
project_files = ["dvt_project.yml", "dbt_project.yml"]
|
|
30
|
+
|
|
31
|
+
for project_file in project_files:
|
|
32
|
+
try:
|
|
33
|
+
project_yml_path = project_dir / project_file
|
|
34
|
+
if not project_yml_path.exists():
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
with open(project_yml_path) as f:
|
|
38
|
+
project = yaml.safe_load(f)
|
|
39
|
+
if project and "profile" in project:
|
|
40
|
+
return project.get("profile")
|
|
41
|
+
# If no explicit profile, use project name as profile name
|
|
42
|
+
if project and "name" in project:
|
|
43
|
+
return project.get("name")
|
|
44
|
+
except Exception:
|
|
45
|
+
# Silently handle any YAML parsing or file read errors
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
return None
|
dbt/config/renderer.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import date
|
|
3
|
+
from typing import Any, Callable, Dict, Optional, Tuple, Union
|
|
4
|
+
|
|
5
|
+
from dbt.adapters.contracts.connection import HasCredentials
|
|
6
|
+
from dbt.clients.jinja import get_rendered
|
|
7
|
+
from dbt.constants import DEPENDENCIES_FILE_NAME, SECRET_PLACEHOLDER
|
|
8
|
+
from dbt.context.base import BaseContext
|
|
9
|
+
from dbt.context.secret import SecretContext
|
|
10
|
+
from dbt.context.target import TargetContext
|
|
11
|
+
from dbt.exceptions import DbtProjectError
|
|
12
|
+
from dbt_common.clients.jinja import catch_jinja
|
|
13
|
+
from dbt_common.constants import SECRET_ENV_PREFIX
|
|
14
|
+
from dbt_common.context import get_invocation_context
|
|
15
|
+
from dbt_common.exceptions import CompilationError, RecursionError
|
|
16
|
+
from dbt_common.utils import deep_map_render
|
|
17
|
+
|
|
18
|
+
Keypath = Tuple[Union[str, int], ...]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseRenderer:
|
|
22
|
+
def __init__(self, context: Dict[str, Any]) -> None:
|
|
23
|
+
self.context = context
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def name(self):
|
|
27
|
+
return "Rendering"
|
|
28
|
+
|
|
29
|
+
def should_render_keypath(self, keypath: Keypath) -> bool:
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
def render_entry(self, value: Any, keypath: Keypath) -> Any:
|
|
33
|
+
if not self.should_render_keypath(keypath):
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
return self.render_value(value, keypath)
|
|
37
|
+
|
|
38
|
+
def render_value(self, value: Any, keypath: Optional[Keypath] = None) -> Any:
|
|
39
|
+
# keypath is ignored (and someone who knows should explain why here)
|
|
40
|
+
if not isinstance(value, str):
|
|
41
|
+
return value if not isinstance(value, date) else value.isoformat()
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
with catch_jinja():
|
|
45
|
+
return get_rendered(value, self.context, native=True)
|
|
46
|
+
except CompilationError as exc:
|
|
47
|
+
msg = f"Could not render {value}: {exc.msg}"
|
|
48
|
+
raise CompilationError(msg) from exc
|
|
49
|
+
|
|
50
|
+
def render_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
51
|
+
try:
|
|
52
|
+
return deep_map_render(self.render_entry, data)
|
|
53
|
+
except RecursionError:
|
|
54
|
+
raise DbtProjectError(
|
|
55
|
+
f"Cycle detected: {self.name} input has a reference to itself", project=data
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _list_if_none(value):
|
|
60
|
+
if value is None:
|
|
61
|
+
value = []
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _dict_if_none(value):
|
|
66
|
+
if value is None:
|
|
67
|
+
value = {}
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _list_if_none_or_string(value):
|
|
72
|
+
value = _list_if_none(value)
|
|
73
|
+
if isinstance(value, str):
|
|
74
|
+
return [value]
|
|
75
|
+
return value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ProjectPostprocessor(Dict[Keypath, Callable[[Any], Any]]):
|
|
79
|
+
def __init__(self) -> None:
|
|
80
|
+
super().__init__()
|
|
81
|
+
|
|
82
|
+
self[("on-run-start",)] = _list_if_none_or_string
|
|
83
|
+
self[("on-run-end",)] = _list_if_none_or_string
|
|
84
|
+
|
|
85
|
+
for k in ("models", "seeds", "snapshots"):
|
|
86
|
+
self[(k,)] = _dict_if_none
|
|
87
|
+
self[(k, "vars")] = _dict_if_none
|
|
88
|
+
self[(k, "pre-hook")] = _list_if_none_or_string
|
|
89
|
+
self[(k, "post-hook")] = _list_if_none_or_string
|
|
90
|
+
self[("seeds", "column_types")] = _dict_if_none
|
|
91
|
+
|
|
92
|
+
def postprocess(self, value: Any, key: Keypath) -> Any:
|
|
93
|
+
if key in self:
|
|
94
|
+
handler = self[key]
|
|
95
|
+
return handler(value)
|
|
96
|
+
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class DbtProjectYamlRenderer(BaseRenderer):
|
|
101
|
+
_KEYPATH_HANDLERS = ProjectPostprocessor()
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self, profile: Optional[HasCredentials] = None, cli_vars: Optional[Dict[str, Any]] = None
|
|
105
|
+
) -> None:
|
|
106
|
+
# Generate contexts here because we want to save the context
|
|
107
|
+
# object in order to retrieve the env_vars. This is almost always
|
|
108
|
+
# a TargetContext, but in the debug task we want a project
|
|
109
|
+
# even when we don't have a profile.
|
|
110
|
+
if cli_vars is None:
|
|
111
|
+
cli_vars = {}
|
|
112
|
+
if profile:
|
|
113
|
+
self.ctx_obj = TargetContext(profile.to_target_dict(), cli_vars)
|
|
114
|
+
else:
|
|
115
|
+
self.ctx_obj = BaseContext(cli_vars) # type:ignore
|
|
116
|
+
context = self.ctx_obj.to_dict()
|
|
117
|
+
super().__init__(context)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def name(self):
|
|
121
|
+
"Project config"
|
|
122
|
+
|
|
123
|
+
# Uses SecretRenderer
|
|
124
|
+
def get_package_renderer(self) -> BaseRenderer:
|
|
125
|
+
return PackageRenderer(self.ctx_obj.cli_vars)
|
|
126
|
+
|
|
127
|
+
def render_project(
|
|
128
|
+
self,
|
|
129
|
+
project: Dict[str, Any],
|
|
130
|
+
project_root: str,
|
|
131
|
+
) -> Dict[str, Any]:
|
|
132
|
+
"""Render the project and insert the project root after rendering."""
|
|
133
|
+
rendered_project = self.render_data(project)
|
|
134
|
+
rendered_project["project-root"] = project_root
|
|
135
|
+
return rendered_project
|
|
136
|
+
|
|
137
|
+
def render_packages(self, packages: Dict[str, Any], packages_specified_path: str):
|
|
138
|
+
"""Render the given packages dict"""
|
|
139
|
+
packages = packages or {} # Sometimes this is none in tests
|
|
140
|
+
package_renderer = self.get_package_renderer()
|
|
141
|
+
if packages_specified_path == DEPENDENCIES_FILE_NAME:
|
|
142
|
+
# We don't want to render the "packages" dictionary that came from dependencies.yml
|
|
143
|
+
return packages
|
|
144
|
+
else:
|
|
145
|
+
return package_renderer.render_data(packages)
|
|
146
|
+
|
|
147
|
+
def render_selectors(self, selectors: Dict[str, Any]):
|
|
148
|
+
return self.render_data(selectors)
|
|
149
|
+
|
|
150
|
+
def render_entry(self, value: Any, keypath: Keypath) -> Any:
|
|
151
|
+
result = super().render_entry(value, keypath)
|
|
152
|
+
return self._KEYPATH_HANDLERS.postprocess(result, keypath)
|
|
153
|
+
|
|
154
|
+
def should_render_keypath(self, keypath: Keypath) -> bool:
|
|
155
|
+
if not keypath:
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
first = keypath[0]
|
|
159
|
+
# run hooks are not rendered
|
|
160
|
+
if first in {"on-run-start", "on-run-end", "query-comment"}:
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
# don't render vars blocks until runtime
|
|
164
|
+
if first == "vars":
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
if first in {"seeds", "models", "snapshots", "tests", "data_tests"}:
|
|
168
|
+
keypath_parts = {(k.lstrip("+ ") if isinstance(k, str) else k) for k in keypath}
|
|
169
|
+
# model-level hooks
|
|
170
|
+
late_rendered_hooks = {"pre-hook", "post-hook", "pre_hook", "post_hook"}
|
|
171
|
+
if keypath_parts.intersection(late_rendered_hooks):
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
return True
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class SecretRenderer(BaseRenderer):
|
|
178
|
+
def __init__(self, cli_vars: Dict[str, Any] = {}) -> None:
|
|
179
|
+
# Generate contexts here because we want to save the context
|
|
180
|
+
# object in order to retrieve the env_vars.
|
|
181
|
+
self.ctx_obj = SecretContext(cli_vars)
|
|
182
|
+
context = self.ctx_obj.to_dict()
|
|
183
|
+
super().__init__(context)
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def name(self):
|
|
187
|
+
return "Secret"
|
|
188
|
+
|
|
189
|
+
def render_value(self, value: Any, keypath: Optional[Keypath] = None) -> Any:
|
|
190
|
+
# First, standard Jinja rendering, with special handling for 'secret' environment variables
|
|
191
|
+
# "{{ env_var('DBT_SECRET_ENV_VAR') }}" -> "$$$DBT_SECRET_START$$$DBT_SECRET_ENV_{VARIABLE_NAME}$$$DBT_SECRET_END$$$"
|
|
192
|
+
# This prevents Jinja manipulation of secrets via macros/filters that might leak partial/modified values in logs
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
rendered = super().render_value(value, keypath)
|
|
196
|
+
except Exception as ex:
|
|
197
|
+
if keypath and "password" in keypath:
|
|
198
|
+
# Passwords sometimes contain jinja-esque characters, but we
|
|
199
|
+
# don't want to render them if they aren't valid jinja.
|
|
200
|
+
rendered = value
|
|
201
|
+
else:
|
|
202
|
+
raise ex
|
|
203
|
+
|
|
204
|
+
# Now, detect instances of the placeholder value ($$$DBT_SECRET_START...DBT_SECRET_END$$$)
|
|
205
|
+
# and replace them with the actual secret value
|
|
206
|
+
if SECRET_ENV_PREFIX in str(rendered):
|
|
207
|
+
search_group = f"({SECRET_ENV_PREFIX}(.*))"
|
|
208
|
+
pattern = SECRET_PLACEHOLDER.format(search_group).replace("$", r"\$")
|
|
209
|
+
m = re.search(
|
|
210
|
+
pattern,
|
|
211
|
+
rendered,
|
|
212
|
+
)
|
|
213
|
+
if m:
|
|
214
|
+
found = m.group(1)
|
|
215
|
+
value = get_invocation_context().env[found]
|
|
216
|
+
replace_this = SECRET_PLACEHOLDER.format(found)
|
|
217
|
+
return rendered.replace(replace_this, value)
|
|
218
|
+
else:
|
|
219
|
+
return rendered
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ProfileRenderer(SecretRenderer):
|
|
223
|
+
@property
|
|
224
|
+
def name(self):
|
|
225
|
+
return "Profile"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class PackageRenderer(SecretRenderer):
|
|
229
|
+
@property
|
|
230
|
+
def name(self):
|
|
231
|
+
return "Packages config"
|