dbt-bouncer 1.31.2rc2__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.2rc2.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.2rc2.dist-info/RECORD +0 -35
  33. {dbt_bouncer-1.31.2rc2.dist-info → dbt_bouncer-2.0.0.dist-info}/WHEEL +0 -0
  34. {dbt_bouncer-1.31.2rc2.dist-info → dbt_bouncer-2.0.0.dist-info}/entry_points.txt +0 -0
  35. {dbt_bouncer-1.31.2rc2.dist-info → dbt_bouncer-2.0.0.dist-info}/licenses/LICENSE +0 -0
  36. {dbt_bouncer-1.31.2rc2.dist-info → dbt_bouncer-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,63 @@
1
+ import re
2
+ from typing import TYPE_CHECKING, Literal
3
+
4
+ from pydantic import ConfigDict, Field
5
+
6
+ from dbt_bouncer.check_base import BaseCheck
7
+
8
+ if TYPE_CHECKING:
9
+ from dbt_bouncer.artifact_parsers.parsers_manifest import (
10
+ DbtBouncerSeedBase,
11
+ )
12
+
13
+ from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
14
+ from dbt_bouncer.utils import get_clean_model_name
15
+
16
+
17
+ class CheckSeedNames(BaseCheck):
18
+ """Seed must have a name that matches the supplied regex.
19
+
20
+ Parameters:
21
+ seed_name_pattern (str): Regexp the seed name must match.
22
+
23
+ Receives:
24
+ seed (DbtBouncerSeedBase): The DbtBouncerSeedBase object to check.
25
+
26
+ Other Parameters:
27
+ description (str | None): Description of what the check does and why it is implemented.
28
+ exclude (str | None): Regex pattern to match the seed path. Seed paths that match the pattern will not be checked.
29
+ include (str | None): Regex pattern to match the seed path. Only seed paths that match the pattern will be checked.
30
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
31
+
32
+ Example(s):
33
+ ```yaml
34
+ manifest_checks:
35
+ - name: check_seed_names
36
+ include: ^seeds
37
+ model_name_pattern: ^raw_
38
+ ```
39
+
40
+ """
41
+
42
+ model_config = ConfigDict(extra="forbid", protected_namespaces=())
43
+
44
+ name: Literal["check_seed_names"]
45
+ seed: "DbtBouncerSeedBase | None" = Field(default=None)
46
+ seed_name_pattern: str
47
+
48
+ def execute(self) -> None:
49
+ """Execute the check.
50
+
51
+ Raises:
52
+ DbtBouncerFailedCheckError: If model name does not match regex.
53
+
54
+ """
55
+ if self.seed is None:
56
+ raise DbtBouncerFailedCheckError("self.seed is None")
57
+ if (
58
+ re.compile(self.seed_name_pattern.strip()).match(str(self.seed.name))
59
+ is None
60
+ ):
61
+ raise DbtBouncerFailedCheckError(
62
+ f"`{get_clean_model_name(self.seed.unique_id)}` does not match the supplied regex `{self.seed_name_pattern.strip()}`."
63
+ )
@@ -1,31 +1,30 @@
1
- # mypy: disable-error-code="union-attr"
2
-
3
-
4
- from typing import TYPE_CHECKING, List, Literal
1
+ from typing import TYPE_CHECKING, Literal
5
2
 
6
3
  from dbt_bouncer.check_base import BaseCheck
7
4
 
8
5
  if TYPE_CHECKING:
9
- from dbt_bouncer.artifact_parsers.parsers_common import (
6
+ from dbt_bouncer.artifact_parsers.parsers_manifest import (
10
7
  DbtBouncerModelBase,
11
8
  DbtBouncerSemanticModelBase,
12
9
  )
13
10
 
14
11
  from pydantic import Field
15
12
 
13
+ from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
14
+
16
15
 
17
16
  class CheckSemanticModelOnNonPublicModels(BaseCheck):
18
17
  """Semantic models should be based on public models only.
19
18
 
20
19
  Receives:
21
- models (List[DbtBouncerModelBase]): List of DbtBouncerModelBase objects parsed from `manifest.json`.
20
+ models (list[DbtBouncerModelBase]): List of DbtBouncerModelBase objects parsed from `manifest.json`.
22
21
  semantic_model (DbtBouncerSemanticModelBase): The DbtBouncerSemanticModelBase object to check.
23
22
 
24
23
  Other Parameters:
25
- description (Optional[str]): Description of what the check does and why it is implemented.
26
- exclude (Optional[str]): Regex pattern to match the semantic model path (i.e the .yml file where the semantic model is configured). Semantic model paths that match the pattern will not be checked.
27
- include (Optional[str]): Regex pattern to match the semantic model path (i.e the .yml file where the semantic model is configured). Only semantic model paths that match the pattern will be checked.
28
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
24
+ description (str | None): Description of what the check does and why it is implemented.
25
+ exclude (str | None): Regex pattern to match the semantic model path (i.e the .yml file where the semantic model is configured). Semantic model paths that match the pattern will not be checked.
26
+ include (str | None): Regex pattern to match the semantic model path (i.e the .yml file where the semantic model is configured). Only semantic model paths that match the pattern will be checked.
27
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
29
28
 
30
29
  Example(s):
31
30
  ```yaml
@@ -35,24 +34,33 @@ class CheckSemanticModelOnNonPublicModels(BaseCheck):
35
34
 
36
35
  """
37
36
 
38
- models: List["DbtBouncerModelBase"] = Field(default=[])
37
+ models: list["DbtBouncerModelBase"] = Field(default=[])
39
38
  name: Literal["check_semantic_model_based_on_non_public_models"]
40
- semantic_model: "DbtBouncerSemanticModelBase" = Field(default=None)
39
+ semantic_model: "DbtBouncerSemanticModelBase | None" = Field(default=None)
41
40
 
42
41
  def execute(self) -> None:
43
- """Execute the check."""
42
+ """Execute the check.
43
+
44
+ Raises:
45
+ DbtBouncerFailedCheckError: If semantic model is based on non-public models.
46
+
47
+ """
48
+ if self.semantic_model is None:
49
+ raise DbtBouncerFailedCheckError("self.semantic_model is None")
50
+
44
51
  non_public_upstream_dependencies = []
45
- for model in self.semantic_model.depends_on.nodes:
52
+ for model in getattr(self.semantic_model.depends_on, "nodes", []) or []:
46
53
  if (
47
54
  next(m for m in self.models if m.unique_id == model).resource_type
48
55
  == "model"
49
56
  and next(m for m in self.models if m.unique_id == model).package_name
50
57
  == self.semantic_model.package_name
51
58
  ):
52
- model = next(m for m in self.models if m.unique_id == model)
53
- if model.access.value != "public":
54
- non_public_upstream_dependencies.append(model.name)
59
+ model_obj = next(m for m in self.models if m.unique_id == model)
60
+ if model_obj.access and model_obj.access.value != "public":
61
+ non_public_upstream_dependencies.append(model_obj.name)
55
62
 
56
- assert not non_public_upstream_dependencies, (
57
- f"Semantic model `{self.semantic_model.name}` is based on a model(s) that is not public: {non_public_upstream_dependencies}."
58
- )
63
+ if non_public_upstream_dependencies:
64
+ raise DbtBouncerFailedCheckError(
65
+ f"Semantic model `{self.semantic_model.name}` is based on a model(s) that is not public: {non_public_upstream_dependencies}."
66
+ )
@@ -1,30 +1,31 @@
1
- # mypy: disable-error-code="union-attr"
2
1
  import re
3
- from typing import TYPE_CHECKING, List, Literal
2
+ from typing import TYPE_CHECKING, Literal
4
3
 
5
4
  from pydantic import Field
6
5
 
7
6
  from dbt_bouncer.check_base import BaseCheck
8
7
 
9
8
  if TYPE_CHECKING:
10
- from dbt_bouncer.artifact_parsers.parsers_common import (
9
+ from dbt_bouncer.artifact_parsers.parsers_manifest import (
11
10
  DbtBouncerSnapshotBase,
12
11
  )
13
12
 
13
+ from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
14
+
14
15
 
15
16
  class CheckSnapshotHasTags(BaseCheck):
16
17
  """Snapshots must have the specified tags.
17
18
 
18
19
  Parameters:
19
- criteria: (Optional[Literal["any", "all", "one"]]): Whether the snapshot must have any, all, or exactly one of the specified tags. Default: `all`.
20
+ criteria: (Literal["any", "all", "one"] | None): Whether the snapshot must have any, all, or exactly one of the specified tags. Default: `all`.
20
21
  snapshot (DbtBouncerSnapshotBase): The DbtBouncerSnapshotBase object to check.
21
- tags (List[str]): List of tags to check for.
22
+ tags (list[str]): List of tags to check for.
22
23
 
23
24
  Other Parameters:
24
- description (Optional[str]): Description of what the check does and why it is implemented.
25
- exclude (Optional[str]): Regex pattern to match the snapshot path. Snapshot paths that match the pattern will not be checked.
26
- include (Optional[str]): Regex pattern to match the snapshot path. Only snapshot paths that match the pattern will be checked.
27
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
25
+ description (str | None): Description of what the check does and why it is implemented.
26
+ exclude (str | None): Regex pattern to match the snapshot path. Snapshot paths that match the pattern will not be checked.
27
+ include (str | None): Regex pattern to match the snapshot path. Only snapshot paths that match the pattern will be checked.
28
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
28
29
 
29
30
  Example(s):
30
31
  ```yaml
@@ -39,22 +40,35 @@ class CheckSnapshotHasTags(BaseCheck):
39
40
 
40
41
  criteria: Literal["any", "all", "one"] = Field(default="all")
41
42
  name: Literal["check_snapshot_has_tags"]
42
- snapshot: "DbtBouncerSnapshotBase" = Field(default=None)
43
- tags: List[str]
43
+ snapshot: "DbtBouncerSnapshotBase | None" = Field(default=None)
44
+ tags: list[str]
44
45
 
45
46
  def execute(self) -> None:
46
- """Execute the check."""
47
+ """Execute the check.
48
+
49
+ Raises:
50
+ DbtBouncerFailedCheckError: If snapshot does not have required tags.
51
+
52
+ """
53
+ if self.snapshot is None:
54
+ raise DbtBouncerFailedCheckError("self.snapshot is None")
55
+ snapshot_tags = self.snapshot.tags or []
47
56
  if self.criteria == "any":
48
- assert any(tag in self.snapshot.tags for tag in self.tags), (
49
- f"`{self.snapshot.name}` does not have any of the required tags: {self.tags}."
50
- )
57
+ if not any(tag in snapshot_tags for tag in self.tags):
58
+ raise DbtBouncerFailedCheckError(
59
+ f"`{self.snapshot.name}` does not have any of the required tags: {self.tags}."
60
+ )
51
61
  elif self.criteria == "all":
52
- missing_tags = [tag for tag in self.tags if tag not in self.snapshot.tags]
53
- assert not missing_tags, (
54
- f"`{self.snapshot.name}` is missing required tags: {missing_tags}."
55
- )
56
- elif self.criteria == "one":
57
- assert sum(tag in self.snapshot.tags for tag in self.tags) == 1, (
62
+ missing_tags = [tag for tag in self.tags if tag not in snapshot_tags]
63
+ if missing_tags:
64
+ raise DbtBouncerFailedCheckError(
65
+ f"`{self.snapshot.name}` is missing required tags: {missing_tags}."
66
+ )
67
+ elif (
68
+ self.criteria == "one"
69
+ and sum(tag in snapshot_tags for tag in self.tags) != 1
70
+ ):
71
+ raise DbtBouncerFailedCheckError(
58
72
  f"`{self.snapshot.name}` must have exactly one of the required tags: {self.tags}."
59
73
  )
60
74
 
@@ -69,10 +83,10 @@ class CheckSnapshotNames(BaseCheck):
69
83
  snapshot (DbtBouncerSnapshotBase): The DbtBouncerSnapshotBase object to check.
70
84
 
71
85
  Other Parameters:
72
- description (Optional[str]): Description of what the check does and why it is implemented.
73
- exclude (Optional[str]): Regex pattern to match the snapshot path. Snapshot paths that match the pattern will not be checked.
74
- include (Optional[str]): Regex pattern to match the snapshot path. Only snapshot paths that match the pattern will be checked.
75
- severity (Optional[Literal["error", "warn"]]): Severity level of the check. Default: `error`.
86
+ description (str | None): Description of what the check does and why it is implemented.
87
+ exclude (str | None): Regex pattern to match the snapshot path. Snapshot paths that match the pattern will not be checked.
88
+ include (str | None): Regex pattern to match the snapshot path. Only snapshot paths that match the pattern will be checked.
89
+ severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
76
90
 
77
91
  Example(s):
78
92
  ```yaml
@@ -85,14 +99,24 @@ class CheckSnapshotNames(BaseCheck):
85
99
  """
86
100
 
87
101
  name: Literal["check_snapshot_names"]
88
- snapshot: "DbtBouncerSnapshotBase" = Field(default=None)
102
+ snapshot: "DbtBouncerSnapshotBase | None" = Field(default=None)
89
103
  snapshot_name_pattern: str
90
104
 
91
105
  def execute(self) -> None:
92
- """Execute the check."""
93
- assert (
94
- re.compile(self.snapshot_name_pattern.strip()).match(self.snapshot.name)
95
- is not None
96
- ), (
97
- f"`{self.snapshot.name}` does not match the supplied regex `{self.snapshot_name_pattern.strip()})`."
98
- )
106
+ """Execute the check.
107
+
108
+ Raises:
109
+ DbtBouncerFailedCheckError: If snapshot name does not match regex.
110
+
111
+ """
112
+ if self.snapshot is None:
113
+ raise DbtBouncerFailedCheckError("self.snapshot is None")
114
+ if (
115
+ re.compile(self.snapshot_name_pattern.strip()).match(
116
+ str(self.snapshot.name)
117
+ )
118
+ is None
119
+ ):
120
+ raise DbtBouncerFailedCheckError(
121
+ f"`{self.snapshot.name}` does not match the supplied regex `{self.snapshot_name_pattern.strip()})`."
122
+ )