Skip to content

Commit ab49815

Browse files
committed
feat(cli): display flow spec with verbose option and tree structured output
1 parent 0c66e1c commit ab49815

File tree

2 files changed

+47
-55
lines changed

2 files changed

+47
-55
lines changed

python/cocoindex/cli.py

Lines changed: 7 additions & 6 deletions
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

Lines changed: 40 additions & 49 deletions
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
@@ -454,61 +454,52 @@ def _lazy_engine_flow() -> _engine.Flow:
454454
return engine_flow
455455
self._lazy_engine_flow = _lazy_engine_flow
456456

457-
def _format_flow(self, flow_dict: dict) -> Text:
458-
output = Text()
457+
def _render_spec(self, verbose: bool = False) -> Tree:
458+
"""
459+
Render the flow spec as a styled rich Tree with hierarchical structure.
460+
"""
461+
tree = Tree(f"Flow: {self.name}", style="cyan")
462+
current_section = None
463+
section_node = None
464+
indent_stack = []
459465

460-
def add_line(content, indent=0, style=None, end="\n"):
461-
output.append(" " * indent)
462-
output.append(content, style=style)
463-
output.append(end)
466+
for i, (section, content, indent) in enumerate(self._get_spec(verbose=verbose)):
467+
# Skip "Scope" entries (see ReactiveOpScope in spec.rs)
468+
if content.startswith("Scope:"):
469+
continue
464470

465-
def format_key_value(key, value, indent):
466-
if isinstance(value, (dict, list)):
467-
add_line(f"- {key}:", indent, style="green")
468-
format_data(value, indent + 2)
469-
else:
470-
add_line(f"- {key}:", indent, style="green", end="")
471-
add_line(f" {value}", style="yellow")
472-
473-
def format_data(data, indent=0):
474-
if isinstance(data, dict):
475-
for key, value in data.items():
476-
format_key_value(key, value, indent)
477-
elif isinstance(data, list):
478-
for i, item in enumerate(data):
479-
format_key_value(f"[{i}]", item, indent)
471+
if section != current_section:
472+
current_section = section
473+
section_node = tree.add(f"{section}:", style="bold magenta")
474+
indent_stack = [(0, section_node)]
475+
476+
while indent_stack and indent_stack[-1][0] >= indent:
477+
indent_stack.pop()
478+
479+
parent = indent_stack[-1][1] if indent_stack else section_node
480+
styled_content = Text(content, style="yellow")
481+
is_parent = any(
482+
next_indent > indent
483+
for _, next_content, next_indent in self._get_spec(verbose=verbose)[i + 1:]
484+
if not next_content.startswith("Scope:")
485+
)
486+
487+
if is_parent:
488+
node = parent.add(styled_content, style=None)
489+
indent_stack.append((indent, node))
480490
else:
481-
add_line(str(data), indent, style="yellow")
482-
483-
# Header
484-
flow_name = flow_dict.get("name", "Unnamed")
485-
add_line(f"Flow: {flow_name}", style="bold cyan")
486-
487-
# Section
488-
for section_title, section_key in [
489-
("Sources:", "import_ops"),
490-
("Processing:", "reactive_ops"),
491-
("Targets:", "export_ops"),
492-
]:
493-
add_line("")
494-
add_line(section_title, style="bold cyan")
495-
format_data(flow_dict.get(section_key, []), indent=0)
496-
497-
return output
498-
499-
def _render_text(self) -> Text:
500-
flow_spec_str = str(self._lazy_engine_flow())
501-
try:
502-
flow_dict = json.loads(flow_spec_str)
503-
return self._format_flow(flow_dict)
504-
except json.JSONDecodeError:
505-
return Text(flow_spec_str)
491+
parent.add(styled_content, style=None)
492+
493+
return tree
494+
495+
def _get_spec(self, verbose: bool = False) -> list[tuple[str, str, int]]:
496+
return self._lazy_engine_flow().get_spec(verbose=verbose)
506497

507-
def _render_schema(self) -> list[tuple[str, str, str]]:
498+
def _get_schema(self) -> list[tuple[str, str, str]]:
508499
return self._lazy_engine_flow().get_schema()
509500

510501
def __str__(self):
511-
return str(self._render_text())
502+
return str(self._get_spec())
512503

513504
def __repr__(self):
514505
return repr(self._lazy_engine_flow())

0 commit comments

Comments
 (0)