Skip to content

Commit 2c47aa9

Browse files
Prevent duplicated rule exception when importing rules and filters (#87)
Fixes #85. ### Implementation - Skip adding imported rules and rule filters to rule registry. - Split `tests/rules/example.py` into `tests/rules/rules.py` and `tests/rules/rule_filters.py`. - Test `Duplicated` exception is not raised by adding an imported `skip_schemaX` rule filter. ### Note Both rules and rule filters can be imported now, but the existing tests cover only rule filters. --------- Co-authored-by: Matthieu Caneill <[email protected]>
1 parent c8a7d27 commit 2c47aa9

File tree

9 files changed

+53
-37
lines changed

9 files changed

+53
-37
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to
1111
- Documenting support for python 3.13. (#86)
1212
- Only show failing rules per default in `HumanReadableFormatter`. Also added
1313
`--show` parameter in the CLI to change this behavior. (#77)
14+
- Ignore imported rules and filters when building the rule registry. (#87)
1415

1516
## [0.8.0] - 2024-11-12
1617

src/dbt_score/rule_registry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ def _load(self, namespace_name: str) -> None:
6262
module = importlib.import_module(module_name)
6363
for obj_name in dir(module):
6464
obj = module.__dict__[obj_name]
65+
# Skip adding objects imported from other modules
66+
if type(obj) is type and module.__name__ != obj.__module__:
67+
continue
6568
if type(obj) is type and issubclass(obj, Rule) and obj is not Rule:
6669
self._add_rule(obj)
6770
if (

tests/resources/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ severity=4
2323
[tool.dbt-score.rules."tests.conftest.rule_with_config"]
2424
model_name="model2"
2525

26-
[tool.dbt-score.rules."tests.rules.example.rule_test_example"]
26+
[tool.dbt-score.rules."tests.rules.rules.rule_test_example"]
2727
severity=4
28-
rule_filter_names=["tests.rules.example.skip_model1"]
28+
rule_filter_names=["tests.rules.rule_filters.skip_model1"]

tests/rules/example.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

tests/rules/rule_filters.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Example rule filters."""
2+
3+
from dbt_score import Model, rule_filter
4+
5+
6+
@rule_filter
7+
def skip_model1(model: Model) -> bool:
8+
"""An example filter."""
9+
return model.name != "model1"
10+
11+
12+
@rule_filter
13+
def skip_schemaX(model: Model) -> bool:
14+
"""An example filter."""
15+
return model.schema != "X"

tests/rules/rules.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Example rules."""
2+
3+
from dbt_score import Model, RuleViolation, rule
4+
5+
from tests.rules.rule_filters import skip_schemaX
6+
7+
8+
@rule(rule_filters={skip_schemaX()})
9+
def rule_test_example(model: Model) -> RuleViolation | None:
10+
"""An example rule."""

tests/test_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def test_load_valid_toml_file(valid_config_path):
1515
assert config.disabled_rules == ["foo.foo", "tests.bar"]
1616
assert config.rules_config["foo.bar"].severity == Severity.CRITICAL
1717
assert (
18-
config.rules_config["tests.rules.example.rule_test_example"].severity
18+
config.rules_config["tests.rules.rules.rule_test_example"].severity
1919
== Severity.CRITICAL
2020
)
2121
assert config.badge_config.third.threshold == 6.5
@@ -28,8 +28,8 @@ def test_load_valid_toml_file(valid_config_path):
2828
assert config.fail_project_under == 7.5
2929
assert config.fail_any_item_under == 6.9
3030
assert config.rules_config[
31-
"tests.rules.example.rule_test_example"
32-
].rule_filter_names == ["tests.rules.example.skip_model1"]
31+
"tests.rules.rules.rule_test_example"
32+
].rule_filter_names == ["tests.rules.rule_filters.skip_model1"]
3333

3434

3535
def test_load_invalid_toml_file(caplog, invalid_config_path):

tests/test_rule_catalog.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ def test_rule_catalog_terminal(capsys, default_config):
1010
stdout = capsys.readouterr().out
1111
assert (
1212
stdout
13-
== """\x1B[1mtests.rules.example.rule_test_example\x1B[0m:
13+
== """\x1B[1mtests.rules.nested.example.rule_test_nested_example\x1B[0m:
1414
An example rule.
1515
16-
\x1B[1mtests.rules.nested.example.rule_test_nested_example\x1B[0m:
16+
\x1B[1mtests.rules.rules.rule_test_example\x1B[0m:
1717
An example rule.
1818
1919
"""
@@ -29,41 +29,41 @@ def test_rule_catalog_markdown(capsys, default_config):
2929
stdout
3030
== """# Doc for tests.rules
3131
32-
## `rule_test_example`
32+
## `rule_test_nested_example`
3333
3434
An example rule.
3535
3636
??? quote "Source code"
3737
```python
38-
@rule()
39-
def rule_test_example(model: Model) -> RuleViolation | None:
38+
@rule
39+
def rule_test_nested_example(model: Model) -> RuleViolation | None:
4040
\"""An example rule.\"""
4141
4242
```
4343
4444
### Default configuration
4545
4646
```toml title="pyproject.toml"
47-
[tool.dbt-score.rules."tests.rules.example.rule_test_example"]
47+
[tool.dbt-score.rules."tests.rules.nested.example.rule_test_nested_example"]
4848
severity = 2
4949
```
5050
51-
## `rule_test_nested_example`
51+
## `rule_test_example`
5252
5353
An example rule.
5454
5555
??? quote "Source code"
5656
```python
57-
@rule
58-
def rule_test_nested_example(model: Model) -> RuleViolation | None:
57+
@rule(rule_filters={skip_schemaX()})
58+
def rule_test_example(model: Model) -> RuleViolation | None:
5959
\"""An example rule.\"""
6060
6161
```
6262
6363
### Default configuration
6464
6565
```toml title="pyproject.toml"
66-
[tool.dbt-score.rules."tests.rules.nested.example.rule_test_nested_example"]
66+
[tool.dbt-score.rules."tests.rules.rules.rule_test_example"]
6767
severity = 2
6868
```
6969

tests/test_rule_registry.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ def test_rule_registry_discovery(default_config):
1212
r = RuleRegistry(default_config)
1313
r._load("tests.rules")
1414
assert sorted(r._rules.keys()) == [
15-
"tests.rules.example.rule_test_example",
1615
"tests.rules.nested.example.rule_test_nested_example",
16+
"tests.rules.rules.rule_test_example",
17+
]
18+
assert list(r._rule_filters.keys()) == [
19+
"tests.rules.rule_filters.skip_model1",
20+
"tests.rules.rule_filters.skip_schemaX",
1721
]
18-
assert list(r._rule_filters.keys()) == ["tests.rules.example.skip_model1"]
1922

2023

2124
def test_disabled_rule_registry_discovery():
@@ -25,7 +28,7 @@ def test_disabled_rule_registry_discovery():
2528
r = RuleRegistry(config)
2629
r._load("tests.rules")
2730
assert sorted(r._rules.keys()) == [
28-
"tests.rules.example.rule_test_example",
31+
"tests.rules.rules.rule_test_example",
2932
]
3033

3134

@@ -35,9 +38,7 @@ def test_configured_rule_registry_discovery(valid_config_path):
3538
config._load_toml_file(str(valid_config_path))
3639
r = RuleRegistry(config)
3740
r._load("tests.rules")
38-
assert (
39-
r.rules["tests.rules.example.rule_test_example"].severity == Severity.CRITICAL
40-
)
41+
assert r.rules["tests.rules.rules.rule_test_example"].severity == Severity.CRITICAL
4142

4243

4344
def test_rule_registry_no_duplicates(default_config):
@@ -63,5 +64,5 @@ def test_rule_registry_rule_filters(valid_config_path, model1, model2):
6364
r._load("tests.rules")
6465
r._load_filters_into_rules()
6566

66-
assert not r.rules["tests.rules.example.rule_test_example"].should_evaluate(model1)
67-
assert r.rules["tests.rules.example.rule_test_example"].should_evaluate(model2)
67+
assert not r.rules["tests.rules.rules.rule_test_example"].should_evaluate(model1)
68+
assert r.rules["tests.rules.rules.rule_test_example"].should_evaluate(model2)

0 commit comments

Comments
 (0)