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
dbt/task/debug.py
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
import importlib
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
from collections import namedtuple
|
|
7
|
+
from enum import Flag
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
import dbt.exceptions
|
|
12
|
+
import dbt_common.clients.system
|
|
13
|
+
import dbt_common.exceptions
|
|
14
|
+
from dbt.adapters.factory import get_adapter, register_adapter
|
|
15
|
+
from dbt.artifacts.schemas.results import RunStatus
|
|
16
|
+
from dbt.cli.flags import Flags
|
|
17
|
+
|
|
18
|
+
from dbt.config import PartialProject, Profile, Project
|
|
19
|
+
from dbt.config.dvt_profile import DVTProfile
|
|
20
|
+
from dbt.config.renderer import DbtProjectYamlRenderer, ProfileRenderer
|
|
21
|
+
from dbt.events.types import DebugCmdOut, DebugCmdResult, OpenCommand
|
|
22
|
+
from dbt.links import ProfileConfigDocs
|
|
23
|
+
from dbt.mp_context import get_mp_context
|
|
24
|
+
from dbt.task.base import BaseTask, get_nearest_project_dir
|
|
25
|
+
from dbt.version import get_installed_version
|
|
26
|
+
from dbt_common.events.format import pluralize
|
|
27
|
+
from dbt_common.events.functions import fire_event
|
|
28
|
+
from dbt_common.ui import green, red
|
|
29
|
+
|
|
30
|
+
ONLY_PROFILE_MESSAGE = """
|
|
31
|
+
A project file (dvt_project.yml or dbt_project.yml) was not found in this directory.
|
|
32
|
+
Using the only profile `{}`.
|
|
33
|
+
""".lstrip()
|
|
34
|
+
|
|
35
|
+
MULTIPLE_PROFILE_MESSAGE = """
|
|
36
|
+
A project file (dvt_project.yml or dbt_project.yml) was not found in this directory.
|
|
37
|
+
dvt found the following profiles:
|
|
38
|
+
{}
|
|
39
|
+
|
|
40
|
+
To debug one of these profiles, run:
|
|
41
|
+
dvt debug --profile [profile-name]
|
|
42
|
+
""".lstrip()
|
|
43
|
+
|
|
44
|
+
COULD_NOT_CONNECT_MESSAGE = """
|
|
45
|
+
dbt was unable to connect to the specified database.
|
|
46
|
+
The database returned the following error:
|
|
47
|
+
|
|
48
|
+
>{err}
|
|
49
|
+
|
|
50
|
+
Check your database credentials and try again. For more information, visit:
|
|
51
|
+
{url}
|
|
52
|
+
""".lstrip()
|
|
53
|
+
|
|
54
|
+
MISSING_PROFILE_MESSAGE = """
|
|
55
|
+
dbt looked for a profiles.yml file in {path}, but did
|
|
56
|
+
not find one. For more information on configuring your profile, consult the
|
|
57
|
+
documentation:
|
|
58
|
+
|
|
59
|
+
{url}
|
|
60
|
+
""".lstrip()
|
|
61
|
+
|
|
62
|
+
FILE_NOT_FOUND = "file not found"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
SubtaskStatus = namedtuple(
|
|
66
|
+
"SubtaskStatus", ["log_msg", "run_status", "details", "summary_message"]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class DebugRunStatus(Flag):
|
|
71
|
+
SUCCESS = True
|
|
72
|
+
FAIL = False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DebugTask(BaseTask):
|
|
76
|
+
def __init__(self, args: Flags) -> None:
|
|
77
|
+
super().__init__(args)
|
|
78
|
+
self.profiles_dir = args.PROFILES_DIR
|
|
79
|
+
self.profile_path = os.path.join(self.profiles_dir, "profiles.yml")
|
|
80
|
+
try:
|
|
81
|
+
self.project_dir = get_nearest_project_dir(self.args.project_dir)
|
|
82
|
+
except dbt_common.exceptions.DbtBaseException:
|
|
83
|
+
# we probably couldn't find a project directory. Set project dir
|
|
84
|
+
# to whatever was given, or default to the current directory.
|
|
85
|
+
if args.project_dir:
|
|
86
|
+
self.project_dir = args.project_dir
|
|
87
|
+
else:
|
|
88
|
+
self.project_dir = Path.cwd()
|
|
89
|
+
# DVT: Support both dvt_project.yml and dbt_project.yml
|
|
90
|
+
dvt_project_path = os.path.join(self.project_dir, "dvt_project.yml")
|
|
91
|
+
dbt_project_path = os.path.join(self.project_dir, "dbt_project.yml")
|
|
92
|
+
if os.path.exists(dvt_project_path):
|
|
93
|
+
self.project_path = dvt_project_path
|
|
94
|
+
else:
|
|
95
|
+
self.project_path = dbt_project_path
|
|
96
|
+
self.cli_vars: Dict[str, Any] = args.vars
|
|
97
|
+
|
|
98
|
+
# set by _load_*
|
|
99
|
+
self.profile: Optional[Profile] = None
|
|
100
|
+
self.raw_profile_data: Optional[Dict[str, Any]] = None
|
|
101
|
+
self.profile_name: Optional[str] = None
|
|
102
|
+
|
|
103
|
+
def run(self) -> bool:
|
|
104
|
+
# WARN: this is a legacy workflow that is not compatible with other runtime flags
|
|
105
|
+
if self.args.config_dir:
|
|
106
|
+
fire_event(
|
|
107
|
+
OpenCommand(
|
|
108
|
+
open_cmd=dbt_common.clients.system.open_dir_cmd(),
|
|
109
|
+
profiles_dir=str(self.profiles_dir),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
return DebugRunStatus.SUCCESS.value
|
|
113
|
+
|
|
114
|
+
version: str = get_installed_version().to_version_string(skip_matcher=True)
|
|
115
|
+
fire_event(DebugCmdOut(msg="dbt version: {}".format(version)))
|
|
116
|
+
fire_event(DebugCmdOut(msg="python version: {}".format(sys.version.split()[0])))
|
|
117
|
+
fire_event(DebugCmdOut(msg="python path: {}".format(sys.executable)))
|
|
118
|
+
fire_event(DebugCmdOut(msg="os info: {}".format(platform.platform())))
|
|
119
|
+
|
|
120
|
+
# Load profile if possible, then load adapter info (which requires the profile)
|
|
121
|
+
load_profile_status: SubtaskStatus = self._load_profile()
|
|
122
|
+
fire_event(DebugCmdOut(msg="Using profiles dir at {}".format(self.profiles_dir)))
|
|
123
|
+
fire_event(DebugCmdOut(msg="Using profiles.yml file at {}".format(self.profile_path)))
|
|
124
|
+
fire_event(DebugCmdOut(msg="Using project file at {}".format(self.project_path)))
|
|
125
|
+
if load_profile_status.run_status == RunStatus.Success:
|
|
126
|
+
if self.profile is None:
|
|
127
|
+
raise dbt_common.exceptions.DbtInternalError(
|
|
128
|
+
"Profile should not be None if loading profile completed"
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
adapter_type: str = self.profile.credentials.type
|
|
132
|
+
|
|
133
|
+
adapter_version: str = self._read_adapter_version(
|
|
134
|
+
f"dbt.adapters.{adapter_type}.__version__"
|
|
135
|
+
)
|
|
136
|
+
fire_event(DebugCmdOut(msg="adapter type: {}".format(adapter_type)))
|
|
137
|
+
fire_event(DebugCmdOut(msg="adapter version: {}".format(adapter_version)))
|
|
138
|
+
|
|
139
|
+
# Get project loaded to do additional checks
|
|
140
|
+
load_project_status: SubtaskStatus = self._load_project()
|
|
141
|
+
|
|
142
|
+
dependencies_statuses: List[SubtaskStatus] = []
|
|
143
|
+
if self.args.connection:
|
|
144
|
+
fire_event(DebugCmdOut(msg="Skipping steps before connection verification"))
|
|
145
|
+
else:
|
|
146
|
+
# this job's status not logged since already accounted for in _load_* commands
|
|
147
|
+
self.test_configuration(load_profile_status.log_msg, load_project_status.log_msg)
|
|
148
|
+
dependencies_statuses = self.test_dependencies()
|
|
149
|
+
|
|
150
|
+
# Test connection
|
|
151
|
+
connection_status = self.test_connection()
|
|
152
|
+
|
|
153
|
+
# Log messages from any fails
|
|
154
|
+
all_statuses: List[SubtaskStatus] = [
|
|
155
|
+
load_profile_status,
|
|
156
|
+
load_project_status,
|
|
157
|
+
*dependencies_statuses,
|
|
158
|
+
connection_status,
|
|
159
|
+
]
|
|
160
|
+
all_failing_statuses: List[SubtaskStatus] = list(
|
|
161
|
+
filter(lambda status: status.run_status == RunStatus.Error, all_statuses)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
failure_count: int = len(all_failing_statuses)
|
|
165
|
+
if failure_count > 0:
|
|
166
|
+
fire_event(DebugCmdResult(msg=red(f"{(pluralize(failure_count, 'check'))} failed:")))
|
|
167
|
+
for status in all_failing_statuses:
|
|
168
|
+
fire_event(DebugCmdResult(msg=f"{status.summary_message}\n"))
|
|
169
|
+
return DebugRunStatus.FAIL.value
|
|
170
|
+
else:
|
|
171
|
+
fire_event(DebugCmdResult(msg=green("All checks passed!")))
|
|
172
|
+
return DebugRunStatus.SUCCESS.value
|
|
173
|
+
|
|
174
|
+
# ==============================
|
|
175
|
+
# Override for elsewhere in core
|
|
176
|
+
# ==============================
|
|
177
|
+
|
|
178
|
+
def interpret_results(self, results):
|
|
179
|
+
return results
|
|
180
|
+
|
|
181
|
+
# ===============
|
|
182
|
+
# Loading profile
|
|
183
|
+
# ===============
|
|
184
|
+
|
|
185
|
+
def _load_profile(self) -> SubtaskStatus:
|
|
186
|
+
"""
|
|
187
|
+
Side effects: load self.profile
|
|
188
|
+
load self.target_name
|
|
189
|
+
load self.raw_profile_data
|
|
190
|
+
"""
|
|
191
|
+
if not os.path.exists(self.profile_path):
|
|
192
|
+
return SubtaskStatus(
|
|
193
|
+
log_msg=red("ERROR not found"),
|
|
194
|
+
run_status=RunStatus.Error,
|
|
195
|
+
details=FILE_NOT_FOUND,
|
|
196
|
+
summary_message=MISSING_PROFILE_MESSAGE.format(
|
|
197
|
+
path=self.profile_path, url=ProfileConfigDocs
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# DVT: Use read_profile() to get automatic connections → outputs conversion
|
|
202
|
+
from dbt.config.profile import read_profile
|
|
203
|
+
raw_profile_data = read_profile(os.path.dirname(self.profile_path))
|
|
204
|
+
if isinstance(raw_profile_data, dict):
|
|
205
|
+
self.raw_profile_data = raw_profile_data
|
|
206
|
+
|
|
207
|
+
profile_errors = []
|
|
208
|
+
profile_names, summary_message = self._choose_profile_names()
|
|
209
|
+
renderer = ProfileRenderer(self.cli_vars)
|
|
210
|
+
for profile_name in profile_names:
|
|
211
|
+
try:
|
|
212
|
+
# DVT: Use DVTProfile.render instead of Profile.render
|
|
213
|
+
profile: Profile = DVTProfile.render(
|
|
214
|
+
renderer,
|
|
215
|
+
profile_name,
|
|
216
|
+
self.args.profile,
|
|
217
|
+
self.args.target,
|
|
218
|
+
# TODO: Generalize safe access to flags.THREADS:
|
|
219
|
+
# https://github.com/dbt-labs/dbt-core/issues/6259
|
|
220
|
+
getattr(self.args, "threads", None),
|
|
221
|
+
)
|
|
222
|
+
except dbt_common.exceptions.DbtConfigError as exc:
|
|
223
|
+
profile_errors.append(str(exc))
|
|
224
|
+
else:
|
|
225
|
+
if len(profile_names) == 1:
|
|
226
|
+
# if a profile was specified, set it on the task
|
|
227
|
+
self.target_name = self._choose_target_name(profile_name)
|
|
228
|
+
self.profile = profile
|
|
229
|
+
|
|
230
|
+
if profile_errors:
|
|
231
|
+
details = "\n\n".join(profile_errors)
|
|
232
|
+
return SubtaskStatus(
|
|
233
|
+
log_msg=red("ERROR invalid"),
|
|
234
|
+
run_status=RunStatus.Error,
|
|
235
|
+
details=details,
|
|
236
|
+
summary_message=(
|
|
237
|
+
summary_message + f"Profile loading failed for the following reason:"
|
|
238
|
+
f"\n{details}"
|
|
239
|
+
f"\n"
|
|
240
|
+
),
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
return SubtaskStatus(
|
|
244
|
+
log_msg=green("OK found and valid"),
|
|
245
|
+
run_status=RunStatus.Success,
|
|
246
|
+
details="",
|
|
247
|
+
summary_message="Profile is valid",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def _choose_profile_names(self) -> Tuple[List[str], str]:
|
|
251
|
+
project_profile: Optional[str] = None
|
|
252
|
+
if os.path.exists(self.project_path):
|
|
253
|
+
try:
|
|
254
|
+
partial = PartialProject.from_project_root(
|
|
255
|
+
os.path.dirname(self.project_path),
|
|
256
|
+
verify_version=bool(self.args.VERSION_CHECK),
|
|
257
|
+
)
|
|
258
|
+
renderer = DbtProjectYamlRenderer(None, self.cli_vars)
|
|
259
|
+
project_profile = partial.render_profile_name(renderer)
|
|
260
|
+
except dbt.exceptions.DbtProjectError:
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
args_profile: Optional[str] = getattr(self.args, "profile", None)
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
return [Profile.pick_profile_name(args_profile, project_profile)], ""
|
|
267
|
+
except dbt_common.exceptions.DbtConfigError:
|
|
268
|
+
pass
|
|
269
|
+
# try to guess
|
|
270
|
+
|
|
271
|
+
profiles = []
|
|
272
|
+
if self.raw_profile_data:
|
|
273
|
+
profiles = [k for k in self.raw_profile_data if k != "config"]
|
|
274
|
+
if project_profile is None:
|
|
275
|
+
summary_message = "Could not load project file\n"
|
|
276
|
+
elif len(profiles) == 0:
|
|
277
|
+
summary_message = "The profiles.yml has no profiles\n"
|
|
278
|
+
elif len(profiles) == 1:
|
|
279
|
+
summary_message = ONLY_PROFILE_MESSAGE.format(profiles[0])
|
|
280
|
+
else:
|
|
281
|
+
summary_message = MULTIPLE_PROFILE_MESSAGE.format(
|
|
282
|
+
"\n".join(" - {}".format(o) for o in profiles)
|
|
283
|
+
)
|
|
284
|
+
return profiles, summary_message
|
|
285
|
+
|
|
286
|
+
def _read_adapter_version(self, module) -> str:
|
|
287
|
+
"""read the version out of a standard adapter file"""
|
|
288
|
+
try:
|
|
289
|
+
version = importlib.import_module(module).version
|
|
290
|
+
except ModuleNotFoundError:
|
|
291
|
+
version = red("ERROR not found")
|
|
292
|
+
except Exception as exc:
|
|
293
|
+
version = red("ERROR {}".format(exc))
|
|
294
|
+
raise dbt.exceptions.DbtInternalError(
|
|
295
|
+
f"Error when reading adapter version from {module}: {exc}"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return version
|
|
299
|
+
|
|
300
|
+
def _choose_target_name(self, profile_name: str):
|
|
301
|
+
has_raw_profile = (
|
|
302
|
+
self.raw_profile_data is not None and profile_name in self.raw_profile_data
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if not has_raw_profile:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
# mypy appeasement, we checked just above
|
|
309
|
+
assert self.raw_profile_data is not None
|
|
310
|
+
raw_profile = self.raw_profile_data[profile_name]
|
|
311
|
+
|
|
312
|
+
renderer = ProfileRenderer(self.cli_vars)
|
|
313
|
+
|
|
314
|
+
# DVT: Use DVTProfile.render_profile instead of Profile.render_profile
|
|
315
|
+
target_name, _ = DVTProfile.render_profile(
|
|
316
|
+
raw_profile=raw_profile,
|
|
317
|
+
profile_name=profile_name,
|
|
318
|
+
target_override=getattr(self.args, "target", None),
|
|
319
|
+
renderer=renderer,
|
|
320
|
+
)
|
|
321
|
+
return target_name
|
|
322
|
+
|
|
323
|
+
# ===============
|
|
324
|
+
# Loading project
|
|
325
|
+
# ===============
|
|
326
|
+
|
|
327
|
+
def _load_project(self) -> SubtaskStatus:
|
|
328
|
+
"""
|
|
329
|
+
Side effect: load self.project
|
|
330
|
+
"""
|
|
331
|
+
if not os.path.exists(self.project_path):
|
|
332
|
+
return SubtaskStatus(
|
|
333
|
+
log_msg=red("ERROR not found"),
|
|
334
|
+
run_status=RunStatus.Error,
|
|
335
|
+
details=FILE_NOT_FOUND,
|
|
336
|
+
summary_message=(
|
|
337
|
+
f"Project loading failed for the following reason:"
|
|
338
|
+
f"\n project path <{self.project_path}> not found"
|
|
339
|
+
),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
renderer = DbtProjectYamlRenderer(self.profile, self.cli_vars)
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
self.project = Project.from_project_root(
|
|
346
|
+
str(self.project_dir),
|
|
347
|
+
renderer,
|
|
348
|
+
verify_version=self.args.VERSION_CHECK,
|
|
349
|
+
)
|
|
350
|
+
except dbt_common.exceptions.DbtConfigError as exc:
|
|
351
|
+
return SubtaskStatus(
|
|
352
|
+
log_msg=red("ERROR invalid"),
|
|
353
|
+
run_status=RunStatus.Error,
|
|
354
|
+
details=str(exc),
|
|
355
|
+
summary_message=(
|
|
356
|
+
f"Project loading failed for the following reason:" f"\n{str(exc)}" f"\n"
|
|
357
|
+
),
|
|
358
|
+
)
|
|
359
|
+
else:
|
|
360
|
+
return SubtaskStatus(
|
|
361
|
+
log_msg=green("OK found and valid"),
|
|
362
|
+
run_status=RunStatus.Success,
|
|
363
|
+
details="",
|
|
364
|
+
summary_message="Project is valid",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def _profile_found(self) -> str:
|
|
368
|
+
if not self.raw_profile_data:
|
|
369
|
+
return red("ERROR not found")
|
|
370
|
+
assert self.raw_profile_data is not None
|
|
371
|
+
if self.profile_name in self.raw_profile_data:
|
|
372
|
+
return green("OK found")
|
|
373
|
+
else:
|
|
374
|
+
return red("ERROR not found")
|
|
375
|
+
|
|
376
|
+
def _target_found(self) -> str:
|
|
377
|
+
requirements = self.raw_profile_data and self.profile_name and self.target_name
|
|
378
|
+
if not requirements:
|
|
379
|
+
return red("ERROR not found")
|
|
380
|
+
# mypy appeasement, we checked just above
|
|
381
|
+
assert self.raw_profile_data is not None
|
|
382
|
+
assert self.profile_name is not None
|
|
383
|
+
assert self.target_name is not None
|
|
384
|
+
if self.profile_name not in self.raw_profile_data:
|
|
385
|
+
return red("ERROR not found")
|
|
386
|
+
profiles = self.raw_profile_data[self.profile_name]["outputs"]
|
|
387
|
+
if self.target_name not in profiles:
|
|
388
|
+
return red("ERROR not found")
|
|
389
|
+
else:
|
|
390
|
+
return green("OK found")
|
|
391
|
+
|
|
392
|
+
# ============
|
|
393
|
+
# Config tests
|
|
394
|
+
# ============
|
|
395
|
+
|
|
396
|
+
def test_git(self) -> SubtaskStatus:
|
|
397
|
+
try:
|
|
398
|
+
dbt_common.clients.system.run_cmd(os.getcwd(), ["git", "--help"])
|
|
399
|
+
except dbt_common.exceptions.ExecutableError as exc:
|
|
400
|
+
return SubtaskStatus(
|
|
401
|
+
log_msg=red("ERROR"),
|
|
402
|
+
run_status=RunStatus.Error,
|
|
403
|
+
details="git error",
|
|
404
|
+
summary_message="Error from git --help: {!s}".format(exc),
|
|
405
|
+
)
|
|
406
|
+
else:
|
|
407
|
+
return SubtaskStatus(
|
|
408
|
+
log_msg=green("OK found"),
|
|
409
|
+
run_status=RunStatus.Success,
|
|
410
|
+
details="",
|
|
411
|
+
summary_message="git is installed and on the path",
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
def test_dependencies(self) -> List[SubtaskStatus]:
|
|
415
|
+
fire_event(DebugCmdOut(msg="Required dependencies:"))
|
|
416
|
+
|
|
417
|
+
git_test_status = self.test_git()
|
|
418
|
+
fire_event(DebugCmdResult(msg=f" - git [{git_test_status.log_msg}]\n"))
|
|
419
|
+
|
|
420
|
+
return [git_test_status]
|
|
421
|
+
|
|
422
|
+
def test_configuration(self, profile_status_msg, project_status_msg):
|
|
423
|
+
fire_event(DebugCmdOut(msg="Configuration:"))
|
|
424
|
+
fire_event(DebugCmdOut(msg=f" profiles.yml file [{profile_status_msg}]"))
|
|
425
|
+
# DVT: Use generic project file message
|
|
426
|
+
project_file_name = os.path.basename(self.project_path)
|
|
427
|
+
fire_event(DebugCmdOut(msg=f" {project_file_name} file [{project_status_msg}]"))
|
|
428
|
+
|
|
429
|
+
# skip profile stuff if we can't find a profile name
|
|
430
|
+
if self.profile_name is not None:
|
|
431
|
+
fire_event(
|
|
432
|
+
DebugCmdOut(
|
|
433
|
+
msg=" profile: {} [{}]\n".format(self.profile_name, self._profile_found())
|
|
434
|
+
)
|
|
435
|
+
)
|
|
436
|
+
fire_event(
|
|
437
|
+
DebugCmdOut(
|
|
438
|
+
msg=" target: {} [{}]\n".format(self.target_name, self._target_found())
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# ===============
|
|
443
|
+
# Connection test
|
|
444
|
+
# ===============
|
|
445
|
+
|
|
446
|
+
@staticmethod
|
|
447
|
+
def attempt_connection(profile) -> Optional[str]:
|
|
448
|
+
"""Return a string containing the error message, or None if there was no error."""
|
|
449
|
+
register_adapter(profile, get_mp_context())
|
|
450
|
+
adapter = get_adapter(profile)
|
|
451
|
+
try:
|
|
452
|
+
with adapter.connection_named("debug"):
|
|
453
|
+
# is defined in adapter class
|
|
454
|
+
adapter.debug_query()
|
|
455
|
+
except Exception as exc:
|
|
456
|
+
return COULD_NOT_CONNECT_MESSAGE.format(
|
|
457
|
+
err=str(exc),
|
|
458
|
+
url=ProfileConfigDocs,
|
|
459
|
+
)
|
|
460
|
+
return None
|
|
461
|
+
|
|
462
|
+
def test_connection(self) -> SubtaskStatus:
|
|
463
|
+
if self.profile is None:
|
|
464
|
+
fire_event(DebugCmdOut(msg="Connection test skipped since no profile was found"))
|
|
465
|
+
return SubtaskStatus(
|
|
466
|
+
log_msg=red("SKIPPED"),
|
|
467
|
+
run_status=RunStatus.Skipped,
|
|
468
|
+
details="No profile found",
|
|
469
|
+
summary_message="Connection test skipped since no profile was found",
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
fire_event(DebugCmdOut(msg="Connection:"))
|
|
473
|
+
for k, v in self.profile.credentials.connection_info():
|
|
474
|
+
fire_event(DebugCmdOut(msg=f" {k}: {v}"))
|
|
475
|
+
|
|
476
|
+
connection_result = self.attempt_connection(self.profile)
|
|
477
|
+
if connection_result is None:
|
|
478
|
+
status = SubtaskStatus(
|
|
479
|
+
log_msg=green("OK connection ok"),
|
|
480
|
+
run_status=RunStatus.Success,
|
|
481
|
+
details="",
|
|
482
|
+
summary_message="Connection test passed",
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
status = SubtaskStatus(
|
|
486
|
+
log_msg=red("ERROR"),
|
|
487
|
+
run_status=RunStatus.Error,
|
|
488
|
+
details="Failure in connecting to db",
|
|
489
|
+
summary_message=connection_result,
|
|
490
|
+
)
|
|
491
|
+
fire_event(DebugCmdOut(msg=f" Connection test: [{status.log_msg}]\n"))
|
|
492
|
+
return status
|
|
493
|
+
|
|
494
|
+
@classmethod
|
|
495
|
+
def validate_connection(cls, target_dict) -> None:
|
|
496
|
+
"""Validate a connection dictionary. On error, raises a DbtConfigError."""
|
|
497
|
+
target_name = "test"
|
|
498
|
+
# make a fake profile that we can parse
|
|
499
|
+
profile_data = {
|
|
500
|
+
"outputs": {
|
|
501
|
+
target_name: target_dict,
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
# this will raise a DbtConfigError on failure
|
|
505
|
+
profile = Profile.from_raw_profile_info(
|
|
506
|
+
raw_profile=profile_data,
|
|
507
|
+
profile_name="",
|
|
508
|
+
target_override=target_name,
|
|
509
|
+
renderer=ProfileRenderer({}),
|
|
510
|
+
)
|
|
511
|
+
result = cls.attempt_connection(profile)
|
|
512
|
+
if result is not None:
|
|
513
|
+
raise dbt.exceptions.DbtProfileError(result, result_type="connection_failure")
|