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
dvt/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# N.B.
|
|
2
|
+
# This will add to the package’s __path__ all subdirectories of directories on sys.path named after the package which effectively combines both modules into a single namespace (dbt.adapters)
|
|
3
|
+
# The matching statement is in plugins/postgres/dbt/__init__.py
|
|
4
|
+
|
|
5
|
+
from pkgutil import extend_path
|
|
6
|
+
|
|
7
|
+
__path__ = extend_path(__path__, __name__)
|
dvt/_pydantic_shim.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
|
|
3
|
+
"""Shim to allow support for both Pydantic 1 and Pydantic 2.
|
|
4
|
+
|
|
5
|
+
dbt-core must support both major versions of Pydantic because dbt-core users might be using an environment with
|
|
6
|
+
either version, and we can't restrict them to one or the other. Here, we essentially import all Pydantic objects
|
|
7
|
+
from version 1 that we use. Throughout the repo, we import these objects from this file instead of from Pydantic
|
|
8
|
+
directly, meaning that we essentially only use Pydantic 1 in dbt-core currently, but without forcing that restriction
|
|
9
|
+
on dbt users. The development environment for this repo should be pinned to Pydantic 1 to ensure devs get appropriate
|
|
10
|
+
type hints.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from importlib.metadata import version
|
|
14
|
+
|
|
15
|
+
pydantic_version = version("pydantic")
|
|
16
|
+
# Pydantic uses semantic versioning, i.e. <major>.<minor>.<patch>, and we need to know the major
|
|
17
|
+
pydantic_major = pydantic_version.split(".")[0]
|
|
18
|
+
|
|
19
|
+
if pydantic_major == "1":
|
|
20
|
+
from pydantic import BaseSettings # noqa: F401
|
|
21
|
+
elif pydantic_major == "2":
|
|
22
|
+
from pydantic.v1 import BaseSettings # noqa: F401
|
|
23
|
+
else:
|
|
24
|
+
raise RuntimeError(
|
|
25
|
+
f"Currently only pydantic 1 and 2 are supported, found pydantic {pydantic_version}"
|
|
26
|
+
)
|
dvt/adapters/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DVT adapter management extensions.
|
|
3
|
+
|
|
4
|
+
This module extends dbt's adapter system to support multi-profile
|
|
5
|
+
source connections and compute layer integration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dvt.adapters.multi_adapter_manager import (
|
|
9
|
+
MultiAdapterManager,
|
|
10
|
+
create_multi_adapter_manager,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"MultiAdapterManager",
|
|
15
|
+
"create_multi_adapter_manager",
|
|
16
|
+
]
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-adapter management for DVT.
|
|
3
|
+
|
|
4
|
+
This module extends dbt's single-adapter model to support multiple
|
|
5
|
+
adapter instances for different profiles (sources + targets).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from multiprocessing.context import SpawnContext
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from dvt.config.profiles_v2 import (
|
|
12
|
+
ProfileReference,
|
|
13
|
+
ProfileRegistry,
|
|
14
|
+
UnifiedProfileConfig,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from dbt.adapters.factory import FACTORY, get_adapter_class_by_name, load_plugin
|
|
18
|
+
from dbt.adapters.protocol import AdapterProtocol
|
|
19
|
+
from dbt_common.events.base_types import EventLevel
|
|
20
|
+
from dbt_common.events.functions import fire_event
|
|
21
|
+
from dbt_common.events.types import Note
|
|
22
|
+
from dbt_common.exceptions import DbtRuntimeError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MultiAdapterManager:
|
|
26
|
+
"""
|
|
27
|
+
Manages multiple adapter instances for different profiles.
|
|
28
|
+
|
|
29
|
+
DVT extends dbt's single-adapter model by allowing:
|
|
30
|
+
1. Multiple source connections (each with its own profile)
|
|
31
|
+
2. Target connection (standard dbt output)
|
|
32
|
+
3. Each profile can use a different adapter type
|
|
33
|
+
|
|
34
|
+
Architecture:
|
|
35
|
+
- Profile name -> Adapter instance mapping
|
|
36
|
+
- Lazy initialization (create adapter when first referenced)
|
|
37
|
+
- Cleanup management for all adapters
|
|
38
|
+
- Integration with dbt's existing adapter factory
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, unified_profiles: UnifiedProfileConfig, mp_context: SpawnContext):
|
|
42
|
+
"""
|
|
43
|
+
Initialize MultiAdapterManager.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
unified_profiles: Unified profile configuration
|
|
47
|
+
mp_context: Multiprocessing context for adapter initialization
|
|
48
|
+
"""
|
|
49
|
+
self.unified_profiles = unified_profiles
|
|
50
|
+
self.mp_context = mp_context
|
|
51
|
+
self._adapters: Dict[str, AdapterProtocol] = {}
|
|
52
|
+
self._profile_to_adapter_type: Dict[str, str] = {}
|
|
53
|
+
|
|
54
|
+
def get_or_create_adapter(
|
|
55
|
+
self,
|
|
56
|
+
profile_name: str,
|
|
57
|
+
adapter_registered_log_level: Optional[EventLevel] = EventLevel.INFO,
|
|
58
|
+
) -> AdapterProtocol:
|
|
59
|
+
"""
|
|
60
|
+
Get or create adapter instance for a profile.
|
|
61
|
+
|
|
62
|
+
This is the main entry point for getting adapters by profile name.
|
|
63
|
+
It handles:
|
|
64
|
+
1. Checking if adapter already exists (cache)
|
|
65
|
+
2. Loading profile configuration
|
|
66
|
+
3. Loading adapter plugin if needed
|
|
67
|
+
4. Creating and registering the adapter
|
|
68
|
+
5. Caching for future use
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
profile_name: Name of the profile to get adapter for
|
|
72
|
+
adapter_registered_log_level: Log level for adapter registration
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Adapter instance
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
DbtRuntimeError: If profile not found or adapter creation fails
|
|
79
|
+
"""
|
|
80
|
+
# Check cache
|
|
81
|
+
if profile_name in self._adapters:
|
|
82
|
+
return self._adapters[profile_name]
|
|
83
|
+
|
|
84
|
+
# Get profile configuration
|
|
85
|
+
profile = self.unified_profiles.get_profile(profile_name)
|
|
86
|
+
if not profile:
|
|
87
|
+
raise DbtRuntimeError(
|
|
88
|
+
f"Profile '{profile_name}' not found in profiles configuration. "
|
|
89
|
+
f"Available profiles: {', '.join(self.unified_profiles.list_profiles())}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Ensure adapter plugin is loaded
|
|
93
|
+
adapter_type = profile.adapter
|
|
94
|
+
try:
|
|
95
|
+
load_plugin(adapter_type)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
raise DbtRuntimeError(
|
|
98
|
+
f"Failed to load adapter plugin '{adapter_type}' for profile '{profile_name}': {e}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Create adapter configuration
|
|
102
|
+
# For DVT, we need to create a minimal AdapterRequiredConfig
|
|
103
|
+
# The actual credentials will come from the profile
|
|
104
|
+
adapter_config = self._create_adapter_config(profile)
|
|
105
|
+
|
|
106
|
+
# Check if an adapter of this type already exists in dbt's FACTORY
|
|
107
|
+
# If so, we can reuse it if the configuration matches
|
|
108
|
+
# Otherwise, create a new instance
|
|
109
|
+
try:
|
|
110
|
+
existing_adapter = FACTORY.lookup_adapter(adapter_type)
|
|
111
|
+
# Check if credentials match - if so, reuse
|
|
112
|
+
if self._credentials_match(existing_adapter, profile):
|
|
113
|
+
# Reusing existing adapter
|
|
114
|
+
self._adapters[profile_name] = existing_adapter
|
|
115
|
+
self._profile_to_adapter_type[profile_name] = adapter_type
|
|
116
|
+
return existing_adapter
|
|
117
|
+
except KeyError:
|
|
118
|
+
# No existing adapter of this type
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
# Create new adapter instance
|
|
122
|
+
adapter_class = get_adapter_class_by_name(adapter_type)
|
|
123
|
+
adapter_instance = adapter_class(adapter_config, self.mp_context) # type: ignore[call-arg]
|
|
124
|
+
|
|
125
|
+
# Cache the adapter
|
|
126
|
+
self._adapters[profile_name] = adapter_instance
|
|
127
|
+
self._profile_to_adapter_type[profile_name] = adapter_type
|
|
128
|
+
|
|
129
|
+
return adapter_instance
|
|
130
|
+
|
|
131
|
+
def _create_adapter_config(self, profile: ProfileReference) -> Any:
|
|
132
|
+
"""
|
|
133
|
+
Create adapter configuration from ProfileReference.
|
|
134
|
+
|
|
135
|
+
This converts DVT's ProfileReference to dbt's expected config format.
|
|
136
|
+
We create a minimal config object that satisfies adapter requirements.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
profile: Profile reference
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Configuration object for adapter initialization
|
|
143
|
+
"""
|
|
144
|
+
# Import credentials class for this adapter type
|
|
145
|
+
adapter_class = get_adapter_class_by_name(profile.adapter)
|
|
146
|
+
credentials_class = adapter_class.Credentials # type: ignore[attr-defined]
|
|
147
|
+
|
|
148
|
+
# Create credentials instance from profile
|
|
149
|
+
credentials_dict = {
|
|
150
|
+
"type": profile.adapter,
|
|
151
|
+
**profile.credentials,
|
|
152
|
+
}
|
|
153
|
+
credentials = credentials_class.from_dict(credentials_dict) # type: ignore[attr-defined]
|
|
154
|
+
|
|
155
|
+
# Create minimal config object
|
|
156
|
+
# Note: This is a simplified config for DVT multi-adapter support
|
|
157
|
+
# Full RuntimeConfig would have more fields, but adapters primarily need credentials
|
|
158
|
+
class MinimalAdapterConfig:
|
|
159
|
+
def __init__(self, creds, name, threads):
|
|
160
|
+
self.credentials = creds
|
|
161
|
+
self.project_name = name
|
|
162
|
+
self.profile_name = name
|
|
163
|
+
self.target_name = name
|
|
164
|
+
self.threads = threads
|
|
165
|
+
# Required protocol fields
|
|
166
|
+
self.query_comment: Dict[str, Any] = {}
|
|
167
|
+
self.cli_vars: Dict[str, Any] = {}
|
|
168
|
+
self.target_path = "target"
|
|
169
|
+
self.log_cache_events = False
|
|
170
|
+
|
|
171
|
+
config = MinimalAdapterConfig(credentials, profile.name, profile.threads)
|
|
172
|
+
return config
|
|
173
|
+
|
|
174
|
+
def _credentials_match(self, adapter: AdapterProtocol, profile: ProfileReference) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
Check if adapter's credentials match the profile.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
adapter: Existing adapter instance
|
|
180
|
+
profile: Profile reference to compare
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
True if credentials match, False otherwise
|
|
184
|
+
"""
|
|
185
|
+
# Get adapter's credentials
|
|
186
|
+
adapter_creds = adapter.config.credentials.to_dict() # type: ignore[attr-defined]
|
|
187
|
+
profile_creds = {
|
|
188
|
+
"type": profile.adapter,
|
|
189
|
+
**profile.credentials,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Compare key fields (excluding transient fields like query comments)
|
|
193
|
+
key_fields = ["type", "host", "port", "user", "database", "schema"]
|
|
194
|
+
for field in key_fields:
|
|
195
|
+
if field in adapter_creds or field in profile_creds:
|
|
196
|
+
if adapter_creds.get(field) != profile_creds.get(field):
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
return True
|
|
200
|
+
|
|
201
|
+
def get_adapter_for_source(self, source_node) -> AdapterProtocol:
|
|
202
|
+
"""
|
|
203
|
+
Get adapter for a source node.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
source_node: Source node with profile reference
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Adapter instance for the source's profile
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
DbtRuntimeError: If source has no profile or profile not found
|
|
213
|
+
"""
|
|
214
|
+
if not hasattr(source_node, "profile") or not source_node.profile:
|
|
215
|
+
raise DbtRuntimeError(
|
|
216
|
+
f"Source '{source_node.name}' has no profile reference. "
|
|
217
|
+
"Sources must specify a profile in sources.yml"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return self.get_or_create_adapter(source_node.profile)
|
|
221
|
+
|
|
222
|
+
def cleanup_all_adapters(self) -> None:
|
|
223
|
+
"""Clean up all managed adapters."""
|
|
224
|
+
for profile_name, adapter in self._adapters.items():
|
|
225
|
+
try:
|
|
226
|
+
adapter.cleanup_connections() # type: ignore[attr-defined]
|
|
227
|
+
fire_event(Note(msg="Cleaned up adapter for profile '{}'".format(profile_name)))
|
|
228
|
+
except Exception as e:
|
|
229
|
+
fire_event(
|
|
230
|
+
Note(
|
|
231
|
+
msg="Error cleaning up adapter for profile '{}': {}".format(
|
|
232
|
+
profile_name, e
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
self._adapters.clear()
|
|
238
|
+
self._profile_to_adapter_type.clear()
|
|
239
|
+
|
|
240
|
+
def list_active_adapters(self) -> Dict[str, str]:
|
|
241
|
+
"""
|
|
242
|
+
List all active adapters.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Dictionary mapping profile name to adapter type
|
|
246
|
+
"""
|
|
247
|
+
return dict(self._profile_to_adapter_type)
|
|
248
|
+
|
|
249
|
+
def get_adapter_count(self) -> int:
|
|
250
|
+
"""Get number of active adapters."""
|
|
251
|
+
return len(self._adapters)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def create_multi_adapter_manager(
|
|
255
|
+
unified_profiles: UnifiedProfileConfig,
|
|
256
|
+
mp_context: SpawnContext,
|
|
257
|
+
) -> MultiAdapterManager:
|
|
258
|
+
"""
|
|
259
|
+
Create MultiAdapterManager instance.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
unified_profiles: Unified profile configuration
|
|
263
|
+
mp_context: Multiprocessing context
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
MultiAdapterManager instance
|
|
267
|
+
"""
|
|
268
|
+
return MultiAdapterManager(unified_profiles, mp_context)
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from dvt.artifacts.exceptions.schemas import IncompatibleSchemaError
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from dbt_common.exceptions import DbtRuntimeError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IncompatibleSchemaError(DbtRuntimeError):
|
|
7
|
+
def __init__(self, expected: str, found: Optional[str] = None) -> None:
|
|
8
|
+
self.expected = expected
|
|
9
|
+
self.found = found
|
|
10
|
+
self.filename = "input file"
|
|
11
|
+
|
|
12
|
+
super().__init__(msg=self.get_message())
|
|
13
|
+
|
|
14
|
+
def add_filename(self, filename: str):
|
|
15
|
+
self.filename = filename
|
|
16
|
+
self.msg = self.get_message()
|
|
17
|
+
|
|
18
|
+
def get_message(self) -> str:
|
|
19
|
+
found_str = "nothing"
|
|
20
|
+
if self.found is not None:
|
|
21
|
+
found_str = f'"{self.found}"'
|
|
22
|
+
|
|
23
|
+
msg = (
|
|
24
|
+
f'Expected a schema version of "{self.expected}" in '
|
|
25
|
+
f"{self.filename}, but found {found_str}. Are you running with a "
|
|
26
|
+
f"different version of dbt?"
|
|
27
|
+
)
|
|
28
|
+
return msg
|
|
29
|
+
|
|
30
|
+
CODE = 10014
|
|
31
|
+
MESSAGE = "Incompatible Schema"
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from dvt.artifacts.resources.base import BaseResource, Docs, FileHash, GraphResource
|
|
2
|
+
from dvt.artifacts.resources.v1.analysis import Analysis
|
|
3
|
+
from dvt.artifacts.resources.v1.catalog import Catalog, CatalogWriteIntegrationConfig
|
|
4
|
+
|
|
5
|
+
# alias to latest resource definitions
|
|
6
|
+
from dvt.artifacts.resources.v1.components import (
|
|
7
|
+
ColumnConfig,
|
|
8
|
+
ColumnInfo,
|
|
9
|
+
CompiledResource,
|
|
10
|
+
Contract,
|
|
11
|
+
DeferRelation,
|
|
12
|
+
DependsOn,
|
|
13
|
+
FreshnessThreshold,
|
|
14
|
+
HasRelationMetadata,
|
|
15
|
+
InjectedCTE,
|
|
16
|
+
NodeVersion,
|
|
17
|
+
ParsedResource,
|
|
18
|
+
ParsedResourceMandatory,
|
|
19
|
+
Quoting,
|
|
20
|
+
RefArgs,
|
|
21
|
+
Time,
|
|
22
|
+
)
|
|
23
|
+
from dvt.artifacts.resources.v1.config import (
|
|
24
|
+
Hook,
|
|
25
|
+
NodeAndTestConfig,
|
|
26
|
+
NodeConfig,
|
|
27
|
+
TestConfig,
|
|
28
|
+
list_str,
|
|
29
|
+
metas,
|
|
30
|
+
)
|
|
31
|
+
from dvt.artifacts.resources.v1.documentation import Documentation
|
|
32
|
+
from dvt.artifacts.resources.v1.exposure import (
|
|
33
|
+
Exposure,
|
|
34
|
+
ExposureConfig,
|
|
35
|
+
ExposureType,
|
|
36
|
+
MaturityType,
|
|
37
|
+
)
|
|
38
|
+
from dvt.artifacts.resources.v1.function import (
|
|
39
|
+
Function,
|
|
40
|
+
FunctionArgument,
|
|
41
|
+
FunctionConfig,
|
|
42
|
+
FunctionMandatory,
|
|
43
|
+
FunctionReturns,
|
|
44
|
+
)
|
|
45
|
+
from dvt.artifacts.resources.v1.generic_test import GenericTest, TestMetadata
|
|
46
|
+
from dvt.artifacts.resources.v1.group import Group, GroupConfig
|
|
47
|
+
from dvt.artifacts.resources.v1.hook import HookNode
|
|
48
|
+
from dvt.artifacts.resources.v1.macro import Macro, MacroArgument, MacroDependsOn
|
|
49
|
+
from dvt.artifacts.resources.v1.metric import (
|
|
50
|
+
ConstantPropertyInput,
|
|
51
|
+
ConversionTypeParams,
|
|
52
|
+
CumulativeTypeParams,
|
|
53
|
+
Metric,
|
|
54
|
+
MetricAggregationParams,
|
|
55
|
+
MetricConfig,
|
|
56
|
+
MetricInput,
|
|
57
|
+
MetricInputMeasure,
|
|
58
|
+
MetricTimeWindow,
|
|
59
|
+
MetricTypeParams,
|
|
60
|
+
)
|
|
61
|
+
from dvt.artifacts.resources.v1.model import (
|
|
62
|
+
CustomGranularity,
|
|
63
|
+
Model,
|
|
64
|
+
ModelConfig,
|
|
65
|
+
ModelFreshness,
|
|
66
|
+
TimeSpine,
|
|
67
|
+
)
|
|
68
|
+
from dvt.artifacts.resources.v1.owner import Owner
|
|
69
|
+
from dvt.artifacts.resources.v1.saved_query import (
|
|
70
|
+
Export,
|
|
71
|
+
ExportConfig,
|
|
72
|
+
QueryParams,
|
|
73
|
+
SavedQuery,
|
|
74
|
+
SavedQueryConfig,
|
|
75
|
+
SavedQueryMandatory,
|
|
76
|
+
)
|
|
77
|
+
from dvt.artifacts.resources.v1.seed import Seed, SeedConfig
|
|
78
|
+
from dvt.artifacts.resources.v1.semantic_layer_components import (
|
|
79
|
+
FileSlice,
|
|
80
|
+
MeasureAggregationParameters,
|
|
81
|
+
NonAdditiveDimension,
|
|
82
|
+
SourceFileMetadata,
|
|
83
|
+
WhereFilter,
|
|
84
|
+
WhereFilterIntersection,
|
|
85
|
+
)
|
|
86
|
+
from dvt.artifacts.resources.v1.semantic_model import (
|
|
87
|
+
Defaults,
|
|
88
|
+
Dimension,
|
|
89
|
+
DimensionTypeParams,
|
|
90
|
+
DimensionValidityParams,
|
|
91
|
+
Entity,
|
|
92
|
+
Measure,
|
|
93
|
+
NodeRelation,
|
|
94
|
+
SemanticLayerElementConfig,
|
|
95
|
+
SemanticModel,
|
|
96
|
+
SemanticModelConfig,
|
|
97
|
+
)
|
|
98
|
+
from dvt.artifacts.resources.v1.singular_test import SingularTest
|
|
99
|
+
from dvt.artifacts.resources.v1.snapshot import Snapshot, SnapshotConfig
|
|
100
|
+
from dvt.artifacts.resources.v1.source_definition import (
|
|
101
|
+
ExternalPartition,
|
|
102
|
+
ExternalTable,
|
|
103
|
+
ParsedSourceMandatory,
|
|
104
|
+
SourceConfig,
|
|
105
|
+
SourceDefinition,
|
|
106
|
+
)
|
|
107
|
+
from dvt.artifacts.resources.v1.sql_operation import SqlOperation
|
|
108
|
+
from dvt.artifacts.resources.v1.unit_test_definition import (
|
|
109
|
+
UnitTestConfig,
|
|
110
|
+
UnitTestDefinition,
|
|
111
|
+
UnitTestFormat,
|
|
112
|
+
UnitTestInputFixture,
|
|
113
|
+
UnitTestNodeVersions,
|
|
114
|
+
UnitTestOutputFixture,
|
|
115
|
+
UnitTestOverrides,
|
|
116
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from dvt.artifacts.resources.types import NodeType
|
|
6
|
+
|
|
7
|
+
from dbt_common.dataclass_schema import dbtClassMixin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class BaseResource(dbtClassMixin):
|
|
12
|
+
name: str
|
|
13
|
+
resource_type: NodeType
|
|
14
|
+
package_name: str
|
|
15
|
+
path: str
|
|
16
|
+
original_file_path: str
|
|
17
|
+
unique_id: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class GraphResource(BaseResource):
|
|
22
|
+
fqn: List[str]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class FileHash(dbtClassMixin):
|
|
27
|
+
name: str # the hash type name
|
|
28
|
+
checksum: str # the hashlib.hash_type().hexdigest() of the file contents
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def empty(cls):
|
|
32
|
+
return FileHash(name="none", checksum="")
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def path(cls, path: str):
|
|
36
|
+
return FileHash(name="path", checksum=path)
|
|
37
|
+
|
|
38
|
+
def __eq__(self, other):
|
|
39
|
+
if not isinstance(other, FileHash):
|
|
40
|
+
return NotImplemented
|
|
41
|
+
|
|
42
|
+
if self.name == "none" or self.name != other.name:
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
return self.checksum == other.checksum
|
|
46
|
+
|
|
47
|
+
def compare(self, contents: str) -> bool:
|
|
48
|
+
"""Compare the file contents with the given hash"""
|
|
49
|
+
if self.name == "none":
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
return self.from_contents(contents, name=self.name) == self.checksum
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_contents(cls, contents: str, name="sha256") -> "FileHash":
|
|
56
|
+
"""Create a file hash from the given file contents. The hash is always
|
|
57
|
+
the utf-8 encoding of the contents given, because dbt only reads files
|
|
58
|
+
as utf-8.
|
|
59
|
+
"""
|
|
60
|
+
data = contents.encode("utf-8")
|
|
61
|
+
checksum = hashlib.new(name, data).hexdigest()
|
|
62
|
+
return cls(name=name, checksum=checksum)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Docs(dbtClassMixin):
|
|
67
|
+
show: bool = True
|
|
68
|
+
node_color: Optional[str] = None
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from dbt_common.dataclass_schema import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AccessType(StrEnum):
|
|
5
|
+
Private = "private"
|
|
6
|
+
Protected = "protected"
|
|
7
|
+
Public = "public"
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def is_valid(cls, item):
|
|
11
|
+
try:
|
|
12
|
+
cls(item)
|
|
13
|
+
except ValueError:
|
|
14
|
+
return False
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NodeType(StrEnum):
|
|
19
|
+
Model = "model"
|
|
20
|
+
Analysis = "analysis"
|
|
21
|
+
Test = "test" # renamed to 'data_test'; preserved as 'test' here for back-compat
|
|
22
|
+
Snapshot = "snapshot"
|
|
23
|
+
Operation = "operation"
|
|
24
|
+
Seed = "seed"
|
|
25
|
+
# TODO: rm?
|
|
26
|
+
RPCCall = "rpc"
|
|
27
|
+
SqlOperation = "sql_operation"
|
|
28
|
+
Documentation = "doc"
|
|
29
|
+
Source = "source"
|
|
30
|
+
Macro = "macro"
|
|
31
|
+
Exposure = "exposure"
|
|
32
|
+
Metric = "metric"
|
|
33
|
+
Group = "group"
|
|
34
|
+
SavedQuery = "saved_query"
|
|
35
|
+
SemanticModel = "semantic_model"
|
|
36
|
+
Unit = "unit_test"
|
|
37
|
+
Fixture = "fixture"
|
|
38
|
+
Function = "function"
|
|
39
|
+
|
|
40
|
+
def pluralize(self) -> str:
|
|
41
|
+
if self is self.Analysis:
|
|
42
|
+
return "analyses"
|
|
43
|
+
elif self is self.SavedQuery:
|
|
44
|
+
return "saved_queries"
|
|
45
|
+
elif self is self.Test:
|
|
46
|
+
return "data_tests"
|
|
47
|
+
return f"{self}s"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class RunHookType(StrEnum):
|
|
51
|
+
Start = "on-run-start"
|
|
52
|
+
End = "on-run-end"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ModelLanguage(StrEnum):
|
|
56
|
+
python = "python"
|
|
57
|
+
sql = "sql"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ModelHookType(StrEnum):
|
|
61
|
+
PreHook = "pre-hook"
|
|
62
|
+
PostHook = "post-hook"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TimePeriod(StrEnum):
|
|
66
|
+
minute = "minute"
|
|
67
|
+
hour = "hour"
|
|
68
|
+
day = "day"
|
|
69
|
+
|
|
70
|
+
def plural(self) -> str:
|
|
71
|
+
return str(self) + "s"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BatchSize(StrEnum):
|
|
75
|
+
hour = "hour"
|
|
76
|
+
day = "day"
|
|
77
|
+
month = "month"
|
|
78
|
+
year = "year"
|
|
79
|
+
|
|
80
|
+
def plural(self) -> str:
|
|
81
|
+
return str(self) + "s"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class FunctionType(StrEnum):
|
|
85
|
+
Scalar = "scalar"
|
|
86
|
+
Aggregate = "aggregate"
|
|
87
|
+
Table = "table"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class FunctionVolatility(StrEnum):
|
|
91
|
+
Deterministic = "deterministic"
|
|
92
|
+
Stable = "stable"
|
|
93
|
+
NonDeterministic = "non-deterministic"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from dvt.artifacts.resources.types import NodeType
|
|
5
|
+
from dvt.artifacts.resources.v1.components import CompiledResource
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Analysis(CompiledResource):
|
|
10
|
+
resource_type: Literal[NodeType.Analysis]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from dbt.adapters.catalogs import CatalogIntegrationConfig
|
|
5
|
+
from dbt_common.dataclass_schema import dbtClassMixin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CatalogWriteIntegrationConfig(CatalogIntegrationConfig):
|
|
10
|
+
name: str
|
|
11
|
+
catalog_type: str
|
|
12
|
+
external_volume: Optional[str] = None
|
|
13
|
+
table_format: Optional[str] = None
|
|
14
|
+
catalog_name: Optional[str] = None
|
|
15
|
+
file_format: Optional[str] = None
|
|
16
|
+
adapter_properties: Dict[str, Any] = field(default_factory=dict)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Catalog(dbtClassMixin):
|
|
21
|
+
name: str
|
|
22
|
+
active_write_integration: Optional[str] = None
|
|
23
|
+
write_integrations: List[CatalogWriteIntegrationConfig] = field(default_factory=list)
|