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,464 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Extended profile configuration for DVT unified profiles.
|
|
3
|
+
|
|
4
|
+
This module extends dbt's profile system to support:
|
|
5
|
+
- Multiple named profiles in one file (not just outputs)
|
|
6
|
+
- Profile references in sources
|
|
7
|
+
- Backward compatibility with dbt profiles
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict, Optional
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
|
|
16
|
+
from dbt_common.events.functions import fire_event
|
|
17
|
+
from dbt_common.events.types import Note
|
|
18
|
+
from dbt_common.exceptions import DbtRuntimeError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ProfileReference:
|
|
23
|
+
"""
|
|
24
|
+
Reference to a named profile.
|
|
25
|
+
|
|
26
|
+
This represents a connection configuration that can be used
|
|
27
|
+
as either a source or a target.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
name: str
|
|
31
|
+
adapter: str
|
|
32
|
+
credentials: Dict[str, Any]
|
|
33
|
+
threads: int = 4
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_dict(cls, name: str, data: Dict[str, Any]) -> "ProfileReference":
|
|
37
|
+
"""
|
|
38
|
+
Create ProfileReference from dictionary.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
name: Profile name
|
|
42
|
+
data: Profile configuration dictionary
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
ProfileReference instance
|
|
46
|
+
"""
|
|
47
|
+
# Extract adapter type (could be 'adapter' or 'type' for dbt compat)
|
|
48
|
+
adapter = data.get("adapter") or data.get("type")
|
|
49
|
+
if not adapter:
|
|
50
|
+
raise DbtRuntimeError(f"Profile '{name}' missing 'adapter' or 'type' field")
|
|
51
|
+
|
|
52
|
+
# Extract threads
|
|
53
|
+
threads = data.get("threads", 4)
|
|
54
|
+
|
|
55
|
+
# Everything else is credentials
|
|
56
|
+
credentials = {k: v for k, v in data.items() if k not in ("adapter", "type", "threads")}
|
|
57
|
+
|
|
58
|
+
return cls(
|
|
59
|
+
name=name,
|
|
60
|
+
adapter=adapter,
|
|
61
|
+
credentials=credentials,
|
|
62
|
+
threads=threads,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def to_connection_dict(self) -> Dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
Convert to connection dictionary for adapter.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Dictionary with adapter type and credentials
|
|
71
|
+
"""
|
|
72
|
+
return {
|
|
73
|
+
"type": self.adapter,
|
|
74
|
+
"threads": self.threads,
|
|
75
|
+
**self.credentials,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class UnifiedProfileConfig:
|
|
80
|
+
"""
|
|
81
|
+
Unified profile configuration supporting both DVT and dbt formats.
|
|
82
|
+
|
|
83
|
+
DVT format (unified profiles):
|
|
84
|
+
postgres_prod:
|
|
85
|
+
adapter: postgres
|
|
86
|
+
host: localhost
|
|
87
|
+
port: 5432
|
|
88
|
+
user: myuser
|
|
89
|
+
# ... more credentials
|
|
90
|
+
|
|
91
|
+
mysql_legacy:
|
|
92
|
+
adapter: mysql
|
|
93
|
+
host: legacy-db
|
|
94
|
+
# ... more credentials
|
|
95
|
+
|
|
96
|
+
dbt format (backward compatible):
|
|
97
|
+
my_project:
|
|
98
|
+
target: dev
|
|
99
|
+
outputs:
|
|
100
|
+
dev:
|
|
101
|
+
type: postgres
|
|
102
|
+
host: localhost
|
|
103
|
+
# ... more credentials
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, profiles: Dict[str, ProfileReference], dbt_profiles: Dict[str, Any]):
|
|
107
|
+
"""
|
|
108
|
+
Initialize UnifiedProfileConfig.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
profiles: Named profiles dictionary
|
|
112
|
+
dbt_profiles: dbt-style profiles (for backward compat)
|
|
113
|
+
"""
|
|
114
|
+
self.profiles = profiles
|
|
115
|
+
self.dbt_profiles = dbt_profiles
|
|
116
|
+
|
|
117
|
+
def get_profile(self, name: str) -> Optional[ProfileReference]:
|
|
118
|
+
"""
|
|
119
|
+
Get profile by name.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
name: Profile name
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
ProfileReference or None if not found
|
|
126
|
+
"""
|
|
127
|
+
return self.profiles.get(name)
|
|
128
|
+
|
|
129
|
+
def get_dbt_profile(self, name: str) -> Optional[Any]:
|
|
130
|
+
"""
|
|
131
|
+
Get dbt-style profile by name.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
name: Profile name
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
DbtProfile dict or None if not found
|
|
138
|
+
"""
|
|
139
|
+
return self.dbt_profiles.get(name)
|
|
140
|
+
|
|
141
|
+
def has_profile(self, name: str) -> bool:
|
|
142
|
+
"""Check if profile exists."""
|
|
143
|
+
return name in self.profiles or name in self.dbt_profiles
|
|
144
|
+
|
|
145
|
+
def list_profiles(self) -> list[str]:
|
|
146
|
+
"""List all available profile names."""
|
|
147
|
+
return sorted(set(list(self.profiles.keys()) + list(self.dbt_profiles.keys())))
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_dict(cls, data: Dict[str, Any]) -> "UnifiedProfileConfig":
|
|
151
|
+
"""
|
|
152
|
+
Parse profiles from dictionary.
|
|
153
|
+
|
|
154
|
+
Supports both DVT unified format and dbt format.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
data: Parsed YAML dictionary
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
UnifiedProfileConfig instance
|
|
161
|
+
"""
|
|
162
|
+
profiles: Dict[str, ProfileReference] = {}
|
|
163
|
+
dbt_profiles: Dict[str, DbtProfile] = {}
|
|
164
|
+
|
|
165
|
+
for name, config in data.items():
|
|
166
|
+
if isinstance(config, dict):
|
|
167
|
+
# Check if this is dbt format (has 'target' and 'outputs')
|
|
168
|
+
if "target" in config and "outputs" in config:
|
|
169
|
+
# This is a dbt-style profile - skip for now
|
|
170
|
+
# We'll handle these with dbt's Profile class
|
|
171
|
+
dbt_profiles[name] = config # Store raw config for now
|
|
172
|
+
else:
|
|
173
|
+
# Check if this has 'adapter' or 'type' field (unified format)
|
|
174
|
+
if "adapter" in config or "type" in config:
|
|
175
|
+
try:
|
|
176
|
+
profiles[name] = ProfileReference.from_dict(name, config)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
fire_event(Note(msg=f"Failed to parse profile '{name}': {e}"))
|
|
179
|
+
else:
|
|
180
|
+
# Could be a nested profile reference or other structure
|
|
181
|
+
# Check if it has a 'profile' key (reference to another profile)
|
|
182
|
+
if "profile" in config:
|
|
183
|
+
# This is a reference - handle in dbt compat layer
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
return cls(profiles=profiles, dbt_profiles=dbt_profiles)
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def load_from_file(cls, file_path: Path) -> "UnifiedProfileConfig":
|
|
190
|
+
"""
|
|
191
|
+
Load profiles from YAML file.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
file_path: Path to profiles.yml
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
UnifiedProfileConfig instance
|
|
198
|
+
|
|
199
|
+
Raises:
|
|
200
|
+
DbtRuntimeError: If file cannot be read or parsed
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
if not file_path.exists():
|
|
204
|
+
fire_event(Note(msg=f"Profiles file not found at {file_path}"))
|
|
205
|
+
return cls(profiles={}, dbt_profiles={})
|
|
206
|
+
|
|
207
|
+
with open(file_path, "r") as f:
|
|
208
|
+
data = yaml.safe_load(f)
|
|
209
|
+
|
|
210
|
+
if data is None:
|
|
211
|
+
fire_event(Note(msg=f"Empty profiles file at {file_path}"))
|
|
212
|
+
return cls(profiles={}, dbt_profiles={})
|
|
213
|
+
|
|
214
|
+
return cls.from_dict(data)
|
|
215
|
+
|
|
216
|
+
except yaml.YAMLError as e:
|
|
217
|
+
raise DbtRuntimeError(f"Failed to parse profiles: {e}")
|
|
218
|
+
except Exception as e:
|
|
219
|
+
raise DbtRuntimeError(f"Failed to load profiles from {file_path}: {e}")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def load_unified_profiles(project_dir: Optional[Path] = None) -> UnifiedProfileConfig:
|
|
223
|
+
"""
|
|
224
|
+
Load unified profiles from standard locations.
|
|
225
|
+
|
|
226
|
+
Searches in order:
|
|
227
|
+
1. <project_root>/profiles.yml
|
|
228
|
+
2. ~/.dbt/profiles.yml
|
|
229
|
+
3. Empty configuration
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
project_dir: Project directory (optional)
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
UnifiedProfileConfig instance
|
|
236
|
+
"""
|
|
237
|
+
# Try project directory first
|
|
238
|
+
if project_dir:
|
|
239
|
+
project_profiles = project_dir / "profiles.yml"
|
|
240
|
+
if project_profiles.exists():
|
|
241
|
+
fire_event(Note(msg=f"Loading profiles from {project_profiles}"))
|
|
242
|
+
return UnifiedProfileConfig.load_from_file(project_profiles)
|
|
243
|
+
|
|
244
|
+
# Try home directory (standard dbt location)
|
|
245
|
+
home_profiles = Path.home() / ".dbt" / "profiles.yml"
|
|
246
|
+
if home_profiles.exists():
|
|
247
|
+
fire_event(Note(msg=f"Loading profiles from {home_profiles}"))
|
|
248
|
+
return UnifiedProfileConfig.load_from_file(home_profiles)
|
|
249
|
+
|
|
250
|
+
# Empty configuration
|
|
251
|
+
fire_event(Note(msg="No profiles.yml found"))
|
|
252
|
+
return UnifiedProfileConfig(profiles={}, dbt_profiles={})
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def resolve_profile_reference(
|
|
256
|
+
profile_name: str,
|
|
257
|
+
unified_profiles: UnifiedProfileConfig,
|
|
258
|
+
) -> Optional[Dict[str, Any]]:
|
|
259
|
+
"""
|
|
260
|
+
Resolve a profile reference to connection configuration.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
profile_name: Name of profile to resolve
|
|
264
|
+
unified_profiles: Unified profile configuration
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Connection dictionary or None if not found
|
|
268
|
+
"""
|
|
269
|
+
# Try unified profile first
|
|
270
|
+
profile = unified_profiles.get_profile(profile_name)
|
|
271
|
+
if profile:
|
|
272
|
+
return profile.to_connection_dict()
|
|
273
|
+
|
|
274
|
+
# Try dbt profile
|
|
275
|
+
dbt_profile = unified_profiles.get_dbt_profile(profile_name)
|
|
276
|
+
if dbt_profile:
|
|
277
|
+
# Return raw config - will be processed by dbt's Profile class
|
|
278
|
+
return dbt_profile
|
|
279
|
+
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class ProfileRegistry:
|
|
284
|
+
"""
|
|
285
|
+
Registry for managing profile instances during execution.
|
|
286
|
+
|
|
287
|
+
This keeps track of all profiles referenced by sources and models,
|
|
288
|
+
and ensures they're properly initialized for use by compute engines.
|
|
289
|
+
|
|
290
|
+
Usage:
|
|
291
|
+
# In CLI commands
|
|
292
|
+
registry = ProfileRegistry(unified_profiles)
|
|
293
|
+
profile = registry.get_or_create_profile("postgres_prod")
|
|
294
|
+
|
|
295
|
+
# Test all profiles
|
|
296
|
+
results = registry.test_all_profiles()
|
|
297
|
+
|
|
298
|
+
# Get adapter for a profile
|
|
299
|
+
adapter = registry.get_adapter("postgres_prod", mp_context)
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
def __init__(self, unified_profiles: UnifiedProfileConfig):
|
|
303
|
+
"""
|
|
304
|
+
Initialize ProfileRegistry.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
unified_profiles: Unified profile configuration
|
|
308
|
+
"""
|
|
309
|
+
self.unified_profiles = unified_profiles
|
|
310
|
+
self._initialized_profiles: Dict[str, Any] = {}
|
|
311
|
+
self._adapters: Dict[str, Any] = {}
|
|
312
|
+
|
|
313
|
+
def get_or_create_profile(self, profile_name: str) -> Optional[Any]:
|
|
314
|
+
"""
|
|
315
|
+
Get or create profile instance.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
profile_name: Profile name
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Profile instance or None if not found
|
|
322
|
+
"""
|
|
323
|
+
# Check cache
|
|
324
|
+
if profile_name in self._initialized_profiles:
|
|
325
|
+
return self._initialized_profiles[profile_name]
|
|
326
|
+
|
|
327
|
+
# Resolve profile
|
|
328
|
+
profile_config = resolve_profile_reference(profile_name, self.unified_profiles)
|
|
329
|
+
if not profile_config:
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
# Create profile instance
|
|
333
|
+
# For now, store the config. Full adapter integration happens in get_adapter()
|
|
334
|
+
self._initialized_profiles[profile_name] = profile_config
|
|
335
|
+
|
|
336
|
+
return profile_config
|
|
337
|
+
|
|
338
|
+
def get_adapter(self, profile_name: str, mp_context: Optional[Any] = None) -> Optional[Any]:
|
|
339
|
+
"""
|
|
340
|
+
Get adapter instance for a profile.
|
|
341
|
+
|
|
342
|
+
This creates and caches adapter instances for profiles.
|
|
343
|
+
Requires multiprocessing context for adapter initialization.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
profile_name: Profile name
|
|
347
|
+
mp_context: Multiprocessing context (required for adapter creation)
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Adapter instance or None if profile not found
|
|
351
|
+
"""
|
|
352
|
+
# Check cache
|
|
353
|
+
if profile_name in self._adapters:
|
|
354
|
+
return self._adapters[profile_name]
|
|
355
|
+
|
|
356
|
+
# Get profile config
|
|
357
|
+
profile = self.get_or_create_profile(profile_name)
|
|
358
|
+
if not profile:
|
|
359
|
+
return None
|
|
360
|
+
|
|
361
|
+
# Need mp_context to create adapters
|
|
362
|
+
if not mp_context:
|
|
363
|
+
raise DbtRuntimeError(
|
|
364
|
+
"mp_context required to create adapter instances. "
|
|
365
|
+
"Use get_or_create_profile() for config-only access."
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Create adapter using MultiAdapterManager
|
|
369
|
+
from dvt.adapters.multi_adapter_manager import MultiAdapterManager
|
|
370
|
+
|
|
371
|
+
manager = MultiAdapterManager(self.unified_profiles, mp_context)
|
|
372
|
+
adapter = manager.get_or_create_adapter(profile_name)
|
|
373
|
+
self._adapters[profile_name] = adapter
|
|
374
|
+
|
|
375
|
+
return adapter
|
|
376
|
+
|
|
377
|
+
def list_all_profiles(self) -> list[str]:
|
|
378
|
+
"""
|
|
379
|
+
List all available profile names.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
List of profile names from unified profiles
|
|
383
|
+
"""
|
|
384
|
+
return self.unified_profiles.list_profiles()
|
|
385
|
+
|
|
386
|
+
def list_initialized_profiles(self) -> list[str]:
|
|
387
|
+
"""
|
|
388
|
+
List profile names that have been initialized.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
List of cached profile names
|
|
392
|
+
"""
|
|
393
|
+
return list(self._initialized_profiles.keys())
|
|
394
|
+
|
|
395
|
+
def test_profile(self, profile_name: str, mp_context: Optional[Any] = None) -> Dict[str, Any]:
|
|
396
|
+
"""
|
|
397
|
+
Test a single profile connection.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
profile_name: Profile name to test
|
|
401
|
+
mp_context: Multiprocessing context for adapter initialization
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Test result dictionary with 'success', 'message', and optional 'error' keys
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
# Get profile config
|
|
408
|
+
profile = self.get_or_create_profile(profile_name)
|
|
409
|
+
if not profile:
|
|
410
|
+
return {
|
|
411
|
+
"success": False,
|
|
412
|
+
"profile": profile_name,
|
|
413
|
+
"message": f"Profile '{profile_name}' not found",
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# If mp_context provided, test actual connection
|
|
417
|
+
if mp_context:
|
|
418
|
+
adapter = self.get_adapter(profile_name, mp_context)
|
|
419
|
+
if adapter:
|
|
420
|
+
# Try to connect
|
|
421
|
+
# Note: Different adapters have different connection methods
|
|
422
|
+
# For now, just verify adapter creation succeeded
|
|
423
|
+
return {
|
|
424
|
+
"success": True,
|
|
425
|
+
"profile": profile_name,
|
|
426
|
+
"adapter_type": getattr(profile, "adapter", "unknown"),
|
|
427
|
+
"message": "Profile configuration valid and adapter created",
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
# Without mp_context, just verify config is loadable
|
|
431
|
+
return {
|
|
432
|
+
"success": True,
|
|
433
|
+
"profile": profile_name,
|
|
434
|
+
"adapter_type": getattr(profile, "adapter", "unknown"),
|
|
435
|
+
"message": "Profile configuration valid",
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
except Exception as e:
|
|
439
|
+
return {
|
|
440
|
+
"success": False,
|
|
441
|
+
"profile": profile_name,
|
|
442
|
+
"message": f"Profile test failed: {str(e)}",
|
|
443
|
+
"error": str(e),
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
def test_all_profiles(self, mp_context: Optional[Any] = None) -> Dict[str, Dict[str, Any]]:
|
|
447
|
+
"""
|
|
448
|
+
Test all available profiles.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
mp_context: Multiprocessing context for adapter initialization
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
Dictionary mapping profile names to test results
|
|
455
|
+
"""
|
|
456
|
+
results = {}
|
|
457
|
+
for profile_name in self.list_all_profiles():
|
|
458
|
+
results[profile_name] = self.test_profile(profile_name, mp_context)
|
|
459
|
+
return results
|
|
460
|
+
|
|
461
|
+
def clear_cache(self) -> None:
|
|
462
|
+
"""Clear profile and adapter instance caches."""
|
|
463
|
+
self._initialized_profiles.clear()
|
|
464
|
+
self._adapters.clear()
|