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/contracts/project.py
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, ClassVar, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from dvt.contracts.util import Identifier, list_str
|
|
5
|
+
from mashumaro.jsonschema.annotations import Pattern
|
|
6
|
+
from mashumaro.types import SerializableType
|
|
7
|
+
from typing_extensions import Annotated
|
|
8
|
+
|
|
9
|
+
from dbt.adapters.contracts.connection import QueryComment
|
|
10
|
+
from dbt_common.contracts.util import Mergeable
|
|
11
|
+
from dbt_common.dataclass_schema import (
|
|
12
|
+
ExtensibleDbtClassMixin,
|
|
13
|
+
ValidationError,
|
|
14
|
+
dbtClassMixin,
|
|
15
|
+
dbtMashConfig,
|
|
16
|
+
)
|
|
17
|
+
from dbt_common.helper_types import NoValue
|
|
18
|
+
|
|
19
|
+
DEFAULT_SEND_ANONYMOUS_USAGE_STATS = True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SemverString(str, SerializableType):
|
|
23
|
+
def _serialize(self) -> str:
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def _deserialize(cls, value: str) -> "SemverString":
|
|
28
|
+
return SemverString(value)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# This supports full semver, but also allows for 2 group version numbers, (allows '1.0').
|
|
32
|
+
sem_ver_pattern = r"^(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)?$"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Quoting(dbtClassMixin, Mergeable):
|
|
37
|
+
schema: Optional[bool] = None
|
|
38
|
+
database: Optional[bool] = None
|
|
39
|
+
project: Optional[bool] = None
|
|
40
|
+
identifier: Optional[bool] = None
|
|
41
|
+
snowflake_ignore_case: Optional[bool] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class Package(dbtClassMixin):
|
|
46
|
+
|
|
47
|
+
# Exclude {'name': None} from to_dict result to avoid changing sha1_hash result
|
|
48
|
+
# when user has not changed their 'packages' configuration.
|
|
49
|
+
def __post_serialize__(self, data, context: Optional[Dict]):
|
|
50
|
+
if "name" in data.keys() and data["name"] is None:
|
|
51
|
+
data.pop("name")
|
|
52
|
+
return data
|
|
53
|
+
return data
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class LocalPackage(Package):
|
|
58
|
+
local: str
|
|
59
|
+
unrendered: Dict[str, Any] = field(default_factory=dict)
|
|
60
|
+
name: Optional[str] = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# `float` also allows `int`, according to PEP484 (and jsonschema!)
|
|
64
|
+
RawVersion = Union[str, float]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class TarballPackage(Package):
|
|
69
|
+
tarball: str
|
|
70
|
+
name: str
|
|
71
|
+
unrendered: Dict[str, Any] = field(default_factory=dict)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class GitPackage(Package):
|
|
76
|
+
git: str
|
|
77
|
+
revision: Optional[RawVersion] = None
|
|
78
|
+
warn_unpinned: Optional[bool] = field(default=None, metadata={"alias": "warn-unpinned"})
|
|
79
|
+
subdirectory: Optional[str] = None
|
|
80
|
+
unrendered: Dict[str, Any] = field(default_factory=dict)
|
|
81
|
+
name: Optional[str] = None
|
|
82
|
+
|
|
83
|
+
def get_revisions(self) -> List[str]:
|
|
84
|
+
if self.revision is None:
|
|
85
|
+
return []
|
|
86
|
+
else:
|
|
87
|
+
return [str(self.revision)]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class PrivatePackage(Package):
|
|
92
|
+
private: str
|
|
93
|
+
provider: Optional[str] = None
|
|
94
|
+
revision: Optional[RawVersion] = None
|
|
95
|
+
warn_unpinned: Optional[bool] = field(default=None, metadata={"alias": "warn-unpinned"})
|
|
96
|
+
subdirectory: Optional[str] = None
|
|
97
|
+
unrendered: Dict[str, Any] = field(default_factory=dict)
|
|
98
|
+
name: Optional[str] = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class RegistryPackage(Package):
|
|
103
|
+
package: str
|
|
104
|
+
version: Union[RawVersion, List[RawVersion]]
|
|
105
|
+
install_prerelease: Optional[bool] = False
|
|
106
|
+
unrendered: Dict[str, Any] = field(default_factory=dict)
|
|
107
|
+
name: Optional[str] = None
|
|
108
|
+
|
|
109
|
+
def get_versions(self) -> List[str]:
|
|
110
|
+
if isinstance(self.version, list):
|
|
111
|
+
return [str(v) for v in self.version]
|
|
112
|
+
else:
|
|
113
|
+
return [str(self.version)]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
PackageSpec = Union[LocalPackage, TarballPackage, GitPackage, RegistryPackage, PrivatePackage]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class PackageConfig(dbtClassMixin):
|
|
121
|
+
packages: List[PackageSpec]
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def validate(cls, data):
|
|
125
|
+
for package in data.get("packages", data):
|
|
126
|
+
# This can happen when the target is a variable that is not filled and results in hangs
|
|
127
|
+
if isinstance(package, dict):
|
|
128
|
+
if package.get("package") == "":
|
|
129
|
+
raise ValidationError(
|
|
130
|
+
"A hub package is missing the value. It is a required property."
|
|
131
|
+
)
|
|
132
|
+
if package.get("local") == "":
|
|
133
|
+
raise ValidationError(
|
|
134
|
+
"A local package is missing the value. It is a required property."
|
|
135
|
+
)
|
|
136
|
+
if package.get("git") == "":
|
|
137
|
+
raise ValidationError(
|
|
138
|
+
"A git package is missing the value. It is a required property."
|
|
139
|
+
)
|
|
140
|
+
if isinstance(package, dict) and package.get("package"):
|
|
141
|
+
if not package["version"]:
|
|
142
|
+
raise ValidationError(
|
|
143
|
+
f"{package['package']} is missing the version. When installing from the Hub "
|
|
144
|
+
"package index, version is a required property"
|
|
145
|
+
)
|
|
146
|
+
if "/" not in package["package"]:
|
|
147
|
+
raise ValidationError(
|
|
148
|
+
f"{package['package']} was not found in the package index. Packages on the index "
|
|
149
|
+
"require a namespace, e.g dbt-labs/dbt_utils"
|
|
150
|
+
)
|
|
151
|
+
super().validate(data)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass
|
|
155
|
+
class ProjectPackageMetadata:
|
|
156
|
+
name: str
|
|
157
|
+
packages: List[PackageSpec]
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def from_project(cls, project):
|
|
161
|
+
return cls(name=project.project_name, packages=project.packages.packages)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass
|
|
165
|
+
class Downloads(ExtensibleDbtClassMixin):
|
|
166
|
+
tarball: str
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class RegistryPackageMetadata(
|
|
171
|
+
ExtensibleDbtClassMixin,
|
|
172
|
+
ProjectPackageMetadata,
|
|
173
|
+
):
|
|
174
|
+
downloads: Downloads
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# A list of all the reserved words that packages may not have as names.
|
|
178
|
+
BANNED_PROJECT_NAMES = {
|
|
179
|
+
"_sql_results",
|
|
180
|
+
"adapter",
|
|
181
|
+
"api",
|
|
182
|
+
"column",
|
|
183
|
+
"config",
|
|
184
|
+
"context",
|
|
185
|
+
"database",
|
|
186
|
+
"env",
|
|
187
|
+
"env_var",
|
|
188
|
+
"exceptions",
|
|
189
|
+
"execute",
|
|
190
|
+
"flags",
|
|
191
|
+
"fromjson",
|
|
192
|
+
"fromyaml",
|
|
193
|
+
"graph",
|
|
194
|
+
"invocation_id",
|
|
195
|
+
"load_agate_table",
|
|
196
|
+
"load_result",
|
|
197
|
+
"log",
|
|
198
|
+
"model",
|
|
199
|
+
"modules",
|
|
200
|
+
"post_hooks",
|
|
201
|
+
"pre_hooks",
|
|
202
|
+
"ref",
|
|
203
|
+
"render",
|
|
204
|
+
"return",
|
|
205
|
+
"run_started_at",
|
|
206
|
+
"schema",
|
|
207
|
+
"source",
|
|
208
|
+
"sql",
|
|
209
|
+
"sql_now",
|
|
210
|
+
"store_result",
|
|
211
|
+
"store_raw_result",
|
|
212
|
+
"target",
|
|
213
|
+
"this",
|
|
214
|
+
"tojson",
|
|
215
|
+
"toyaml",
|
|
216
|
+
"try_or_compiler_error",
|
|
217
|
+
"var",
|
|
218
|
+
"write",
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass
|
|
223
|
+
class Project(dbtClassMixin):
|
|
224
|
+
_hyphenated: ClassVar[bool] = True
|
|
225
|
+
# Annotated is used by mashumaro for jsonschema generation
|
|
226
|
+
name: Annotated[Identifier, Pattern(r"^[^\d\W]\w*$")]
|
|
227
|
+
config_version: Optional[int] = 2
|
|
228
|
+
# Annotated is used by mashumaro for jsonschema generation
|
|
229
|
+
version: Optional[Union[Annotated[SemverString, Pattern(sem_ver_pattern)], float]] = None
|
|
230
|
+
project_root: Optional[str] = None
|
|
231
|
+
source_paths: Optional[List[str]] = None
|
|
232
|
+
model_paths: Optional[List[str]] = None
|
|
233
|
+
macro_paths: Optional[List[str]] = None
|
|
234
|
+
data_paths: Optional[List[str]] = None # deprecated
|
|
235
|
+
seed_paths: Optional[List[str]] = None
|
|
236
|
+
test_paths: Optional[List[str]] = None
|
|
237
|
+
analysis_paths: Optional[List[str]] = None
|
|
238
|
+
docs_paths: Optional[List[str]] = None
|
|
239
|
+
asset_paths: Optional[List[str]] = None
|
|
240
|
+
function_paths: Optional[List[str]] = None
|
|
241
|
+
target_path: Optional[str] = None
|
|
242
|
+
snapshot_paths: Optional[List[str]] = None
|
|
243
|
+
clean_targets: Optional[List[str]] = None
|
|
244
|
+
profile: Optional[str] = None
|
|
245
|
+
log_path: Optional[str] = None
|
|
246
|
+
packages_install_path: Optional[str] = None
|
|
247
|
+
quoting: Optional[Quoting] = None
|
|
248
|
+
on_run_start: Optional[List[str]] = field(default_factory=list_str)
|
|
249
|
+
on_run_end: Optional[List[str]] = field(default_factory=list_str)
|
|
250
|
+
require_dbt_version: Optional[Union[List[str], str]] = None
|
|
251
|
+
dispatch: List[Dict[str, Any]] = field(default_factory=list)
|
|
252
|
+
models: Dict[str, Any] = field(default_factory=dict)
|
|
253
|
+
seeds: Dict[str, Any] = field(default_factory=dict)
|
|
254
|
+
snapshots: Dict[str, Any] = field(default_factory=dict)
|
|
255
|
+
analyses: Dict[str, Any] = field(default_factory=dict)
|
|
256
|
+
sources: Dict[str, Any] = field(default_factory=dict)
|
|
257
|
+
tests: Dict[str, Any] = field(default_factory=dict) # deprecated
|
|
258
|
+
data_tests: Dict[str, Any] = field(default_factory=dict)
|
|
259
|
+
unit_tests: Dict[str, Any] = field(default_factory=dict)
|
|
260
|
+
metrics: Dict[str, Any] = field(default_factory=dict)
|
|
261
|
+
semantic_models: Dict[str, Any] = field(default_factory=dict)
|
|
262
|
+
saved_queries: Dict[str, Any] = field(default_factory=dict)
|
|
263
|
+
exposures: Dict[str, Any] = field(default_factory=dict)
|
|
264
|
+
functions: Dict[str, Any] = field(default_factory=dict)
|
|
265
|
+
vars: Optional[Dict[str, Any]] = field(
|
|
266
|
+
default=None,
|
|
267
|
+
metadata=dict(
|
|
268
|
+
description="map project names to their vars override dicts",
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
packages: List[PackageSpec] = field(default_factory=list)
|
|
272
|
+
query_comment: Optional[Union[QueryComment, NoValue, str]] = field(default_factory=NoValue)
|
|
273
|
+
restrict_access: bool = False
|
|
274
|
+
dbt_cloud: Optional[Dict[str, Any]] = None
|
|
275
|
+
flags: Dict[str, Any] = field(default_factory=dict)
|
|
276
|
+
|
|
277
|
+
class Config(dbtMashConfig):
|
|
278
|
+
# These tell mashumaro to use aliases for jsonschema and for "from_dict"
|
|
279
|
+
aliases = {
|
|
280
|
+
"config_version": "config-version",
|
|
281
|
+
"project_root": "project-root",
|
|
282
|
+
"source_paths": "source-paths",
|
|
283
|
+
"model_paths": "model-paths",
|
|
284
|
+
"macro_paths": "macro-paths",
|
|
285
|
+
"data_paths": "data-paths",
|
|
286
|
+
"seed_paths": "seed-paths",
|
|
287
|
+
"test_paths": "test-paths",
|
|
288
|
+
"analysis_paths": "analysis-paths",
|
|
289
|
+
"docs_paths": "docs-paths",
|
|
290
|
+
"asset_paths": "asset-paths",
|
|
291
|
+
"function_paths": "function-paths",
|
|
292
|
+
"target_path": "target-path",
|
|
293
|
+
"snapshot_paths": "snapshot-paths",
|
|
294
|
+
"clean_targets": "clean-targets",
|
|
295
|
+
"log_path": "log-path",
|
|
296
|
+
"packages_install_path": "packages-install-path",
|
|
297
|
+
"on_run_start": "on-run-start",
|
|
298
|
+
"on_run_end": "on-run-end",
|
|
299
|
+
"require_dbt_version": "require-dbt-version",
|
|
300
|
+
"query_comment": "query-comment",
|
|
301
|
+
"restrict_access": "restrict-access",
|
|
302
|
+
"semantic_models": "semantic-models",
|
|
303
|
+
"saved_queries": "saved-queries",
|
|
304
|
+
"dbt_cloud": "dbt-cloud",
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@classmethod
|
|
308
|
+
def validate(cls, data):
|
|
309
|
+
super().validate(data)
|
|
310
|
+
if data["name"] in BANNED_PROJECT_NAMES:
|
|
311
|
+
raise ValidationError(f"Invalid project name: {data['name']} is a reserved word")
|
|
312
|
+
# validate dispatch config
|
|
313
|
+
if "dispatch" in data and data["dispatch"]:
|
|
314
|
+
entries = data["dispatch"]
|
|
315
|
+
for entry in entries:
|
|
316
|
+
if (
|
|
317
|
+
"macro_namespace" not in entry
|
|
318
|
+
or "search_order" not in entry
|
|
319
|
+
or not isinstance(entry["search_order"], list)
|
|
320
|
+
):
|
|
321
|
+
raise ValidationError(f"Invalid project dispatch config: {entry}")
|
|
322
|
+
if "dbt_cloud" in data and not isinstance(data["dbt_cloud"], dict):
|
|
323
|
+
raise ValidationError(
|
|
324
|
+
f"Invalid dbt_cloud config. Expected a 'dict' but got '{type(data['dbt_cloud'])}'"
|
|
325
|
+
)
|
|
326
|
+
if data.get("tests", None) and data.get("data_tests", None):
|
|
327
|
+
raise ValidationError(
|
|
328
|
+
"Invalid project config: cannot have both 'tests' and 'data_tests' defined"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass
|
|
333
|
+
class ProjectFlags(ExtensibleDbtClassMixin):
|
|
334
|
+
cache_selected_only: Optional[bool] = None
|
|
335
|
+
debug: Optional[bool] = None
|
|
336
|
+
fail_fast: Optional[bool] = None
|
|
337
|
+
indirect_selection: Optional[str] = None
|
|
338
|
+
log_format: Optional[str] = None
|
|
339
|
+
log_format_file: Optional[str] = None
|
|
340
|
+
log_level: Optional[str] = None
|
|
341
|
+
log_level_file: Optional[str] = None
|
|
342
|
+
partial_parse: Optional[bool] = None
|
|
343
|
+
populate_cache: Optional[bool] = None
|
|
344
|
+
printer_width: Optional[int] = None
|
|
345
|
+
send_anonymous_usage_stats: bool = DEFAULT_SEND_ANONYMOUS_USAGE_STATS
|
|
346
|
+
static_parser: Optional[bool] = None
|
|
347
|
+
use_colors: Optional[bool] = None
|
|
348
|
+
use_colors_file: Optional[bool] = None
|
|
349
|
+
use_experimental_parser: Optional[bool] = None
|
|
350
|
+
version_check: Optional[bool] = None
|
|
351
|
+
warn_error: Optional[bool] = None
|
|
352
|
+
warn_error_options: Optional[Dict[str, Union[str, List[str]]]] = None
|
|
353
|
+
write_json: Optional[bool] = None
|
|
354
|
+
|
|
355
|
+
# legacy behaviors - https://github.com/dbt-labs/dbt-core/blob/main/docs/guides/behavior-change-flags.md
|
|
356
|
+
require_batched_execution_for_custom_microbatch_strategy: bool = False
|
|
357
|
+
require_event_names_in_deprecations: bool = False
|
|
358
|
+
require_explicit_package_overrides_for_builtin_materializations: bool = True
|
|
359
|
+
require_resource_names_without_spaces: bool = True
|
|
360
|
+
source_freshness_run_project_hooks: bool = True
|
|
361
|
+
skip_nodes_if_on_run_start_fails: bool = False
|
|
362
|
+
state_modified_compare_more_unrendered_values: bool = False
|
|
363
|
+
state_modified_compare_vars: bool = False
|
|
364
|
+
require_yaml_configuration_for_mf_time_spines: bool = False
|
|
365
|
+
require_nested_cumulative_type_params: bool = False
|
|
366
|
+
validate_macro_args: bool = False
|
|
367
|
+
require_all_warnings_handled_by_warn_error: bool = False
|
|
368
|
+
require_generic_test_arguments_property: bool = True
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def project_only_flags(self) -> Dict[str, Any]:
|
|
372
|
+
return {
|
|
373
|
+
"require_batched_execution_for_custom_microbatch_strategy": self.require_batched_execution_for_custom_microbatch_strategy,
|
|
374
|
+
"require_explicit_package_overrides_for_builtin_materializations": self.require_explicit_package_overrides_for_builtin_materializations,
|
|
375
|
+
"require_resource_names_without_spaces": self.require_resource_names_without_spaces,
|
|
376
|
+
"source_freshness_run_project_hooks": self.source_freshness_run_project_hooks,
|
|
377
|
+
"skip_nodes_if_on_run_start_fails": self.skip_nodes_if_on_run_start_fails,
|
|
378
|
+
"state_modified_compare_more_unrendered_values": self.state_modified_compare_more_unrendered_values,
|
|
379
|
+
"state_modified_compare_vars": self.state_modified_compare_vars,
|
|
380
|
+
"require_yaml_configuration_for_mf_time_spines": self.require_yaml_configuration_for_mf_time_spines,
|
|
381
|
+
"require_nested_cumulative_type_params": self.require_nested_cumulative_type_params,
|
|
382
|
+
"validate_macro_args": self.validate_macro_args,
|
|
383
|
+
"require_all_warnings_handled_by_warn_error": self.require_all_warnings_handled_by_warn_error,
|
|
384
|
+
"require_generic_test_arguments_property": self.require_generic_test_arguments_property,
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@dataclass
|
|
389
|
+
class ProfileConfig(dbtClassMixin):
|
|
390
|
+
profile_name: str
|
|
391
|
+
target_name: str
|
|
392
|
+
threads: int
|
|
393
|
+
# TODO: make this a dynamic union of some kind?
|
|
394
|
+
credentials: Optional[Dict[str, Any]]
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@dataclass
|
|
398
|
+
class ConfiguredQuoting(Quoting):
|
|
399
|
+
identifier: bool = True
|
|
400
|
+
schema: bool = True
|
|
401
|
+
database: Optional[bool] = None
|
|
402
|
+
project: Optional[bool] = None
|
|
403
|
+
snowflake_ignore_case: Optional[bool] = None
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@dataclass
|
|
407
|
+
class Configuration(Project, ProfileConfig):
|
|
408
|
+
cli_vars: Dict[str, Any] = field(
|
|
409
|
+
default_factory=dict,
|
|
410
|
+
metadata={"preserve_underscore": True},
|
|
411
|
+
)
|
|
412
|
+
quoting: Optional[ConfiguredQuoting] = None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@dataclass
|
|
416
|
+
class ProjectList(dbtClassMixin):
|
|
417
|
+
projects: Dict[str, Project]
|
dvt/contracts/results.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
|
|
3
|
+
# This file is temporary, in order to not break various adapter tests, etc, until
|
|
4
|
+
# they are updated to use the new locations.
|
|
5
|
+
|
|
6
|
+
from dvt.artifacts.schemas.base import (
|
|
7
|
+
ArtifactMixin,
|
|
8
|
+
BaseArtifactMetadata,
|
|
9
|
+
VersionedSchema,
|
|
10
|
+
schema_version,
|
|
11
|
+
)
|
|
12
|
+
from dvt.artifacts.schemas.catalog import (
|
|
13
|
+
CatalogArtifact,
|
|
14
|
+
CatalogKey,
|
|
15
|
+
CatalogMetadata,
|
|
16
|
+
CatalogResults,
|
|
17
|
+
CatalogTable,
|
|
18
|
+
ColumnMetadata,
|
|
19
|
+
StatsItem,
|
|
20
|
+
TableMetadata,
|
|
21
|
+
)
|
|
22
|
+
from dvt.artifacts.schemas.freshness import (
|
|
23
|
+
FreshnessErrorEnum,
|
|
24
|
+
FreshnessExecutionResultArtifact,
|
|
25
|
+
FreshnessMetadata,
|
|
26
|
+
FreshnessNodeOutput,
|
|
27
|
+
FreshnessNodeResult,
|
|
28
|
+
FreshnessResult,
|
|
29
|
+
PartialSourceFreshnessResult,
|
|
30
|
+
SourceFreshnessOutput,
|
|
31
|
+
SourceFreshnessResult,
|
|
32
|
+
SourceFreshnessRuntimeError,
|
|
33
|
+
process_freshness_result,
|
|
34
|
+
)
|
|
35
|
+
from dvt.artifacts.schemas.results import (
|
|
36
|
+
BaseResult,
|
|
37
|
+
ExecutionResult,
|
|
38
|
+
FreshnessStatus,
|
|
39
|
+
NodeResult,
|
|
40
|
+
NodeStatus,
|
|
41
|
+
RunningStatus,
|
|
42
|
+
RunStatus,
|
|
43
|
+
TestStatus,
|
|
44
|
+
TimingInfo,
|
|
45
|
+
collect_timing_info,
|
|
46
|
+
)
|
|
47
|
+
from dvt.artifacts.schemas.run import (
|
|
48
|
+
RunExecutionResult,
|
|
49
|
+
RunResult,
|
|
50
|
+
RunResultsArtifact,
|
|
51
|
+
RunResultsMetadata,
|
|
52
|
+
process_run_result,
|
|
53
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Dict, List, Union
|
|
3
|
+
|
|
4
|
+
from dbt_common.dataclass_schema import dbtClassMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class SelectorDefinition(dbtClassMixin):
|
|
9
|
+
name: str
|
|
10
|
+
definition: Union[str, Dict[str, Any]]
|
|
11
|
+
description: str = ""
|
|
12
|
+
default: bool = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class SelectorFile(dbtClassMixin):
|
|
17
|
+
selectors: List[SelectorDefinition]
|
|
18
|
+
version: int = 2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# @dataclass
|
|
22
|
+
# class SelectorCollection:
|
|
23
|
+
# packages: Dict[str, List[SelectorFile]] = field(default_factory=dict)
|
dvt/contracts/sql.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Any, Dict, List, Optional, Sequence
|
|
5
|
+
|
|
6
|
+
from dvt.artifacts.schemas.base import VersionedSchema, schema_version
|
|
7
|
+
from dvt.artifacts.schemas.results import ExecutionResult, TimingInfo
|
|
8
|
+
from dvt.artifacts.schemas.run import RunExecutionResult, RunResult, RunResultsArtifact
|
|
9
|
+
from dvt.contracts.graph.nodes import ResultNode
|
|
10
|
+
from dvt.events.types import ArtifactWritten
|
|
11
|
+
|
|
12
|
+
from dbt_common.dataclass_schema import dbtClassMixin
|
|
13
|
+
from dbt_common.events.functions import fire_event
|
|
14
|
+
|
|
15
|
+
TaskTags = Optional[Dict[str, Any]]
|
|
16
|
+
TaskID = uuid.UUID
|
|
17
|
+
|
|
18
|
+
# Outputs
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class RemoteCompileResultMixin(VersionedSchema):
|
|
23
|
+
raw_code: str
|
|
24
|
+
compiled_code: str
|
|
25
|
+
node: ResultNode
|
|
26
|
+
timing: List[TimingInfo]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
@schema_version("remote-compile-result", 1)
|
|
31
|
+
class RemoteCompileResult(RemoteCompileResultMixin):
|
|
32
|
+
generated_at: datetime = field(
|
|
33
|
+
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def error(self) -> None:
|
|
38
|
+
# TODO: Can we delete this? It's never set anywhere else and never accessed
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
@schema_version("remote-execution-result", 1)
|
|
44
|
+
class RemoteExecutionResult(ExecutionResult):
|
|
45
|
+
results: Sequence[RunResult]
|
|
46
|
+
args: Dict[str, Any] = field(default_factory=dict)
|
|
47
|
+
generated_at: datetime = field(
|
|
48
|
+
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def write(self, path: str) -> None:
|
|
52
|
+
writable = RunResultsArtifact.from_execution_results(
|
|
53
|
+
generated_at=self.generated_at,
|
|
54
|
+
results=self.results,
|
|
55
|
+
elapsed_time=self.elapsed_time,
|
|
56
|
+
args=self.args,
|
|
57
|
+
)
|
|
58
|
+
writable.write(path)
|
|
59
|
+
fire_event(ArtifactWritten(artifact_type=writable.__class__.__name__, artifact_path=path))
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_local_result(
|
|
63
|
+
cls,
|
|
64
|
+
base: RunExecutionResult,
|
|
65
|
+
) -> "RemoteExecutionResult":
|
|
66
|
+
return cls(
|
|
67
|
+
generated_at=base.generated_at,
|
|
68
|
+
results=base.results,
|
|
69
|
+
elapsed_time=base.elapsed_time,
|
|
70
|
+
args=base.args,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ResultTable(dbtClassMixin):
|
|
76
|
+
column_names: List[str]
|
|
77
|
+
rows: List[Any]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
@schema_version("remote-run-result", 1)
|
|
82
|
+
class RemoteRunResult(RemoteCompileResultMixin):
|
|
83
|
+
table: ResultTable
|
|
84
|
+
generated_at: datetime = field(
|
|
85
|
+
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
|
|
86
|
+
)
|
dvt/contracts/state.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from dvt.artifacts.exceptions import IncompatibleSchemaError
|
|
5
|
+
from dvt.artifacts.schemas.freshness import FreshnessExecutionResultArtifact
|
|
6
|
+
from dvt.artifacts.schemas.manifest import WritableManifest
|
|
7
|
+
from dvt.artifacts.schemas.run import RunResultsArtifact
|
|
8
|
+
from dvt.constants import RUN_RESULTS_FILE_NAME
|
|
9
|
+
from dvt.contracts.graph.manifest import Manifest
|
|
10
|
+
from dvt.events.types import WarnStateTargetEqual
|
|
11
|
+
|
|
12
|
+
from dbt_common.events.functions import fire_event
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_result_state(results_path) -> Optional[RunResultsArtifact]:
|
|
16
|
+
if results_path.exists() and results_path.is_file():
|
|
17
|
+
try:
|
|
18
|
+
return RunResultsArtifact.read_and_check_versions(str(results_path))
|
|
19
|
+
except IncompatibleSchemaError as exc:
|
|
20
|
+
exc.add_filename(str(results_path))
|
|
21
|
+
raise
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PreviousState:
|
|
26
|
+
def __init__(self, state_path: Path, target_path: Path, project_root: Path) -> None:
|
|
27
|
+
self.state_path: Path = state_path
|
|
28
|
+
self.target_path: Path = target_path
|
|
29
|
+
self.project_root: Path = project_root
|
|
30
|
+
self.manifest: Optional[Manifest] = None
|
|
31
|
+
self.results: Optional[RunResultsArtifact] = None
|
|
32
|
+
self.sources: Optional[FreshnessExecutionResultArtifact] = None
|
|
33
|
+
self.sources_current: Optional[FreshnessExecutionResultArtifact] = None
|
|
34
|
+
|
|
35
|
+
if self.state_path == self.target_path:
|
|
36
|
+
fire_event(WarnStateTargetEqual(state_path=str(self.state_path)))
|
|
37
|
+
|
|
38
|
+
# Note: if state_path is absolute, project_root will be ignored.
|
|
39
|
+
manifest_path = self.project_root / self.state_path / "manifest.json"
|
|
40
|
+
if manifest_path.exists() and manifest_path.is_file():
|
|
41
|
+
try:
|
|
42
|
+
writable_manifest = WritableManifest.read_and_check_versions(str(manifest_path))
|
|
43
|
+
self.manifest = Manifest.from_writable_manifest(writable_manifest)
|
|
44
|
+
except IncompatibleSchemaError as exc:
|
|
45
|
+
exc.add_filename(str(manifest_path))
|
|
46
|
+
raise
|
|
47
|
+
|
|
48
|
+
results_path = self.project_root / self.state_path / RUN_RESULTS_FILE_NAME
|
|
49
|
+
self.results = load_result_state(results_path)
|
|
50
|
+
|
|
51
|
+
sources_path = self.project_root / self.state_path / "sources.json"
|
|
52
|
+
if sources_path.exists() and sources_path.is_file():
|
|
53
|
+
try:
|
|
54
|
+
self.sources = FreshnessExecutionResultArtifact.read_and_check_versions(
|
|
55
|
+
str(sources_path)
|
|
56
|
+
)
|
|
57
|
+
except IncompatibleSchemaError as exc:
|
|
58
|
+
exc.add_filename(str(sources_path))
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
sources_current_path = self.project_root / self.target_path / "sources.json"
|
|
62
|
+
if sources_current_path.exists() and sources_current_path.is_file():
|
|
63
|
+
try:
|
|
64
|
+
self.sources_current = FreshnessExecutionResultArtifact.read_and_check_versions(
|
|
65
|
+
str(sources_current_path)
|
|
66
|
+
)
|
|
67
|
+
except IncompatibleSchemaError as exc:
|
|
68
|
+
exc.add_filename(str(sources_current_path))
|
|
69
|
+
raise
|
dvt/contracts/util.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Any, List, Tuple
|
|
2
|
+
|
|
3
|
+
# Leave imports of `Mergeable` to preserve import paths
|
|
4
|
+
from dbt_common.contracts.util import Mergeable # noqa:F401
|
|
5
|
+
from dbt_common.dataclass_schema import ValidatedStringMixin, ValidationError
|
|
6
|
+
|
|
7
|
+
SourceKey = Tuple[str, str]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def list_str() -> List[str]:
|
|
11
|
+
"""Mypy gets upset about stuff like:
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Optional, List
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Foo:
|
|
18
|
+
x: Optional[List[str]] = field(default_factory=list)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Because `list` could be any kind of list, I guess
|
|
22
|
+
"""
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Identifier(ValidatedStringMixin):
|
|
27
|
+
"""Our definition of a valid Identifier is the same as what's valid for an unquoted database table name.
|
|
28
|
+
|
|
29
|
+
That is:
|
|
30
|
+
1. It can contain a-z, A-Z, 0-9, and _
|
|
31
|
+
1. It cannot start with a number
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
ValidationRegex = r"^[^\d\W]\w*$"
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def is_valid(cls, value: Any) -> bool:
|
|
38
|
+
if not isinstance(value, str):
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
cls.validate(value)
|
|
43
|
+
except ValidationError:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
return True
|