dvt-core 0.52.2__cp310-cp310-macosx_10_9_x86_64.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.

Files changed (275) hide show
  1. dbt/__init__.py +7 -0
  2. dbt/_pydantic_shim.py +26 -0
  3. dbt/artifacts/__init__.py +0 -0
  4. dbt/artifacts/exceptions/__init__.py +1 -0
  5. dbt/artifacts/exceptions/schemas.py +31 -0
  6. dbt/artifacts/resources/__init__.py +116 -0
  7. dbt/artifacts/resources/base.py +67 -0
  8. dbt/artifacts/resources/types.py +93 -0
  9. dbt/artifacts/resources/v1/analysis.py +10 -0
  10. dbt/artifacts/resources/v1/catalog.py +23 -0
  11. dbt/artifacts/resources/v1/components.py +274 -0
  12. dbt/artifacts/resources/v1/config.py +277 -0
  13. dbt/artifacts/resources/v1/documentation.py +11 -0
  14. dbt/artifacts/resources/v1/exposure.py +51 -0
  15. dbt/artifacts/resources/v1/function.py +52 -0
  16. dbt/artifacts/resources/v1/generic_test.py +31 -0
  17. dbt/artifacts/resources/v1/group.py +21 -0
  18. dbt/artifacts/resources/v1/hook.py +11 -0
  19. dbt/artifacts/resources/v1/macro.py +29 -0
  20. dbt/artifacts/resources/v1/metric.py +172 -0
  21. dbt/artifacts/resources/v1/model.py +145 -0
  22. dbt/artifacts/resources/v1/owner.py +10 -0
  23. dbt/artifacts/resources/v1/saved_query.py +111 -0
  24. dbt/artifacts/resources/v1/seed.py +41 -0
  25. dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  26. dbt/artifacts/resources/v1/semantic_model.py +314 -0
  27. dbt/artifacts/resources/v1/singular_test.py +14 -0
  28. dbt/artifacts/resources/v1/snapshot.py +91 -0
  29. dbt/artifacts/resources/v1/source_definition.py +84 -0
  30. dbt/artifacts/resources/v1/sql_operation.py +10 -0
  31. dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
  32. dbt/artifacts/schemas/__init__.py +0 -0
  33. dbt/artifacts/schemas/base.py +191 -0
  34. dbt/artifacts/schemas/batch_results.py +24 -0
  35. dbt/artifacts/schemas/catalog/__init__.py +11 -0
  36. dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  37. dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
  38. dbt/artifacts/schemas/freshness/__init__.py +1 -0
  39. dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  40. dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
  41. dbt/artifacts/schemas/manifest/__init__.py +2 -0
  42. dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  43. dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
  44. dbt/artifacts/schemas/results.py +147 -0
  45. dbt/artifacts/schemas/run/__init__.py +2 -0
  46. dbt/artifacts/schemas/run/v5/__init__.py +0 -0
  47. dbt/artifacts/schemas/run/v5/run.py +184 -0
  48. dbt/artifacts/schemas/upgrades/__init__.py +4 -0
  49. dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  50. dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  51. dbt/artifacts/utils/validation.py +153 -0
  52. dbt/cli/__init__.py +1 -0
  53. dbt/cli/context.py +17 -0
  54. dbt/cli/exceptions.py +57 -0
  55. dbt/cli/flags.py +560 -0
  56. dbt/cli/main.py +2039 -0
  57. dbt/cli/option_types.py +121 -0
  58. dbt/cli/options.py +80 -0
  59. dbt/cli/params.py +804 -0
  60. dbt/cli/requires.py +490 -0
  61. dbt/cli/resolvers.py +50 -0
  62. dbt/cli/types.py +40 -0
  63. dbt/clients/__init__.py +0 -0
  64. dbt/clients/checked_load.py +83 -0
  65. dbt/clients/git.py +164 -0
  66. dbt/clients/jinja.py +206 -0
  67. dbt/clients/jinja_static.py +245 -0
  68. dbt/clients/registry.py +192 -0
  69. dbt/clients/yaml_helper.py +68 -0
  70. dbt/compilation.py +876 -0
  71. dbt/compute/__init__.py +14 -0
  72. dbt/compute/engines/__init__.py +12 -0
  73. dbt/compute/engines/spark_engine.py +624 -0
  74. dbt/compute/federated_executor.py +837 -0
  75. dbt/compute/filter_pushdown.cpython-310-darwin.so +0 -0
  76. dbt/compute/filter_pushdown.py +273 -0
  77. dbt/compute/jar_provisioning.cpython-310-darwin.so +0 -0
  78. dbt/compute/jar_provisioning.py +255 -0
  79. dbt/compute/java_compat.cpython-310-darwin.so +0 -0
  80. dbt/compute/java_compat.py +689 -0
  81. dbt/compute/jdbc_utils.cpython-310-darwin.so +0 -0
  82. dbt/compute/jdbc_utils.py +678 -0
  83. dbt/compute/smart_selector.cpython-310-darwin.so +0 -0
  84. dbt/compute/smart_selector.py +311 -0
  85. dbt/compute/strategies/__init__.py +54 -0
  86. dbt/compute/strategies/base.py +165 -0
  87. dbt/compute/strategies/dataproc.py +207 -0
  88. dbt/compute/strategies/emr.py +203 -0
  89. dbt/compute/strategies/local.py +364 -0
  90. dbt/compute/strategies/standalone.py +262 -0
  91. dbt/config/__init__.py +4 -0
  92. dbt/config/catalogs.py +94 -0
  93. dbt/config/compute.cpython-310-darwin.so +0 -0
  94. dbt/config/compute.py +547 -0
  95. dbt/config/dvt_profile.cpython-310-darwin.so +0 -0
  96. dbt/config/dvt_profile.py +342 -0
  97. dbt/config/profile.py +422 -0
  98. dbt/config/project.py +873 -0
  99. dbt/config/project_utils.py +28 -0
  100. dbt/config/renderer.py +231 -0
  101. dbt/config/runtime.py +553 -0
  102. dbt/config/selectors.py +208 -0
  103. dbt/config/utils.py +77 -0
  104. dbt/constants.py +28 -0
  105. dbt/context/__init__.py +0 -0
  106. dbt/context/base.py +745 -0
  107. dbt/context/configured.py +135 -0
  108. dbt/context/context_config.py +382 -0
  109. dbt/context/docs.py +82 -0
  110. dbt/context/exceptions_jinja.py +178 -0
  111. dbt/context/macro_resolver.py +195 -0
  112. dbt/context/macros.py +171 -0
  113. dbt/context/manifest.py +72 -0
  114. dbt/context/providers.py +2249 -0
  115. dbt/context/query_header.py +13 -0
  116. dbt/context/secret.py +58 -0
  117. dbt/context/target.py +74 -0
  118. dbt/contracts/__init__.py +0 -0
  119. dbt/contracts/files.py +413 -0
  120. dbt/contracts/graph/__init__.py +0 -0
  121. dbt/contracts/graph/manifest.py +1904 -0
  122. dbt/contracts/graph/metrics.py +97 -0
  123. dbt/contracts/graph/model_config.py +70 -0
  124. dbt/contracts/graph/node_args.py +42 -0
  125. dbt/contracts/graph/nodes.py +1806 -0
  126. dbt/contracts/graph/semantic_manifest.py +232 -0
  127. dbt/contracts/graph/unparsed.py +811 -0
  128. dbt/contracts/project.py +417 -0
  129. dbt/contracts/results.py +53 -0
  130. dbt/contracts/selection.py +23 -0
  131. dbt/contracts/sql.py +85 -0
  132. dbt/contracts/state.py +68 -0
  133. dbt/contracts/util.py +46 -0
  134. dbt/deprecations.py +346 -0
  135. dbt/deps/__init__.py +0 -0
  136. dbt/deps/base.py +152 -0
  137. dbt/deps/git.py +195 -0
  138. dbt/deps/local.py +79 -0
  139. dbt/deps/registry.py +130 -0
  140. dbt/deps/resolver.py +149 -0
  141. dbt/deps/tarball.py +120 -0
  142. dbt/docs/source/_ext/dbt_click.py +119 -0
  143. dbt/docs/source/conf.py +32 -0
  144. dbt/env_vars.py +64 -0
  145. dbt/event_time/event_time.py +40 -0
  146. dbt/event_time/sample_window.py +60 -0
  147. dbt/events/__init__.py +15 -0
  148. dbt/events/base_types.py +36 -0
  149. dbt/events/core_types_pb2.py +2 -0
  150. dbt/events/logging.py +108 -0
  151. dbt/events/types.py +2516 -0
  152. dbt/exceptions.py +1486 -0
  153. dbt/flags.py +89 -0
  154. dbt/graph/__init__.py +11 -0
  155. dbt/graph/cli.py +247 -0
  156. dbt/graph/graph.py +172 -0
  157. dbt/graph/queue.py +214 -0
  158. dbt/graph/selector.py +374 -0
  159. dbt/graph/selector_methods.py +975 -0
  160. dbt/graph/selector_spec.py +222 -0
  161. dbt/graph/thread_pool.py +18 -0
  162. dbt/hooks.py +21 -0
  163. dbt/include/README.md +49 -0
  164. dbt/include/__init__.py +3 -0
  165. dbt/include/starter_project/.gitignore +4 -0
  166. dbt/include/starter_project/README.md +15 -0
  167. dbt/include/starter_project/__init__.py +3 -0
  168. dbt/include/starter_project/analyses/.gitkeep +0 -0
  169. dbt/include/starter_project/dbt_project.yml +36 -0
  170. dbt/include/starter_project/macros/.gitkeep +0 -0
  171. dbt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  172. dbt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  173. dbt/include/starter_project/models/example/schema.yml +21 -0
  174. dbt/include/starter_project/seeds/.gitkeep +0 -0
  175. dbt/include/starter_project/snapshots/.gitkeep +0 -0
  176. dbt/include/starter_project/tests/.gitkeep +0 -0
  177. dbt/internal_deprecations.py +26 -0
  178. dbt/jsonschemas/__init__.py +3 -0
  179. dbt/jsonschemas/jsonschemas.py +309 -0
  180. dbt/jsonschemas/project/0.0.110.json +4717 -0
  181. dbt/jsonschemas/project/0.0.85.json +2015 -0
  182. dbt/jsonschemas/resources/0.0.110.json +2636 -0
  183. dbt/jsonschemas/resources/0.0.85.json +2536 -0
  184. dbt/jsonschemas/resources/latest.json +6773 -0
  185. dbt/links.py +4 -0
  186. dbt/materializations/__init__.py +0 -0
  187. dbt/materializations/incremental/__init__.py +0 -0
  188. dbt/materializations/incremental/microbatch.py +236 -0
  189. dbt/mp_context.py +8 -0
  190. dbt/node_types.py +37 -0
  191. dbt/parser/__init__.py +23 -0
  192. dbt/parser/analysis.py +21 -0
  193. dbt/parser/base.py +548 -0
  194. dbt/parser/common.py +266 -0
  195. dbt/parser/docs.py +52 -0
  196. dbt/parser/fixtures.py +51 -0
  197. dbt/parser/functions.py +30 -0
  198. dbt/parser/generic_test.py +100 -0
  199. dbt/parser/generic_test_builders.py +333 -0
  200. dbt/parser/hooks.py +118 -0
  201. dbt/parser/macros.py +137 -0
  202. dbt/parser/manifest.py +2204 -0
  203. dbt/parser/models.py +573 -0
  204. dbt/parser/partial.py +1178 -0
  205. dbt/parser/read_files.py +445 -0
  206. dbt/parser/schema_generic_tests.py +422 -0
  207. dbt/parser/schema_renderer.py +111 -0
  208. dbt/parser/schema_yaml_readers.py +935 -0
  209. dbt/parser/schemas.py +1466 -0
  210. dbt/parser/search.py +149 -0
  211. dbt/parser/seeds.py +28 -0
  212. dbt/parser/singular_test.py +20 -0
  213. dbt/parser/snapshots.py +44 -0
  214. dbt/parser/sources.py +558 -0
  215. dbt/parser/sql.py +62 -0
  216. dbt/parser/unit_tests.py +621 -0
  217. dbt/plugins/__init__.py +20 -0
  218. dbt/plugins/contracts.py +9 -0
  219. dbt/plugins/exceptions.py +2 -0
  220. dbt/plugins/manager.py +163 -0
  221. dbt/plugins/manifest.py +21 -0
  222. dbt/profiler.py +20 -0
  223. dbt/py.typed +1 -0
  224. dbt/query_analyzer.cpython-310-darwin.so +0 -0
  225. dbt/query_analyzer.py +410 -0
  226. dbt/runners/__init__.py +2 -0
  227. dbt/runners/exposure_runner.py +7 -0
  228. dbt/runners/no_op_runner.py +45 -0
  229. dbt/runners/saved_query_runner.py +7 -0
  230. dbt/selected_resources.py +8 -0
  231. dbt/task/__init__.py +0 -0
  232. dbt/task/base.py +503 -0
  233. dbt/task/build.py +197 -0
  234. dbt/task/clean.py +56 -0
  235. dbt/task/clone.py +161 -0
  236. dbt/task/compile.py +150 -0
  237. dbt/task/compute.py +454 -0
  238. dbt/task/debug.py +505 -0
  239. dbt/task/deps.py +280 -0
  240. dbt/task/docs/__init__.py +3 -0
  241. dbt/task/docs/generate.py +660 -0
  242. dbt/task/docs/index.html +250 -0
  243. dbt/task/docs/serve.py +29 -0
  244. dbt/task/freshness.py +322 -0
  245. dbt/task/function.py +121 -0
  246. dbt/task/group_lookup.py +46 -0
  247. dbt/task/init.py +553 -0
  248. dbt/task/java.py +316 -0
  249. dbt/task/list.py +236 -0
  250. dbt/task/printer.py +175 -0
  251. dbt/task/retry.py +175 -0
  252. dbt/task/run.py +1306 -0
  253. dbt/task/run_operation.py +141 -0
  254. dbt/task/runnable.py +758 -0
  255. dbt/task/seed.py +103 -0
  256. dbt/task/show.py +149 -0
  257. dbt/task/snapshot.py +56 -0
  258. dbt/task/spark.py +414 -0
  259. dbt/task/sql.py +110 -0
  260. dbt/task/target_sync.py +759 -0
  261. dbt/task/test.py +464 -0
  262. dbt/tests/fixtures/__init__.py +1 -0
  263. dbt/tests/fixtures/project.py +620 -0
  264. dbt/tests/util.py +651 -0
  265. dbt/tracking.py +529 -0
  266. dbt/utils/__init__.py +3 -0
  267. dbt/utils/artifact_upload.py +151 -0
  268. dbt/utils/utils.py +408 -0
  269. dbt/version.py +268 -0
  270. dvt_cli/__init__.py +72 -0
  271. dvt_core-0.52.2.dist-info/METADATA +286 -0
  272. dvt_core-0.52.2.dist-info/RECORD +275 -0
  273. dvt_core-0.52.2.dist-info/WHEEL +5 -0
  274. dvt_core-0.52.2.dist-info/entry_points.txt +2 -0
  275. dvt_core-0.52.2.dist-info/top_level.txt +2 -0
