Coverage for src / graphable / views / asciiflow.py: 100%
41 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
1from dataclasses import dataclass
2from logging import getLogger
3from pathlib import Path
4from typing import Any, Callable
6from ..graph import Graph
7from ..graphable import Graphable
8from ..registry import register_view
10logger = getLogger(__name__)
13@dataclass
14class AsciiflowStylingConfig:
15 """
16 Configuration for ASCII flowchart representation of the graph.
18 Attributes:
19 node_text_fnc: Function to generate the text representation of a node.
20 show_tags: Whether to include tags in the output.
21 """
23 node_text_fnc: Callable[[Graphable[Any]], str] = lambda n: n.reference
24 show_tags: bool = False
27def create_topology_ascii_flow(
28 graph: Graph, config: AsciiflowStylingConfig | None = None
29) -> str:
30 """
31 Create an ASCII-based flowchart representation of the graph.
32 Unlike TextTree, this explicitly shows multiple parents by listing connections.
34 Args:
35 graph (Graph): The graph to convert.
36 config (AsciiflowStylingConfig | None): Styling configuration.
38 Returns:
39 str: The ASCII flowchart representation.
40 """
41 if config is None:
42 config = AsciiflowStylingConfig()
44 logger.debug("Creating ASCII flowchart text.")
46 lines: list[str] = []
48 # We'll group by "levels" using topological order to give a sense of flow
49 nodes = graph.topological_order()
51 for node in nodes:
52 node_text = config.node_text_fnc(node)
53 tags_info = (
54 f" [{', '.join(node.tags)}]" if config.show_tags and node.tags else ""
55 )
57 # Box the node
58 box_width = len(node_text + tags_info) + 2
59 top_border = "+" + "-" * box_width + "+"
60 middle = f"| {node_text}{tags_info} |"
62 lines.append(top_border)
63 lines.append(middle)
64 lines.append(top_border)
66 # Show dependencies (outgoing edges)
67 internal_deps = list(graph.internal_dependents(node))
68 if internal_deps:
69 for i, (dependent, _) in enumerate(internal_deps):
70 dep_text = config.node_text_fnc(dependent)
71 if i == 0:
72 lines.append(f" v\n +--> {dep_text}")
73 else:
74 lines.append(f" +--> {dep_text}")
76 lines.append("") # Spacer between nodes
78 return "\n".join(lines)
81@register_view(".ascii", creator_fnc=create_topology_ascii_flow)
82def export_topology_ascii_flow(
83 graph: Graph, output: Path, config: AsciiflowStylingConfig | None = None
84) -> None:
85 """
86 Export the graph to an ASCII flowchart file.
88 Args:
89 graph (Graph): The graph to export.
90 output (Path): The output file path.
91 config (AsciiFlowStylingConfig | None): Styling configuration.
92 """
93 logger.info(f"Exporting ASCII flowchart to: {output}")
94 with open(output, "w+") as f:
95 f.write(create_topology_ascii_flow(graph, config))