Coverage for lintro / formatters / core / format_registry.py: 88%
48 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Core formatting abstractions and style registry.
3This module provides:
4- OutputStyle: Abstract base class for output style renderers
5- TableDescriptor: Interface for describing table columns and rows
6- Style registry functions for looking up format styles
7"""
9from __future__ import annotations
11from abc import ABC, abstractmethod
12from functools import lru_cache
13from typing import TYPE_CHECKING, Any
15from lintro.enums.output_format import OutputFormat
17if TYPE_CHECKING:
18 pass
21# =============================================================================
22# Abstract Base Classes
23# =============================================================================
26class OutputStyle(ABC):
27 """Abstract base class for output style renderers.
29 Implementations convert tabular data into a concrete textual
30 representation (e.g., grid, markdown, plain).
31 """
33 @abstractmethod
34 def format(
35 self,
36 columns: list[str],
37 rows: list[list[Any]],
38 tool_name: str | None = None,
39 **kwargs: Any,
40 ) -> str:
41 """Format a table given columns and rows.
43 Args:
44 columns: List of column header names.
45 rows: List of rows, where each row is a list of values.
46 tool_name: Optional tool name for metadata-rich formats.
47 **kwargs: Additional renderer-specific context.
49 Returns:
50 str: Formatted table as a string.
51 """
52 pass
55class TableDescriptor(ABC):
56 """Describe how to extract tabular data for a tool's issues.
58 Concrete implementations define column ordering and how to map issue
59 objects into a list of column values.
60 """
62 @abstractmethod
63 def get_columns(self) -> list[str]:
64 """Return the list of column names in order."""
65 pass
67 @abstractmethod
68 def get_rows(
69 self,
70 issues: list[Any],
71 ) -> list[list[Any]]:
72 """Return the values for each column for a list of issues.
74 Args:
75 issues: List of issue objects to extract data from.
77 Returns:
78 list[list]: Nested list representing table rows and columns.
79 """
80 pass
83# =============================================================================
84# Style Registry
85# =============================================================================
88@lru_cache(maxsize=1)
89def _create_style_instances() -> dict[OutputFormat, OutputStyle]:
90 """Create singleton instances of all output styles.
92 Uses lazy imports to avoid circular dependencies and improve startup time.
93 Results are cached to ensure style instances are reused.
95 Returns:
96 dict[OutputFormat, OutputStyle]: Mapping of format to style instance.
97 """
98 from lintro.formatters.styles.csv import CsvStyle
99 from lintro.formatters.styles.github import GitHubStyle
100 from lintro.formatters.styles.grid import GridStyle
101 from lintro.formatters.styles.html import HtmlStyle
102 from lintro.formatters.styles.json import JsonStyle
103 from lintro.formatters.styles.markdown import MarkdownStyle
104 from lintro.formatters.styles.plain import PlainStyle
106 return {
107 OutputFormat.PLAIN: PlainStyle(),
108 OutputFormat.GRID: GridStyle(),
109 OutputFormat.MARKDOWN: MarkdownStyle(),
110 OutputFormat.HTML: HtmlStyle(),
111 OutputFormat.JSON: JsonStyle(),
112 OutputFormat.CSV: CsvStyle(),
113 OutputFormat.GITHUB: GitHubStyle(),
114 }
117def get_style(format_key: OutputFormat | str) -> OutputStyle:
118 """Get the output style for a given format.
120 Args:
121 format_key: Output format as enum or string (e.g., "grid", "plain").
123 Returns:
124 OutputStyle: The appropriate style instance for formatting.
125 Falls back to GridStyle for unknown formats to maintain
126 backward compatibility.
127 """
128 styles = _create_style_instances()
130 # Handle string keys for backward compatibility
131 if isinstance(format_key, str):
132 format_key_lower = format_key.lower()
133 try:
134 format_key = OutputFormat(format_key_lower)
135 except ValueError:
136 # Try matching by name
137 for fmt in OutputFormat:
138 if (
139 fmt.value == format_key_lower
140 or fmt.name.lower() == format_key_lower
141 ):
142 format_key = fmt
143 break
144 else:
145 # Fallback to cached GridStyle for unknown formats
146 return styles[OutputFormat.GRID]
148 style = styles.get(format_key)
149 if style is None:
150 # Fallback to cached grid style
151 return styles[OutputFormat.GRID]
153 return style
156def get_format_map() -> dict[OutputFormat, OutputStyle]:
157 """Get the complete format map for direct access.
159 This provides backward compatibility for code that expects a FORMAT_MAP dict.
161 Returns:
162 dict[OutputFormat, OutputStyle]: Complete mapping of all formats to styles.
163 """
164 return _create_style_instances()
167def get_string_format_map() -> dict[str, OutputStyle]:
168 """Get format map with string keys for backward compatibility.
170 Some formatters use string keys like "grid" instead of OutputFormat.GRID.
172 Returns:
173 dict[str, OutputStyle]: Mapping with string keys.
174 """
175 styles = _create_style_instances()
176 return {fmt.value: style for fmt, style in styles.items()}
179# Convenience constants for common use cases
180DEFAULT_FORMAT = OutputFormat.GRID