Coverage for src/prosemark/ports/console_port.py: 100%
25 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1"""Abstract base class for output formatting."""
3from abc import ABC, abstractmethod
4from typing import TYPE_CHECKING
6if TYPE_CHECKING: # pragma: no cover
7 from prosemark.domain.models import Binder, BinderItem
10class ConsolePort(ABC):
11 """Abstract base class for output formatting.
13 Defines the contract for displaying formatted text output to users.
14 This abstract base class enables:
16 * Clean separation between business logic and UI presentation
17 * Testable output through dependency injection and mocking
18 * Support for different output targets (console, GUI, web interface)
19 * Hexagonal architecture compliance by isolating presentation concerns
20 * Future extensibility to different user interface adapters
22 The MVP uses this for displaying binder structure trees, audit results,
23 and general command output. Implementations should handle formatting,
24 colors, tree rendering, and other presentation concerns.
26 Examples:
27 >>> class TestConsolePort(ConsolePort):
28 ... def print(self, msg: str) -> None:
29 ... print(f'[TEST] {msg}')
30 >>> console = TestConsolePort()
31 >>> console.print('Hello, world!')
32 [TEST] Hello, world!
34 """
36 @abstractmethod
37 def print(self, msg: str) -> None:
38 """Display formatted message to the user.
40 This method must be implemented by concrete subclasses to provide
41 specific output formatting and targeting (stdout, GUI, web, etc.).
43 Implementations should handle all presentation concerns including:
44 - Message formatting and styling
45 - Color support and terminal detection
46 - Tree rendering with appropriate connectors
47 - Output stream selection (stdout vs stderr)
49 Args:
50 msg: The formatted message content to display
52 Raises:
53 NotImplementedError: If not implemented by a concrete subclass
55 """
56 msg = 'Subclasses must implement the print() method' # pragma: no cover
57 raise NotImplementedError(msg) # pragma: no cover
59 def print_info(self, msg: str) -> None:
60 """Display an informational message.
62 Args:
63 msg: The informational message content to display
65 """
66 self.print(f'INFO: {msg}')
68 def print_success(self, msg: str) -> None:
69 """Display a success message.
71 Args:
72 msg: The success message content to display
74 """
75 self.print(f'SUCCESS: {msg}')
77 def print_warning(self, msg: str) -> None:
78 """Display a warning message.
80 Args:
81 msg: The warning message content to display
83 """
84 self.print(f'WARNING: {msg}')
86 def print_error(self, msg: str) -> None:
87 """Display an error message.
89 Args:
90 msg: The error message content to display
92 """
93 self.print(f'ERROR: {msg}')
95 def print_tree(self, binder: 'Binder') -> None:
96 """Display a formatted tree representation of a binder structure.
98 Default implementation provides basic tree rendering. Subclasses can
99 override for custom formatting and visual styles.
101 Args:
102 binder: The Binder object containing the hierarchical structure
104 """
105 # Basic implementation - just print a simple representation
106 self.print(f'Binder: {binder.project_title}')
107 for item in binder.children:
108 self._print_item(item, indent=0)
110 def _print_item(self, item: 'BinderItem', indent: int) -> None:
111 """Helper method to print a binder item with indentation."""
112 prefix = ' ' * indent + '- '
113 display = f'{item.display_title}'
114 if item.node_id:
115 display += f' ({item.node_id})'
116 self.print(f'{prefix}{display}')
118 for child in item.children:
119 self._print_item(child, indent + 1)