Skip to content

Commit a195509

Browse files
authored
feat(poly check): report unused bricks in projects (#360)
* feat(check): collect unused bricks in a project * feat(check): print unused bricks in a project * feat(check): report unused bricks when running in strict mode * bump Poetry plugin to 1.39.0 * bump CLI to 1.32.0 * test(check): add tests for the collect unused bricks function
1 parent ec73971 commit a195509

File tree

7 files changed

+77
-7
lines changed

7 files changed

+77
-7
lines changed

components/polylith/check/collect.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,35 @@ def diff(known_bricks: Set[str], bases: Set[str], components: Set[str]) -> Set[s
3737
return known_bricks.difference(bricks)
3838

3939

40+
def flatten_imported_bricks(imports: dict) -> Set[str]:
41+
return set().union(*imports.values())
42+
43+
44+
def to_flattened_imports(brick_imports: dict) -> Set[str]:
45+
flattened_bases = flatten_imported_bricks(brick_imports["bases"])
46+
flattened_components = flatten_imported_bricks(brick_imports["components"])
47+
48+
return set().union(flattened_bases, flattened_components)
49+
50+
4051
def imports_diff(
4152
brick_imports: dict, bases: Set[str], components: Set[str]
4253
) -> Set[str]:
43-
flattened_bases = set().union(*brick_imports["bases"].values())
44-
flattened_components = set().union(*brick_imports["components"].values())
45-
46-
flattened_imports = set().union(flattened_bases, flattened_components)
54+
flattened_imports = to_flattened_imports(brick_imports)
4755

4856
return diff(flattened_imports, bases, components)
57+
58+
59+
def is_used(brick: str, imported_bricks: dict) -> bool:
60+
return any(k for k, v in imported_bricks.items() if k != brick and brick in v)
61+
62+
63+
def find_unused_bricks(
64+
brick_imports: dict, bases: Set[str], components: Set[str]
65+
) -> Set[str]:
66+
all_brick_imports = {**brick_imports["bases"], **brick_imports["components"]}
67+
68+
bricks = to_flattened_imports(brick_imports).difference(bases)
69+
used_bricks = {brick for brick in bricks if is_used(brick, all_brick_imports)}
70+
71+
return components.difference(used_bricks)

components/polylith/check/report.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,19 @@ def print_missing_deps(diff: Set[str], project_name: str) -> None:
4242

4343
missing = ", ".join(sorted(diff))
4444

45-
console.print(f":thinking_face: Cannot locate {missing} in {project_name}")
45+
console.print(f":thinking_face: Cannot locate [data]{missing}[/] in [proj]{project_name}[/]")
46+
47+
48+
def print_unused_bricks(bricks: Set[str], project_name: str) -> None:
49+
if not bricks:
50+
return
51+
52+
console = Console(theme=theme.poly_theme)
53+
54+
unused = ", ".join(sorted(bricks))
55+
verb = "Are" if len(bricks) > 1 else "Is"
56+
57+
console.print(f":mag_right: {verb} [comp]{unused}[/] needed in [proj]{project_name}[/]?")
4658

4759

4860
def collect_all_imports(root: Path, ns: str, project_data: dict) -> dict:
@@ -85,9 +97,12 @@ def create_report(
8597
third_party_imports, third_party_libs, is_strict
8698
)
8799

100+
unused_bricks = collect.find_unused_bricks(brick_imports, bases, components)
101+
88102
return {
89103
"brick_imports": brick_imports,
90104
"third_party_imports": third_party_imports,
91105
"brick_diff": brick_diff,
92106
"libs_diff": libs_diff,
107+
"unused_bricks": unused_bricks,
93108
}

components/polylith/commands/check.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ def run_each(
106106
check.report.print_missing_deps(details["brick_diff"], name)
107107
check.report.print_missing_deps(details["libs_diff"], name)
108108

109+
if is_strict and not is_quiet:
110+
check.report.print_unused_bricks(details["unused_bricks"], name)
111+
109112
return res, details
110113

111114

projects/poetry_polylith_plugin/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "poetry-polylith-plugin"
3-
version = "1.38.2"
3+
version = "1.39.0"
44
description = "A Poetry plugin that adds tooling support for the Polylith Architecture"
55
authors = ["David Vujic"]
66
homepage = "https://davidvujic.github.io/python-polylith-docs/"

projects/polylith_cli/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "polylith-cli"
3-
version = "1.31.3"
3+
version = "1.32.0"
44
description = "Python tooling support for the Polylith Architecture"
55
authors = ['David Vujic']
66
homepage = "https://davidvujic.github.io/python-polylith-docs/"

test/components/polylith/check/__init__.py

Whitespace-only changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from polylith.check import collect
2+
3+
brick_imports = {
4+
"bases": {"base_one": {"base_one", "component_one"}},
5+
"components": {
6+
"component_one": {"component_one", "component_two", "component_tree"},
7+
"component_two": {"component_two"},
8+
"component_three": {"component_three"},
9+
},
10+
}
11+
12+
bases = {"base_one"}
13+
14+
15+
def test_find_unused_bricks_returns_no_unused_bricks():
16+
components = {"component_one", "component_two", "component_tree"}
17+
18+
res = collect.find_unused_bricks(brick_imports, bases, components)
19+
20+
assert res == set()
21+
22+
23+
def test_find_unused_bricks_returns_unused_bricks():
24+
expected = {"component_four"}
25+
components = set().union({"component_one", "component_two"}, expected)
26+
27+
res = collect.find_unused_bricks(brick_imports, bases, components)
28+
29+
assert res == expected

0 commit comments

Comments
 (0)