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,148 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
|
4
|
+
|
|
5
|
+
from dvt.contracts.graph.nodes import ResultNode
|
|
6
|
+
|
|
7
|
+
from dbt_common.dataclass_schema import StrEnum, dbtClassMixin
|
|
8
|
+
from dbt_common.events.helpers import datetime_to_json_string
|
|
9
|
+
from dbt_common.utils import cast_to_int, cast_to_str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class TimingInfo(dbtClassMixin):
|
|
14
|
+
"""
|
|
15
|
+
Represents a step in the execution of a node.
|
|
16
|
+
`name` should be one of: compile, execute, or other
|
|
17
|
+
Do not call directly, use `collect_timing_info` instead.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
started_at: Optional[datetime] = None
|
|
22
|
+
completed_at: Optional[datetime] = None
|
|
23
|
+
|
|
24
|
+
def begin(self):
|
|
25
|
+
self.started_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
26
|
+
|
|
27
|
+
def end(self):
|
|
28
|
+
self.completed_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
29
|
+
|
|
30
|
+
def to_msg_dict(self):
|
|
31
|
+
msg_dict = {"name": str(self.name)}
|
|
32
|
+
if self.started_at:
|
|
33
|
+
msg_dict["started_at"] = datetime_to_json_string(self.started_at)
|
|
34
|
+
if self.completed_at:
|
|
35
|
+
msg_dict["completed_at"] = datetime_to_json_string(self.completed_at)
|
|
36
|
+
return msg_dict
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# This is a context manager
|
|
40
|
+
class collect_timing_info:
|
|
41
|
+
def __init__(self, name: str, callback: Callable[[TimingInfo], None]) -> None:
|
|
42
|
+
self.timing_info = TimingInfo(name=name)
|
|
43
|
+
self.callback = callback
|
|
44
|
+
|
|
45
|
+
def __enter__(self):
|
|
46
|
+
self.timing_info.begin()
|
|
47
|
+
|
|
48
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
49
|
+
self.timing_info.end()
|
|
50
|
+
self.callback(self.timing_info)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class RunningStatus(StrEnum):
|
|
54
|
+
Started = "started"
|
|
55
|
+
Compiling = "compiling"
|
|
56
|
+
Executing = "executing"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class NodeStatus(StrEnum):
|
|
60
|
+
Success = "success"
|
|
61
|
+
Error = "error"
|
|
62
|
+
Fail = "fail"
|
|
63
|
+
Warn = "warn"
|
|
64
|
+
Skipped = "skipped"
|
|
65
|
+
PartialSuccess = "partial success"
|
|
66
|
+
Pass = "pass"
|
|
67
|
+
RuntimeErr = "runtime error"
|
|
68
|
+
NoOp = "no-op"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RunStatus(StrEnum):
|
|
72
|
+
Success = NodeStatus.Success
|
|
73
|
+
Error = NodeStatus.Error
|
|
74
|
+
Skipped = NodeStatus.Skipped
|
|
75
|
+
PartialSuccess = NodeStatus.PartialSuccess
|
|
76
|
+
NoOp = NodeStatus.NoOp
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class TestStatus(StrEnum):
|
|
80
|
+
__test__ = False
|
|
81
|
+
Pass = NodeStatus.Pass
|
|
82
|
+
Error = NodeStatus.Error
|
|
83
|
+
Fail = NodeStatus.Fail
|
|
84
|
+
Warn = NodeStatus.Warn
|
|
85
|
+
Skipped = NodeStatus.Skipped
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FreshnessStatus(StrEnum):
|
|
89
|
+
Pass = NodeStatus.Pass
|
|
90
|
+
Warn = NodeStatus.Warn
|
|
91
|
+
Error = NodeStatus.Error
|
|
92
|
+
RuntimeErr = NodeStatus.RuntimeErr
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class BaseResult(dbtClassMixin):
|
|
97
|
+
status: Union[RunStatus, TestStatus, FreshnessStatus]
|
|
98
|
+
timing: List[TimingInfo]
|
|
99
|
+
thread_id: str
|
|
100
|
+
execution_time: float
|
|
101
|
+
adapter_response: Dict[str, Any]
|
|
102
|
+
message: Optional[str]
|
|
103
|
+
failures: Optional[int]
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def __pre_deserialize__(cls, data):
|
|
107
|
+
data = super().__pre_deserialize__(data)
|
|
108
|
+
if "message" not in data:
|
|
109
|
+
data["message"] = None
|
|
110
|
+
if "failures" not in data:
|
|
111
|
+
data["failures"] = None
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
def to_msg_dict(self):
|
|
115
|
+
msg_dict = {
|
|
116
|
+
"status": str(self.status),
|
|
117
|
+
"message": cast_to_str(self.message),
|
|
118
|
+
"thread": self.thread_id,
|
|
119
|
+
"execution_time": self.execution_time,
|
|
120
|
+
"num_failures": cast_to_int(self.failures),
|
|
121
|
+
"timing_info": [ti.to_msg_dict() for ti in self.timing],
|
|
122
|
+
"adapter_response": self.adapter_response,
|
|
123
|
+
}
|
|
124
|
+
return msg_dict
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class NodeResult(BaseResult):
|
|
129
|
+
node: ResultNode
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class ExecutionResult(dbtClassMixin):
|
|
134
|
+
results: Sequence[BaseResult]
|
|
135
|
+
elapsed_time: float
|
|
136
|
+
|
|
137
|
+
def __len__(self):
|
|
138
|
+
return len(self.results)
|
|
139
|
+
|
|
140
|
+
def __iter__(self):
|
|
141
|
+
return iter(self.results)
|
|
142
|
+
|
|
143
|
+
def __getitem__(self, idx):
|
|
144
|
+
return self.results[idx]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# due to issues with typing.Union collapsing subclasses, this can't subclass
|
|
148
|
+
# PartialResult
|
|
File without changes
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import threading
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Any, Dict, Iterable, Optional, Sequence, Tuple
|
|
8
|
+
|
|
9
|
+
# https://github.com/dbt-labs/dbt-core/issues/10098
|
|
10
|
+
# Needed for Mashumaro serialization of RunResult below
|
|
11
|
+
# TODO: investigate alternative approaches to restore conditional import
|
|
12
|
+
# if TYPE_CHECKING:
|
|
13
|
+
import agate
|
|
14
|
+
from dvt.artifacts.resources import CompiledResource
|
|
15
|
+
from dvt.artifacts.schemas.base import (
|
|
16
|
+
ArtifactMixin,
|
|
17
|
+
BaseArtifactMetadata,
|
|
18
|
+
get_artifact_schema_version,
|
|
19
|
+
schema_version,
|
|
20
|
+
)
|
|
21
|
+
from dvt.artifacts.schemas.batch_results import BatchResults
|
|
22
|
+
from dvt.artifacts.schemas.results import (
|
|
23
|
+
BaseResult,
|
|
24
|
+
ExecutionResult,
|
|
25
|
+
NodeResult,
|
|
26
|
+
ResultNode,
|
|
27
|
+
RunStatus,
|
|
28
|
+
)
|
|
29
|
+
from dvt.exceptions import scrub_secrets
|
|
30
|
+
|
|
31
|
+
from dbt_common.clients.system import write_json
|
|
32
|
+
from dbt_common.constants import SECRET_ENV_PREFIX
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class RunResult(NodeResult):
|
|
37
|
+
agate_table: Optional["agate.Table"] = field(
|
|
38
|
+
default=None, metadata={"serialize": lambda x: None, "deserialize": lambda x: None}
|
|
39
|
+
)
|
|
40
|
+
batch_results: Optional[BatchResults] = None
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def skipped(self):
|
|
44
|
+
return self.status == RunStatus.Skipped
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_node(cls, node: ResultNode, status: RunStatus, message: Optional[str]):
|
|
48
|
+
thread_id = threading.current_thread().name
|
|
49
|
+
return RunResult(
|
|
50
|
+
status=status,
|
|
51
|
+
thread_id=thread_id,
|
|
52
|
+
execution_time=0,
|
|
53
|
+
timing=[],
|
|
54
|
+
message=message,
|
|
55
|
+
node=node,
|
|
56
|
+
adapter_response={},
|
|
57
|
+
failures=None,
|
|
58
|
+
batch_results=None,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class RunResultsMetadata(BaseArtifactMetadata):
|
|
64
|
+
dbt_schema_version: str = field(
|
|
65
|
+
default_factory=lambda: str(RunResultsArtifact.dbt_schema_version)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class RunResultOutput(BaseResult):
|
|
71
|
+
unique_id: str
|
|
72
|
+
compiled: Optional[bool]
|
|
73
|
+
compiled_code: Optional[str]
|
|
74
|
+
relation_name: Optional[str]
|
|
75
|
+
batch_results: Optional[BatchResults] = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def process_run_result(result: RunResult) -> RunResultOutput:
|
|
79
|
+
|
|
80
|
+
compiled = isinstance(result.node, CompiledResource)
|
|
81
|
+
|
|
82
|
+
return RunResultOutput(
|
|
83
|
+
unique_id=result.node.unique_id,
|
|
84
|
+
status=result.status,
|
|
85
|
+
timing=result.timing,
|
|
86
|
+
thread_id=result.thread_id,
|
|
87
|
+
execution_time=result.execution_time,
|
|
88
|
+
message=result.message,
|
|
89
|
+
adapter_response=result.adapter_response,
|
|
90
|
+
failures=result.failures,
|
|
91
|
+
batch_results=result.batch_results,
|
|
92
|
+
compiled=result.node.compiled if compiled else None, # type:ignore
|
|
93
|
+
compiled_code=result.node.compiled_code if compiled else None, # type:ignore
|
|
94
|
+
relation_name=result.node.relation_name if compiled else None, # type:ignore
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class RunExecutionResult(
|
|
100
|
+
ExecutionResult,
|
|
101
|
+
):
|
|
102
|
+
results: Sequence[RunResult]
|
|
103
|
+
args: Dict[str, Any] = field(default_factory=dict)
|
|
104
|
+
generated_at: datetime = field(
|
|
105
|
+
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def write(self, path: str):
|
|
109
|
+
writable = RunResultsArtifact.from_execution_results(
|
|
110
|
+
results=self.results,
|
|
111
|
+
elapsed_time=self.elapsed_time,
|
|
112
|
+
generated_at=self.generated_at,
|
|
113
|
+
args=self.args,
|
|
114
|
+
)
|
|
115
|
+
writable.write(path)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass
|
|
119
|
+
@schema_version("run-results", 6)
|
|
120
|
+
class RunResultsArtifact(ExecutionResult, ArtifactMixin):
|
|
121
|
+
results: Sequence[RunResultOutput]
|
|
122
|
+
args: Dict[str, Any] = field(default_factory=dict)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def from_execution_results(
|
|
126
|
+
cls,
|
|
127
|
+
results: Sequence[RunResult],
|
|
128
|
+
elapsed_time: float,
|
|
129
|
+
generated_at: datetime,
|
|
130
|
+
args: Dict,
|
|
131
|
+
):
|
|
132
|
+
processed_results = [
|
|
133
|
+
process_run_result(result) for result in results if isinstance(result, RunResult)
|
|
134
|
+
]
|
|
135
|
+
meta = RunResultsMetadata(
|
|
136
|
+
dbt_schema_version=str(cls.dbt_schema_version),
|
|
137
|
+
generated_at=generated_at,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
secret_vars = [
|
|
141
|
+
v for k, v in args["vars"].items() if k.startswith(SECRET_ENV_PREFIX) and v.strip()
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
scrubbed_args = copy.deepcopy(args)
|
|
145
|
+
|
|
146
|
+
# scrub secrets in invocation command
|
|
147
|
+
scrubbed_args["invocation_command"] = scrub_secrets(
|
|
148
|
+
scrubbed_args["invocation_command"], secret_vars
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# scrub secrets in vars dict
|
|
152
|
+
scrubbed_args["vars"] = {
|
|
153
|
+
k: scrub_secrets(v, secret_vars) for k, v in scrubbed_args["vars"].items()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return cls(
|
|
157
|
+
metadata=meta, results=processed_results, elapsed_time=elapsed_time, args=scrubbed_args
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def compatible_previous_versions(cls) -> Iterable[Tuple[str, int]]:
|
|
162
|
+
return [
|
|
163
|
+
("run-results", 4),
|
|
164
|
+
("run-results", 5),
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def upgrade_schema_version(cls, data):
|
|
169
|
+
"""This overrides the "upgrade_schema_version" call in VersionedSchema (via
|
|
170
|
+
ArtifactMixin) to modify the dictionary passed in from earlier versions of the run_results.
|
|
171
|
+
"""
|
|
172
|
+
run_results_schema_version = get_artifact_schema_version(data)
|
|
173
|
+
# If less than the current version (v5), preprocess contents to match latest schema version
|
|
174
|
+
if run_results_schema_version <= 5:
|
|
175
|
+
# In v5, we added 'compiled' attributes to each result entry
|
|
176
|
+
# Going forward, dbt expects these to be populated
|
|
177
|
+
for result in data["results"]:
|
|
178
|
+
result["compiled"] = False
|
|
179
|
+
result["compiled_code"] = ""
|
|
180
|
+
result["relation_name"] = ""
|
|
181
|
+
return cls.from_dict(data)
|
|
182
|
+
|
|
183
|
+
def write(self, path: str):
|
|
184
|
+
write_json(path, self.to_dict(omit_none=False))
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
def rename_sql_attr(node_content: dict) -> dict:
|
|
2
|
+
if "raw_sql" in node_content:
|
|
3
|
+
node_content["raw_code"] = node_content.pop("raw_sql")
|
|
4
|
+
if "compiled_sql" in node_content:
|
|
5
|
+
node_content["compiled_code"] = node_content.pop("compiled_sql")
|
|
6
|
+
node_content["language"] = "sql"
|
|
7
|
+
return node_content
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def upgrade_ref_content(node_content: dict) -> dict:
|
|
11
|
+
# In v1.5 we switched Node.refs from List[List[str]] to List[Dict[str, Union[NodeVersion, str]]]
|
|
12
|
+
# Previous versions did not have a version keyword argument for ref
|
|
13
|
+
if "refs" in node_content:
|
|
14
|
+
upgraded_refs = []
|
|
15
|
+
for ref in node_content["refs"]:
|
|
16
|
+
if isinstance(ref, list):
|
|
17
|
+
if len(ref) == 1:
|
|
18
|
+
upgraded_refs.append({"package": None, "name": ref[0], "version": None})
|
|
19
|
+
else:
|
|
20
|
+
upgraded_refs.append({"package": ref[0], "name": ref[1], "version": None})
|
|
21
|
+
node_content["refs"] = upgraded_refs
|
|
22
|
+
return node_content
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def upgrade_node_content(node_content):
|
|
26
|
+
rename_sql_attr(node_content)
|
|
27
|
+
upgrade_ref_content(node_content)
|
|
28
|
+
if node_content["resource_type"] != "seed" and "root_path" in node_content:
|
|
29
|
+
del node_content["root_path"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def upgrade_seed_content(node_content):
|
|
33
|
+
# Remove compilation related attributes
|
|
34
|
+
for attr_name in (
|
|
35
|
+
"language",
|
|
36
|
+
"refs",
|
|
37
|
+
"sources",
|
|
38
|
+
"metrics",
|
|
39
|
+
"compiled_path",
|
|
40
|
+
"compiled",
|
|
41
|
+
"compiled_code",
|
|
42
|
+
"extra_ctes_injected",
|
|
43
|
+
"extra_ctes",
|
|
44
|
+
"relation_name",
|
|
45
|
+
):
|
|
46
|
+
if attr_name in node_content:
|
|
47
|
+
del node_content[attr_name]
|
|
48
|
+
# In v1.4, we switched SeedNode.depends_on from DependsOn to MacroDependsOn
|
|
49
|
+
node_content.get("depends_on", {}).pop("nodes", None)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def drop_v9_and_prior_metrics(manifest: dict) -> None:
|
|
53
|
+
manifest["metrics"] = {}
|
|
54
|
+
filtered_disabled_entries = {}
|
|
55
|
+
for entry_name, resource_list in manifest.get("disabled", {}).items():
|
|
56
|
+
filtered_resource_list = []
|
|
57
|
+
for resource in resource_list:
|
|
58
|
+
if resource.get("resource_type") != "metric":
|
|
59
|
+
filtered_resource_list.append(resource)
|
|
60
|
+
filtered_disabled_entries[entry_name] = filtered_resource_list
|
|
61
|
+
|
|
62
|
+
manifest["disabled"] = filtered_disabled_entries
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _convert_dct_with_filter(v10_dct_with_opt_filter):
|
|
66
|
+
"""Upgrage the filter object from v10 to v11.
|
|
67
|
+
|
|
68
|
+
v10 filters from a serialized manifest looked like:
|
|
69
|
+
{..., 'filter': {'where_sql_template': '<filter_value>'}}
|
|
70
|
+
whereas v11 filters look like:
|
|
71
|
+
{..., 'filter': {'where_filters': [{'where_sql_template': '<filter_value>'}, ...]}}
|
|
72
|
+
"""
|
|
73
|
+
if v10_dct_with_opt_filter is not None and v10_dct_with_opt_filter.get("filter") is not None:
|
|
74
|
+
v10_dct_with_opt_filter["filter"] = {"where_filters": [v10_dct_with_opt_filter["filter"]]}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _convert_metric(v10_metric_dict):
|
|
78
|
+
"""Upgrades a v10 metric object to a v11 metric object.
|
|
79
|
+
|
|
80
|
+
Specifcally the following properties change
|
|
81
|
+
1. metric.filter
|
|
82
|
+
2. metric.type_params.measure.filter
|
|
83
|
+
3. metric.type_params.input_measures[x].filter
|
|
84
|
+
4. metric.type_params.numerator.filter
|
|
85
|
+
5. metric.type_params.denominator.filter
|
|
86
|
+
6. metric.type_params.metrics[x].filter"
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# handles top level metric filter
|
|
90
|
+
_convert_dct_with_filter(v10_metric_dict)
|
|
91
|
+
|
|
92
|
+
type_params = v10_metric_dict.get("type_params")
|
|
93
|
+
if type_params is not None:
|
|
94
|
+
_convert_dct_with_filter(type_params.get("measure"))
|
|
95
|
+
_convert_dct_with_filter(type_params.get("numerator"))
|
|
96
|
+
_convert_dct_with_filter(type_params.get("denominator"))
|
|
97
|
+
|
|
98
|
+
# handles metric.type_params.input_measures[x].filter
|
|
99
|
+
input_measures = type_params.get("input_measures")
|
|
100
|
+
if input_measures is not None:
|
|
101
|
+
for input_measure in input_measures:
|
|
102
|
+
_convert_dct_with_filter(input_measure)
|
|
103
|
+
|
|
104
|
+
# handles metric.type_params.metrics[x].filter
|
|
105
|
+
metrics = type_params.get("metrics")
|
|
106
|
+
if metrics is not None:
|
|
107
|
+
for metric in metrics:
|
|
108
|
+
_convert_dct_with_filter(metric)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def upgrade_v10_metric_filters(manifest: dict):
|
|
112
|
+
"""Handles metric filters changes from v10 to v11."""
|
|
113
|
+
|
|
114
|
+
metrics = manifest.get("metrics", {})
|
|
115
|
+
for metric in metrics.values():
|
|
116
|
+
_convert_metric(metric)
|
|
117
|
+
|
|
118
|
+
disabled_nodes = manifest.get("disabled", {})
|
|
119
|
+
for unique_id, nodes in disabled_nodes.items():
|
|
120
|
+
if unique_id.split(".")[0] == "metric":
|
|
121
|
+
for node in nodes:
|
|
122
|
+
_convert_metric(node)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def upgrade_manifest_json(manifest: dict, manifest_schema_version: int) -> dict:
|
|
126
|
+
# this should remain 9 while the check in `upgrade_schema_version` may change
|
|
127
|
+
if manifest_schema_version <= 9:
|
|
128
|
+
drop_v9_and_prior_metrics(manifest=manifest)
|
|
129
|
+
elif manifest_schema_version == 10:
|
|
130
|
+
upgrade_v10_metric_filters(manifest=manifest)
|
|
131
|
+
|
|
132
|
+
for node_content in manifest.get("nodes", {}).values():
|
|
133
|
+
upgrade_node_content(node_content)
|
|
134
|
+
if node_content["resource_type"] == "seed":
|
|
135
|
+
upgrade_seed_content(node_content)
|
|
136
|
+
for disabled in manifest.get("disabled", {}).values():
|
|
137
|
+
# There can be multiple disabled nodes for the same unique_id
|
|
138
|
+
# so make sure all the nodes get the attr renamed
|
|
139
|
+
for node_content in disabled:
|
|
140
|
+
upgrade_node_content(node_content)
|
|
141
|
+
if node_content["resource_type"] == "seed":
|
|
142
|
+
upgrade_seed_content(node_content)
|
|
143
|
+
# add group key
|
|
144
|
+
if "groups" not in manifest:
|
|
145
|
+
manifest["groups"] = {}
|
|
146
|
+
if "group_map" not in manifest:
|
|
147
|
+
manifest["group_map"] = {}
|
|
148
|
+
# add unit_tests key
|
|
149
|
+
if "unit_tests" not in manifest:
|
|
150
|
+
manifest["unit_tests"] = {}
|
|
151
|
+
for metric_content in manifest.get("metrics", {}).values():
|
|
152
|
+
# handle attr renames + value translation ("expression" -> "derived")
|
|
153
|
+
metric_content = upgrade_ref_content(metric_content)
|
|
154
|
+
if "root_path" in metric_content:
|
|
155
|
+
del metric_content["root_path"]
|
|
156
|
+
for exposure_content in manifest.get("exposures", {}).values():
|
|
157
|
+
exposure_content = upgrade_ref_content(exposure_content)
|
|
158
|
+
if "root_path" in exposure_content:
|
|
159
|
+
del exposure_content["root_path"]
|
|
160
|
+
for source_content in manifest.get("sources", {}).values():
|
|
161
|
+
if "root_path" in source_content:
|
|
162
|
+
del source_content["root_path"]
|
|
163
|
+
for macro_content in manifest.get("macros", {}).values():
|
|
164
|
+
if "root_path" in macro_content:
|
|
165
|
+
del macro_content["root_path"]
|
|
166
|
+
for doc_content in manifest.get("docs", {}).values():
|
|
167
|
+
if "root_path" in doc_content:
|
|
168
|
+
del doc_content["root_path"]
|
|
169
|
+
doc_content["resource_type"] = "doc"
|
|
170
|
+
if "semantic_models" not in manifest:
|
|
171
|
+
manifest["semantic_models"] = {}
|
|
172
|
+
if "saved_queries" not in manifest:
|
|
173
|
+
manifest["saved_queries"] = {}
|
|
174
|
+
return manifest
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
HTML_COLORS = [
|
|
4
|
+
"aliceblue",
|
|
5
|
+
"antiquewhite",
|
|
6
|
+
"aqua",
|
|
7
|
+
"aquamarine",
|
|
8
|
+
"azure",
|
|
9
|
+
"beige",
|
|
10
|
+
"bisque",
|
|
11
|
+
"black",
|
|
12
|
+
"blanchedalmond",
|
|
13
|
+
"blue",
|
|
14
|
+
"blueviolet",
|
|
15
|
+
"brown",
|
|
16
|
+
"burlywood",
|
|
17
|
+
"cadetblue",
|
|
18
|
+
"chartreuse",
|
|
19
|
+
"chocolate",
|
|
20
|
+
"coral",
|
|
21
|
+
"cornflowerblue",
|
|
22
|
+
"cornsilk",
|
|
23
|
+
"crimson",
|
|
24
|
+
"cyan",
|
|
25
|
+
"darkblue",
|
|
26
|
+
"darkcyan",
|
|
27
|
+
"darkgoldenrod",
|
|
28
|
+
"darkgray",
|
|
29
|
+
"darkgreen",
|
|
30
|
+
"darkkhaki",
|
|
31
|
+
"darkmagenta",
|
|
32
|
+
"darkolivegreen",
|
|
33
|
+
"darkorange",
|
|
34
|
+
"darkorchid",
|
|
35
|
+
"darkred",
|
|
36
|
+
"darksalmon",
|
|
37
|
+
"darkseagreen",
|
|
38
|
+
"darkslateblue",
|
|
39
|
+
"darkslategray",
|
|
40
|
+
"darkturquoise",
|
|
41
|
+
"darkviolet",
|
|
42
|
+
"deeppink",
|
|
43
|
+
"deepskyblue",
|
|
44
|
+
"dimgray",
|
|
45
|
+
"dodgerblue",
|
|
46
|
+
"firebrick",
|
|
47
|
+
"floralwhite",
|
|
48
|
+
"forestgreen",
|
|
49
|
+
"fuchsia",
|
|
50
|
+
"gainsboro",
|
|
51
|
+
"ghostwhite",
|
|
52
|
+
"gold",
|
|
53
|
+
"goldenrod",
|
|
54
|
+
"gray",
|
|
55
|
+
"green",
|
|
56
|
+
"greenyellow",
|
|
57
|
+
"honeydew",
|
|
58
|
+
"hotpink",
|
|
59
|
+
"indianred",
|
|
60
|
+
"indigo",
|
|
61
|
+
"ivory",
|
|
62
|
+
"khaki",
|
|
63
|
+
"lavender",
|
|
64
|
+
"lavenderblush",
|
|
65
|
+
"lawngreen",
|
|
66
|
+
"lemonchiffon",
|
|
67
|
+
"lightblue",
|
|
68
|
+
"lightcoral",
|
|
69
|
+
"lightcyan",
|
|
70
|
+
"lightgoldenrodyellow",
|
|
71
|
+
"lightgray",
|
|
72
|
+
"lightgreen",
|
|
73
|
+
"lightpink",
|
|
74
|
+
"lightsalmon",
|
|
75
|
+
"lightsalmon",
|
|
76
|
+
"lightseagreen",
|
|
77
|
+
"lightskyblue",
|
|
78
|
+
"lightslategray",
|
|
79
|
+
"lightsteelblue",
|
|
80
|
+
"lightyellow",
|
|
81
|
+
"lime",
|
|
82
|
+
"limegreen",
|
|
83
|
+
"linen",
|
|
84
|
+
"magenta",
|
|
85
|
+
"maroon",
|
|
86
|
+
"mediumaquamarine",
|
|
87
|
+
"mediumblue",
|
|
88
|
+
"mediumorchid",
|
|
89
|
+
"mediumpurple",
|
|
90
|
+
"mediumseagreen",
|
|
91
|
+
"mediumslateblue",
|
|
92
|
+
"mediumslateblue",
|
|
93
|
+
"mediumspringgreen",
|
|
94
|
+
"mediumturquoise",
|
|
95
|
+
"mediumvioletred",
|
|
96
|
+
"midnightblue",
|
|
97
|
+
"mintcream",
|
|
98
|
+
"mistyrose",
|
|
99
|
+
"moccasin",
|
|
100
|
+
"navajowhite",
|
|
101
|
+
"navy",
|
|
102
|
+
"oldlace",
|
|
103
|
+
"olive",
|
|
104
|
+
"olivedrab",
|
|
105
|
+
"orange",
|
|
106
|
+
"orangered",
|
|
107
|
+
"orchid",
|
|
108
|
+
"palegoldenrod",
|
|
109
|
+
"palegreen",
|
|
110
|
+
"paleturquoise",
|
|
111
|
+
"palevioletred",
|
|
112
|
+
"papayawhip",
|
|
113
|
+
"peachpuff",
|
|
114
|
+
"peru",
|
|
115
|
+
"pink",
|
|
116
|
+
"plum",
|
|
117
|
+
"powderblue",
|
|
118
|
+
"purple",
|
|
119
|
+
"rebeccapurple",
|
|
120
|
+
"red",
|
|
121
|
+
"rosybrown",
|
|
122
|
+
"royalblue",
|
|
123
|
+
"saddlebrown",
|
|
124
|
+
"salmon",
|
|
125
|
+
"sandybrown",
|
|
126
|
+
"seagreen",
|
|
127
|
+
"seashell",
|
|
128
|
+
"sienna",
|
|
129
|
+
"silver",
|
|
130
|
+
"skyblue",
|
|
131
|
+
"slateblue",
|
|
132
|
+
"slategray",
|
|
133
|
+
"snow",
|
|
134
|
+
"springgreen",
|
|
135
|
+
"steelblue",
|
|
136
|
+
"tan",
|
|
137
|
+
"teal",
|
|
138
|
+
"thistle",
|
|
139
|
+
"tomato",
|
|
140
|
+
"turquoise",
|
|
141
|
+
"violet",
|
|
142
|
+
"wheat",
|
|
143
|
+
"white",
|
|
144
|
+
"whitesmoke",
|
|
145
|
+
"yellow",
|
|
146
|
+
"yellowgreen",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def validate_color(color: str) -> bool:
|
|
151
|
+
match_hex = re.search(r"^#(?:[0-9a-f]{3}){1,2}$", color.lower())
|
|
152
|
+
match_html_color_name = color.lower() in HTML_COLORS
|
|
153
|
+
return bool(match_hex or match_html_color_name)
|
dvt/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import cli as dbt_cli # noqa
|
dvt/cli/context.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from dvt.cli.main import cli as dbt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def make_context(args, command=dbt) -> Optional[click.Context]:
|
|
8
|
+
try:
|
|
9
|
+
ctx = command.make_context(command.name, args)
|
|
10
|
+
except click.exceptions.Exit:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
ctx.invoked_subcommand = ctx.protected_args[0] if ctx.protected_args else None
|
|
14
|
+
ctx.obj = {}
|
|
15
|
+
|
|
16
|
+
return ctx
|