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.

Files changed (261) hide show
  1. dvt/__init__.py +7 -0
  2. dvt/_pydantic_shim.py +26 -0
  3. dvt/adapters/__init__.py +16 -0
  4. dvt/adapters/multi_adapter_manager.py +268 -0
  5. dvt/artifacts/__init__.py +0 -0
  6. dvt/artifacts/exceptions/__init__.py +1 -0
  7. dvt/artifacts/exceptions/schemas.py +31 -0
  8. dvt/artifacts/resources/__init__.py +116 -0
  9. dvt/artifacts/resources/base.py +68 -0
  10. dvt/artifacts/resources/types.py +93 -0
  11. dvt/artifacts/resources/v1/analysis.py +10 -0
  12. dvt/artifacts/resources/v1/catalog.py +23 -0
  13. dvt/artifacts/resources/v1/components.py +275 -0
  14. dvt/artifacts/resources/v1/config.py +282 -0
  15. dvt/artifacts/resources/v1/documentation.py +11 -0
  16. dvt/artifacts/resources/v1/exposure.py +52 -0
  17. dvt/artifacts/resources/v1/function.py +53 -0
  18. dvt/artifacts/resources/v1/generic_test.py +32 -0
  19. dvt/artifacts/resources/v1/group.py +22 -0
  20. dvt/artifacts/resources/v1/hook.py +11 -0
  21. dvt/artifacts/resources/v1/macro.py +30 -0
  22. dvt/artifacts/resources/v1/metric.py +173 -0
  23. dvt/artifacts/resources/v1/model.py +146 -0
  24. dvt/artifacts/resources/v1/owner.py +10 -0
  25. dvt/artifacts/resources/v1/saved_query.py +112 -0
  26. dvt/artifacts/resources/v1/seed.py +42 -0
  27. dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  28. dvt/artifacts/resources/v1/semantic_model.py +315 -0
  29. dvt/artifacts/resources/v1/singular_test.py +14 -0
  30. dvt/artifacts/resources/v1/snapshot.py +92 -0
  31. dvt/artifacts/resources/v1/source_definition.py +85 -0
  32. dvt/artifacts/resources/v1/sql_operation.py +10 -0
  33. dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
  34. dvt/artifacts/schemas/__init__.py +0 -0
  35. dvt/artifacts/schemas/base.py +191 -0
  36. dvt/artifacts/schemas/batch_results.py +24 -0
  37. dvt/artifacts/schemas/catalog/__init__.py +12 -0
  38. dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  39. dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
  40. dvt/artifacts/schemas/freshness/__init__.py +1 -0
  41. dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  42. dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
  43. dvt/artifacts/schemas/manifest/__init__.py +2 -0
  44. dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  45. dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
  46. dvt/artifacts/schemas/results.py +148 -0
  47. dvt/artifacts/schemas/run/__init__.py +2 -0
  48. dvt/artifacts/schemas/run/v5/__init__.py +0 -0
  49. dvt/artifacts/schemas/run/v5/run.py +184 -0
  50. dvt/artifacts/schemas/upgrades/__init__.py +4 -0
  51. dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  52. dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  53. dvt/artifacts/utils/validation.py +153 -0
  54. dvt/cli/__init__.py +1 -0
  55. dvt/cli/context.py +16 -0
  56. dvt/cli/exceptions.py +56 -0
  57. dvt/cli/flags.py +558 -0
  58. dvt/cli/main.py +971 -0
  59. dvt/cli/option_types.py +121 -0
  60. dvt/cli/options.py +79 -0
  61. dvt/cli/params.py +803 -0
  62. dvt/cli/requires.py +478 -0
  63. dvt/cli/resolvers.py +32 -0
  64. dvt/cli/types.py +40 -0
  65. dvt/clients/__init__.py +0 -0
  66. dvt/clients/checked_load.py +82 -0
  67. dvt/clients/git.py +164 -0
  68. dvt/clients/jinja.py +206 -0
  69. dvt/clients/jinja_static.py +245 -0
  70. dvt/clients/registry.py +192 -0
  71. dvt/clients/yaml_helper.py +68 -0
  72. dvt/compilation.py +833 -0
  73. dvt/compute/__init__.py +26 -0
  74. dvt/compute/base.py +288 -0
  75. dvt/compute/engines/__init__.py +13 -0
  76. dvt/compute/engines/duckdb_engine.py +368 -0
  77. dvt/compute/engines/spark_engine.py +273 -0
  78. dvt/compute/query_analyzer.py +212 -0
  79. dvt/compute/router.py +483 -0
  80. dvt/config/__init__.py +4 -0
  81. dvt/config/catalogs.py +95 -0
  82. dvt/config/compute_config.py +406 -0
  83. dvt/config/profile.py +411 -0
  84. dvt/config/profiles_v2.py +464 -0
  85. dvt/config/project.py +893 -0
  86. dvt/config/renderer.py +232 -0
  87. dvt/config/runtime.py +491 -0
  88. dvt/config/selectors.py +209 -0
  89. dvt/config/utils.py +78 -0
  90. dvt/connectors/.gitignore +6 -0
  91. dvt/connectors/README.md +306 -0
  92. dvt/connectors/catalog.yml +217 -0
  93. dvt/connectors/download_connectors.py +300 -0
  94. dvt/constants.py +29 -0
  95. dvt/context/__init__.py +0 -0
  96. dvt/context/base.py +746 -0
  97. dvt/context/configured.py +136 -0
  98. dvt/context/context_config.py +350 -0
  99. dvt/context/docs.py +82 -0
  100. dvt/context/exceptions_jinja.py +179 -0
  101. dvt/context/macro_resolver.py +195 -0
  102. dvt/context/macros.py +171 -0
  103. dvt/context/manifest.py +73 -0
  104. dvt/context/providers.py +2198 -0
  105. dvt/context/query_header.py +14 -0
  106. dvt/context/secret.py +59 -0
  107. dvt/context/target.py +74 -0
  108. dvt/contracts/__init__.py +0 -0
  109. dvt/contracts/files.py +413 -0
  110. dvt/contracts/graph/__init__.py +0 -0
  111. dvt/contracts/graph/manifest.py +1904 -0
  112. dvt/contracts/graph/metrics.py +98 -0
  113. dvt/contracts/graph/model_config.py +71 -0
  114. dvt/contracts/graph/node_args.py +42 -0
  115. dvt/contracts/graph/nodes.py +1806 -0
  116. dvt/contracts/graph/semantic_manifest.py +233 -0
  117. dvt/contracts/graph/unparsed.py +812 -0
  118. dvt/contracts/project.py +417 -0
  119. dvt/contracts/results.py +53 -0
  120. dvt/contracts/selection.py +23 -0
  121. dvt/contracts/sql.py +86 -0
  122. dvt/contracts/state.py +69 -0
  123. dvt/contracts/util.py +46 -0
  124. dvt/deprecations.py +347 -0
  125. dvt/deps/__init__.py +0 -0
  126. dvt/deps/base.py +153 -0
  127. dvt/deps/git.py +196 -0
  128. dvt/deps/local.py +80 -0
  129. dvt/deps/registry.py +131 -0
  130. dvt/deps/resolver.py +149 -0
  131. dvt/deps/tarball.py +121 -0
  132. dvt/docs/source/_ext/dbt_click.py +118 -0
  133. dvt/docs/source/conf.py +32 -0
  134. dvt/env_vars.py +64 -0
  135. dvt/event_time/event_time.py +40 -0
  136. dvt/event_time/sample_window.py +60 -0
  137. dvt/events/__init__.py +16 -0
  138. dvt/events/base_types.py +37 -0
  139. dvt/events/core_types_pb2.py +2 -0
  140. dvt/events/logging.py +109 -0
  141. dvt/events/types.py +2534 -0
  142. dvt/exceptions.py +1487 -0
  143. dvt/flags.py +89 -0
  144. dvt/graph/__init__.py +11 -0
  145. dvt/graph/cli.py +248 -0
  146. dvt/graph/graph.py +172 -0
  147. dvt/graph/queue.py +213 -0
  148. dvt/graph/selector.py +375 -0
  149. dvt/graph/selector_methods.py +976 -0
  150. dvt/graph/selector_spec.py +223 -0
  151. dvt/graph/thread_pool.py +18 -0
  152. dvt/hooks.py +21 -0
  153. dvt/include/README.md +49 -0
  154. dvt/include/__init__.py +3 -0
  155. dvt/include/global_project.py +4 -0
  156. dvt/include/starter_project/.gitignore +4 -0
  157. dvt/include/starter_project/README.md +15 -0
  158. dvt/include/starter_project/__init__.py +3 -0
  159. dvt/include/starter_project/analyses/.gitkeep +0 -0
  160. dvt/include/starter_project/dvt_project.yml +36 -0
  161. dvt/include/starter_project/macros/.gitkeep +0 -0
  162. dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  163. dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  164. dvt/include/starter_project/models/example/schema.yml +21 -0
  165. dvt/include/starter_project/seeds/.gitkeep +0 -0
  166. dvt/include/starter_project/snapshots/.gitkeep +0 -0
  167. dvt/include/starter_project/tests/.gitkeep +0 -0
  168. dvt/internal_deprecations.py +27 -0
  169. dvt/jsonschemas/__init__.py +3 -0
  170. dvt/jsonschemas/jsonschemas.py +309 -0
  171. dvt/jsonschemas/project/0.0.110.json +4717 -0
  172. dvt/jsonschemas/project/0.0.85.json +2015 -0
  173. dvt/jsonschemas/resources/0.0.110.json +2636 -0
  174. dvt/jsonschemas/resources/0.0.85.json +2536 -0
  175. dvt/jsonschemas/resources/latest.json +6773 -0
  176. dvt/links.py +4 -0
  177. dvt/materializations/__init__.py +0 -0
  178. dvt/materializations/incremental/__init__.py +0 -0
  179. dvt/materializations/incremental/microbatch.py +235 -0
  180. dvt/mp_context.py +8 -0
  181. dvt/node_types.py +37 -0
  182. dvt/parser/__init__.py +23 -0
  183. dvt/parser/analysis.py +21 -0
  184. dvt/parser/base.py +549 -0
  185. dvt/parser/common.py +267 -0
  186. dvt/parser/docs.py +52 -0
  187. dvt/parser/fixtures.py +51 -0
  188. dvt/parser/functions.py +30 -0
  189. dvt/parser/generic_test.py +100 -0
  190. dvt/parser/generic_test_builders.py +334 -0
  191. dvt/parser/hooks.py +119 -0
  192. dvt/parser/macros.py +137 -0
  193. dvt/parser/manifest.py +2204 -0
  194. dvt/parser/models.py +574 -0
  195. dvt/parser/partial.py +1179 -0
  196. dvt/parser/read_files.py +445 -0
  197. dvt/parser/schema_generic_tests.py +423 -0
  198. dvt/parser/schema_renderer.py +111 -0
  199. dvt/parser/schema_yaml_readers.py +936 -0
  200. dvt/parser/schemas.py +1467 -0
  201. dvt/parser/search.py +149 -0
  202. dvt/parser/seeds.py +28 -0
  203. dvt/parser/singular_test.py +20 -0
  204. dvt/parser/snapshots.py +44 -0
  205. dvt/parser/sources.py +557 -0
  206. dvt/parser/sql.py +63 -0
  207. dvt/parser/unit_tests.py +622 -0
  208. dvt/plugins/__init__.py +20 -0
  209. dvt/plugins/contracts.py +10 -0
  210. dvt/plugins/exceptions.py +2 -0
  211. dvt/plugins/manager.py +164 -0
  212. dvt/plugins/manifest.py +21 -0
  213. dvt/profiler.py +20 -0
  214. dvt/py.typed +1 -0
  215. dvt/runners/__init__.py +2 -0
  216. dvt/runners/exposure_runner.py +7 -0
  217. dvt/runners/no_op_runner.py +46 -0
  218. dvt/runners/saved_query_runner.py +7 -0
  219. dvt/selected_resources.py +8 -0
  220. dvt/task/__init__.py +0 -0
  221. dvt/task/base.py +504 -0
  222. dvt/task/build.py +197 -0
  223. dvt/task/clean.py +57 -0
  224. dvt/task/clone.py +162 -0
  225. dvt/task/compile.py +151 -0
  226. dvt/task/compute.py +366 -0
  227. dvt/task/debug.py +650 -0
  228. dvt/task/deps.py +280 -0
  229. dvt/task/docs/__init__.py +3 -0
  230. dvt/task/docs/generate.py +408 -0
  231. dvt/task/docs/index.html +250 -0
  232. dvt/task/docs/serve.py +28 -0
  233. dvt/task/freshness.py +323 -0
  234. dvt/task/function.py +122 -0
  235. dvt/task/group_lookup.py +46 -0
  236. dvt/task/init.py +374 -0
  237. dvt/task/list.py +237 -0
  238. dvt/task/printer.py +176 -0
  239. dvt/task/profiles.py +256 -0
  240. dvt/task/retry.py +175 -0
  241. dvt/task/run.py +1146 -0
  242. dvt/task/run_operation.py +142 -0
  243. dvt/task/runnable.py +802 -0
  244. dvt/task/seed.py +104 -0
  245. dvt/task/show.py +150 -0
  246. dvt/task/snapshot.py +57 -0
  247. dvt/task/sql.py +111 -0
  248. dvt/task/test.py +464 -0
  249. dvt/tests/fixtures/__init__.py +1 -0
  250. dvt/tests/fixtures/project.py +620 -0
  251. dvt/tests/util.py +651 -0
  252. dvt/tracking.py +529 -0
  253. dvt/utils/__init__.py +3 -0
  254. dvt/utils/artifact_upload.py +151 -0
  255. dvt/utils/utils.py +408 -0
  256. dvt/version.py +249 -0
  257. dvt_core-1.11.0b4.dist-info/METADATA +252 -0
  258. dvt_core-1.11.0b4.dist-info/RECORD +261 -0
  259. dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
  260. dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
  261. dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,192 @@
