Skip to content

Commit 4e0bb32

Browse files
authored
feat(pyo3): implement flow spec pretty print and add verbose mode (#442)
* feat: implement flow spec getter in pyo3 bindings * feat(py): add verbose option for detailed output in spec getter * feat(cli): display flow spec with verbose option and tree structured output * refactor: update spec retrieval to use enum for concise/verbose outputs * refactor: update spec formatting logic to use `SpecFormatter` trait * refactor: render spec line recursively on rust side * feat: use bulk load to collect rendered spec line * refactor: move rendered spec related struct into pymod
1 parent 3c4f641 commit 4e0bb32

File tree

4 files changed

+351
-67
lines changed

4 files changed

+351
-67
lines changed

python/cocoindex/cli.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,17 @@ def ls(show_all: bool):
5555

5656
@cli.command()
5757
@click.argument("flow_name", type=str, required=False)
58-
@click.option("--color/--no-color", default=True)
59-
def show(flow_name: str | None, color: bool):
58+
@click.option("--color/--no-color", default=True, help="Enable or disable colored output.")
59+
@click.option("--verbose", is_flag=True, help="Show verbose output with full details.")
60+
def show(flow_name: str | None, color: bool, verbose: bool):
6061
"""
61-
Show the flow spec in a readable format with colored output,
62-
including the schema.
62+
Show the flow spec and schema in a readable format with colored output.
6363
"""
6464
flow = _flow_by_name(flow_name)
6565
console = Console(no_color=not color)
66-
console.print(flow._render_text())
66+
console.print(flow._render_spec(verbose=verbose))
6767

68+
console.print()
6869
table = Table(
6970
title=f"Schema for Flow: {flow.name}",
7071
show_header=True,
@@ -74,7 +75,7 @@ def show(flow_name: str | None, color: bool):
7475
table.add_column("Type", style="green")
7576
table.add_column("Attributes", style="yellow")
7677

77-
for field_name, field_type, attr_str in flow._render_schema():
78+
for field_name, field_type, attr_str in flow._get_schema():
7879
table.add_row(field_name, field_type, attr_str)
7980

8081
console.print(table)

python/cocoindex/flow.py

+22-50
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from enum import Enum
1616
from dataclasses import dataclass
1717
from rich.text import Text
18-
from rich.console import Console
18+
from rich.tree import Tree
1919

2020
from . import _engine
2121
from . import index
@@ -462,61 +462,33 @@ def _lazy_engine_flow() -> _engine.Flow:
462462
return engine_flow
463463
self._lazy_engine_flow = _lazy_engine_flow
464464

465-
def _format_flow(self, flow_dict: dict) -> Text:
466-
output = Text()
465+
def _render_spec(self, verbose: bool = False) -> Tree:
466+
"""
467+
Render the flow spec as a styled rich Tree with hierarchical structure.
468+
"""
469+
spec = self._get_spec(verbose=verbose)
470+
tree = Tree(f"Flow: {self.name}", style="cyan")
467471

468-
def add_line(content, indent=0, style=None, end="\n"):
469-
output.append(" " * indent)
470-
output.append(content, style=style)
471-
output.append(end)
472+
def build_tree(label: str, lines: list):
473+
node = Tree(label, style="bold magenta" if lines else "cyan")
474+
for line in lines:
475+
child_node = node.add(Text(line.content, style="yellow"))
476+
child_node.children = build_tree("", line.children).children
477+
return node
472478

473-
def format_key_value(key, value, indent):
474-
if isinstance(value, (dict, list)):
475-
add_line(f"- {key}:", indent, style="green")
476-
format_data(value, indent + 2)
477-
else:
478-
add_line(f"- {key}:", indent, style="green", end="")
479-
add_line(f" {value}", style="yellow")
480-
481-
def format_data(data, indent=0):
482-
if isinstance(data, dict):
483-
for key, value in data.items():
484-
format_key_value(key, value, indent)
485-
elif isinstance(data, list):
486-
for i, item in enumerate(data):
487-
format_key_value(f"[{i}]", item, indent)
488-
else:
489-
add_line(str(data), indent, style="yellow")
490-
491-
# Header
492-
flow_name = flow_dict.get("name", "Unnamed")
493-
add_line(f"Flow: {flow_name}", style="bold cyan")
494-
495-
# Section
496-
for section_title, section_key in [
497-
("Sources:", "import_ops"),
498-
("Processing:", "reactive_ops"),
499-
("Targets:", "export_ops"),
500-
]:
501-
add_line("")
502-
add_line(section_title, style="bold cyan")
503-
format_data(flow_dict.get(section_key, []), indent=0)
504-
505-
return output
506-
507-
def _render_text(self) -> Text:
508-
flow_spec_str = str(self._lazy_engine_flow())
509-
try:
510-
flow_dict = json.loads(flow_spec_str)
511-
return self._format_flow(flow_dict)
512-
except json.JSONDecodeError:
513-
return Text(flow_spec_str)
479+
for section, lines in spec.sections:
480+
section_node = build_tree(f"{section}:", lines)
481+
tree.children.append(section_node)
482+
return tree
483+
484+
def _get_spec(self, verbose: bool = False) -> list[tuple[str, str, int]]:
485+
return self._lazy_engine_flow().get_spec(output_mode="verbose" if verbose else "concise")
514486

515-
def _render_schema(self) -> list[tuple[str, str, str]]:
487+
def _get_schema(self) -> list[tuple[str, str, str]]:
516488
return self._lazy_engine_flow().get_schema()
517489

518490
def __str__(self):
519-
return str(self._render_text())
491+
return str(self._get_spec())
520492

521493
def __repr__(self):
522494
return repr(self._lazy_engine_flow())

0 commit comments

Comments
 (0)