diff --git a/bases/polylith/cli/core.py b/bases/polylith/cli/core.py index db6aba7e..8580ae09 100644 --- a/bases/polylith/cli/core.py +++ b/bases/polylith/cli/core.py @@ -120,14 +120,17 @@ def sync_command( @app.command("deps") -def deps_command(directory: Annotated[str, options.directory] = ""): +def deps_command( + directory: Annotated[str, options.directory] = "", + brick: Annotated[str, options.brick] = "", +): """Visualize the dependencies between bricks.""" root = repo.get_workspace_root(Path.cwd()) ns = configuration.get_namespace_from_config(root) dir_path = Path(directory).as_posix() if directory else None - commands.deps.run(root, ns, dir_path) + commands.deps.run(root, ns, dir_path, brick or None) if __name__ == "__main__": diff --git a/bases/polylith/cli/options.py b/bases/polylith/cli/options.py index 5a142808..e76bbf18 100644 --- a/bases/polylith/cli/options.py +++ b/bases/polylith/cli/options.py @@ -15,3 +15,5 @@ verbose = Option(help="More verbose output.") quiet = Option(help="Do not output any messages.") + +brick = Option(help="Shows dependencies for selected brick.") diff --git a/components/polylith/commands/deps.py b/components/polylith/commands/deps.py index e307bf05..c490b0b0 100644 --- a/components/polylith/commands/deps.py +++ b/components/polylith/commands/deps.py @@ -4,12 +4,10 @@ from polylith import bricks, deps, info -def print_report(root: Path, ns: str, bases: Set[str], components: Set[str]): +def get_imports(root: Path, ns: str, bases: Set[str], components: Set[str]) -> dict: brick_imports = deps.get_brick_imports(root, ns, bases, components) - flattened = {**brick_imports["bases"], **brick_imports["components"]} - - deps.print_deps(bases, components, flattened) + return {**brick_imports["bases"], **brick_imports["components"]} def pick_name(data: List[dict]) -> Set[str]: @@ -30,11 +28,17 @@ def get_components(root: Path, ns: str, project_data: dict) -> Set[str]: return pick_name(bricks.get_components_data(root, ns)) -def run(root: Path, ns: str, directory: Union[str, None]): +def run(root: Path, ns: str, directory: Union[str, None], brick: Union[str, None]): projects_data = info.get_projects_data(root, ns) if directory else [] project = next((p for p in projects_data if directory in p["path"].as_posix()), {}) bases = get_bases(root, ns, project) components = get_components(root, ns, project) - print_report(root, ns, bases, components) + imports = get_imports(root, ns, bases, components) + + if brick and imports.get(brick): + deps.print_brick_deps(brick, bases, components, imports) + return + + deps.print_deps(bases, components, imports) diff --git a/components/polylith/deps/__init__.py b/components/polylith/deps/__init__.py index f90052ed..d91cbdca 100644 --- a/components/polylith/deps/__init__.py +++ b/components/polylith/deps/__init__.py @@ -1,4 +1,4 @@ from polylith.deps.core import get_brick_imports -from polylith.deps.report import print_deps +from polylith.deps.report import print_brick_deps, print_deps -__all__ = ["get_brick_imports", "print_deps"] +__all__ = ["get_brick_imports", "print_brick_deps", "print_deps"] diff --git a/components/polylith/deps/report.py b/components/polylith/deps/report.py index 5c93ae2f..b5ec098e 100644 --- a/components/polylith/deps/report.py +++ b/components/polylith/deps/report.py @@ -1,4 +1,5 @@ from functools import reduce +from itertools import zip_longest from typing import List, Set, Tuple from polylith.reporting import theme @@ -7,10 +8,6 @@ from rich.table import Table -def calculate_tag(brick: str, project_data: dict) -> str: - return "base" if brick in project_data.get("bases", []) else "comp" - - def to_col(brick: str, tag: str) -> str: name = "\n".join(brick) @@ -80,5 +77,68 @@ def print_deps(bases: Set[str], components: Set[str], import_data: dict): table.add_row(*row) console = Console(theme=theme.poly_theme) + console.print(table, overflow="ellipsis") + + +def without(key: str, bricks: Set[str]) -> Set[str]: + return {b for b in bricks if b != key} + + +def sorted_usings(usings: Set[str], bases: Set[str], components: Set[str]) -> List[str]: + usings_bases = sorted({b for b in usings if b in bases}) + usings_components = sorted({c for c in usings if c in components}) + + return usings_components + usings_bases + + +def sorted_used_by( + brick: str, bases: Set[str], components: Set[str], import_data: dict +) -> List[str]: + brick_used_by = without(brick, {k for k, v in import_data.items() if brick in v}) + + return sorted_usings(brick_used_by, bases, components) + + +def sorted_uses( + brick: str, bases: Set[str], components: Set[str], import_data: dict +) -> List[str]: + brick_uses = without(brick, {b for b in import_data[brick]}) + + return sorted_usings(brick_uses, bases, components) + + +def calculate_tag(brick: str, bases: Set[str]) -> str: + return "base" if brick in bases else "comp" + + +def print_brick_deps( + brick: str, bases: Set[str], components: Set[str], import_data: dict +): + brick_used_by = sorted_used_by(brick, bases, components, import_data) + brick_uses = sorted_uses(brick, bases, components, import_data) + + tag = calculate_tag(brick, bases) + + console = Console(theme=theme.poly_theme) + + table = Table(box=box.SIMPLE_HEAD) + table.add_column("[data]used by[/]") + table.add_column(":backhand_index_pointing_left:") + table.add_column(f"[{tag}]{brick}[/]") + table.add_column(":backhand_index_pointing_right:") + table.add_column("[data]uses[/]") + + for item in zip_longest(brick_used_by, brick_uses): + used_by, uses = item + + used_by_tag = calculate_tag(used_by, bases) if used_by else "" + uses_tag = calculate_tag(uses, bases) if uses else "" + + left = f"[{used_by_tag}]{used_by}[/]" if used_by else "" + right = f"[{uses_tag}]{uses}[/]" if uses else "" + + row = [left, "", "", "", right] + + table.add_row(*row) console.print(table, overflow="ellipsis") diff --git a/components/polylith/poetry/commands/deps.py b/components/polylith/poetry/commands/deps.py index 7c3470e5..67c4cc22 100644 --- a/components/polylith/poetry/commands/deps.py +++ b/components/polylith/poetry/commands/deps.py @@ -1,4 +1,5 @@ from pathlib import Path +from cleo.helpers import option from poetry.console.commands.command import Command from polylith import commands, configuration, repo @@ -8,13 +9,23 @@ class DepsCommand(Command): name = "poly deps" description = "Visualize the dependencies between bricks." + options = [ + option( + long_name="brick", + description="Shows dependencies for selected brick", + flag=False, + ), + ] + def handle(self) -> int: directory = self.option("directory") + brick = self.option("brick") + root = repo.get_workspace_root(Path.cwd()) ns = configuration.get_namespace_from_config(root) dir_path = Path(directory).as_posix() if directory else None - commands.deps.run(root, ns, dir_path) + commands.deps.run(root, ns, dir_path, brick) return 0 diff --git a/projects/poetry_polylith_plugin/pyproject.toml b/projects/poetry_polylith_plugin/pyproject.toml index 863d5771..48f2e09e 100644 --- a/projects/poetry_polylith_plugin/pyproject.toml +++ b/projects/poetry_polylith_plugin/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-polylith-plugin" -version = "1.16.0" +version = "1.17.0" description = "A Poetry plugin that adds tooling support for the Polylith Architecture" authors = ["David Vujic"] homepage = "https://davidvujic.github.io/python-polylith-docs/" diff --git a/projects/polylith_cli/pyproject.toml b/projects/polylith_cli/pyproject.toml index 84844939..32ffc5b1 100644 --- a/projects/polylith_cli/pyproject.toml +++ b/projects/polylith_cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "polylith-cli" -version = "1.3.0" +version = "1.4.0" description = "Python tooling support for the Polylith Architecture" authors = ['David Vujic'] homepage = "https://davidvujic.github.io/python-polylith-docs/"