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,179 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import NoReturn
|
|
3
|
+
|
|
4
|
+
from dvt.events.types import JinjaLogWarning, SnapshotTimestampWarning
|
|
5
|
+
from dvt.exceptions import (
|
|
6
|
+
AmbiguousAliasError,
|
|
7
|
+
AmbiguousCatalogMatchError,
|
|
8
|
+
CompilationError,
|
|
9
|
+
ContractError,
|
|
10
|
+
DependencyError,
|
|
11
|
+
DependencyNotFoundError,
|
|
12
|
+
DuplicatePatchPathError,
|
|
13
|
+
DuplicateResourceNameError,
|
|
14
|
+
FailFastError,
|
|
15
|
+
MissingRelationError,
|
|
16
|
+
PropertyYMLError,
|
|
17
|
+
env_secrets,
|
|
18
|
+
scrub_secrets,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from dbt.adapters.exceptions import (
|
|
22
|
+
ColumnTypeMissingError,
|
|
23
|
+
MissingConfigError,
|
|
24
|
+
MissingMaterializationError,
|
|
25
|
+
RelationWrongTypeError,
|
|
26
|
+
)
|
|
27
|
+
from dbt.adapters.exceptions.cache import CacheInconsistencyError
|
|
28
|
+
from dbt_common.events.functions import warn_or_error
|
|
29
|
+
from dbt_common.exceptions import (
|
|
30
|
+
DataclassNotDictError,
|
|
31
|
+
DbtDatabaseError,
|
|
32
|
+
DbtRuntimeError,
|
|
33
|
+
NotImplementedError,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def warn(msg, node=None):
|
|
38
|
+
warn_or_error(JinjaLogWarning(msg=msg), node=node)
|
|
39
|
+
return ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def missing_config(model, name) -> NoReturn:
|
|
43
|
+
raise MissingConfigError(unique_id=model.unique_id, name=name)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def missing_materialization(model, adapter_type) -> NoReturn:
|
|
47
|
+
raise MissingMaterializationError(
|
|
48
|
+
materialization=model.config.materialized, adapter_type=adapter_type
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def missing_relation(relation, model=None) -> NoReturn:
|
|
53
|
+
raise MissingRelationError(relation, model)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def raise_ambiguous_alias(node_1, node_2, duped_name=None) -> NoReturn:
|
|
57
|
+
raise AmbiguousAliasError(node_1, node_2, duped_name)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def raise_ambiguous_catalog_match(unique_id, match_1, match_2) -> NoReturn:
|
|
61
|
+
raise AmbiguousCatalogMatchError(unique_id, match_1, match_2)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def raise_cache_inconsistent(message) -> NoReturn:
|
|
65
|
+
raise CacheInconsistencyError(message)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def raise_dataclass_not_dict(obj) -> NoReturn:
|
|
69
|
+
raise DataclassNotDictError(obj)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def raise_compiler_error(msg, node=None) -> NoReturn:
|
|
73
|
+
raise CompilationError(msg, node)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def raise_contract_error(yaml_columns, sql_columns) -> NoReturn:
|
|
77
|
+
raise ContractError(yaml_columns, sql_columns)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def raise_database_error(msg, node=None) -> NoReturn:
|
|
81
|
+
raise DbtDatabaseError(msg, node)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def raise_dep_not_found(node, node_description, required_pkg) -> NoReturn:
|
|
85
|
+
raise DependencyNotFoundError(node, node_description, required_pkg)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def raise_dependency_error(msg) -> NoReturn:
|
|
89
|
+
raise DependencyError(scrub_secrets(msg, env_secrets()))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def raise_duplicate_patch_name(patch_1, existing_patch_path) -> NoReturn:
|
|
93
|
+
raise DuplicatePatchPathError(patch_1, existing_patch_path)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def raise_duplicate_resource_name(node_1, node_2) -> NoReturn:
|
|
97
|
+
raise DuplicateResourceNameError(node_1, node_2)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def raise_invalid_property_yml_version(path, issue) -> NoReturn:
|
|
101
|
+
raise PropertyYMLError(path, issue)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def raise_not_implemented(msg) -> NoReturn:
|
|
105
|
+
raise NotImplementedError(msg)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def relation_wrong_type(relation, expected_type, model=None) -> NoReturn:
|
|
109
|
+
raise RelationWrongTypeError(relation, expected_type, model)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def column_type_missing(column_names) -> NoReturn:
|
|
113
|
+
raise ColumnTypeMissingError(column_names)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def raise_fail_fast_error(msg, node=None) -> NoReturn:
|
|
117
|
+
raise FailFastError(msg, node=node)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def warn_snapshot_timestamp_data_types(
|
|
121
|
+
snapshot_time_data_type: str, updated_at_data_type: str
|
|
122
|
+
) -> None:
|
|
123
|
+
warn_or_error(
|
|
124
|
+
SnapshotTimestampWarning(
|
|
125
|
+
snapshot_time_data_type=snapshot_time_data_type,
|
|
126
|
+
updated_at_data_type=updated_at_data_type,
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Update this when a new function should be added to the
|
|
132
|
+
# dbt context's `exceptions` key!
|
|
133
|
+
CONTEXT_EXPORTS = {
|
|
134
|
+
fn.__name__: fn
|
|
135
|
+
for fn in [
|
|
136
|
+
warn,
|
|
137
|
+
missing_config,
|
|
138
|
+
missing_materialization,
|
|
139
|
+
missing_relation,
|
|
140
|
+
raise_ambiguous_alias,
|
|
141
|
+
raise_ambiguous_catalog_match,
|
|
142
|
+
raise_cache_inconsistent,
|
|
143
|
+
raise_dataclass_not_dict,
|
|
144
|
+
raise_compiler_error,
|
|
145
|
+
raise_database_error,
|
|
146
|
+
raise_dep_not_found,
|
|
147
|
+
raise_dependency_error,
|
|
148
|
+
raise_duplicate_patch_name,
|
|
149
|
+
raise_duplicate_resource_name,
|
|
150
|
+
raise_invalid_property_yml_version,
|
|
151
|
+
raise_not_implemented,
|
|
152
|
+
relation_wrong_type,
|
|
153
|
+
raise_contract_error,
|
|
154
|
+
column_type_missing,
|
|
155
|
+
raise_fail_fast_error,
|
|
156
|
+
warn_snapshot_timestamp_data_types,
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# wraps context based exceptions in node info
|
|
162
|
+
def wrapper(model):
|
|
163
|
+
def wrap(func):
|
|
164
|
+
@functools.wraps(func)
|
|
165
|
+
def inner(*args, **kwargs):
|
|
166
|
+
try:
|
|
167
|
+
return func(*args, **kwargs)
|
|
168
|
+
except DbtRuntimeError as exc:
|
|
169
|
+
exc.add_node(model)
|
|
170
|
+
raise exc
|
|
171
|
+
|
|
172
|
+
return inner
|
|
173
|
+
|
|
174
|
+
return wrap
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def wrapped_exports(model):
|
|
178
|
+
wrap = wrapper(model)
|
|
179
|
+
return {name: wrap(export) for name, export in CONTEXT_EXPORTS.items()}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from typing import Dict, MutableMapping, Optional
|
|
2
|
+
|
|
3
|
+
from dvt.clients.jinja import MacroGenerator
|
|
4
|
+
from dvt.contracts.graph.nodes import Macro
|
|
5
|
+
from dvt.exceptions import DuplicateMacroNameError, PackageNotFoundForMacroError
|
|
6
|
+
from dvt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
|
7
|
+
|
|
8
|
+
MacroNamespace = Dict[str, Macro]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# This class builds the MacroResolver by adding macros
|
|
12
|
+
# to various categories for finding macros in the right order,
|
|
13
|
+
# so that higher precedence macros are found first.
|
|
14
|
+
# This functionality is also provided by the MacroNamespace,
|
|
15
|
+
# but the intention is to eventually replace that class.
|
|
16
|
+
# This enables us to get the macro unique_id without
|
|
17
|
+
# processing every macro in the project.
|
|
18
|
+
# Note: the root project macros override everything in the
|
|
19
|
+
# dbt internal projects. External projects (dependencies) will
|
|
20
|
+
# use their own macros first, then pull from the root project
|
|
21
|
+
# followed by dbt internal projects.
|
|
22
|
+
class MacroResolver:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
macros: MutableMapping[str, Macro],
|
|
26
|
+
root_project_name: str,
|
|
27
|
+
internal_package_names,
|
|
28
|
+
) -> None:
|
|
29
|
+
self.root_project_name = root_project_name
|
|
30
|
+
self.macros = macros
|
|
31
|
+
# internal packages comes from get_adapter_package_names
|
|
32
|
+
self.internal_package_names = internal_package_names
|
|
33
|
+
|
|
34
|
+
# To be filled in from macros.
|
|
35
|
+
self.internal_packages: Dict[str, MacroNamespace] = {}
|
|
36
|
+
self.packages: Dict[str, MacroNamespace] = {}
|
|
37
|
+
self.root_package_macros: MacroNamespace = {}
|
|
38
|
+
|
|
39
|
+
# add the macros to internal_packages, packages, and root packages
|
|
40
|
+
self.add_macros()
|
|
41
|
+
self._build_internal_packages_namespace()
|
|
42
|
+
self._build_macros_by_name()
|
|
43
|
+
|
|
44
|
+
def _build_internal_packages_namespace(self) -> None:
|
|
45
|
+
# Iterate in reverse-order and overwrite: the packages that are first
|
|
46
|
+
# in the list are the ones we want to "win".
|
|
47
|
+
self.internal_packages_namespace: MacroNamespace = {}
|
|
48
|
+
for pkg in reversed(self.internal_package_names):
|
|
49
|
+
if pkg in self.internal_packages:
|
|
50
|
+
# Turn the internal packages into a flat namespace
|
|
51
|
+
self.internal_packages_namespace.update(self.internal_packages[pkg])
|
|
52
|
+
|
|
53
|
+
# search order:
|
|
54
|
+
# local_namespace (package of particular node), not including
|
|
55
|
+
# the internal packages or the root package
|
|
56
|
+
# This means that within an extra package, it uses its own macros
|
|
57
|
+
# root package namespace
|
|
58
|
+
# non-internal packages (that aren't local or root)
|
|
59
|
+
# dbt internal packages
|
|
60
|
+
def _build_macros_by_name(self) -> None:
|
|
61
|
+
macros_by_name = {}
|
|
62
|
+
|
|
63
|
+
# all internal packages (already in the right order)
|
|
64
|
+
for macro in self.internal_packages_namespace.values():
|
|
65
|
+
macros_by_name[macro.name] = macro
|
|
66
|
+
|
|
67
|
+
# non-internal packages
|
|
68
|
+
for fnamespace in self.packages.values():
|
|
69
|
+
for macro in fnamespace.values():
|
|
70
|
+
macros_by_name[macro.name] = macro
|
|
71
|
+
|
|
72
|
+
# root package macros
|
|
73
|
+
for macro in self.root_package_macros.values():
|
|
74
|
+
macros_by_name[macro.name] = macro
|
|
75
|
+
|
|
76
|
+
self.macros_by_name = macros_by_name
|
|
77
|
+
|
|
78
|
+
def _add_macro_to(
|
|
79
|
+
self,
|
|
80
|
+
package_namespaces: Dict[str, MacroNamespace],
|
|
81
|
+
macro: Macro,
|
|
82
|
+
) -> None:
|
|
83
|
+
if macro.package_name in package_namespaces:
|
|
84
|
+
namespace = package_namespaces[macro.package_name]
|
|
85
|
+
else:
|
|
86
|
+
namespace = {}
|
|
87
|
+
package_namespaces[macro.package_name] = namespace
|
|
88
|
+
|
|
89
|
+
if macro.name in namespace:
|
|
90
|
+
raise DuplicateMacroNameError(macro, macro, macro.package_name)
|
|
91
|
+
package_namespaces[macro.package_name][macro.name] = macro
|
|
92
|
+
|
|
93
|
+
def add_macro(self, macro: Macro) -> None:
|
|
94
|
+
macro_name: str = macro.name
|
|
95
|
+
|
|
96
|
+
# internal macros (from plugins) will be processed separately from
|
|
97
|
+
# project macros, so store them in a different place
|
|
98
|
+
if macro.package_name in self.internal_package_names:
|
|
99
|
+
self._add_macro_to(self.internal_packages, macro)
|
|
100
|
+
else:
|
|
101
|
+
# if it's not an internal package
|
|
102
|
+
self._add_macro_to(self.packages, macro)
|
|
103
|
+
# add to root_package_macros if it's in the root package
|
|
104
|
+
if macro.package_name == self.root_project_name:
|
|
105
|
+
self.root_package_macros[macro_name] = macro
|
|
106
|
+
|
|
107
|
+
def add_macros(self) -> None:
|
|
108
|
+
for macro in self.macros.values():
|
|
109
|
+
self.add_macro(macro)
|
|
110
|
+
|
|
111
|
+
def get_macro(self, local_package, macro_name) -> Optional[Macro]:
|
|
112
|
+
local_package_macros = {}
|
|
113
|
+
# If the macro is explicitly prefixed with an internal namespace
|
|
114
|
+
# (e.g. 'dbt.some_macro'), look there first
|
|
115
|
+
if local_package in self.internal_package_names:
|
|
116
|
+
local_package_macros = self.internal_packages[local_package]
|
|
117
|
+
# If the macro is explicitly prefixed with a different package name
|
|
118
|
+
# (e.g. 'dbt_utils.some_macro'), look there first
|
|
119
|
+
if local_package not in self.internal_package_names and local_package in self.packages:
|
|
120
|
+
local_package_macros = self.packages[local_package]
|
|
121
|
+
# First: search the specified package for this macro
|
|
122
|
+
if macro_name in local_package_macros:
|
|
123
|
+
return local_package_macros[macro_name]
|
|
124
|
+
# Now look up in the standard search order
|
|
125
|
+
if macro_name in self.macros_by_name:
|
|
126
|
+
return self.macros_by_name[macro_name]
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
def get_macro_id(self, local_package, macro_name) -> Optional[str]:
|
|
130
|
+
macro = self.get_macro(local_package, macro_name)
|
|
131
|
+
if macro is None:
|
|
132
|
+
return None
|
|
133
|
+
else:
|
|
134
|
+
return macro.unique_id
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# Currently this is just used by test processing in the schema
|
|
138
|
+
# parser (in connection with the MacroResolver). Future work
|
|
139
|
+
# will extend the use of these classes to other parsing areas.
|
|
140
|
+
# One of the features of this class compared to the MacroNamespace
|
|
141
|
+
# is that you can limit the number of macros provided to the
|
|
142
|
+
# context dictionary in the 'to_dict' manifest method.
|
|
143
|
+
class TestMacroNamespace:
|
|
144
|
+
def __init__(self, macro_resolver, ctx, node, thread_ctx, depends_on_macros):
|
|
145
|
+
self.macro_resolver = macro_resolver
|
|
146
|
+
self.ctx = ctx
|
|
147
|
+
self.node = node # can be none
|
|
148
|
+
self.thread_ctx = thread_ctx
|
|
149
|
+
self.local_namespace = {}
|
|
150
|
+
self.project_namespace = {}
|
|
151
|
+
if depends_on_macros:
|
|
152
|
+
dep_macros = []
|
|
153
|
+
self.recursively_get_depends_on_macros(depends_on_macros, dep_macros)
|
|
154
|
+
for macro_unique_id in dep_macros:
|
|
155
|
+
if macro_unique_id in self.macro_resolver.macros:
|
|
156
|
+
# Split up the macro unique_id to get the project_name
|
|
157
|
+
(_, project_name, macro_name) = macro_unique_id.split(".")
|
|
158
|
+
# Save the plain macro_name in the local_namespace
|
|
159
|
+
macro = self.macro_resolver.macros[macro_unique_id]
|
|
160
|
+
macro_gen = MacroGenerator(
|
|
161
|
+
macro,
|
|
162
|
+
self.ctx,
|
|
163
|
+
self.node,
|
|
164
|
+
self.thread_ctx,
|
|
165
|
+
)
|
|
166
|
+
self.local_namespace[macro_name] = macro_gen
|
|
167
|
+
# We also need the two part macro name
|
|
168
|
+
if project_name not in self.project_namespace:
|
|
169
|
+
self.project_namespace[project_name] = {}
|
|
170
|
+
self.project_namespace[project_name][macro_name] = macro_gen
|
|
171
|
+
|
|
172
|
+
def recursively_get_depends_on_macros(self, depends_on_macros, dep_macros):
|
|
173
|
+
for macro_unique_id in depends_on_macros:
|
|
174
|
+
if macro_unique_id in dep_macros:
|
|
175
|
+
continue
|
|
176
|
+
dep_macros.append(macro_unique_id)
|
|
177
|
+
if macro_unique_id in self.macro_resolver.macros:
|
|
178
|
+
macro = self.macro_resolver.macros[macro_unique_id]
|
|
179
|
+
if macro.depends_on.macros:
|
|
180
|
+
self.recursively_get_depends_on_macros(macro.depends_on.macros, dep_macros)
|
|
181
|
+
|
|
182
|
+
def get_from_package(self, package_name: Optional[str], name: str) -> Optional[MacroGenerator]:
|
|
183
|
+
macro = None
|
|
184
|
+
if package_name is None:
|
|
185
|
+
macro = self.macro_resolver.macros_by_name.get(name)
|
|
186
|
+
elif package_name == GLOBAL_PROJECT_NAME:
|
|
187
|
+
macro = self.macro_resolver.internal_packages_namespace.get(name)
|
|
188
|
+
elif package_name in self.macro_resolver.packages:
|
|
189
|
+
macro = self.macro_resolver.packages[package_name].get(name)
|
|
190
|
+
else:
|
|
191
|
+
raise PackageNotFoundForMacroError(package_name)
|
|
192
|
+
if not macro:
|
|
193
|
+
return None
|
|
194
|
+
macro_func = MacroGenerator(macro, self.ctx, self.node, self.thread_ctx)
|
|
195
|
+
return macro_func
|
dvt/context/macros.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from typing import Any, Dict, Iterable, Iterator, List, Mapping, Optional, Set, Union
|
|
2
|
+
|
|
3
|
+
from dvt.clients.jinja import MacroGenerator, MacroStack
|
|
4
|
+
from dvt.contracts.graph.nodes import Macro
|
|
5
|
+
from dvt.exceptions import DuplicateMacroNameError, PackageNotFoundForMacroError
|
|
6
|
+
from dvt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
|
7
|
+
|
|
8
|
+
FlatNamespace = Dict[str, MacroGenerator]
|
|
9
|
+
NamespaceMember = Union[FlatNamespace, MacroGenerator]
|
|
10
|
+
FullNamespace = Dict[str, NamespaceMember]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# The point of this class is to collect the various macros
|
|
14
|
+
# and provide the ability to flatten them into the ManifestContexts
|
|
15
|
+
# that are created for jinja, so that macro calls can be resolved.
|
|
16
|
+
# Creates special iterators and _keys methods to flatten the lists.
|
|
17
|
+
# When this class is created it has a static 'local_namespace' which
|
|
18
|
+
# depends on the package of the node, so it only works for one
|
|
19
|
+
# particular local package at a time for "flattening" into a context.
|
|
20
|
+
# 'get_by_package' should work for any macro.
|
|
21
|
+
class MacroNamespace(Mapping):
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
global_namespace: FlatNamespace, # root package macros
|
|
25
|
+
local_namespace: FlatNamespace, # packages for *this* node
|
|
26
|
+
global_project_namespace: FlatNamespace, # internal packages
|
|
27
|
+
packages: Dict[str, FlatNamespace], # non-internal packages
|
|
28
|
+
):
|
|
29
|
+
self.global_namespace: FlatNamespace = global_namespace
|
|
30
|
+
self.local_namespace: FlatNamespace = local_namespace
|
|
31
|
+
self.packages: Dict[str, FlatNamespace] = packages
|
|
32
|
+
self.global_project_namespace: FlatNamespace = global_project_namespace
|
|
33
|
+
|
|
34
|
+
def _search_order(self) -> Iterable[Union[FullNamespace, FlatNamespace]]:
|
|
35
|
+
yield self.local_namespace # local package
|
|
36
|
+
yield self.global_namespace # root package
|
|
37
|
+
# TODO CT-211
|
|
38
|
+
yield self.packages # type: ignore[misc] # non-internal packages
|
|
39
|
+
yield {
|
|
40
|
+
# TODO CT-211
|
|
41
|
+
GLOBAL_PROJECT_NAME: self.global_project_namespace, # type: ignore[misc] # dbt
|
|
42
|
+
}
|
|
43
|
+
yield self.global_project_namespace # other internal project besides dbt
|
|
44
|
+
|
|
45
|
+
# provides special keys method for MacroNamespace iterator
|
|
46
|
+
# returns keys from local_namespace, global_namespace, packages,
|
|
47
|
+
# global_project_namespace
|
|
48
|
+
def _keys(self) -> Set[str]:
|
|
49
|
+
keys: Set[str] = set()
|
|
50
|
+
for search in self._search_order():
|
|
51
|
+
keys.update(search)
|
|
52
|
+
return keys
|
|
53
|
+
|
|
54
|
+
# special iterator using special keys
|
|
55
|
+
def __iter__(self) -> Iterator[str]:
|
|
56
|
+
for key in self._keys():
|
|
57
|
+
yield key
|
|
58
|
+
|
|
59
|
+
def __len__(self):
|
|
60
|
+
return len(self._keys())
|
|
61
|
+
|
|
62
|
+
def __getitem__(self, key: str) -> NamespaceMember:
|
|
63
|
+
for dct in self._search_order():
|
|
64
|
+
if key in dct:
|
|
65
|
+
return dct[key]
|
|
66
|
+
raise KeyError(key)
|
|
67
|
+
|
|
68
|
+
def get_from_package(self, package_name: Optional[str], name: str) -> Optional[MacroGenerator]:
|
|
69
|
+
if package_name is None:
|
|
70
|
+
return self.get(name)
|
|
71
|
+
elif package_name == GLOBAL_PROJECT_NAME:
|
|
72
|
+
return self.global_project_namespace.get(name)
|
|
73
|
+
elif package_name in self.packages:
|
|
74
|
+
return self.packages[package_name].get(name)
|
|
75
|
+
else:
|
|
76
|
+
raise PackageNotFoundForMacroError(package_name)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# This class builds the MacroNamespace by adding macros to
|
|
80
|
+
# internal_packages or packages, and locals/globals.
|
|
81
|
+
# Call 'build_namespace' to return a MacroNamespace.
|
|
82
|
+
# This is used by ManifestContext (and subclasses)
|
|
83
|
+
class MacroNamespaceBuilder:
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
root_package: str,
|
|
87
|
+
search_package: str,
|
|
88
|
+
thread_ctx: MacroStack,
|
|
89
|
+
internal_packages: List[str],
|
|
90
|
+
node: Optional[Any] = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
self.root_package = root_package
|
|
93
|
+
self.search_package = search_package
|
|
94
|
+
# internal packages comes from get_adapter_package_names
|
|
95
|
+
self.internal_package_names = set(internal_packages)
|
|
96
|
+
self.internal_package_names_order = internal_packages
|
|
97
|
+
# macro_func is added here if in root package, since
|
|
98
|
+
# the root package acts as a "global" namespace, overriding
|
|
99
|
+
# everything else except local external package macro calls
|
|
100
|
+
self.globals: FlatNamespace = {}
|
|
101
|
+
# macro_func is added here if it's the package for this node
|
|
102
|
+
self.locals: FlatNamespace = {}
|
|
103
|
+
# Create a dictionary of [package name][macro name] =
|
|
104
|
+
# MacroGenerator object which acts like a function
|
|
105
|
+
self.internal_packages: Dict[str, FlatNamespace] = {}
|
|
106
|
+
self.packages: Dict[str, FlatNamespace] = {}
|
|
107
|
+
self.thread_ctx = thread_ctx
|
|
108
|
+
self.node = node
|
|
109
|
+
|
|
110
|
+
def _add_macro_to(
|
|
111
|
+
self,
|
|
112
|
+
hierarchy: Dict[str, FlatNamespace],
|
|
113
|
+
macro: Macro,
|
|
114
|
+
macro_func: MacroGenerator,
|
|
115
|
+
):
|
|
116
|
+
if macro.package_name in hierarchy:
|
|
117
|
+
namespace = hierarchy[macro.package_name]
|
|
118
|
+
else:
|
|
119
|
+
namespace = {}
|
|
120
|
+
hierarchy[macro.package_name] = namespace
|
|
121
|
+
|
|
122
|
+
if macro.name in namespace:
|
|
123
|
+
raise DuplicateMacroNameError(macro_func.macro, macro, macro.package_name)
|
|
124
|
+
hierarchy[macro.package_name][macro.name] = macro_func
|
|
125
|
+
|
|
126
|
+
def add_macro(self, macro: Macro, ctx: Dict[str, Any]) -> None:
|
|
127
|
+
macro_name: str = macro.name
|
|
128
|
+
|
|
129
|
+
# MacroGenerator is in clients/jinja.py
|
|
130
|
+
# a MacroGenerator object is a callable object that will
|
|
131
|
+
# execute the MacroGenerator.__call__ function
|
|
132
|
+
macro_func: MacroGenerator = MacroGenerator(macro, ctx, self.node, self.thread_ctx)
|
|
133
|
+
|
|
134
|
+
# internal macros (from plugins) will be processed separately from
|
|
135
|
+
# project macros, so store them in a different place
|
|
136
|
+
if macro.package_name in self.internal_package_names:
|
|
137
|
+
self._add_macro_to(self.internal_packages, macro, macro_func)
|
|
138
|
+
else:
|
|
139
|
+
# if it's not an internal package
|
|
140
|
+
self._add_macro_to(self.packages, macro, macro_func)
|
|
141
|
+
# add to locals if it's the package this node is in
|
|
142
|
+
if macro.package_name == self.search_package:
|
|
143
|
+
self.locals[macro_name] = macro_func
|
|
144
|
+
# add to globals if it's in the root package
|
|
145
|
+
elif macro.package_name == self.root_package:
|
|
146
|
+
self.globals[macro_name] = macro_func
|
|
147
|
+
|
|
148
|
+
def add_macros(self, macros: Iterable[Macro], ctx: Dict[str, Any]) -> None:
|
|
149
|
+
for macro in macros:
|
|
150
|
+
self.add_macro(macro, ctx)
|
|
151
|
+
|
|
152
|
+
def build_namespace(
|
|
153
|
+
self, macros_by_package: Dict[str, Dict[str, Macro]], ctx: Dict[str, Any]
|
|
154
|
+
) -> MacroNamespace:
|
|
155
|
+
for package in macros_by_package.values():
|
|
156
|
+
self.add_macros(package.values(), ctx)
|
|
157
|
+
|
|
158
|
+
# Iterate in reverse-order and overwrite: the packages that are first
|
|
159
|
+
# in the list are the ones we want to "win".
|
|
160
|
+
global_project_namespace: FlatNamespace = {}
|
|
161
|
+
for pkg in reversed(self.internal_package_names_order):
|
|
162
|
+
if pkg in self.internal_packages:
|
|
163
|
+
# add the macros pointed to by this package name
|
|
164
|
+
global_project_namespace.update(self.internal_packages[pkg])
|
|
165
|
+
|
|
166
|
+
return MacroNamespace(
|
|
167
|
+
global_namespace=self.globals, # root package macros
|
|
168
|
+
local_namespace=self.locals, # packages for *this* node
|
|
169
|
+
global_project_namespace=global_project_namespace, # internal packages
|
|
170
|
+
packages=self.packages, # non internal_packages
|
|
171
|
+
)
|
dvt/context/manifest.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from dvt.clients.jinja import MacroStack
|
|
4
|
+
from dvt.context.macro_resolver import TestMacroNamespace
|
|
5
|
+
from dvt.contracts.graph.manifest import Manifest
|
|
6
|
+
|
|
7
|
+
from dbt.adapters.contracts.connection import AdapterRequiredConfig
|
|
8
|
+
|
|
9
|
+
from .base import contextproperty
|
|
10
|
+
from .configured import ConfiguredContext
|
|
11
|
+
from .macros import MacroNamespace, MacroNamespaceBuilder
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ManifestContext(ConfiguredContext):
|
|
15
|
+
"""The Macro context has everything in the target context, plus the macros
|
|
16
|
+
in the manifest.
|
|
17
|
+
|
|
18
|
+
The given macros can override any previous context values, which will be
|
|
19
|
+
available as if they were accessed relative to the package name.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# subclasses are QueryHeaderContext and ProviderContext
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
config: AdapterRequiredConfig,
|
|
26
|
+
manifest: Manifest,
|
|
27
|
+
search_package: str,
|
|
28
|
+
) -> None:
|
|
29
|
+
super().__init__(config)
|
|
30
|
+
self.manifest = manifest
|
|
31
|
+
# this is the package of the node for which this context was built
|
|
32
|
+
self.search_package = search_package
|
|
33
|
+
self.macro_stack = MacroStack()
|
|
34
|
+
# This namespace is used by the BaseDatabaseWrapper in jinja rendering.
|
|
35
|
+
# The namespace is passed to it when it's constructed. It expects
|
|
36
|
+
# to be able to do: namespace.get_from_package(..)
|
|
37
|
+
self.namespace = self._build_namespace()
|
|
38
|
+
|
|
39
|
+
def _build_namespace(self) -> MacroNamespace:
|
|
40
|
+
# this takes all the macros in the manifest and adds them
|
|
41
|
+
# to the MacroNamespaceBuilder stored in self.namespace
|
|
42
|
+
builder = self._get_namespace_builder()
|
|
43
|
+
return builder.build_namespace(self.manifest.get_macros_by_package(), self._ctx)
|
|
44
|
+
|
|
45
|
+
def _get_namespace_builder(self) -> MacroNamespaceBuilder:
|
|
46
|
+
# avoid an import loop
|
|
47
|
+
from dbt.adapters.factory import get_adapter_package_names
|
|
48
|
+
|
|
49
|
+
internal_packages: List[str] = get_adapter_package_names(self.config.credentials.type)
|
|
50
|
+
return MacroNamespaceBuilder(
|
|
51
|
+
self.config.project_name,
|
|
52
|
+
self.search_package,
|
|
53
|
+
self.macro_stack,
|
|
54
|
+
internal_packages,
|
|
55
|
+
None,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# This does not use the Mashumaro code
|
|
59
|
+
def to_dict(self):
|
|
60
|
+
dct = super().to_dict()
|
|
61
|
+
# This moves all of the macros in the 'namespace' into top level
|
|
62
|
+
# keys in the manifest dictionary
|
|
63
|
+
if isinstance(self.namespace, TestMacroNamespace):
|
|
64
|
+
dct.update(self.namespace.local_namespace)
|
|
65
|
+
dct.update(self.namespace.project_namespace)
|
|
66
|
+
else:
|
|
67
|
+
dct.update(self.namespace)
|
|
68
|
+
|
|
69
|
+
return dct
|
|
70
|
+
|
|
71
|
+
@contextproperty()
|
|
72
|
+
def context_macro_stack(self):
|
|
73
|
+
return self.macro_stack
|