__init__.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. """Display module for CLI output rendering.
  2. This package provides centralized display management with mixin-based architecture.
  3. DisplayManager inherits from multiple mixins to provide a flat, cohesive API.
  4. """
  5. from __future__ import annotations
  6. from rich.console import Console
  7. from .display_base import BaseDisplay
  8. from .display_icons import IconManager
  9. from .display_settings import DisplaySettings
  10. from .display_status import StatusDisplay
  11. from .display_table import TableDisplay
  12. from .display_template import TemplateDisplay
  13. from .display_variable import VariableDisplay
  14. # Console instances for stdout and stderr
  15. console = Console()
  16. console_err = Console(stderr=True)
  17. class DisplayManager:
  18. """Main display coordinator using composition.
  19. This class composes specialized display components to provide a unified API.
  20. Each component handles a specific concern (status, tables, templates, variables).
  21. Design Principles:
  22. - Composition over inheritance
  23. - Explicit dependencies
  24. - Clear separation of concerns
  25. - Easy to test and extend
  26. """
  27. def __init__(self, quiet: bool = False, settings: DisplaySettings | None = None):
  28. """Initialize DisplayManager with composed display components.
  29. Args:
  30. quiet: If True, suppress all non-error output
  31. settings: Optional DisplaySettings instance for customization
  32. """
  33. self.quiet = quiet
  34. self.settings = settings or DisplaySettings()
  35. # Create base display component (includes utilities)
  36. self.base = BaseDisplay(self.settings, quiet)
  37. # Create specialized display components
  38. self.status = StatusDisplay(self.settings, quiet, self.base)
  39. self.variables = VariableDisplay(self.settings, self.base)
  40. self.templates = TemplateDisplay(self.settings, self.base, self.variables, self.status)
  41. self.tables = TableDisplay(self.settings, self.base)
  42. # ===== Delegate to base display =====
  43. def text(self, text: str, style: str | None = None) -> None:
  44. """Display plain text."""
  45. return self.base.text(text, style)
  46. def heading(self, text: str, style: str | None = None) -> None:
  47. """Display a heading."""
  48. return self.base.heading(text, style)
  49. def section(self, title: str, description: str | None = None) -> None:
  50. """Display a section header with optional description.
  51. Args:
  52. title: Section title
  53. description: Optional section description
  54. """
  55. self.base.text("")
  56. self.base.text(f"[bold cyan]{title}[/bold cyan]")
  57. if description:
  58. self.base.text(f"[dim]{description}[/dim]")
  59. def table(
  60. self,
  61. headers: list[str] | None = None,
  62. rows: list[tuple] | None = None,
  63. title: str | None = None,
  64. show_header: bool = True,
  65. borderless: bool = False,
  66. ) -> None:
  67. """Display a table."""
  68. return self.base.table(headers, rows, title, show_header, borderless)
  69. def tree(self, root_label: str, nodes: dict | list) -> None:
  70. """Display a tree."""
  71. return self.base.tree(root_label, nodes)
  72. def code(self, code_text: str, language: str | None = None) -> None:
  73. """Display code."""
  74. return self.base.code(code_text, language)
  75. def progress(self, *columns):
  76. """Create a progress bar."""
  77. return self.base.progress(*columns)
  78. def file_tree(
  79. self,
  80. root_label: str,
  81. files: list,
  82. file_info_fn: callable,
  83. title: str | None = None,
  84. ) -> None:
  85. """Display a file tree structure."""
  86. return self.base.file_tree(root_label, files, file_info_fn, title)
  87. # ===== Formatting utilities =====
  88. def truncate(self, value: str, max_length: int | None = None) -> str:
  89. """Truncate string value."""
  90. return self.base.truncate(value, max_length)
  91. def format_file_size(self, size_bytes: int) -> str:
  92. """Format file size in human-readable format."""
  93. return self.base.format_file_size(size_bytes)
  94. def data_table(
  95. self,
  96. columns: list[dict],
  97. rows: list,
  98. title: str | None = None,
  99. row_formatter: callable | None = None,
  100. ) -> None:
  101. """Display a data table with configurable columns."""
  102. return self.tables.data_table(columns, rows, title, row_formatter)
  103. def display_status_table(
  104. self,
  105. title: str,
  106. rows: list[tuple[str, str, bool]],
  107. columns: tuple[str, str] = ("Item", "Status"),
  108. ) -> None:
  109. """Display a status table with success/error indicators."""
  110. return self.tables.render_status_table(title, rows, columns)
  111. # ===== Delegate to status display =====
  112. def error(self, message: str, context: str | None = None, details: str | None = None) -> None:
  113. """Display an error message."""
  114. return self.status.error(message, context, details)
  115. def warning(self, message: str, context: str | None = None, details: str | None = None) -> None:
  116. """Display a warning message."""
  117. return self.status.warning(message, context, details)
  118. def success(self, message: str, context: str | None = None) -> None:
  119. """Display a success message."""
  120. return self.status.success(message, context)
  121. def info(self, message: str, context: str | None = None) -> None:
  122. """Display an info message."""
  123. return self.status.info(message, context)
  124. def skipped(self, message: str, reason: str | None = None) -> None:
  125. """Display skipped message."""
  126. return self.status.skipped(message, reason)
  127. # ===== Helper methods =====
  128. def get_lock_icon(self) -> str:
  129. """Get lock icon."""
  130. return self.base.get_lock_icon()
  131. def print_table(self, table) -> None:
  132. """Print a pre-built Rich Table object.
  133. Args:
  134. table: Rich Table object to print
  135. """
  136. return self.base._print_table(table)
  137. # Export public API
  138. __all__ = [
  139. "DisplayManager",
  140. "DisplaySettings",
  141. "IconManager",
  142. "console",
  143. "console_err",
  144. ]