diff --git a/odxtools/cli/_print_utils.py b/odxtools/cli/_print_utils.py index fea6ad97..4050a1e0 100644 --- a/odxtools/cli/_print_utils.py +++ b/odxtools/cli/_print_utils.py @@ -4,7 +4,8 @@ from typing import Any, Callable, Dict, List, Optional, Union import markdownify -from tabulate import tabulate # TODO: switch to rich tables +from rich.table import Table +from rich.padding import Padding from ..description import Description from ..diaglayers.diaglayer import DiagLayer @@ -82,9 +83,7 @@ def print_service_parameters(service: DiagService, f" Identifying Prefix: 0x{const_prefix.hex().upper()} ({bytes(const_prefix)!r})") print_fn(f" Parameters:") table = extract_parameter_tabulation_data(list(service.request.parameters)) - table_str = textwrap.indent(tabulate(table, headers='keys', tablefmt='presto'), " ") - print_fn() - print_fn(table_str) + print_fn(Padding(table, pad=(0, 0, 0, 4))) print_fn() else: print_fn(f" No Request!") @@ -97,8 +96,7 @@ def print_service_parameters(service: DiagService, print_fn(f" Positive Response '{resp.short_name}':") print_fn(f" Parameters:\n") table = extract_parameter_tabulation_data(list(resp.parameters)) - table_str = textwrap.indent(tabulate(table, headers='keys', tablefmt='presto'), " ") - print_fn(table_str) + print_fn(Padding(table, pad=(0, 0, 0, 4))) print_fn() # Negative Response @@ -109,8 +107,7 @@ def print_service_parameters(service: DiagService, print_fn(f" Negative Response '{resp.short_name}':") print_fn(f" Parameters:\n") table = extract_parameter_tabulation_data(list(resp.parameters)) - table_str = textwrap.indent(tabulate(table, headers='keys', tablefmt='presto'), " ") - print_fn(table_str) + print_fn(Padding(table, pad=(0, 0, 0, 4))) print_fn() print_fn("\n") @@ -138,15 +135,30 @@ def extract_service_tabulation_data(services: List[DiagService]) -> Dict[str, An return {'Name': name, 'Semantic': semantic, 'Hex-Request': request} -def extract_parameter_tabulation_data(parameters: List[Parameter]) -> Dict[str, Any]: +def extract_parameter_tabulation_data(parameters: List[Parameter]) -> Table: # extracts data of parameters of diagnostic services into Dictionary which can be printed by tabulate module # TODO: consider indentation - name = [] - byte = [] + # Create Rich table + table = Table( + title="", show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) + + # Add columns with appropriate styling + table.add_column("Name", style="green") + table.add_column("Byte Position", justify="right", style="yellow") + table.add_column("Bit Length", justify="right", style="yellow") + table.add_column("Semantic", justify="left", style="white") + table.add_column("Parameter Type", justify="left", style="white") + table.add_column("Data Type", justify="left", style="white") + table.add_column("Value", justify="left", style="yellow") + table.add_column("Value Type", justify="left", style="white") + table.add_column("Linked DOP", justify="left", style="white") + + name: List[str] = [] + byte: List[Optional[int]] = [] bit_length: List[Optional[int]] = [] - semantic = [] - param_type = [] + semantic: List[Optional[str]] = [] + param_type: List[Optional[str]] = [] value: List[Optional[str]] = [] value_type: List[Optional[str]] = [] data_type: List[Optional[str]] = [] @@ -216,42 +228,43 @@ def extract_parameter_tabulation_data(parameters: List[Parameter]) -> Dict[str, value_type.append(None) dop.append(None) - return { - 'Name': name, - 'Byte Position': byte, - 'Bit Length': bit_length, - 'Semantic': semantic, - 'Parameter Type': param_type, - 'Data Type': data_type, - 'Value': value, - 'Value Type': value_type, - 'Linked DOP': dop - } + for lst in [byte, semantic, bit_length, value, value_type, data_type, dop]: + lst[:] = ["" if x is None else x for x in lst] # type: ignore[attr-defined, index] + # Add all rows at once by zipping dictionary values + rows = zip(name, byte, bit_length, semantic, param_type, data_type, value, value_type, dop) + for row in rows: + table.add_row(*map(str, row)) + return table -def print_dl_metrics(variants: List[DiagLayer], print_fn: Callable[..., Any] = print) -> None: - name = [] - type = [] - num_services = [] - num_dops = [] - num_comparam_refs = [] +def print_dl_metrics(variants: List[DiagLayer], print_fn: Callable[..., Any] = print) -> None: + """ + Print diagnostic layer metrics using Rich tables. + Args: + variants: List of diagnostic layer variants to analyze + print_fn: Optional callable for custom print handling (defaults to built-in print) + """ + # Create Rich table + table = Table( + title="", show_header=True, header_style="bold cyan", border_style="blue", show_lines=True) + + # Add columns with appropriate styling + table.add_column("Name", style="green") + table.add_column("Variant Type", style="magenta") + table.add_column("Number of Services", justify="right", style="yellow") + table.add_column("Number of DOPs", justify="right", style="yellow") + table.add_column("Number of communication parameters", justify="right", style="yellow") + + # Process each variant for variant in variants: assert isinstance(variant, DiagLayer) all_services: List[Union[DiagService, SingleEcuJob]] = sorted( variant.services, key=lambda x: x.short_name) - name.append(variant.short_name) - type.append(variant.variant_type.value) - num_services.append(len(all_services)) ddds = variant.diag_data_dictionary_spec - num_dops.append(len(ddds.data_object_props)) - num_comparam_refs.append(len(getattr(variant, "comparams_refs", []))) - - table = { - 'Name': name, - 'Variant Type': type, - 'Number of Services': num_services, - 'Number of DOPs': num_dops, - 'Number of communication parameters': num_comparam_refs - } - print_fn(tabulate(table, headers='keys', tablefmt='presto')) + + # Add row to table + table.add_row(variant.short_name, variant.variant_type.value, str(len(all_services)), + str(len(ddds.data_object_props)), + str(len(getattr(variant, "comparams_refs", [])))) + print_fn(table) diff --git a/odxtools/cli/browse.py b/odxtools/cli/browse.py index 244ad468..468f2897 100644 --- a/odxtools/cli/browse.py +++ b/odxtools/cli/browse.py @@ -5,7 +5,6 @@ from typing import List, Optional, Union, cast import InquirerPy.prompt as IP_prompt -from tabulate import tabulate # TODO: switch to rich tables from ..database import Database from ..dataobjectproperty import DataObjectProperty @@ -356,8 +355,7 @@ def browse(odxdb: Database) -> None: if codec is not None: assert isinstance(codec, (Request, Response)) table = extract_parameter_tabulation_data(codec.parameters) - table_str = tabulate(table, headers='keys', tablefmt='presto') - print(table_str) + print(table) encode_message_interactively(codec, ask_user_confirmation=True)