dvt-core 1.11.0b4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dvt-core might be problematic. Click here for more details.
- dvt/__init__.py +7 -0
- dvt/_pydantic_shim.py +26 -0
- dvt/adapters/__init__.py +16 -0
- dvt/adapters/multi_adapter_manager.py +268 -0
- dvt/artifacts/__init__.py +0 -0
- dvt/artifacts/exceptions/__init__.py +1 -0
- dvt/artifacts/exceptions/schemas.py +31 -0
- dvt/artifacts/resources/__init__.py +116 -0
- dvt/artifacts/resources/base.py +68 -0
- dvt/artifacts/resources/types.py +93 -0
- dvt/artifacts/resources/v1/analysis.py +10 -0
- dvt/artifacts/resources/v1/catalog.py +23 -0
- dvt/artifacts/resources/v1/components.py +275 -0
- dvt/artifacts/resources/v1/config.py +282 -0
- dvt/artifacts/resources/v1/documentation.py +11 -0
- dvt/artifacts/resources/v1/exposure.py +52 -0
- dvt/artifacts/resources/v1/function.py +53 -0
- dvt/artifacts/resources/v1/generic_test.py +32 -0
- dvt/artifacts/resources/v1/group.py +22 -0
- dvt/artifacts/resources/v1/hook.py +11 -0
- dvt/artifacts/resources/v1/macro.py +30 -0
- dvt/artifacts/resources/v1/metric.py +173 -0
- dvt/artifacts/resources/v1/model.py +146 -0
- dvt/artifacts/resources/v1/owner.py +10 -0
- dvt/artifacts/resources/v1/saved_query.py +112 -0
- dvt/artifacts/resources/v1/seed.py +42 -0
- dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dvt/artifacts/resources/v1/semantic_model.py +315 -0
- dvt/artifacts/resources/v1/singular_test.py +14 -0
- dvt/artifacts/resources/v1/snapshot.py +92 -0
- dvt/artifacts/resources/v1/source_definition.py +85 -0
- dvt/artifacts/resources/v1/sql_operation.py +10 -0
- dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
- dvt/artifacts/schemas/__init__.py +0 -0
- dvt/artifacts/schemas/base.py +191 -0
- dvt/artifacts/schemas/batch_results.py +24 -0
- dvt/artifacts/schemas/catalog/__init__.py +12 -0
- dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
- dvt/artifacts/schemas/freshness/__init__.py +1 -0
- dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
- dvt/artifacts/schemas/manifest/__init__.py +2 -0
- dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
- dvt/artifacts/schemas/results.py +148 -0
- dvt/artifacts/schemas/run/__init__.py +2 -0
- dvt/artifacts/schemas/run/v5/__init__.py +0 -0
- dvt/artifacts/schemas/run/v5/run.py +184 -0
- dvt/artifacts/schemas/upgrades/__init__.py +4 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dvt/artifacts/utils/validation.py +153 -0
- dvt/cli/__init__.py +1 -0
- dvt/cli/context.py +16 -0
- dvt/cli/exceptions.py +56 -0
- dvt/cli/flags.py +558 -0
- dvt/cli/main.py +971 -0
- dvt/cli/option_types.py +121 -0
- dvt/cli/options.py +79 -0
- dvt/cli/params.py +803 -0
- dvt/cli/requires.py +478 -0
- dvt/cli/resolvers.py +32 -0
- dvt/cli/types.py +40 -0
- dvt/clients/__init__.py +0 -0
- dvt/clients/checked_load.py +82 -0
- dvt/clients/git.py +164 -0
- dvt/clients/jinja.py +206 -0
- dvt/clients/jinja_static.py +245 -0
- dvt/clients/registry.py +192 -0
- dvt/clients/yaml_helper.py +68 -0
- dvt/compilation.py +833 -0
- dvt/compute/__init__.py +26 -0
- dvt/compute/base.py +288 -0
- dvt/compute/engines/__init__.py +13 -0
- dvt/compute/engines/duckdb_engine.py +368 -0
- dvt/compute/engines/spark_engine.py +273 -0
- dvt/compute/query_analyzer.py +212 -0
- dvt/compute/router.py +483 -0
- dvt/config/__init__.py +4 -0
- dvt/config/catalogs.py +95 -0
- dvt/config/compute_config.py +406 -0
- dvt/config/profile.py +411 -0
- dvt/config/profiles_v2.py +464 -0
- dvt/config/project.py +893 -0
- dvt/config/renderer.py +232 -0
- dvt/config/runtime.py +491 -0
- dvt/config/selectors.py +209 -0
- dvt/config/utils.py +78 -0
- dvt/connectors/.gitignore +6 -0
- dvt/connectors/README.md +306 -0
- dvt/connectors/catalog.yml +217 -0
- dvt/connectors/download_connectors.py +300 -0
- dvt/constants.py +29 -0
- dvt/context/__init__.py +0 -0
- dvt/context/base.py +746 -0
- dvt/context/configured.py +136 -0
- dvt/context/context_config.py +350 -0
- dvt/context/docs.py +82 -0
- dvt/context/exceptions_jinja.py +179 -0
- dvt/context/macro_resolver.py +195 -0
- dvt/context/macros.py +171 -0
- dvt/context/manifest.py +73 -0
- dvt/context/providers.py +2198 -0
- dvt/context/query_header.py +14 -0
- dvt/context/secret.py +59 -0
- dvt/context/target.py +74 -0
- dvt/contracts/__init__.py +0 -0
- dvt/contracts/files.py +413 -0
- dvt/contracts/graph/__init__.py +0 -0
- dvt/contracts/graph/manifest.py +1904 -0
- dvt/contracts/graph/metrics.py +98 -0
- dvt/contracts/graph/model_config.py +71 -0
- dvt/contracts/graph/node_args.py +42 -0
- dvt/contracts/graph/nodes.py +1806 -0
- dvt/contracts/graph/semantic_manifest.py +233 -0
- dvt/contracts/graph/unparsed.py +812 -0
- dvt/contracts/project.py +417 -0
- dvt/contracts/results.py +53 -0
- dvt/contracts/selection.py +23 -0
- dvt/contracts/sql.py +86 -0
- dvt/contracts/state.py +69 -0
- dvt/contracts/util.py +46 -0
- dvt/deprecations.py +347 -0
- dvt/deps/__init__.py +0 -0
- dvt/deps/base.py +153 -0
- dvt/deps/git.py +196 -0
- dvt/deps/local.py +80 -0
- dvt/deps/registry.py +131 -0
- dvt/deps/resolver.py +149 -0
- dvt/deps/tarball.py +121 -0
- dvt/docs/source/_ext/dbt_click.py +118 -0
- dvt/docs/source/conf.py +32 -0
- dvt/env_vars.py +64 -0
- dvt/event_time/event_time.py +40 -0
- dvt/event_time/sample_window.py +60 -0
- dvt/events/__init__.py +16 -0
- dvt/events/base_types.py +37 -0
- dvt/events/core_types_pb2.py +2 -0
- dvt/events/logging.py +109 -0
- dvt/events/types.py +2534 -0
- dvt/exceptions.py +1487 -0
- dvt/flags.py +89 -0
- dvt/graph/__init__.py +11 -0
- dvt/graph/cli.py +248 -0
- dvt/graph/graph.py +172 -0
- dvt/graph/queue.py +213 -0
- dvt/graph/selector.py +375 -0
- dvt/graph/selector_methods.py +976 -0
- dvt/graph/selector_spec.py +223 -0
- dvt/graph/thread_pool.py +18 -0
- dvt/hooks.py +21 -0
- dvt/include/README.md +49 -0
- dvt/include/__init__.py +3 -0
- dvt/include/global_project.py +4 -0
- dvt/include/starter_project/.gitignore +4 -0
- dvt/include/starter_project/README.md +15 -0
- dvt/include/starter_project/__init__.py +3 -0
- dvt/include/starter_project/analyses/.gitkeep +0 -0
- dvt/include/starter_project/dvt_project.yml +36 -0
- dvt/include/starter_project/macros/.gitkeep +0 -0
- dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dvt/include/starter_project/models/example/schema.yml +21 -0
- dvt/include/starter_project/seeds/.gitkeep +0 -0
- dvt/include/starter_project/snapshots/.gitkeep +0 -0
- dvt/include/starter_project/tests/.gitkeep +0 -0
- dvt/internal_deprecations.py +27 -0
- dvt/jsonschemas/__init__.py +3 -0
- dvt/jsonschemas/jsonschemas.py +309 -0
- dvt/jsonschemas/project/0.0.110.json +4717 -0
- dvt/jsonschemas/project/0.0.85.json +2015 -0
- dvt/jsonschemas/resources/0.0.110.json +2636 -0
- dvt/jsonschemas/resources/0.0.85.json +2536 -0
- dvt/jsonschemas/resources/latest.json +6773 -0
- dvt/links.py +4 -0
- dvt/materializations/__init__.py +0 -0
- dvt/materializations/incremental/__init__.py +0 -0
- dvt/materializations/incremental/microbatch.py +235 -0
- dvt/mp_context.py +8 -0
- dvt/node_types.py +37 -0
- dvt/parser/__init__.py +23 -0
- dvt/parser/analysis.py +21 -0
- dvt/parser/base.py +549 -0
- dvt/parser/common.py +267 -0
- dvt/parser/docs.py +52 -0
- dvt/parser/fixtures.py +51 -0
- dvt/parser/functions.py +30 -0
- dvt/parser/generic_test.py +100 -0
- dvt/parser/generic_test_builders.py +334 -0
- dvt/parser/hooks.py +119 -0
- dvt/parser/macros.py +137 -0
- dvt/parser/manifest.py +2204 -0
- dvt/parser/models.py +574 -0
- dvt/parser/partial.py +1179 -0
- dvt/parser/read_files.py +445 -0
- dvt/parser/schema_generic_tests.py +423 -0
- dvt/parser/schema_renderer.py +111 -0
- dvt/parser/schema_yaml_readers.py +936 -0
- dvt/parser/schemas.py +1467 -0
- dvt/parser/search.py +149 -0
- dvt/parser/seeds.py +28 -0
- dvt/parser/singular_test.py +20 -0
- dvt/parser/snapshots.py +44 -0
- dvt/parser/sources.py +557 -0
- dvt/parser/sql.py +63 -0
- dvt/parser/unit_tests.py +622 -0
- dvt/plugins/__init__.py +20 -0
- dvt/plugins/contracts.py +10 -0
- dvt/plugins/exceptions.py +2 -0
- dvt/plugins/manager.py +164 -0
- dvt/plugins/manifest.py +21 -0
- dvt/profiler.py +20 -0
- dvt/py.typed +1 -0
- dvt/runners/__init__.py +2 -0
- dvt/runners/exposure_runner.py +7 -0
- dvt/runners/no_op_runner.py +46 -0
- dvt/runners/saved_query_runner.py +7 -0
- dvt/selected_resources.py +8 -0
- dvt/task/__init__.py +0 -0
- dvt/task/base.py +504 -0
- dvt/task/build.py +197 -0
- dvt/task/clean.py +57 -0
- dvt/task/clone.py +162 -0
- dvt/task/compile.py +151 -0
- dvt/task/compute.py +366 -0
- dvt/task/debug.py +650 -0
- dvt/task/deps.py +280 -0
- dvt/task/docs/__init__.py +3 -0
- dvt/task/docs/generate.py +408 -0
- dvt/task/docs/index.html +250 -0
- dvt/task/docs/serve.py +28 -0
- dvt/task/freshness.py +323 -0
- dvt/task/function.py +122 -0
- dvt/task/group_lookup.py +46 -0
- dvt/task/init.py +374 -0
- dvt/task/list.py +237 -0
- dvt/task/printer.py +176 -0
- dvt/task/profiles.py +256 -0
- dvt/task/retry.py +175 -0
- dvt/task/run.py +1146 -0
- dvt/task/run_operation.py +142 -0
- dvt/task/runnable.py +802 -0
- dvt/task/seed.py +104 -0
- dvt/task/show.py +150 -0
- dvt/task/snapshot.py +57 -0
- dvt/task/sql.py +111 -0
- dvt/task/test.py +464 -0
- dvt/tests/fixtures/__init__.py +1 -0
- dvt/tests/fixtures/project.py +620 -0
- dvt/tests/util.py +651 -0
- dvt/tracking.py +529 -0
- dvt/utils/__init__.py +3 -0
- dvt/utils/artifact_upload.py +151 -0
- dvt/utils/utils.py +408 -0
- dvt/version.py +249 -0
- dvt_core-1.11.0b4.dist-info/METADATA +252 -0
- dvt_core-1.11.0b4.dist-info/RECORD +261 -0
- dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
- dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
- dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
dvt/deps/local.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from dvt.config.project import PartialProject, Project
|
|
5
|
+
from dvt.config.renderer import PackageRenderer
|
|
6
|
+
from dvt.contracts.project import LocalPackage, ProjectPackageMetadata
|
|
7
|
+
from dvt.deps.base import PinnedPackage, UnpinnedPackage
|
|
8
|
+
from dvt.events.types import DepsCreatingLocalSymlink, DepsSymlinkNotAvailable
|
|
9
|
+
|
|
10
|
+
from dbt_common.clients import system
|
|
11
|
+
from dbt_common.events.functions import fire_event
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LocalPackageMixin:
|
|
15
|
+
def __init__(self, local: str) -> None:
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.local = local
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self):
|
|
21
|
+
return self.local
|
|
22
|
+
|
|
23
|
+
def source_type(self):
|
|
24
|
+
return "local"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LocalPinnedPackage(LocalPackageMixin, PinnedPackage):
|
|
28
|
+
def __init__(self, local: str) -> None:
|
|
29
|
+
super().__init__(local)
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> Dict[str, str]:
|
|
32
|
+
return {
|
|
33
|
+
"local": self.local,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def get_version(self):
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def nice_version_name(self):
|
|
40
|
+
return "<local @ {}>".format(self.local)
|
|
41
|
+
|
|
42
|
+
def resolve_path(self, project):
|
|
43
|
+
return system.resolve_path_from_base(
|
|
44
|
+
self.local,
|
|
45
|
+
project.project_root,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def _fetch_metadata(
|
|
49
|
+
self, project: Project, renderer: PackageRenderer
|
|
50
|
+
) -> ProjectPackageMetadata:
|
|
51
|
+
partial = PartialProject.from_project_root(self.resolve_path(project))
|
|
52
|
+
return partial.render_package_metadata(renderer)
|
|
53
|
+
|
|
54
|
+
def install(self, project, renderer):
|
|
55
|
+
src_path = self.resolve_path(project)
|
|
56
|
+
dest_path = self.get_installation_path(project, renderer)
|
|
57
|
+
|
|
58
|
+
if system.path_exists(dest_path):
|
|
59
|
+
if not system.path_is_symlink(dest_path):
|
|
60
|
+
system.rmdir(dest_path)
|
|
61
|
+
else:
|
|
62
|
+
system.remove_file(dest_path)
|
|
63
|
+
try:
|
|
64
|
+
fire_event(DepsCreatingLocalSymlink())
|
|
65
|
+
system.make_symlink(src_path, dest_path)
|
|
66
|
+
except OSError:
|
|
67
|
+
fire_event(DepsSymlinkNotAvailable())
|
|
68
|
+
shutil.copytree(src_path, dest_path)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class LocalUnpinnedPackage(LocalPackageMixin, UnpinnedPackage[LocalPinnedPackage]):
|
|
72
|
+
@classmethod
|
|
73
|
+
def from_contract(cls, contract: LocalPackage) -> "LocalUnpinnedPackage":
|
|
74
|
+
return cls(local=contract.local)
|
|
75
|
+
|
|
76
|
+
def incorporate(self, other: "LocalUnpinnedPackage") -> "LocalUnpinnedPackage":
|
|
77
|
+
return LocalUnpinnedPackage(local=self.local)
|
|
78
|
+
|
|
79
|
+
def resolved(self) -> LocalPinnedPackage:
|
|
80
|
+
return LocalPinnedPackage(local=self.local)
|
dvt/deps/registry.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from typing import Dict, List
|
|
2
|
+
|
|
3
|
+
from dvt.clients import registry
|
|
4
|
+
from dvt.contracts.project import RegistryPackage, RegistryPackageMetadata
|
|
5
|
+
from dvt.deps.base import PinnedPackage, UnpinnedPackage
|
|
6
|
+
from dvt.exceptions import (
|
|
7
|
+
DependencyError,
|
|
8
|
+
PackageNotFoundError,
|
|
9
|
+
PackageVersionNotFoundError,
|
|
10
|
+
)
|
|
11
|
+
from dvt.flags import get_flags
|
|
12
|
+
from dvt.version import get_installed_version
|
|
13
|
+
|
|
14
|
+
from dbt_common import semver
|
|
15
|
+
from dbt_common.exceptions import VersionsNotCompatibleError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RegistryPackageMixin:
|
|
19
|
+
def __init__(self, package: str) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.package = package
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def name(self):
|
|
25
|
+
return self.package
|
|
26
|
+
|
|
27
|
+
def source_type(self) -> str:
|
|
28
|
+
return "hub"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RegistryPinnedPackage(RegistryPackageMixin, PinnedPackage):
|
|
32
|
+
def __init__(self, package: str, version: str, version_latest: str) -> None:
|
|
33
|
+
super().__init__(package)
|
|
34
|
+
self.version = version
|
|
35
|
+
self.version_latest = version_latest
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def name(self):
|
|
39
|
+
return self.package
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict[str, str]:
|
|
42
|
+
return {
|
|
43
|
+
"package": self.package,
|
|
44
|
+
"version": self.version,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def source_type(self):
|
|
48
|
+
return "hub"
|
|
49
|
+
|
|
50
|
+
def get_version(self):
|
|
51
|
+
return self.version
|
|
52
|
+
|
|
53
|
+
def get_version_latest(self):
|
|
54
|
+
return self.version_latest
|
|
55
|
+
|
|
56
|
+
def nice_version_name(self):
|
|
57
|
+
return "version {}".format(self.version)
|
|
58
|
+
|
|
59
|
+
def _fetch_metadata(self, project, renderer) -> RegistryPackageMetadata:
|
|
60
|
+
dct = registry.package_version(self.package, self.version)
|
|
61
|
+
return RegistryPackageMetadata.from_dict(dct)
|
|
62
|
+
|
|
63
|
+
def install(self, project, renderer):
|
|
64
|
+
self._install(project, renderer)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RegistryUnpinnedPackage(RegistryPackageMixin, UnpinnedPackage[RegistryPinnedPackage]):
|
|
68
|
+
def __init__(
|
|
69
|
+
self, package: str, versions: List[semver.VersionSpecifier], install_prerelease: bool
|
|
70
|
+
) -> None:
|
|
71
|
+
super().__init__(package)
|
|
72
|
+
self.versions = versions
|
|
73
|
+
self.install_prerelease = install_prerelease
|
|
74
|
+
|
|
75
|
+
def _check_in_index(self):
|
|
76
|
+
index = registry.index_cached()
|
|
77
|
+
if self.package not in index:
|
|
78
|
+
raise PackageNotFoundError(self.package)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_contract(cls, contract: RegistryPackage) -> "RegistryUnpinnedPackage":
|
|
82
|
+
raw_version = contract.get_versions()
|
|
83
|
+
|
|
84
|
+
versions = [semver.VersionSpecifier.from_version_string(v) for v in raw_version]
|
|
85
|
+
return cls(
|
|
86
|
+
package=contract.package,
|
|
87
|
+
versions=versions,
|
|
88
|
+
install_prerelease=bool(contract.install_prerelease),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def incorporate(self, other: "RegistryUnpinnedPackage") -> "RegistryUnpinnedPackage":
|
|
92
|
+
return RegistryUnpinnedPackage(
|
|
93
|
+
package=self.package,
|
|
94
|
+
install_prerelease=self.install_prerelease,
|
|
95
|
+
versions=self.versions + other.versions,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def resolved(self) -> RegistryPinnedPackage:
|
|
99
|
+
self._check_in_index()
|
|
100
|
+
try:
|
|
101
|
+
range_ = semver.reduce_versions(*self.versions)
|
|
102
|
+
except VersionsNotCompatibleError as e:
|
|
103
|
+
new_msg = "Version error for package {}: {}".format(self.name, e)
|
|
104
|
+
raise DependencyError(new_msg) from e
|
|
105
|
+
flags = get_flags()
|
|
106
|
+
should_version_check = bool(flags.VERSION_CHECK)
|
|
107
|
+
dbt_version = get_installed_version()
|
|
108
|
+
compatible_versions = registry.get_compatible_versions(
|
|
109
|
+
self.package, dbt_version, should_version_check
|
|
110
|
+
)
|
|
111
|
+
prerelease_version_specified = any(bool(version.prerelease) for version in self.versions)
|
|
112
|
+
installable = semver.filter_installable(
|
|
113
|
+
compatible_versions, self.install_prerelease or prerelease_version_specified
|
|
114
|
+
)
|
|
115
|
+
if installable:
|
|
116
|
+
# for now, pick a version and then recurse. later on,
|
|
117
|
+
# we'll probably want to traverse multiple options
|
|
118
|
+
# so we can match packages. not going to make a difference
|
|
119
|
+
# right now.
|
|
120
|
+
target = semver.resolve_to_specific_version(range_, installable)
|
|
121
|
+
else:
|
|
122
|
+
target = None
|
|
123
|
+
if not target:
|
|
124
|
+
# raise an exception if no installable target version is found
|
|
125
|
+
raise PackageVersionNotFoundError(
|
|
126
|
+
self.package, range_, installable, should_version_check
|
|
127
|
+
)
|
|
128
|
+
latest_compatible = installable[-1]
|
|
129
|
+
return RegistryPinnedPackage(
|
|
130
|
+
package=self.package, version=target, version_latest=latest_compatible
|
|
131
|
+
)
|
dvt/deps/resolver.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, Dict, Iterator, List, NoReturn, Set, Type
|
|
3
|
+
|
|
4
|
+
from dvt.config import Project
|
|
5
|
+
from dvt.config.renderer import PackageRenderer
|
|
6
|
+
from dvt.contracts.project import (
|
|
7
|
+
GitPackage,
|
|
8
|
+
LocalPackage,
|
|
9
|
+
PackageSpec,
|
|
10
|
+
PrivatePackage,
|
|
11
|
+
RegistryPackage,
|
|
12
|
+
TarballPackage,
|
|
13
|
+
)
|
|
14
|
+
from dvt.deps.base import BasePackage, PinnedPackage, UnpinnedPackage
|
|
15
|
+
from dvt.deps.git import GitUnpinnedPackage
|
|
16
|
+
from dvt.deps.local import LocalUnpinnedPackage
|
|
17
|
+
from dvt.deps.registry import RegistryUnpinnedPackage
|
|
18
|
+
from dvt.deps.tarball import TarballUnpinnedPackage
|
|
19
|
+
from dvt.exceptions import (
|
|
20
|
+
DependencyError,
|
|
21
|
+
DuplicateDependencyToRootError,
|
|
22
|
+
DuplicateProjectDependencyError,
|
|
23
|
+
MismatchedDependencyTypeError,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class PackageListing:
|
|
29
|
+
packages: Dict[str, UnpinnedPackage] = field(default_factory=dict)
|
|
30
|
+
|
|
31
|
+
def __len__(self):
|
|
32
|
+
return len(self.packages)
|
|
33
|
+
|
|
34
|
+
def __bool__(self):
|
|
35
|
+
return bool(self.packages)
|
|
36
|
+
|
|
37
|
+
def _pick_key(self, key: BasePackage) -> str:
|
|
38
|
+
for name in key.all_names():
|
|
39
|
+
if name in self.packages:
|
|
40
|
+
return name
|
|
41
|
+
return key.name
|
|
42
|
+
|
|
43
|
+
def __contains__(self, key: BasePackage):
|
|
44
|
+
for name in key.all_names():
|
|
45
|
+
if name in self.packages:
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def __getitem__(self, key: BasePackage):
|
|
49
|
+
key_str: str = self._pick_key(key)
|
|
50
|
+
return self.packages[key_str]
|
|
51
|
+
|
|
52
|
+
def __setitem__(self, key: BasePackage, value):
|
|
53
|
+
key_str: str = self._pick_key(key)
|
|
54
|
+
self.packages[key_str] = value
|
|
55
|
+
|
|
56
|
+
def _mismatched_types(self, old: UnpinnedPackage, new: UnpinnedPackage) -> NoReturn:
|
|
57
|
+
raise MismatchedDependencyTypeError(new, old)
|
|
58
|
+
|
|
59
|
+
def incorporate(self, package: UnpinnedPackage):
|
|
60
|
+
key: str = self._pick_key(package)
|
|
61
|
+
if key in self.packages:
|
|
62
|
+
existing: UnpinnedPackage = self.packages[key]
|
|
63
|
+
if not isinstance(existing, type(package)):
|
|
64
|
+
self._mismatched_types(existing, package)
|
|
65
|
+
self.packages[key] = existing.incorporate(package)
|
|
66
|
+
else:
|
|
67
|
+
self.packages[key] = package
|
|
68
|
+
|
|
69
|
+
def update_from(self, src: List[PackageSpec]) -> None:
|
|
70
|
+
pkg: UnpinnedPackage
|
|
71
|
+
for contract in src:
|
|
72
|
+
if isinstance(contract, LocalPackage):
|
|
73
|
+
pkg = LocalUnpinnedPackage.from_contract(contract)
|
|
74
|
+
elif isinstance(contract, TarballPackage):
|
|
75
|
+
pkg = TarballUnpinnedPackage.from_contract(contract)
|
|
76
|
+
elif isinstance(contract, GitPackage):
|
|
77
|
+
pkg = GitUnpinnedPackage.from_contract(contract)
|
|
78
|
+
elif isinstance(contract, PrivatePackage):
|
|
79
|
+
raise DependencyError(
|
|
80
|
+
f'Cannot resolve private package {contract.private} because git provider integration is missing. Please use a "git" package instead.'
|
|
81
|
+
)
|
|
82
|
+
elif isinstance(contract, RegistryPackage):
|
|
83
|
+
pkg = RegistryUnpinnedPackage.from_contract(contract)
|
|
84
|
+
else:
|
|
85
|
+
raise DependencyError("Invalid package type {}".format(type(contract)))
|
|
86
|
+
self.incorporate(pkg)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def from_contracts(cls: Type["PackageListing"], src: List[PackageSpec]) -> "PackageListing":
|
|
90
|
+
self = cls({})
|
|
91
|
+
self.update_from(src)
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def resolved(self) -> List[PinnedPackage]:
|
|
95
|
+
return [p.resolved() for p in self.packages.values()]
|
|
96
|
+
|
|
97
|
+
def __iter__(self) -> Iterator[UnpinnedPackage]:
|
|
98
|
+
return iter(self.packages.values())
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _check_for_duplicate_project_names(
|
|
102
|
+
final_deps: List[PinnedPackage],
|
|
103
|
+
project: Project,
|
|
104
|
+
renderer: PackageRenderer,
|
|
105
|
+
):
|
|
106
|
+
seen: Set[str] = set()
|
|
107
|
+
for package in final_deps:
|
|
108
|
+
project_name = package.get_project_name(project, renderer)
|
|
109
|
+
if project_name in seen:
|
|
110
|
+
raise DuplicateProjectDependencyError(project_name)
|
|
111
|
+
elif project_name == project.project_name:
|
|
112
|
+
raise DuplicateDependencyToRootError(project_name)
|
|
113
|
+
seen.add(project_name)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def resolve_packages(
|
|
117
|
+
packages: List[PackageSpec],
|
|
118
|
+
project: Project,
|
|
119
|
+
cli_vars: Dict[str, Any],
|
|
120
|
+
) -> List[PinnedPackage]:
|
|
121
|
+
pending = PackageListing.from_contracts(packages)
|
|
122
|
+
final = PackageListing()
|
|
123
|
+
|
|
124
|
+
renderer = PackageRenderer(cli_vars)
|
|
125
|
+
|
|
126
|
+
while pending:
|
|
127
|
+
next_pending = PackageListing()
|
|
128
|
+
# resolve the dependency in question
|
|
129
|
+
for package in pending:
|
|
130
|
+
final.incorporate(package)
|
|
131
|
+
target = final[package].resolved().fetch_metadata(project, renderer)
|
|
132
|
+
next_pending.update_from(target.packages)
|
|
133
|
+
pending = next_pending
|
|
134
|
+
|
|
135
|
+
resolved = final.resolved()
|
|
136
|
+
_check_for_duplicate_project_names(resolved, project, renderer)
|
|
137
|
+
return resolved
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def resolve_lock_packages(packages: List[PackageSpec]) -> List[PinnedPackage]:
|
|
141
|
+
lock_packages = PackageListing.from_contracts(packages)
|
|
142
|
+
final = PackageListing()
|
|
143
|
+
|
|
144
|
+
for package in lock_packages:
|
|
145
|
+
final.incorporate(package)
|
|
146
|
+
|
|
147
|
+
resolved = final.resolved()
|
|
148
|
+
|
|
149
|
+
return resolved
|
dvt/deps/tarball.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
from dvt.config.project import PartialProject
|
|
7
|
+
from dvt.contracts.project import TarballPackage
|
|
8
|
+
from dvt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
|
|
9
|
+
from dvt.events.types import DepsScrubbedPackageName
|
|
10
|
+
from dvt.exceptions import DependencyError, env_secrets, scrub_secrets
|
|
11
|
+
|
|
12
|
+
from dbt_common.clients import system
|
|
13
|
+
from dbt_common.events.functions import warn_or_error
|
|
14
|
+
from dbt_common.utils.connection import connection_exception_retry
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TarballPackageMixin:
|
|
18
|
+
def __init__(self, tarball: str, tarball_unrendered: str) -> None:
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.tarball = tarball
|
|
21
|
+
self.tarball_unrendered = tarball_unrendered
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def name(self):
|
|
25
|
+
return self.tarball
|
|
26
|
+
|
|
27
|
+
def source_type(self) -> str:
|
|
28
|
+
return "tarball"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TarballPinnedPackage(TarballPackageMixin, PinnedPackage):
|
|
32
|
+
def __init__(self, tarball: str, tarball_unrendered: str, package: str) -> None:
|
|
33
|
+
super().__init__(tarball, tarball_unrendered)
|
|
34
|
+
self.package = package
|
|
35
|
+
self.version = "tarball"
|
|
36
|
+
self.tar_path = os.path.join(Path(get_downloads_path()), self.package)
|
|
37
|
+
self.untarred_path = f"{self.tar_path}_untarred"
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def name(self):
|
|
41
|
+
return self.package
|
|
42
|
+
|
|
43
|
+
def to_dict(self) -> Dict[str, str]:
|
|
44
|
+
tarball_scrubbed = scrub_secrets(self.tarball_unrendered, env_secrets())
|
|
45
|
+
if self.tarball_unrendered != tarball_scrubbed:
|
|
46
|
+
warn_or_error(DepsScrubbedPackageName(package_name=tarball_scrubbed))
|
|
47
|
+
return {
|
|
48
|
+
"tarball": tarball_scrubbed,
|
|
49
|
+
"name": self.package,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def get_version(self):
|
|
53
|
+
return self.version
|
|
54
|
+
|
|
55
|
+
def nice_version_name(self):
|
|
56
|
+
return f"tarball (url: {self.tarball})"
|
|
57
|
+
|
|
58
|
+
def _fetch_metadata(self, project, renderer):
|
|
59
|
+
"""Download and untar the project and parse metadata from the project folder."""
|
|
60
|
+
download_untar_fn = functools.partial(
|
|
61
|
+
self.download_and_untar, self.tarball, self.tar_path, self.untarred_path, self.name
|
|
62
|
+
)
|
|
63
|
+
connection_exception_retry(download_untar_fn, 5)
|
|
64
|
+
|
|
65
|
+
tar_contents = os.listdir(self.untarred_path)
|
|
66
|
+
if len(tar_contents) != 1:
|
|
67
|
+
raise DependencyError(
|
|
68
|
+
f"Incorrect structure for package extracted from {self.tarball}."
|
|
69
|
+
f"The extracted package needs to follow the structure {self.name}/<package_contents>."
|
|
70
|
+
)
|
|
71
|
+
child_folder = os.listdir(self.untarred_path)[0]
|
|
72
|
+
|
|
73
|
+
self.untarred_path = os.path.join(self.untarred_path, child_folder)
|
|
74
|
+
partial = PartialProject.from_project_root(self.untarred_path)
|
|
75
|
+
metadata = partial.render_package_metadata(renderer)
|
|
76
|
+
metadata.name = self.package if self.package else metadata.name
|
|
77
|
+
return metadata
|
|
78
|
+
|
|
79
|
+
def install(self, project, renderer):
|
|
80
|
+
download_untar_fn = functools.partial(
|
|
81
|
+
self.download_and_untar, self.tarball, self.tar_path, self.untarred_path, self.name
|
|
82
|
+
)
|
|
83
|
+
connection_exception_retry(download_untar_fn, 5)
|
|
84
|
+
dest_path = self.get_installation_path(project, renderer)
|
|
85
|
+
if os.path.exists(dest_path):
|
|
86
|
+
if system.path_is_symlink(dest_path):
|
|
87
|
+
system.remove_file(dest_path)
|
|
88
|
+
else:
|
|
89
|
+
system.rmdir(dest_path)
|
|
90
|
+
system.move(self.untarred_path, dest_path)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TarballUnpinnedPackage(TarballPackageMixin, UnpinnedPackage[TarballPinnedPackage]):
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
tarball: str,
|
|
97
|
+
tarball_unrendered: str,
|
|
98
|
+
package: str,
|
|
99
|
+
) -> None:
|
|
100
|
+
super().__init__(tarball, tarball_unrendered)
|
|
101
|
+
# setup to recycle RegistryPinnedPackage fns
|
|
102
|
+
self.package = package
|
|
103
|
+
self.version = "tarball"
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def from_contract(cls, contract: TarballPackage) -> "TarballUnpinnedPackage":
|
|
107
|
+
return cls(
|
|
108
|
+
tarball=contract.tarball,
|
|
109
|
+
tarball_unrendered=(contract.unrendered.get("tarball") or contract.tarball),
|
|
110
|
+
package=contract.name,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def incorporate(self, other: "TarballUnpinnedPackage") -> "TarballUnpinnedPackage":
|
|
114
|
+
return TarballUnpinnedPackage(
|
|
115
|
+
tarball=self.tarball, tarball_unrendered=self.tarball_unrendered, package=self.package
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def resolved(self) -> TarballPinnedPackage:
|
|
119
|
+
return TarballPinnedPackage(
|
|
120
|
+
tarball=self.tarball, tarball_unrendered=self.tarball_unrendered, package=self.package
|
|
121
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import click.types as click_t
|
|
6
|
+
import dvt.cli.option_types as dbt_t
|
|
7
|
+
from docutils import nodes
|
|
8
|
+
from docutils.parsers.rst import Directive
|
|
9
|
+
|
|
10
|
+
PARAM_TYPE_MAP = {
|
|
11
|
+
click_t.BoolParamType: lambda _: "boolean",
|
|
12
|
+
click_t.Choice: lambda c: f"choice: {c.choices}",
|
|
13
|
+
click_t.IntParamType: lambda _: "int",
|
|
14
|
+
click_t.Path: lambda _: "path",
|
|
15
|
+
click_t.StringParamType: lambda _: "string",
|
|
16
|
+
dbt_t.YAML: lambda _: "YAML",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_command(cmd) -> nodes.section:
|
|
21
|
+
cmd_name = cmd.name.replace("-", "_")
|
|
22
|
+
section = nodes.section(
|
|
23
|
+
"",
|
|
24
|
+
nodes.title(text=f"Command: {cmd_name}"),
|
|
25
|
+
ids=[cmd_name],
|
|
26
|
+
names=[cmd_name],
|
|
27
|
+
)
|
|
28
|
+
section.extend(format_params(cmd))
|
|
29
|
+
return section
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def format_params(cmd) -> t.List[nodes.section]:
|
|
33
|
+
lines = []
|
|
34
|
+
for param in cmd.params:
|
|
35
|
+
uid = f"{cmd.name}|{param.name}"
|
|
36
|
+
param_section = nodes.section(
|
|
37
|
+
"",
|
|
38
|
+
nodes.title(text=param.name),
|
|
39
|
+
ids=[uid],
|
|
40
|
+
names=[uid],
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
get_type_str = PARAM_TYPE_MAP.get(type(param.type), lambda _: "unknown")
|
|
44
|
+
type_str = get_type_str(param.type)
|
|
45
|
+
|
|
46
|
+
param_section.append(nodes.paragraph(text=f"Type: {type_str}"))
|
|
47
|
+
help_txt = getattr(param, "help", None)
|
|
48
|
+
if help_txt is not None:
|
|
49
|
+
param_section.append(nodes.paragraph(text=help_txt))
|
|
50
|
+
lines.append(param_section)
|
|
51
|
+
return lines
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_module(module_path: str, error) -> t.Union[click.Command, click.Group]:
|
|
55
|
+
try:
|
|
56
|
+
module_name, attr_name = module_path.split(":", 1)
|
|
57
|
+
except ValueError: # noqa
|
|
58
|
+
raise error(f'"{module_path}" is not of format "module:parser"')
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
mod = __import__(module_name, globals(), locals(), [attr_name])
|
|
62
|
+
except Exception: # noqa
|
|
63
|
+
raise error(
|
|
64
|
+
f'Failed to import "{attr_name}" from "{module_name}". '
|
|
65
|
+
f"The following exception was raised:\n{traceback.format_exc()}"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if not hasattr(mod, attr_name):
|
|
69
|
+
raise error(f'Module "{module_name}" has no attribute "{attr_name}"')
|
|
70
|
+
|
|
71
|
+
parser = getattr(mod, attr_name)
|
|
72
|
+
|
|
73
|
+
if not isinstance(parser, (click.Command, click.Group)):
|
|
74
|
+
raise error(
|
|
75
|
+
f'"{type(parser)}" of type "{module_path}" is not click.Command'
|
|
76
|
+
' or click.Group."click.BaseCommand"'
|
|
77
|
+
)
|
|
78
|
+
return parser
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DBTClick(Directive):
|
|
82
|
+
has_content = False
|
|
83
|
+
required_arguments = 1
|
|
84
|
+
|
|
85
|
+
def run(self):
|
|
86
|
+
section = nodes.section(
|
|
87
|
+
"",
|
|
88
|
+
ids=["dbt-section"],
|
|
89
|
+
names=["dbt-section"],
|
|
90
|
+
)
|
|
91
|
+
cmds = self._get_commands(self.arguments[0])
|
|
92
|
+
for cmd in cmds:
|
|
93
|
+
command_section = format_command(cmd)
|
|
94
|
+
section.extend(command_section)
|
|
95
|
+
return [section]
|
|
96
|
+
|
|
97
|
+
def _get_commands(self, module: str) -> t.List[click.Command]:
|
|
98
|
+
click_group = load_module(module, self.error)
|
|
99
|
+
if type(click_group) is not click.Group:
|
|
100
|
+
raise self.error('Type "click.Group" not supported in dbt_click extension')
|
|
101
|
+
cmd_strs = [cmd for cmd in click_group.commands]
|
|
102
|
+
cmd_strs.sort()
|
|
103
|
+
cmds = []
|
|
104
|
+
for cmd_str in cmd_strs:
|
|
105
|
+
cmd = click_group.commands.get(cmd_str)
|
|
106
|
+
if cmd is not None:
|
|
107
|
+
cmds.append(cmd)
|
|
108
|
+
return cmds
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def setup(app) -> t.Dict[str, t.Any]:
|
|
112
|
+
app.add_directive("dbt_click", DBTClick)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"version": "0.1",
|
|
116
|
+
"parallel_read_safe": True,
|
|
117
|
+
"parallel_write_safe": True,
|
|
118
|
+
}
|
dvt/docs/source/conf.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
# Configuration file for the Sphinx documentation builder.
|
|
6
|
+
#
|
|
7
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
8
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.abspath("../../.."))
|
|
11
|
+
sys.path.insert(0, os.path.abspath("./_ext"))
|
|
12
|
+
|
|
13
|
+
# -- Project information -----------------------------------------------------
|
|
14
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
15
|
+
|
|
16
|
+
project = "dbt-core"
|
|
17
|
+
copyright = "2022, dbt Labs"
|
|
18
|
+
author = "dbt Labs"
|
|
19
|
+
|
|
20
|
+
# -- General configuration ---------------------------------------------------
|
|
21
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
22
|
+
|
|
23
|
+
extensions = ["dbt_click"]
|
|
24
|
+
|
|
25
|
+
templates_path = ["_templates"]
|
|
26
|
+
exclude_patterns: t.List[str] = []
|
|
27
|
+
|
|
28
|
+
# -- Options for HTML output -------------------------------------------------
|
|
29
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
30
|
+
|
|
31
|
+
html_theme = "alabaster"
|
|
32
|
+
html_static_path = ["_static"]
|
dvt/env_vars.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from dvt.cli import params
|
|
5
|
+
from dvt.deprecations import warn
|
|
6
|
+
from dvt.exceptions import DbtInternalError
|
|
7
|
+
|
|
8
|
+
from dbt_common.constants import ENGINE_ENV_PREFIX
|
|
9
|
+
from dbt_common.context import get_invocation_context
|
|
10
|
+
|
|
11
|
+
# These are env vars that are not in the params module, but are still allowed to be set.
|
|
12
|
+
# New additions to this list should use the new naming scheme, unless they are being added because
|
|
13
|
+
# they already existed, but we didn't know about them previously.
|
|
14
|
+
# TODO: Should at least some of these become (undocumented) cli param options?
|
|
15
|
+
_ADDITIONAL_ENGINE_ENV_VARS: List[str] = [
|
|
16
|
+
"DBT_INVOCATION_ENV",
|
|
17
|
+
"DBT_RECORDED_FILE_PATH",
|
|
18
|
+
"DBT_TEST_STATE_MODIFIED", # TODO: This is testing related, should we do this differently?
|
|
19
|
+
"DBT_PACKAGE_HUB_URL",
|
|
20
|
+
"DBT_DOWNLOAD_DIR",
|
|
21
|
+
"DBT_PP_FILE_DIFF_TEST", # TODO: This is testing related, should we do this differently?
|
|
22
|
+
"DBT_PP_TEST", # TODO: This is testing related, should we do this differently?
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, init=False)
|
|
27
|
+
class EngineEnvVar:
|
|
28
|
+
name: str
|
|
29
|
+
old_name: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
def __init__(self, envvar: str) -> None:
|
|
32
|
+
if envvar.startswith(ENGINE_ENV_PREFIX):
|
|
33
|
+
object.__setattr__(self, "name", envvar)
|
|
34
|
+
object.__setattr__(self, "old_name", None)
|
|
35
|
+
elif envvar.startswith("DBT"):
|
|
36
|
+
object.__setattr__(self, "name", envvar.replace("DBT", f"{ENGINE_ENV_PREFIX}"))
|
|
37
|
+
object.__setattr__(self, "old_name", envvar)
|
|
38
|
+
else:
|
|
39
|
+
raise DbtInternalError(
|
|
40
|
+
f"Invalid environment variable: {envvar}, this will only happen if we add a new option to dbt that has an envvar that doesn't start with DBT_ or {ENGINE_ENV_PREFIX}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Here we are creating a set of all known engine env vars. This is used in this moduleto create an allow list of dbt
|
|
45
|
+
# engine env vars. We also use it in the cli flags module to cross propagate engine env vars with their old non-engine prefixed names.
|
|
46
|
+
KNOWN_ENGINE_ENV_VARS: set[EngineEnvVar] = {
|
|
47
|
+
EngineEnvVar(envvar=envvar)
|
|
48
|
+
for envvar in [*params.KNOWN_ENV_VARS, *_ADDITIONAL_ENGINE_ENV_VARS]
|
|
49
|
+
}
|
|
50
|
+
_ALLOWED_ENV_VARS: set[str] = {envvar.name for envvar in KNOWN_ENGINE_ENV_VARS}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def validate_engine_env_vars() -> None:
|
|
54
|
+
"""
|
|
55
|
+
Validate that any set environment variables that begin with the engine prefix are allowed.
|
|
56
|
+
"""
|
|
57
|
+
env_vars = get_invocation_context()._env
|
|
58
|
+
for env_var in env_vars.keys():
|
|
59
|
+
if env_var.startswith(ENGINE_ENV_PREFIX) and env_var not in _ALLOWED_ENV_VARS:
|
|
60
|
+
warn(
|
|
61
|
+
"environment-variable-namespace-deprecation",
|
|
62
|
+
env_var=env_var,
|
|
63
|
+
reserved_prefix=ENGINE_ENV_PREFIX,
|
|
64
|
+
)
|