Skip to content

Commit c6dbeda

Browse files
authored
feat(poly deps): view and validate brick interfaces (#413)
* wip * refactor(check): move grouping of brick imports to the 'imports' module * wip: interface command * wip: parse brick usages in modules * wip(interface): print brick usage that is not matching the interface * wip: parse brick usages in modules * wip: parse brick interface * refactor(import): put usages feature in separate module * feat(interfaces): show interface and invalid usage when passing in a cli option * bump Poetry plugin to 1.48.0 * bump CLI to 1.42.0 * refactor(typing): use union instead of optional * fix(poly deps): reorganizing the ordering of the output for --brick and --interface * refactor(interface): extract into separate modules
1 parent 8a50c6e commit c6dbeda

22 files changed

Lines changed: 702 additions & 18 deletions

File tree

bases/polylith/cli/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def sync_command(
191191
def deps_command(
192192
directory: Annotated[str, options.directory] = "",
193193
brick: Annotated[str, options.brick] = "",
194+
interface: Annotated[bool, options.interface] = False,
194195
save: Annotated[bool, options.save] = False,
195196
):
196197
"""Visualize the dependencies between bricks."""
@@ -205,6 +206,7 @@ def deps_command(
205206
"brick": brick or None,
206207
"save": save,
207208
"output": output,
209+
"show_interface": interface,
208210
}
209211

210212
commands.deps.run(root, ns, cli_options)

bases/polylith/cli/options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919

2020
brick = Option(help="Shows dependencies for selected brick.")
2121
save = Option(help="Store the contents of this command to file.")
22+
interface = Option(help="Show the brick interface.")
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from polylith.check import collect, grouping, report
1+
from polylith.check import collect, report
22

3-
__all__ = ["collect", "grouping", "report"]
3+
__all__ = ["collect", "report"]

components/polylith/check/collect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from pathlib import Path
22
from typing import Set
33

4-
from polylith import check, imports, workspace
4+
from polylith import imports, workspace
55

66

77
def extract_bricks(paths: Set[Path], ns: str) -> dict:
88
all_imports = imports.fetch_all_imports(paths)
99

10-
return check.grouping.extract_brick_imports(all_imports, ns)
10+
return imports.extract_brick_imports(all_imports, ns)
1111

1212

1313
def with_unknown_components(root: Path, ns: str, brick_imports: dict) -> dict:

components/polylith/check/report.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import Set
33

44
from polylith import imports, libs, workspace
5-
from polylith.check import collect, grouping
5+
from polylith.check import collect
66
from polylith.reporting import theme
77
from rich.console import Console
88

@@ -78,8 +78,8 @@ def extract_collected_imports(
7878
ns: str, imports_in_bases: dict, imports_in_components: dict
7979
) -> dict:
8080
brick_imports = {
81-
"bases": grouping.extract_brick_imports(imports_in_bases, ns),
82-
"components": grouping.extract_brick_imports(imports_in_components, ns),
81+
"bases": imports.grouping.extract_brick_imports(imports_in_bases, ns),
82+
"components": imports.grouping.extract_brick_imports(imports_in_components, ns),
8383
}
8484

8585
third_party_imports = {

components/polylith/commands/deps.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from pathlib import Path
22
from typing import List, Set
33

4-
from polylith import bricks, deps, info
4+
from polylith import bricks, deps, info, interface
55

66

77
def get_imports(root: Path, ns: str, bricks: dict) -> dict:
@@ -30,9 +30,21 @@ def get_components(root: Path, ns: str, project_data: dict) -> Set[str]:
3030
return pick_name(bricks.get_components_data(root, ns))
3131

3232

33+
def used_by_as_bricks(bricks: dict, brick_deps: dict) -> dict:
34+
bases = bricks["bases"]
35+
components = bricks["components"]
36+
37+
used_by = brick_deps["used_by"]
38+
return {
39+
"bases": {b for b in used_by if b in bases},
40+
"components": {b for b in used_by if b in components},
41+
}
42+
43+
3344
def run(root: Path, ns: str, options: dict):
3445
directory = options.get("directory")
3546
brick = options.get("brick")
47+
show_interface = options.get("show_interface")
3648

3749
projects_data = info.get_projects_data(root, ns) if directory else []
3850
project = next((p for p in projects_data if directory in p["path"].as_posix()), {})
@@ -53,10 +65,19 @@ def run(root: Path, ns: str, options: dict):
5365

5466
if brick and imports.get(brick):
5567
brick_deps = bricks_deps[brick]
68+
used_bricks = used_by_as_bricks(bricks, brick_deps)
69+
5670
circular_deps = circular_bricks.get(brick)
5771

5872
deps.print_brick_deps(brick, bricks, brick_deps, options)
5973

74+
if show_interface:
75+
interface.report.print_brick_interface(root, ns, brick, used_bricks)
76+
77+
interface.report.print_brick_interface_invalid_usage(
78+
root, ns, brick, used_bricks
79+
)
80+
6081
if circular_deps:
6182
deps.print_brick_with_circular_deps(brick, circular_deps, bricks)
6283

components/polylith/deps/report.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from polylith.reporting import theme
77
from rich import box
88
from rich.console import Console
9+
from rich.padding import Padding
910
from rich.table import Table
1011

1112

@@ -157,7 +158,7 @@ def print_brick_with_circular_deps(brick: str, deps: Set[str], bricks: dict) ->
157158
prefix = ":information:"
158159
message = f"[{tag}]{brick}[/] [data]is used by[/] {others} [data]and also uses[/] {others}[data].[/]"
159160

160-
console.print(f"{prefix} {message}", overflow="ellipsis")
161+
console.print(Padding(f"{prefix} {message}", (0, 0, 0, 1)), overflow="ellipsis")
161162

162163

163164
def print_bricks_with_circular_deps(circular_bricks: dict, bricks: dict) -> None:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
from polylith.imports.grouping import (
2+
extract_brick_imports,
3+
extract_brick_imports_with_namespaces,
4+
)
15
from polylith.imports.parser import (
26
extract_top_ns,
37
fetch_all_imports,
48
fetch_excluded_imports,
59
list_imports,
10+
parse_module,
611
)
12+
from polylith.imports.usages import SYMBOLS, extract_api, fetch_brick_import_usages
713

814
__all__ = [
15+
"extract_brick_imports",
16+
"extract_brick_imports_with_namespaces",
17+
"extract_api",
918
"extract_top_ns",
1019
"fetch_all_imports",
20+
"fetch_brick_import_usages",
1121
"fetch_excluded_imports",
1222
"list_imports",
23+
"parse_module",
24+
"SYMBOLS",
1325
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@ def extract_brick_imports(all_imports: dict, top_ns) -> dict:
3434
with_only_brick_names = only_brick_names(with_only_bricks)
3535

3636
return exclude_empty(with_only_brick_names)
37+
38+
39+
def extract_brick_imports_with_namespaces(all_imports: dict, top_ns) -> dict:
40+
with_only_bricks = only_bricks(all_imports, top_ns)
41+
42+
return exclude_empty(with_only_bricks)

components/polylith/imports/parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def parse_node(node: ast.AST) -> Union[dict, None]:
6868
return None
6969

7070

71-
def parse_module(path: Path) -> ast.AST:
71+
def parse_module(path: Path) -> ast.Module:
7272
with open(path.as_posix(), "r", encoding="utf-8", errors="ignore") as f:
7373
tree = ast.parse(f.read(), path.name)
7474

@@ -88,7 +88,7 @@ def extract_imports(path: Path) -> List[str]:
8888
return [i for i in includes if i not in excludes]
8989

9090

91-
def extract_and_flatten(py_modules: Iterable) -> Set[str]:
91+
def extract_imports_and_flatten(py_modules: Iterable) -> Set[str]:
9292
return {i for m in py_modules for i in extract_imports(m)}
9393

9494

@@ -104,7 +104,7 @@ def find_files(path: Path) -> Iterable:
104104
def list_imports(path: Path) -> Set[str]:
105105
py_modules = find_files(path)
106106

107-
return extract_and_flatten(py_modules)
107+
return extract_imports_and_flatten(py_modules)
108108

109109

110110
def fetch_all_imports(paths: Set[Path]) -> dict:
@@ -122,7 +122,7 @@ def list_excluded_imports(path: Path, excludes: Set[str]) -> Set[str]:
122122

123123
filtered = [p for p in py_modules if should_exclude(p, excludes)]
124124

125-
return extract_and_flatten(filtered)
125+
return extract_imports_and_flatten(filtered)
126126

127127

128128
def fetch_excluded_imports(paths: Set[Path], excludes: Set[str]) -> dict:

0 commit comments

Comments
 (0)