1
+ import functools
2
+ import os
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import requests
6
+ from dvt import deprecations
7
+ from dvt.events.types import (
8
+ RegistryIndexProgressGETRequest,
9
+ RegistryIndexProgressGETResponse,
10
+ RegistryProgressGETRequest,
11
+ RegistryProgressGETResponse,
12
+ RegistryResponseExtraNestedKeys,
13
+ RegistryResponseMissingNestedKeys,
14
+ RegistryResponseMissingTopKeys,
15
+ RegistryResponseUnexpectedType,
16
+ )
17
+ from dvt.utils import memoized
18
+
19
+ from dbt_common import semver
20
+ from dbt_common.events.functions import fire_event
21
+ from dbt_common.utils.connection import connection_exception_retry
22
+
23
+ if os.getenv("DBT_PACKAGE_HUB_URL"):
24
+ DEFAULT_REGISTRY_BASE_URL = os.getenv("DBT_PACKAGE_HUB_URL")
25
+ else:
26
+ DEFAULT_REGISTRY_BASE_URL = "https://hub.getdbt.com/"
27
+
28
+
29
+ def _get_url(name, registry_base_url=None):
30
+ if registry_base_url is None:
31
+ registry_base_url = DEFAULT_REGISTRY_BASE_URL
32
+ url = "api/v1/{}.json".format(name)
33
+
34
+ return "{}{}".format(registry_base_url, url)
35
+
36
+
37
+ def _check_package_redirect(package_name: str, response: Dict[str, Any]) -> None:
38
+ # Either redirectnamespace or redirectname in the JSON response indicate a redirect
39
+ # redirectnamespace redirects based on package ownership
40
+ # redirectname redirects based on package name
41
+ # Both can be present at the same time, or neither. Fails gracefully to old name
42
+ if ("redirectnamespace" in response) or ("redirectname" in response):
43
+ use_namespace = response.get("redirectnamespace") or response["namespace"]
44
+ use_name = response.get("redirectname") or response["name"]
45
+
46
+ new_nwo = f"{use_namespace}/{use_name}"
47
+ deprecations.warn("package-redirect", old_name=package_name, new_name=new_nwo)
48
+
49
+
50
+ def _get_package_with_retries(
51
+ package_name: str, registry_base_url: Optional[str] = None
52
+ ) -> Dict[str, Any]:
53
+ get_fn = functools.partial(_get, package_name, registry_base_url)
54
+ response: Dict[str, Any] = connection_exception_retry(get_fn, 5)
55
+ _check_package_redirect(package_name, response)
56
+ return response
57
+
58
+
59
+ def _get(package_name, registry_base_url=None):
60
+ url = _get_url(package_name, registry_base_url)
61
+ fire_event(RegistryProgressGETRequest(url=url))
62
+ # all exceptions from requests get caught in the retry logic so no need to wrap this here
63
+ resp = requests.get(url, timeout=30)
64
+ fire_event(RegistryProgressGETResponse(url=url, resp_code=resp.status_code))
65
+ resp.raise_for_status()
66
+
67
+ # The response should always be a dictionary. Anything else is unexpected, raise error.
68
+ # Raising this error will cause this function to retry (if called within _get_package_with_retries)
69
+ # and hopefully get a valid response. This seems to happen when there's an issue with the Hub.
70
+ # Since we control what we expect the HUB to return, this is safe.
71
+ # See https://github.com/dbt-labs/dbt-core/issues/4577
72
+ # and https://github.com/dbt-labs/dbt-core/issues/4849
73
+ response = resp.json()
74
+
75
+ if not isinstance(response, dict): # This will also catch Nonetype
76
+ error_msg = (
77
+ f"Request error: Expected a response type of <dict> but got {type(response)} instead"
78
+ )
79
+ fire_event(RegistryResponseUnexpectedType(response=response))
80
+ raise requests.exceptions.ContentDecodingError(error_msg, response=resp)
81
+
82
+ # check for expected top level keys
83
+ expected_keys = {"name", "versions"}
84
+ if not expected_keys.issubset(response):
85
+ error_msg = (
86
+ f"Request error: Expected the response to contain keys {expected_keys} "
87
+ f"but is missing {expected_keys.difference(set(response))}"
88
+ )
89
+ fire_event(RegistryResponseMissingTopKeys(response=response))
90
+ raise requests.exceptions.ContentDecodingError(error_msg, response=resp)
91
+
92
+ # check for the keys we need nested under each version
93
+ expected_version_keys = {"name", "packages", "downloads"}
94
+ all_keys = set().union(*(response["versions"][d] for d in response["versions"]))
95
+ if not expected_version_keys.issubset(all_keys):
96
+ error_msg = (
97
+ "Request error: Expected the response for the version to contain keys "
98
+ f"{expected_version_keys} but is missing {expected_version_keys.difference(all_keys)}"
99
+ )
100
+ fire_event(RegistryResponseMissingNestedKeys(response=response))
101
+ raise requests.exceptions.ContentDecodingError(error_msg, response=resp)
102
+
103
+ # all version responses should contain identical keys.
104
+ has_extra_keys = set().difference(*(response["versions"][d] for d in response["versions"]))
105
+ if has_extra_keys:
106
+ error_msg = (
107
+ "Request error: Keys for all versions do not match. Found extra key(s) "
108
+ f"of {has_extra_keys}."
109
+ )
110
+ fire_event(RegistryResponseExtraNestedKeys(response=response))
111
+ raise requests.exceptions.ContentDecodingError(error_msg, response=resp)
112
+
113
+ return response
114
+
115
+
116
+ _get_cached = memoized(_get_package_with_retries)
117
+
118
+
119
+ def package(package_name, registry_base_url=None) -> Dict[str, Any]:
120
+ # returns a dictionary of metadata for all versions of a package
121
+ response = _get_cached(package_name, registry_base_url)
122
+ return response["versions"]
123
+
124
+
125
+ def package_version(package_name, version, registry_base_url=None) -> Dict[str, Any]:
126
+ # returns the metadata of a specific version of a package
127
+ response = package(package_name, registry_base_url)
128
+ return response[version]
129
+
130
+
131
+ def is_compatible_version(package_spec, dbt_version) -> bool:
132
+ require_dbt_version = package_spec.get("require_dbt_version")
133
+ if not require_dbt_version:
134
+ # if version requirements are missing or empty, assume any version is compatible
135
+ return True
136
+ else:
137
+ # determine whether dbt_version satisfies this package's require-dbt-version config
138
+ if not isinstance(require_dbt_version, list):
139
+ require_dbt_version = [require_dbt_version]
140
+ supported_versions = [
141
+ semver.VersionSpecifier.from_version_string(v) for v in require_dbt_version
142
+ ]
143
+ return semver.versions_compatible(dbt_version, *supported_versions)
144
+
145
+
146
+ def get_compatible_versions(package_name, dbt_version, should_version_check) -> List["str"]:
147
+ # returns a list of all available versions of a package
148
+ response = package(package_name)
149
+
150
+ # if the user doesn't care about installing compatible versions, just return them all
151
+ if not should_version_check:
152
+ return list(response)
153
+
154
+ # otherwise, only return versions that are compatible with the installed version of dbt-core
155
+ else:
156
+ compatible_versions = [
157
+ pkg_version
158
+ for pkg_version, info in response.items()
159
+ if is_compatible_version(info, dbt_version)
160
+ ]
161
+ return compatible_versions
162
+
163
+
164
+ def _get_index(registry_base_url=None):
165
+ url = _get_url("index", registry_base_url)
166
+ fire_event(RegistryIndexProgressGETRequest(url=url))
167
+ # all exceptions from requests get caught in the retry logic so no need to wrap this here
168
+ resp = requests.get(url, timeout=30)
169
+ fire_event(RegistryIndexProgressGETResponse(url=url, resp_code=resp.status_code))
170
+ resp.raise_for_status()
171
+
172
+ # The response should be a list. Anything else is unexpected, raise an error.
173
+ # Raising this error will cause this function to retry and hopefully get a valid response.
174
+
175
+ response = resp.json()
176
+
177
+ if not isinstance(response, list): # This will also catch Nonetype
178
+ error_msg = (
179
+ f"Request error: The response type of {type(response)} is not valid: {resp.text}"
180
+ )
181
+ raise requests.exceptions.ContentDecodingError(error_msg, response=resp)
182
+
183
+ return response
184
+
185
+
186
+ def index(registry_base_url=None) -> List[str]:
187
+ # this returns a list of all packages on the Hub
188
+ get_index_fn = functools.partial(_get_index, registry_base_url)
189
+ return connection_exception_retry(get_index_fn, 5)
190
+
191
+
192
+ index_cached = memoized(index)
@@ -0,0 +1,68 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ import yaml
4
+
5
+ import dbt_common.exceptions
6
+ import dbt_common.exceptions.base
7
+
8
+ # the C version is faster, but it doesn't always exist
9
+ try:
10
+ from yaml import CDumper as Dumper
11
+ from yaml import CLoader as Loader
12
+ from yaml import CSafeLoader as SafeLoader
13
+ except ImportError:
14
+ from yaml import Dumper, Loader, SafeLoader # type: ignore # noqa: F401
15
+
16
+
17
+ YAML_ERROR_MESSAGE = """
18
+ Syntax error near line {line_number}
19
+ ------------------------------
20
+ {nice_error}
21
+
22
+ Raw Error:
23
+ ------------------------------
24
+ {raw_error}
25
+ """.strip()
26
+
27
+
28
+ def line_no(i, line, width=3):
29
+ line_number = str(i).ljust(width)
30
+ return "{}| {}".format(line_number, line)
31
+
32
+
33
+ def prefix_with_line_numbers(string, no_start, no_end):
34
+ line_list = string.split("\n")
35
+
36
+ numbers = range(no_start, no_end)
37
+ relevant_lines = line_list[no_start:no_end]
38
+
39
+ return "\n".join([line_no(i + 1, line) for (i, line) in zip(numbers, relevant_lines)])
40
+
41
+
42
+ def contextualized_yaml_error(raw_contents, error):
43
+ mark = error.problem_mark
44
+
45
+ min_line = max(mark.line - 3, 0)
46
+ max_line = mark.line + 4
47
+
48
+ nice_error = prefix_with_line_numbers(raw_contents, min_line, max_line)
49
+
50
+ return YAML_ERROR_MESSAGE.format(
51
+ line_number=mark.line + 1, nice_error=nice_error, raw_error=error
52
+ )
53
+
54
+
55
+ def safe_load(contents) -> Optional[Dict[str, Any]]:
56
+ return yaml.load(contents, Loader=SafeLoader)
57
+
58
+
59
+ def load_yaml_text(contents, path=None, loader=SafeLoader) -> Optional[Dict[str, Any]]:
60
+ try:
61
+ return yaml.load(contents, loader)
62
+ except (yaml.scanner.ScannerError, yaml.YAMLError) as e:
63
+ if hasattr(e, "problem_mark"):
64
+ error = contextualized_yaml_error(contents, e)
65
+ else:
66
+ error = str(e)
67
+
68
+ raise dbt_common.exceptions.base.DbtValidationError(error)