dbt-bouncer 1.31.2rc3__py3-none-any.whl → 2.0.0__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.
Files changed (36) hide show
  1. dbt_bouncer/artifact_parsers/dbt_cloud/catalog_latest.py +21 -21
  2. dbt_bouncer/artifact_parsers/dbt_cloud/manifest_latest.py +1745 -1745
  3. dbt_bouncer/artifact_parsers/dbt_cloud/run_results_latest.py +22 -22
  4. dbt_bouncer/artifact_parsers/parsers_catalog.py +26 -24
  5. dbt_bouncer/artifact_parsers/parsers_common.py +57 -36
  6. dbt_bouncer/artifact_parsers/parsers_manifest.py +98 -69
  7. dbt_bouncer/artifact_parsers/parsers_run_results.py +32 -19
  8. dbt_bouncer/check_base.py +22 -11
  9. dbt_bouncer/checks/catalog/check_catalog_sources.py +22 -12
  10. dbt_bouncer/checks/catalog/check_columns.py +175 -105
  11. dbt_bouncer/checks/common.py +24 -3
  12. dbt_bouncer/checks/manifest/check_exposures.py +79 -52
  13. dbt_bouncer/checks/manifest/check_lineage.py +69 -40
  14. dbt_bouncer/checks/manifest/check_macros.py +177 -104
  15. dbt_bouncer/checks/manifest/check_metadata.py +28 -18
  16. dbt_bouncer/checks/manifest/check_models.py +842 -496
  17. dbt_bouncer/checks/manifest/check_seeds.py +63 -0
  18. dbt_bouncer/checks/manifest/check_semantic_models.py +28 -20
  19. dbt_bouncer/checks/manifest/check_snapshots.py +57 -33
  20. dbt_bouncer/checks/manifest/check_sources.py +246 -137
  21. dbt_bouncer/checks/manifest/check_unit_tests.py +97 -54
  22. dbt_bouncer/checks/run_results/check_run_results.py +34 -20
  23. dbt_bouncer/config_file_parser.py +47 -28
  24. dbt_bouncer/config_file_validator.py +11 -8
  25. dbt_bouncer/global_context.py +31 -0
  26. dbt_bouncer/main.py +128 -67
  27. dbt_bouncer/runner.py +61 -31
  28. dbt_bouncer/utils.py +146 -50
  29. dbt_bouncer/version.py +1 -1
  30. {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0.dist-info}/METADATA +15 -15
  31. dbt_bouncer-2.0.0.dist-info/RECORD +37 -0
  32. dbt_bouncer-1.31.2rc3.dist-info/RECORD +0 -35
  33. {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0.dist-info}/WHEEL +0 -0
  34. {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0.dist-info}/entry_points.txt +0 -0
  35. {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0.dist-info}/licenses/LICENSE +0 -0
  36. {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
- # mypy: disable-error-code="union-attr"
2
-
3
1
  import re
2
+ from pathlib import Path
4
3
  from typing import TYPE_CHECKING, ClassVar, Literal
5
4
 
6
5
  from pydantic import Field
@@ -15,6 +14,7 @@ from jinja2 import Environment, nodes
15
14
  from jinja2_simple_tags import StandaloneTag
16
15
 
17
16
  from dbt_bouncer.check_base import BaseCheck
17
+ from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
18
18
 
19
19
 
20
20
  class TagExtension(StandaloneTag):
@@ -24,14 +24,17 @@ class TagExtension(StandaloneTag):
24
24
  class CheckMacroArgumentsDescriptionPopulated(BaseCheck):
25
25
  """Macro arguments must have a populated description.
26
26
 
27
+ Parameters:
28
+ min_description_length (int | None): Minimum length required for the description to be considered populated.
29
+
27
30
  Receives:
28
31
  macro (Macros): The Macros object to check.
29
32
 
30
33
  Other Parameters:
31
- description (Optional[str]): Description of what the check does and why it is implemented.
32
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
33
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
34
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
34
+ description (str | None): Description of what the check does and why it is implemented.
35
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
36
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
37
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
35
38
 
36
39
  Example(s):
37
40
  ```yaml
@@ -44,20 +47,33 @@ class CheckMacroArgumentsDescriptionPopulated(BaseCheck):
44
47
  - name: check_macro_arguments_description_populated
45
48
  include: ^macros/common
46
49
  ```
50
+ ```yaml
51
+ manifest_checks:
52
+ - name: check_macro_arguments_description_populated
53
+ min_description_length: 25 # Setting a stricter requirement for description length
54
+ ```
47
55
 
48
56
  """
49
57
 
50
- macro: "Macros" = Field(default=None)
58
+ min_description_length: int | None = Field(default=None)
59
+ macro: "Macros | None" = Field(default=None)
51
60
  name: Literal["check_macro_arguments_description_populated"]
52
61
 
53
62
  def execute(self) -> None:
54
- """Execute the check."""
63
+ """Execute the check.
64
+
65
+ Raises:
66
+ DbtBouncerFailedCheckError: If macro arguments are not populated.
67
+
68
+ """
69
+ if self.macro is None:
70
+ raise DbtBouncerFailedCheckError("self.macro is None")
55
71
  environment = Environment(autoescape=True, extensions=[TagExtension])
56
72
  ast = environment.parse(self.macro.macro_sql)
57
73
 
58
74
  if hasattr(ast.body[0], "args"):
59
75
  # Assume macro is a "true" macro
60
- macro_arguments = [a.name for a in ast.body[0].args]
76
+ macro_arguments = [a.name for a in getattr(ast.body[0], "args", [])]
61
77
  else:
62
78
  if "materialization" in [
63
79
  x.value.value
@@ -83,17 +99,22 @@ class CheckMacroArgumentsDescriptionPopulated(BaseCheck):
83
99
  # macro.arguments: List of args manually added to the properties file
84
100
 
85
101
  non_complying_args = []
86
- for arg in macro_arguments:
87
- macro_doc_raw = [x for x in self.macro.arguments if x.name == arg]
88
- if macro_doc_raw == [] or (
89
- arg not in [x.name for x in self.macro.arguments]
90
- or len(macro_doc_raw[0].description.strip()) <= 4
91
- ):
92
- non_complying_args.append(arg)
93
-
94
- assert non_complying_args == [], (
95
- f"Macro `{self.macro.name}` does not have a populated description for the following argument(s): {non_complying_args}."
96
- )
102
+ if self.macro.arguments:
103
+ for arg in macro_arguments:
104
+ macro_doc_raw = [x for x in self.macro.arguments if x.name == arg]
105
+ if macro_doc_raw == [] or (
106
+ arg not in [x.name for x in self.macro.arguments]
107
+ or not self._is_description_populated(
108
+ str(macro_doc_raw[0].description or ""),
109
+ self.min_description_length,
110
+ )
111
+ ):
112
+ non_complying_args.append(arg)
113
+
114
+ if non_complying_args != []:
115
+ raise DbtBouncerFailedCheckError(
116
+ f"Macro `{self.macro.name}` does not have a populated description for the following argument(s): {non_complying_args}."
117
+ )
97
118
 
98
119
 
99
120
  class CheckMacroCodeDoesNotContainRegexpPattern(BaseCheck):
@@ -106,48 +127,59 @@ class CheckMacroCodeDoesNotContainRegexpPattern(BaseCheck):
106
127
  macro (Macros): The Macros object to check.
107
128
 
108
129
  Other Parameters:
109
- description (Optional[str]): Description of what the check does and why it is implemented.
110
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
111
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
112
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
130
+ description (str | None): Description of what the check does and why it is implemented.
131
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
132
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
133
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
113
134
 
114
135
  Example(s):
115
136
  ```yaml
116
137
  manifest_checks:
117
- # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02
138
+ # Prefer `coalesce` over `ifnull`: [https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02](https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02)
118
139
  - name: check_macro_code_does_not_contain_regexp_pattern
119
140
  regexp_pattern: .*[i][f][n][u][l][l].*
120
141
  ```
121
142
 
122
143
  """
123
144
 
124
- macro: "Macros" = Field(default=None)
145
+ macro: "Macros | None" = Field(default=None)
125
146
  name: Literal["check_macro_code_does_not_contain_regexp_pattern"]
126
147
  regexp_pattern: str
127
148
 
128
149
  def execute(self) -> None:
129
- """Execute the check."""
130
- assert (
150
+ """Execute the check.
151
+
152
+ Raises:
153
+ DbtBouncerFailedCheckError: If macro code contains banned string.
154
+
155
+ """
156
+ if self.macro is None:
157
+ raise DbtBouncerFailedCheckError("self.macro is None")
158
+ if (
131
159
  re.compile(self.regexp_pattern.strip(), flags=re.DOTALL).match(
132
160
  self.macro.macro_sql
133
161
  )
134
- is None
135
- ), (
136
- f"Macro `{self.macro.name}` contains a banned string: `{self.regexp_pattern.strip()}`."
137
- )
162
+ is not None
163
+ ):
164
+ raise DbtBouncerFailedCheckError(
165
+ f"Macro `{self.macro.name}` contains a banned string: `{self.regexp_pattern.strip()}`."
166
+ )
138
167
 
139
168
 
140
169
  class CheckMacroDescriptionPopulated(BaseCheck):
141
170
  """Macros must have a populated description.
142
171
 
172
+ Parameters:
173
+ min_description_length (int | None): Minimum length required for the description to be considered populated.
174
+
143
175
  Receives:
144
176
  macro (Macros): The Macros object to check.
145
177
 
146
178
  Other Parameters:
147
- description (Optional[str]): Description of what the check does and why it is implemented.
148
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
149
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
150
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
179
+ description (str | None): Description of what the check does and why it is implemented.
180
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
181
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
182
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
151
183
 
152
184
  Example(s):
153
185
  ```yaml
@@ -163,14 +195,25 @@ class CheckMacroDescriptionPopulated(BaseCheck):
163
195
 
164
196
  """
165
197
 
166
- macro: "Macros" = Field(default=None)
198
+ macro: "Macros | None" = Field(default=None)
199
+ min_description_length: int | None = Field(default=None)
167
200
  name: Literal["check_macro_description_populated"]
168
201
 
169
202
  def execute(self) -> None:
170
- """Execute the check."""
171
- assert self.is_description_populated(self.macro.description), (
172
- f"Macro `{self.macro.name}` does not have a populated description."
173
- )
203
+ """Execute the check.
204
+
205
+ Raises:
206
+ DbtBouncerFailedCheckError: If macro description is not populated.
207
+
208
+ """
209
+ if self.macro is None:
210
+ raise DbtBouncerFailedCheckError("self.macro is None")
211
+ if not self._is_description_populated(
212
+ str(self.macro.description or ""), self.min_description_length
213
+ ):
214
+ raise DbtBouncerFailedCheckError(
215
+ f"Macro `{self.macro.name}` does not have a populated description."
216
+ )
174
217
 
175
218
 
176
219
  class CheckMacroMaxNumberOfLines(BaseCheck):
@@ -183,10 +226,10 @@ class CheckMacroMaxNumberOfLines(BaseCheck):
183
226
  macro (Macros): The Macros object to check.
184
227
 
185
228
  Other Parameters:
186
- description (Optional[str]): Description of what the check does and why it is implemented.
187
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
188
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
189
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
229
+ description (str | None): Description of what the check does and why it is implemented.
230
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
231
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
232
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
190
233
 
191
234
  Example(s):
192
235
  ```yaml
@@ -201,17 +244,25 @@ class CheckMacroMaxNumberOfLines(BaseCheck):
201
244
 
202
245
  """
203
246
 
204
- macro: "Macros" = Field(default=None)
247
+ macro: "Macros | None" = Field(default=None)
205
248
  name: Literal["check_macro_max_number_of_lines"]
206
249
  max_number_of_lines: int = Field(default=50)
207
250
 
208
251
  def execute(self) -> None:
209
- """Execute the check."""
252
+ """Execute the check.
253
+
254
+ Raises:
255
+ DbtBouncerFailedCheckError: If max lines exceeded.
256
+
257
+ """
258
+ if self.macro is None:
259
+ raise DbtBouncerFailedCheckError("self.macro is None")
210
260
  actual_number_of_lines = self.macro.macro_sql.count("\n") + 1
211
261
 
212
- assert actual_number_of_lines <= self.max_number_of_lines, (
213
- f"Macro `{self.macro.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({self.max_number_of_lines})."
214
- )
262
+ if actual_number_of_lines > self.max_number_of_lines:
263
+ raise DbtBouncerFailedCheckError(
264
+ f"Macro `{self.macro.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({self.max_number_of_lines})."
265
+ )
215
266
 
216
267
 
217
268
  class CheckMacroNameMatchesFileName(BaseCheck):
@@ -223,10 +274,10 @@ class CheckMacroNameMatchesFileName(BaseCheck):
223
274
  macro (Macros): The Macros object to check.
224
275
 
225
276
  Other Parameters:
226
- description (Optional[str]): Description of what the check does and why it is implemented.
227
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
228
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
229
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
277
+ description (str | None): Description of what the check does and why it is implemented.
278
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
279
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
280
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
230
281
 
231
282
  Example(s):
232
283
  ```yaml
@@ -236,27 +287,31 @@ class CheckMacroNameMatchesFileName(BaseCheck):
236
287
 
237
288
  """
238
289
 
239
- macro: "Macros" = Field(default=None)
290
+ macro: "Macros | None" = Field(default=None)
240
291
  name: Literal["check_macro_name_matches_file_name"]
241
292
 
242
293
  def execute(self) -> None:
243
- """Execute the check."""
294
+ """Execute the check.
295
+
296
+ Raises:
297
+ DbtBouncerFailedCheckError: If macro name does not match file name.
298
+
299
+ """
300
+ if self.macro is None:
301
+ raise DbtBouncerFailedCheckError("self.macro is None")
302
+ file_path = Path(clean_path_str(self.macro.original_file_path))
303
+ file_stem = file_path.stem
304
+
244
305
  if self.macro.name.startswith("test_"):
245
- assert (
246
- self.macro.name[5:]
247
- == clean_path_str(self.macro.original_file_path)
248
- .split("/")[-1]
249
- .split(".")[0]
250
- ), (
251
- f"Macro `{self.macro.unique_id}` is not in a file named `{self.macro.name[5:]}.sql`."
252
- )
306
+ if self.macro.name[5:] != file_stem:
307
+ raise DbtBouncerFailedCheckError(
308
+ f"Macro `{self.macro.unique_id}` is not in a file named `{self.macro.name[5:]}.sql`."
309
+ )
253
310
  else:
254
- assert (
255
- self.macro.name
256
- == clean_path_str(self.macro.original_file_path)
257
- .split("/")[-1]
258
- .split(".")[0]
259
- ), f"Macro `{self.macro.name}` is not in a file of the same name."
311
+ if self.macro.name != file_stem:
312
+ raise DbtBouncerFailedCheckError(
313
+ f"Macro `{self.macro.name}` is not in a file of the same name."
314
+ )
260
315
 
261
316
 
262
317
  class CheckMacroPropertyFileLocation(BaseCheck):
@@ -266,10 +321,10 @@ class CheckMacroPropertyFileLocation(BaseCheck):
266
321
  macro (Macros): The Macros object to check.
267
322
 
268
323
  Other Parameters:
269
- description (Optional[str]): Description of what the check does and why it is implemented.
270
- exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
271
- include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
272
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
324
+ description (str | None): Description of what the check does and why it is implemented.
325
+ exclude (str | None): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.
326
+ include (str | None): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
327
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
273
328
 
274
329
  Example(s):
275
330
  ```yaml
@@ -279,39 +334,57 @@ class CheckMacroPropertyFileLocation(BaseCheck):
279
334
 
280
335
  """
281
336
 
282
- macro: "Macros" = Field(default=None)
337
+ macro: "Macros | None" = Field(default=None)
283
338
  name: Literal["check_macro_property_file_location"]
284
339
 
285
340
  def execute(self) -> None:
286
- """Execute the check."""
287
- expected_substr = "_".join(
288
- clean_path_str(self.macro.original_file_path)[6:].split("/")[:-1]
289
- )
290
-
291
- assert clean_path_str(self.macro.patch_path) is not None, (
292
- f"Macro `{self.macro.name}` is not defined in a `.yml` properties file."
293
- )
294
- properties_yml_name = clean_path_str(self.macro.patch_path).split("/")[-1]
295
-
296
- if clean_path_str(self.macro.original_file_path).startswith(
297
- "tests/",
298
- ): # Do not check generic tests (which are also macros)
341
+ """Execute the check.
342
+
343
+ Raises:
344
+ DbtBouncerFailedCheckError: If property file location is incorrect.
345
+
346
+ """
347
+ if self.macro is None:
348
+ raise DbtBouncerFailedCheckError("self.macro is None")
349
+ original_path = Path(clean_path_str(self.macro.original_file_path))
350
+
351
+ # Logic matches previous manual splitting:
352
+ # If path is `macros/utils/file.sql`, we want `_utils`.
353
+ # We assume the first part of the path is the root (e.g. 'macros' or 'tests').
354
+ subdir_parts = original_path.parent.parts[1:]
355
+ expected_substr = "_" + "_".join(subdir_parts) if subdir_parts else ""
356
+
357
+ if self.macro.patch_path is None:
358
+ raise DbtBouncerFailedCheckError(
359
+ f"Macro `{self.macro.name}` is not defined in a `.yml` properties file."
360
+ )
361
+ clean_patch_path = clean_path_str(self.macro.patch_path)
362
+ if clean_patch_path is None:
363
+ raise DbtBouncerFailedCheckError(
364
+ f"Macro `{self.macro.name}` has an invalid patch path."
365
+ )
366
+
367
+ patch_path = Path(clean_patch_path)
368
+ properties_yml_name = patch_path.name
369
+
370
+ if original_path.parts[0] == "tests":
371
+ # Do not check generic tests (which are also macros)
299
372
  pass
300
373
  elif expected_substr == "": # i.e. macro in ./macros
301
- assert properties_yml_name == "_macros.yml", (
302
- f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) should be `_macros.yml`."
303
- )
374
+ if properties_yml_name != "_macros.yml":
375
+ raise DbtBouncerFailedCheckError(
376
+ f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) should be `_macros.yml`."
377
+ )
304
378
  else:
305
- assert properties_yml_name.startswith(
306
- "_",
307
- ), (
308
- f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) does not start with an underscore."
309
- )
310
- assert expected_substr in properties_yml_name, (
311
- f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)."
312
- )
313
- assert properties_yml_name.endswith(
314
- "__macros.yml",
315
- ), (
316
- f"The properties file for `{self.macro.name.name}` (`{properties_yml_name}`) does not end with `__macros.yml`."
317
- )
379
+ if not properties_yml_name.startswith("_"):
380
+ raise DbtBouncerFailedCheckError(
381
+ f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) does not start with an underscore."
382
+ )
383
+ if expected_substr not in properties_yml_name:
384
+ raise DbtBouncerFailedCheckError(
385
+ f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)."
386
+ )
387
+ if not properties_yml_name.endswith("__macros.yml"):
388
+ raise DbtBouncerFailedCheckError(
389
+ f"The properties file for `{self.macro.name}` (`{properties_yml_name}`) does not end with `__macros.yml`."
390
+ )
@@ -1,12 +1,13 @@
1
- # mypy: disable-error-code="union-attr"
2
-
3
1
  import re
4
- from typing import TYPE_CHECKING, Literal, Optional
2
+ from typing import TYPE_CHECKING, Literal
5
3
 
6
4
  from pydantic import BaseModel, ConfigDict, Field
7
5
 
8
6
  if TYPE_CHECKING:
9
- from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerManifest
7
+ from dbt_bouncer.artifact_parsers.parsers_manifest import DbtBouncerManifest
8
+
9
+
10
+ from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
10
11
 
11
12
 
12
13
  class CheckProjectName(BaseModel):
@@ -19,8 +20,8 @@ class CheckProjectName(BaseModel):
19
20
  manifest_obj (DbtBouncerManifest): The manifest object.
20
21
 
21
22
  Other Parameters:
22
- description (Optional[str]): Description of what the check does and why it is implemented.
23
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
23
+ description (str | None): Description of what the check does and why it is implemented.
24
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
24
25
 
25
26
  Example(s):
26
27
  ```yaml
@@ -33,33 +34,42 @@ class CheckProjectName(BaseModel):
33
34
 
34
35
  model_config = ConfigDict(extra="forbid")
35
36
 
36
- description: Optional[str] = Field(
37
+ description: str | None = Field(
37
38
  default=None,
38
39
  description="Description of what the check does and why it is implemented.",
39
40
  )
40
- index: Optional[int] = Field(
41
+ index: int | None = Field(
41
42
  default=None,
42
43
  description="Index to uniquely identify the check, calculated at runtime.",
43
44
  )
44
- manifest_obj: "DbtBouncerManifest" = Field(default=None)
45
+ manifest_obj: "DbtBouncerManifest | None" = Field(default=None)
45
46
  name: Literal["check_project_name"]
46
- package_name: Optional[str] = Field(default=None)
47
+ package_name: str | None = Field(default=None)
47
48
  project_name_pattern: str
48
- severity: Optional[Literal["error", "warn"]] = Field(
49
+ severity: Literal["error", "warn"] | None = Field(
49
50
  default="error",
50
51
  description="Severity of the check, one of 'error' or 'warn'.",
51
52
  )
52
53
 
53
54
  def execute(self) -> None:
54
- """Execute the check."""
55
+ """Execute the check.
56
+
57
+ Raises:
58
+ DbtBouncerFailedCheckError: If project name does not match regex.
59
+
60
+ """
61
+ if self.manifest_obj is None:
62
+ raise DbtBouncerFailedCheckError("self.manifest_obj is None")
63
+
55
64
  package_name = (
56
65
  self.package_name or self.manifest_obj.manifest.metadata.project_name
57
66
  )
58
- assert (
67
+ if (
59
68
  re.compile(self.project_name_pattern.strip()).match(
60
- package_name,
69
+ str(package_name),
70
+ )
71
+ is None
72
+ ):
73
+ raise DbtBouncerFailedCheckError(
74
+ f"Project name (`{package_name}`) does not conform to the supplied regex `({self.project_name_pattern.strip()})`."
61
75
  )
62
- is not None
63
- ), (
64
- f"Project name (`{package_name}`) does not conform to the supplied regex `({self.project_name_pattern.strip()})`."
65
- )