dbt/config/project.py ADDED
@@ -0,0 +1,873 @@
1
+ import os
2
+ from copy import deepcopy
3
+ from dataclasses import dataclass, field
4
+ from itertools import chain
5
+ from typing import Any, Dict, List, Mapping, Optional, TypeVar, Union
6
+
7
+ from typing_extensions import Protocol, runtime_checkable
8
+
9
+ from dbt import deprecations
10
+ from dbt.adapters.contracts.connection import QueryComment
11
+ from dbt.clients.checked_load import (
12
+ checked_load,
13
+ issue_deprecation_warnings_for_failures,
14
+ )
15
+ from dbt.clients.yaml_helper import load_yaml_text
16
+ from dbt.config.selectors import SelectorDict
17
+ from dbt.config.utils import normalize_warn_error_options
18
+ from dbt.constants import (
19
+ DBT_PROJECT_FILE_NAME,
20
+ DEPENDENCIES_FILE_NAME,
21
+ PACKAGE_LOCK_HASH_KEY,
22
+ PACKAGES_FILE_NAME,
23
+ )
24
+ from dbt.contracts.project import PackageConfig
25
+ from dbt.contracts.project import Project as ProjectContract
26
+ from dbt.contracts.project import ProjectFlags, ProjectPackageMetadata, SemverString
27
+ from dbt.exceptions import (
28
+ DbtExclusivePropertyUseError,
29
+ DbtProjectError,
30
+ DbtRuntimeError,
31
+ ProjectContractBrokenError,
32
+ ProjectContractError,
33
+ )
34
+ from dbt.flags import get_flags
35
+ from dbt.graph import SelectionSpec
36
+ from dbt.node_types import NodeType
37
+ from dbt.utils import MultiDict, coerce_dict_str, md5
38
+ from dbt.version import get_installed_version
39
+ from dbt_common.clients.system import load_file_contents, path_exists
40
+ from dbt_common.dataclass_schema import ValidationError
41
+ from dbt_common.exceptions import SemverError
42
+ from dbt_common.helper_types import NoValue
43
+ from dbt_common.semver import VersionSpecifier, versions_compatible
44
+
45
+ from .renderer import DbtProjectYamlRenderer, PackageRenderer
46
+ from .selectors import (
47
+ SelectorConfig,
48
+ selector_config_from_data,
49
+ selector_data_from_root,
50
+ )
51
+
52
+ INVALID_VERSION_ERROR = """\
53
+ This version of dbt is not supported with the '{package}' package.
54
+ Installed version of dbt: {installed}
55
+ Required version of dbt for '{package}': {version_spec}
56
+ Check for a different version of the '{package}' package, or run dbt again with \
57
+ --no-version-check
58
+ """
59
+
60
+
61
+ IMPOSSIBLE_VERSION_ERROR = """\
62
+ The package version requirement can never be satisfied for the '{package}
63
+ package.
64
+ Required versions of dbt for '{package}': {version_spec}
65
+ Check for a different version of the '{package}' package, or run dbt again with \
66
+ --no-version-check
67
+ """
68
+
69
+ MALFORMED_PACKAGE_ERROR = """\
70
+ The packages.yml file in this project is malformed. Please double check
71
+ the contents of this file and fix any errors before retrying.
72
+
73
+ You can find more information on the syntax for this file here:
74
+ https://docs.getdbt.com/docs/package-management
75
+
76
+ Validator Error:
77
+ {error}
78
+ """
79
+
80
+ MISSING_DBT_PROJECT_ERROR = """\
81
+ No {DBT_PROJECT_FILE_NAME} found at expected path {path}
82
+ Verify that each entry within packages.yml (and their transitive dependencies) contains a file named {DBT_PROJECT_FILE_NAME}
83
+ """
84
+
85
+
86
+ @runtime_checkable
87
+ class IsFQNResource(Protocol):
88
+ fqn: List[str]
89
+ resource_type: NodeType
90
+ package_name: str
91
+
92
+
93
+ def _load_yaml(path, validate: bool = False):
94
+ contents = load_file_contents(path)
95
+ if validate:
96
+ result, failures = checked_load(contents)
97
+ issue_deprecation_warnings_for_failures(failures=failures, file=path)
98
+ return result
99
+ else:
100
+ return load_yaml_text(contents)
101
+
102
+
103
+ def load_yml_dict(file_path):
104
+ ret = {}
105
+ if path_exists(file_path):
106
+ ret = _load_yaml(file_path) or {}
107
+ return ret
108
+
109
+
110
+ def package_and_project_data_from_root(project_root):
111
+ packages_yml_dict = load_yml_dict(f"{project_root}/{PACKAGES_FILE_NAME}")
112
+ dependencies_yml_dict = load_yml_dict(f"{project_root}/{DEPENDENCIES_FILE_NAME}")
113
+
114
+ if "packages" in packages_yml_dict and "packages" in dependencies_yml_dict:
115
+ msg = "The 'packages' key cannot be specified in both packages.yml and dependencies.yml"
116
+ raise DbtProjectError(msg)
117
+ if "projects" in packages_yml_dict:
118
+ msg = "The 'projects' key cannot be specified in packages.yml"
119
+ raise DbtProjectError(msg)
120
+
121
+ packages_specified_path = PACKAGES_FILE_NAME
122
+ packages_dict = {}
123
+ if "packages" in dependencies_yml_dict:
124
+ packages_dict["packages"] = dependencies_yml_dict["packages"]
125
+ packages_specified_path = DEPENDENCIES_FILE_NAME
126
+ else: # don't check for "packages" here so we capture invalid keys in packages.yml
127
+ packages_dict = packages_yml_dict
128
+
129
+ return packages_dict, packages_specified_path
130
+
131
+
132
+ def package_config_from_data(
133
+ packages_data: Dict[str, Any],
134
+ unrendered_packages_data: Optional[Dict[str, Any]] = None,
135
+ ) -> PackageConfig:
136
+ if not packages_data:
137
+ packages_data = {"packages": []}
138
+
139
+ # this depends on the two lists being in the same order
140
+ if unrendered_packages_data:
141
+ unrendered_packages_data = deepcopy(unrendered_packages_data)
142
+ for i in range(0, len(packages_data.get("packages", []))):
143
+ packages_data["packages"][i]["unrendered"] = unrendered_packages_data["packages"][i]
144
+
145
+ if PACKAGE_LOCK_HASH_KEY in packages_data:
146
+ packages_data.pop(PACKAGE_LOCK_HASH_KEY)
147
+ try:
148
+ PackageConfig.validate(packages_data)
149
+ packages = PackageConfig.from_dict(packages_data)
150
+ except ValidationError as e:
151
+ raise DbtProjectError(MALFORMED_PACKAGE_ERROR.format(error=str(e.message))) from e
152
+ return packages
153
+
154
+
155
+ def _parse_versions(versions: Union[List[str], str]) -> List[VersionSpecifier]:
156
+ """Parse multiple versions as read from disk. The versions value may be any
157
+ one of:
158
+ - a single version string ('>0.12.1')
159
+ - a single string specifying multiple comma-separated versions
160
+ ('>0.11.1,<=0.12.2')
161
+ - an array of single-version strings (['>0.11.1', '<=0.12.2'])
162
+
163
+ Regardless, this will return a list of VersionSpecifiers
164
+ """
165
+ if isinstance(versions, str):
166
+ versions = versions.split(",")
167
+ return [VersionSpecifier.from_version_string(v) for v in versions]
168
+
169
+
170
+ def _all_source_paths(*args: List[str]) -> List[str]:
171
+ paths = chain(*args)
172
+ # Strip trailing slashes since the path is the same even though the name is not
173
+ stripped_paths = map(lambda s: s.rstrip("/"), paths)
174
+ return list(set(stripped_paths))
175
+
176
+
177
+ T = TypeVar("T")
178
+
179
+
180
+ def flag_or(flag: Optional[T], value: Optional[T], default: T) -> T:
181
+ if flag is None:
182
+ return value_or(value, default)
183
+ else:
184
+ return flag
185
+
186
+
187
+ def value_or(value: Optional[T], default: T) -> T:
188
+ if value is None:
189
+ return default
190
+ else:
191
+ return value
192
+
193
+
194
+ def load_raw_project(project_root: str, validate: bool = False) -> Dict[str, Any]:
195
+ project_root = os.path.normpath(project_root)
196
+ project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
197
+
198
+ # get the project.yml contents
199
+ if not path_exists(project_yaml_filepath):
200
+ raise DbtProjectError(
201
+ MISSING_DBT_PROJECT_ERROR.format(
202
+ path=project_yaml_filepath, DBT_PROJECT_FILE_NAME=DBT_PROJECT_FILE_NAME
203
+ )
204
+ )
205
+
206
+ project_dict = _load_yaml(project_yaml_filepath, validate=validate)
207
+
208
+ if validate:
209
+ from dbt.jsonschemas.jsonschemas import jsonschema_validate, project_schema
210
+
211
+ jsonschema_validate(
212
+ schema=project_schema(), json=project_dict, file_path=project_yaml_filepath
213
+ )
214
+
215
+ if not isinstance(project_dict, dict):
216
+ raise DbtProjectError(f"{DBT_PROJECT_FILE_NAME} does not parse to a dictionary")
217
+
218
+ if "tests" in project_dict and "data_tests" not in project_dict:
219
+ project_dict["data_tests"] = project_dict.pop("tests")
220
+
221
+ return project_dict
222
+
223
+
224
+ def _query_comment_from_cfg(
225
+ cfg_query_comment: Union[QueryComment, NoValue, str, None]
226
+ ) -> QueryComment:
227
+ if not cfg_query_comment:
228
+ return QueryComment(comment="")
229
+
230
+ if isinstance(cfg_query_comment, str):
231
+ return QueryComment(comment=cfg_query_comment)
232
+
233
+ if isinstance(cfg_query_comment, NoValue):
234
+ return QueryComment()
235
+
236
+ return cfg_query_comment
237
+
238
+
239
+ def validate_version(dbt_version: List[VersionSpecifier], project_name: str):
240
+ """Ensure this package works with the installed version of dbt."""
241
+ installed = get_installed_version()
242
+ if not versions_compatible(*dbt_version):
243
+ msg = IMPOSSIBLE_VERSION_ERROR.format(
244
+ package=project_name, version_spec=[x.to_version_string() for x in dbt_version]
245
+ )
246
+ raise DbtProjectError(msg)
247
+
248
+ if not versions_compatible(installed, *dbt_version):
249
+ msg = INVALID_VERSION_ERROR.format(
250
+ package=project_name,
251
+ installed=installed.to_version_string(),
252
+ version_spec=[x.to_version_string() for x in dbt_version],
253
+ )
254
+ raise DbtProjectError(msg)
255
+
256
+
257
+ def _get_required_version(
258
+ project_dict: Dict[str, Any],
259
+ verify_version: bool,
260
+ ) -> List[VersionSpecifier]:
261
+ dbt_raw_version: Union[List[str], str] = ">=0.0.0"
262
+ required = project_dict.get("require-dbt-version")
263
+ if required is not None:
264
+ dbt_raw_version = required
265
+
266
+ try:
267
+ dbt_version = _parse_versions(dbt_raw_version)
268
+ except SemverError as e:
269
+ raise DbtProjectError(str(e)) from e
270
+
271
+ if verify_version:
272
+ # no name is also an error that we want to raise
273
+ if "name" not in project_dict:
274
+ raise DbtProjectError(
275
+ 'Required "name" field not present in project',
276
+ )
277
+ validate_version(dbt_version, project_dict["name"])
278
+
279
+ return dbt_version
280
+
281
+
282
+ @dataclass
283
+ class RenderComponents:
284
+ project_dict: Dict[str, Any] = field(metadata=dict(description="The project dictionary"))
285
+ packages_dict: Dict[str, Any] = field(metadata=dict(description="The packages dictionary"))
286
+ selectors_dict: Dict[str, Any] = field(metadata=dict(description="The selectors dictionary"))
287
+
288
+
289
+ @dataclass
290
+ class PartialProject(RenderComponents):
291
+ # This class includes the project_dict, packages_dict, selectors_dict, etc from RenderComponents
292
+ profile_name: Optional[str] = field(
293
+ metadata=dict(description="The unrendered profile name in the project, if set")
294
+ )
295
+ project_name: Optional[str] = field(
296
+ metadata=dict(
297
+ description=(
298
+ "The name of the project. This should always be set and will not be rendered"
299
+ )
300
+ )
301
+ )
302
+ project_root: str = field(
303
+ metadata=dict(description="The root directory of the project"),
304
+ )
305
+ verify_version: bool = field(
306
+ metadata=dict(description=("If True, verify the dbt version matches the required version"))
307
+ )
308
+ packages_specified_path: str = field(
309
+ metadata=dict(description="The filename where packages were specified")
310
+ )
311
+
312
+ def render_profile_name(self, renderer) -> Optional[str]:
313
+ if self.profile_name is None:
314
+ return None
315
+ return renderer.render_value(self.profile_name)
316
+
317
+ def get_rendered(
318
+ self,
319
+ renderer: DbtProjectYamlRenderer,
320
+ ) -> RenderComponents:
321
+ rendered_project = renderer.render_project(self.project_dict, self.project_root)
322
+ rendered_packages = renderer.render_packages(
323
+ self.packages_dict, self.packages_specified_path
324
+ )
325
+ rendered_selectors = renderer.render_selectors(self.selectors_dict)
326
+
327
+ return RenderComponents(
328
+ project_dict=rendered_project,
329
+ packages_dict=rendered_packages,
330
+ selectors_dict=rendered_selectors,
331
+ )
332
+
333
+ # Called by Project.from_project_root which first calls PartialProject.from_project_root
334
+ def render(self, renderer: DbtProjectYamlRenderer) -> "Project":
335
+ try:
336
+ rendered = self.get_rendered(renderer)
337
+ return self.create_project(rendered)
338
+ except DbtProjectError as exc:
339
+ if exc.path is None:
340
+ exc.path = os.path.join(self.project_root, DBT_PROJECT_FILE_NAME)
341
+ raise
342
+
343
+ def render_package_metadata(self, renderer: PackageRenderer) -> ProjectPackageMetadata:
344
+ packages_data = renderer.render_data(self.packages_dict)
345
+ packages_config = package_config_from_data(packages_data, self.packages_dict)
346
+ if not self.project_name:
347
+ raise DbtProjectError(f"Package defined in {DBT_PROJECT_FILE_NAME} must have a name!")
348
+ return ProjectPackageMetadata(self.project_name, packages_config.packages)
349
+
350
+ def check_config_path(
351
+ self, project_dict, deprecated_path, expected_path=None, default_value=None
352
+ ):
353
+ if deprecated_path in project_dict:
354
+ if expected_path in project_dict:
355
+ msg = (
356
+ "{deprecated_path} and {expected_path} cannot both be defined. The "
357
+ "`{deprecated_path}` config has been deprecated in favor of `{expected_path}`. "
358
+ f"Please update your `{DBT_PROJECT_FILE_NAME}` configuration to reflect this "
359
+ "change."
360
+ )
361
+ raise DbtProjectError(
362
+ msg.format(deprecated_path=deprecated_path, expected_path=expected_path)
363
+ )
364
+ # this field is no longer supported, but many projects may specify it with the default value
365
+ # if so, let's only raise this deprecation warning if they set a custom value
366
+ if not default_value or project_dict[deprecated_path] != default_value:
367
+ kwargs = {"deprecated_path": deprecated_path}
368
+ if expected_path:
369
+ kwargs.update({"exp_path": expected_path})
370
+ deprecations.warn(f"project-config-{deprecated_path}", **kwargs)
371
+
372
+ def create_project(self, rendered: RenderComponents) -> "Project":
373
+ unrendered = RenderComponents(
374
+ project_dict=self.project_dict,
375
+ packages_dict=self.packages_dict,
376
+ selectors_dict=self.selectors_dict,
377
+ )
378
+ dbt_version = _get_required_version(
379
+ rendered.project_dict,
380
+ verify_version=self.verify_version,
381
+ )
382
+
383
+ self.check_config_path(rendered.project_dict, "source-paths", "model-paths")
384
+ self.check_config_path(rendered.project_dict, "data-paths", "seed-paths")
385
+ self.check_config_path(rendered.project_dict, "log-path", default_value="logs")
386
+ self.check_config_path(rendered.project_dict, "target-path", default_value="target")
387
+
388
+ try:
389
+ ProjectContract.validate(rendered.project_dict)
390
+ cfg = ProjectContract.from_dict(rendered.project_dict)
391
+ except ValidationError as e:
392
+ raise ProjectContractError(e) from e
393
+ # name/version are required in the Project definition, so we can assume
394
+ # they are present
395
+ name = cfg.name
396
+ version = cfg.version
397
+ # this is added at project_dict parse time and should always be here
398
+ # once we see it.
399
+ if cfg.project_root is None:
400
+ raise DbtProjectError("cfg must have a project root!")
401
+ else:
402
+ project_root = cfg.project_root
403
+ # this is only optional in the sense that if it's not present, it needs
404
+ # to have been a cli argument.
405
+ profile_name = cfg.profile
406
+ # these are all the defaults
407
+
408
+ # `source_paths` is deprecated but still allowed. Copy it into
409
+ # `model_paths` to simlify logic throughout the rest of the system.
410
+ model_paths: List[str] = value_or(
411
+ cfg.model_paths if "model-paths" in rendered.project_dict else cfg.source_paths,
412
+ ["models"],
413
+ )
414
+ macro_paths: List[str] = value_or(cfg.macro_paths, ["macros"])
415
+ # `data_paths` is deprecated but still allowed. Copy it into
416
+ # `seed_paths` to simlify logic throughout the rest of the system.
417
+ seed_paths: List[str] = value_or(
418
+ cfg.seed_paths if "seed-paths" in rendered.project_dict else cfg.data_paths, ["seeds"]
419
+ )
420
+ test_paths: List[str] = value_or(cfg.test_paths, ["tests"])
421
+ analysis_paths: List[str] = value_or(cfg.analysis_paths, ["analyses"])
422
+ snapshot_paths: List[str] = value_or(cfg.snapshot_paths, ["snapshots"])
423
+ function_paths: List[str] = value_or(cfg.function_paths, ["functions"])
424
+
425
+ all_source_paths: List[str] = _all_source_paths(
426
+ model_paths,
427
+ seed_paths,
428
+ snapshot_paths,
429
+ analysis_paths,
430
+ macro_paths,
431
+ test_paths,
432
+ function_paths,
433
+ )
434
+
435
+ docs_paths: List[str] = value_or(cfg.docs_paths, all_source_paths)
436
+ asset_paths: List[str] = value_or(cfg.asset_paths, [])
437
+ global_flags = get_flags()
438
+
439
+ flag_target_path = str(global_flags.TARGET_PATH) if global_flags.TARGET_PATH else None
440
+ target_path: str = flag_or(flag_target_path, cfg.target_path, "target")
441
+ log_path: str = str(global_flags.LOG_PATH)
442
+
443
+ clean_targets: List[str] = value_or(cfg.clean_targets, [target_path])
444
+ packages_install_path: str = value_or(cfg.packages_install_path, "dbt_packages")
445
+ # in the default case we'll populate this once we know the adapter type
446
+ # It would be nice to just pass along a Quoting here, but that would
447
+ # break many things
448
+ quoting: Dict[str, Any] = {}
449
+ if cfg.quoting is not None:
450
+ quoting = cfg.quoting.to_dict(omit_none=True)
451
+
452
+ dispatch: List[Dict[str, Any]]
453
+ models: Dict[str, Any]
454
+ seeds: Dict[str, Any]
455
+ snapshots: Dict[str, Any]
456
+ sources: Dict[str, Any]
457
+ data_tests: Dict[str, Any]
458
+ unit_tests: Dict[str, Any]
459
+ metrics: Dict[str, Any]
460
+ semantic_models: Dict[str, Any]
461
+ saved_queries: Dict[str, Any]
462
+ exposures: Dict[str, Any]
463
+ functions: Dict[str, Any]
464
+ vars_value: VarProvider
465
+ dbt_cloud: Dict[str, Any]
466
+
467
+ dispatch = cfg.dispatch
468
+ models = cfg.models
469
+ seeds = cfg.seeds
470
+ snapshots = cfg.snapshots
471
+ sources = cfg.sources
472
+ # the `tests` config is deprecated but still allowed. Copy it into
473
+ # `data_tests` to simplify logic throughout the rest of the system.
474
+ data_tests = cfg.data_tests if "data_tests" in rendered.project_dict else cfg.tests
475
+ unit_tests = cfg.unit_tests
476
+ metrics = cfg.metrics
477
+ semantic_models = cfg.semantic_models
478
+ saved_queries = cfg.saved_queries
479
+ exposures = cfg.exposures
480
+ functions = cfg.functions
481
+ if cfg.vars is None:
482
+ vars_dict: Dict[str, Any] = {}
483
+ else:
484
+ vars_dict = cfg.vars
485
+
486
+ vars_value = VarProvider(vars_dict)
487
+ # There will never be any project_env_vars when it's first created
488
+ project_env_vars: Dict[str, Any] = {}
489
+ on_run_start: List[str] = value_or(cfg.on_run_start, [])
490
+ on_run_end: List[str] = value_or(cfg.on_run_end, [])
491
+
492
+ query_comment = _query_comment_from_cfg(cfg.query_comment)
493
+ packages: PackageConfig = package_config_from_data(
494
+ rendered.packages_dict, unrendered.packages_dict
495
+ )
496
+ selectors = selector_config_from_data(rendered.selectors_dict)
497
+ manifest_selectors: Dict[str, Any] = {}
498
+ if rendered.selectors_dict and rendered.selectors_dict["selectors"]:
499
+ # this is a dict with a single key 'selectors' pointing to a list
500
+ # of dicts.
501
+ manifest_selectors = SelectorDict.parse_from_selectors_list(
502
+ rendered.selectors_dict["selectors"]
503
+ )
504
+ dbt_cloud = cfg.dbt_cloud
505
+ flags: Dict[str, Any] = cfg.flags
506
+
507
+ project = Project(
508
+ project_name=name,
509
+ version=version,
510
+ project_root=project_root,
511
+ profile_name=profile_name,
512
+ model_paths=model_paths,
513
+ macro_paths=macro_paths,
514
+ seed_paths=seed_paths,
515
+ test_paths=test_paths,
516
+ analysis_paths=analysis_paths,
517
+ docs_paths=docs_paths,
518
+ asset_paths=asset_paths,
519
+ target_path=target_path,
520
+ snapshot_paths=snapshot_paths,
521
+ function_paths=function_paths,
522
+ clean_targets=clean_targets,
523
+ log_path=log_path,
524
+ packages_install_path=packages_install_path,
525
+ packages_specified_path=self.packages_specified_path,
526
+ quoting=quoting,
527
+ models=models,
528
+ on_run_start=on_run_start,
529
+ on_run_end=on_run_end,
530
+ dispatch=dispatch,
531
+ seeds=seeds,
532
+ snapshots=snapshots,
533
+ dbt_version=dbt_version,
534
+ packages=packages,
535
+ manifest_selectors=manifest_selectors,
536
+ selectors=selectors,
537
+ query_comment=query_comment,
538
+ sources=sources,
539
+ data_tests=data_tests,
540
+ unit_tests=unit_tests,
541
+ metrics=metrics,
542
+ semantic_models=semantic_models,
543
+ saved_queries=saved_queries,
544
+ exposures=exposures,
545
+ functions=functions,
546
+ vars=vars_value,
547
+ config_version=cfg.config_version,
548
+ unrendered=unrendered,
549
+ project_env_vars=project_env_vars,
550
+ restrict_access=cfg.restrict_access,
551
+ dbt_cloud=dbt_cloud,
552
+ flags=flags,
553
+ )
554
+ # sanity check - this means an internal issue
555
+ project.validate()
556
+ return project
557
+
558
+ @classmethod
559
+ def from_dicts(
560
+ cls,
561
+ project_root: str,
562
+ project_dict: Dict[str, Any],
563
+ packages_dict: Dict[str, Any],
564
+ selectors_dict: Optional[Dict[str, Any]],
565
+ *,
566
+ verify_version: bool = False,
567
+ packages_specified_path: str = PACKAGES_FILE_NAME,
568
+ ):
569
+ """Construct a partial project from its constituent dicts."""
570
+ project_name = project_dict.get("name")
571
+ profile_name = project_dict.get("profile")
572
+
573
+ # Create a PartialProject
574
+ return cls(
575
+ profile_name=profile_name,
576
+ project_name=project_name,
577
+ project_root=project_root,
578
+ project_dict=project_dict,
579
+ packages_dict=packages_dict,
580
+ selectors_dict=selectors_dict, # type: ignore
581
+ verify_version=verify_version,
582
+ packages_specified_path=packages_specified_path,
583
+ )
584
+
585
+ @classmethod
586
+ def from_project_root(
587
+ cls, project_root: str, *, verify_version: bool = False, validate: bool = False
588
+ ) -> "PartialProject":
589
+ project_root = os.path.normpath(project_root)
590
+ project_dict = load_raw_project(project_root, validate=validate)
591
+ (
592
+ packages_dict,
593
+ packages_specified_path,
594
+ ) = package_and_project_data_from_root(project_root)
595
+ selectors_dict = selector_data_from_root(project_root)
596
+
597
+ return cls.from_dicts(
598
+ project_root=project_root,
599
+ project_dict=project_dict,
600
+ selectors_dict=selectors_dict,
601
+ packages_dict=packages_dict,
602
+ verify_version=verify_version,
603
+ packages_specified_path=packages_specified_path,
604
+ )
605
+
606
+
607
+ class VarProvider:
608
+ """Var providers are tied to a particular Project."""
609
+
610
+ def __init__(self, vars: Dict[str, Dict[str, Any]]) -> None:
611
+ self.vars = vars
612
+
613
+ def vars_for(self, node: IsFQNResource, adapter_type: str) -> Mapping[str, Any]:
614
+ # in v2, vars are only either project or globally scoped
615
+ merged = MultiDict([self.vars])
616
+ merged.add(self.vars.get(node.package_name, {}))
617
+ return merged
618
+
619
+ def to_dict(self):
620
+ return self.vars
621
+
622
+
623
+ # The Project class is included in RuntimeConfig, so any attribute
624
+ # additions must also be set where the RuntimeConfig class is created
625
+ @dataclass
626
+ class Project:
627
+ project_name: str
628
+ version: Optional[Union[SemverString, float]]
629
+ project_root: str
630
+ profile_name: Optional[str]
631
+ model_paths: List[str]
632
+ macro_paths: List[str]
633
+ seed_paths: List[str]
634
+ test_paths: List[str]
635
+ analysis_paths: List[str]
636
+ docs_paths: List[str]
637
+ asset_paths: List[str]
638
+ target_path: str
639
+ snapshot_paths: List[str]
640
+ function_paths: List[str]
641
+ clean_targets: List[str]
642
+ log_path: str
643
+ packages_install_path: str
644
+ packages_specified_path: str
645
+ quoting: Dict[str, Any]
646
+ models: Dict[str, Any]
647
+ on_run_start: List[str]
648
+ on_run_end: List[str]
649
+ dispatch: List[Dict[str, Any]]
650
+ seeds: Dict[str, Any]
651
+ snapshots: Dict[str, Any]
652
+ sources: Dict[str, Any]
653
+ data_tests: Dict[str, Any]
654
+ unit_tests: Dict[str, Any]
655
+ metrics: Dict[str, Any]
656
+ semantic_models: Dict[str, Any]
657
+ saved_queries: Dict[str, Any]
658
+ exposures: Dict[str, Any]
659
+ functions: Dict[str, Any]
660
+ vars: VarProvider
661
+ dbt_version: List[VersionSpecifier]
662
+ packages: PackageConfig
663
+ manifest_selectors: Dict[str, Any]
664
+ selectors: SelectorConfig
665
+ query_comment: QueryComment
666
+ config_version: int
667
+ unrendered: RenderComponents
668
+ project_env_vars: Dict[str, Any]
669
+ restrict_access: bool
670
+ dbt_cloud: Dict[str, Any]
671
+ flags: Dict[str, Any]
672
+
673
+ @property
674
+ def all_source_paths(self) -> List[str]:
675
+ return _all_source_paths(
676
+ self.model_paths,
677
+ self.seed_paths,
678
+ self.snapshot_paths,
679
+ self.analysis_paths,
680
+ self.macro_paths,
681
+ self.test_paths,
682
+ self.function_paths,
683
+ )
684
+
685
+ @property
686
+ def generic_test_paths(self):
687
+ generic_test_paths = []
688
+ for test_path in self.test_paths:
689
+ generic_test_paths.append(os.path.join(test_path, "generic"))
690
+ return generic_test_paths
691
+
692
+ @property
693
+ def fixture_paths(self):
694
+ fixture_paths = []
695
+ for test_path in self.test_paths:
696
+ fixture_paths.append(os.path.join(test_path, "fixtures"))
697
+ return fixture_paths
698
+
699
+ def __str__(self):
700
+ cfg = self.to_project_config(with_packages=True)
701
+ return str(cfg)
702
+
703
+ def __eq__(self, other):
704
+ if not (isinstance(other, self.__class__) and isinstance(self, other.__class__)):
705
+ return False
706
+ return self.to_project_config(with_packages=True) == other.to_project_config(
707
+ with_packages=True
708
+ )
709
+
710
+ def to_project_config(self, with_packages=False):
711
+ """Return a dict representation of the config that could be written to
712
+ disk with `yaml.safe_dump` to get this configuration.
713
+
714
+ :param with_packages bool: If True, include the serialized packages
715
+ file in the root.
716
+ :returns dict: The serialized profile.
717
+ """
718
+ result = deepcopy(
719
+ {
720
+ "name": self.project_name,
721
+ "version": self.version,
722
+ "project-root": self.project_root,
723
+ "profile": self.profile_name,
724
+ "model-paths": self.model_paths,
725
+ "macro-paths": self.macro_paths,
726
+ "seed-paths": self.seed_paths,
727
+ "test-paths": self.test_paths,
728
+ "analysis-paths": self.analysis_paths,
729
+ "docs-paths": self.docs_paths,
730
+ "asset-paths": self.asset_paths,
731
+ "target-path": self.target_path,
732
+ "snapshot-paths": self.snapshot_paths,
733
+ "clean-targets": self.clean_targets,
734
+ "log-path": self.log_path,
735
+ "quoting": self.quoting,
736
+ "models": self.models,
737
+ "on-run-start": self.on_run_start,
738
+ "on-run-end": self.on_run_end,
739
+ "dispatch": self.dispatch,
740
+ "seeds": self.seeds,
741
+ "snapshots": self.snapshots,
742
+ "sources": self.sources,
743
+ "data_tests": self.data_tests,
744
+ "unit_tests": self.unit_tests,
745
+ "metrics": self.metrics,
746
+ "semantic-models": self.semantic_models,
747
+ "saved-queries": self.saved_queries,
748
+ "exposures": self.exposures,
749
+ "functions": self.functions,
750
+ "vars": self.vars.to_dict(),
751
+ "require-dbt-version": [v.to_version_string() for v in self.dbt_version],
752
+ "restrict-access": self.restrict_access,
753
+ "dbt-cloud": self.dbt_cloud,
754
+ "flags": self.flags,
755
+ }
756
+ )
757
+ if self.query_comment:
758
+ result["query-comment"] = self.query_comment.to_dict(omit_none=True)
759
+
760
+ if with_packages:
761
+ result.update(self.packages.to_dict(omit_none=True))
762
+
763
+ return result
764
+
765
+ def validate(self):
766
+ try:
767
+ ProjectContract.validate(self.to_project_config())
768
+ except ValidationError as e:
769
+ raise ProjectContractBrokenError(e) from e
770
+
771
+ # Called by:
772
+ # RtConfig.load_dependencies => RtConfig.load_projects => RtConfig.new_project => Project.from_project_root
773
+ # RtConfig.from_args => RtConfig.collect_parts => load_project => Project.from_project_root
774
+ @classmethod
775
+ def from_project_root(
776
+ cls,
777
+ project_root: str,
778
+ renderer: DbtProjectYamlRenderer,
779
+ *,
780
+ verify_version: bool = False,
781
+ validate: bool = False,
782
+ ) -> "Project":
783
+ partial = PartialProject.from_project_root(
784
+ project_root, verify_version=verify_version, validate=validate
785
+ )
786
+ return partial.render(renderer)
787
+
788
+ def hashed_name(self):
789
+ return md5(self.project_name)
790
+
791
+ def get_selector(self, name: str) -> Union[SelectionSpec, bool]:
792
+ if name not in self.selectors:
793
+ raise DbtRuntimeError(
794
+ f"Could not find selector named {name}, expected one of {list(self.selectors)}"
795
+ )
796
+ return self.selectors[name]["definition"]
797
+
798
+ def get_default_selector_name(self) -> Union[str, None]:
799
+ """This function fetch the default selector to use on `dbt run` (if any)
800
+ :return: either a selector if default is set or None
801
+ :rtype: Union[SelectionSpec, None]
802
+ """
803
+ for selector_name, selector in self.selectors.items():
804
+ if selector["default"] is True:
805
+ return selector_name
806
+
807
+ return None
808
+
809
+ def get_macro_search_order(self, macro_namespace: str):
810
+ for dispatch_entry in self.dispatch:
811
+ if dispatch_entry["macro_namespace"] == macro_namespace:
812
+ return dispatch_entry["search_order"]
813
+ return None
814
+
815
+ @property
816
+ def project_target_path(self):
817
+ # If target_path is absolute, project_root will not be included
818
+ return os.path.join(self.project_root, self.target_path)
819
+
820
+
821
+ def read_project_flags(project_dir: str, profiles_dir: str) -> ProjectFlags:
822
+ try:
823
+ project_flags: Dict[str, Any] = {}
824
+ # Read project_flags from dbt_project.yml first
825
+ # Flags are instantiated before the project, so we don't
826
+ # want to throw an error for non-existence of dbt_project.yml here
827
+ # because it breaks things.
828
+ project_root = os.path.normpath(project_dir)
829
+ project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
830
+ if path_exists(project_yaml_filepath):
831
+ try:
832
+ project_dict = load_raw_project(project_root)
833
+ if "flags" in project_dict:
834
+ project_flags = project_dict.pop("flags")
835
+ except Exception:
836
+ # This is probably a yaml load error.The error will be reported
837
+ # later, when the project loads.
838
+ pass
839
+
840
+ from dbt.config.profile import read_profile
841
+
842
+ profile = read_profile(profiles_dir)
843
+ profile_project_flags: Optional[Dict[str, Any]] = {}
844
+ if profile:
845
+ profile_project_flags = coerce_dict_str(profile.get("config", {}))
846
+
847
+ if project_flags and profile_project_flags:
848
+ raise DbtProjectError(
849
+ f"Do not specify both 'config' in profiles.yml and 'flags' in {DBT_PROJECT_FILE_NAME}. "
850
+ "Using 'config' in profiles.yml is deprecated."
851
+ )
852
+
853
+ if profile_project_flags:
854
+ # This can't use WARN_ERROR or WARN_ERROR_OPTIONS because they're in
855
+ # the config that we're loading. Uses special "buffer" method and fired after flags are initialized in preflight.
856
+ deprecations.buffer("project-flags-moved")
857
+ project_flags = profile_project_flags
858
+
859
+ if project_flags is not None:
860
+ # handle collapsing `include` and `error` as well as collapsing `exclude` and `warn`
861
+ # for warn_error_options
862
+ warn_error_options = project_flags.get("warn_error_options", {})
863
+ normalize_warn_error_options(warn_error_options)
864
+
865
+ ProjectFlags.validate(project_flags)
866
+ return ProjectFlags.from_dict(project_flags)
867
+ except (DbtProjectError, DbtExclusivePropertyUseError) as exc:
868
+ # We don't want to eat the DbtProjectError for UserConfig to ProjectFlags or
869
+ # DbtConfigError for warn_error_options munging
870
+ raise exc
871
+ except (DbtRuntimeError, ValidationError):
872
+ pass
873
+ return ProjectFlags()