Coverage for src / sql_tool / formatters / base.py: 90%
20 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-14 15:28 -0500
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-14 15:28 -0500
1"""Formatter protocol and registry for output formatting."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Protocol, runtime_checkable
7if TYPE_CHECKING:
8 from collections.abc import Iterator
10 from sql_tool.core.models import QueryResult
13@runtime_checkable
14class Formatter(Protocol):
15 """Protocol for output formatters.
17 Each formatter transforms a QueryResult into lines of formatted text.
18 Yielding strings (rather than returning a single string) enables
19 streaming output for large result sets without buffering everything
20 in memory.
21 """
23 def format(self, result: QueryResult) -> Iterator[str]:
24 """Transform a QueryResult into formatted output lines."""
25 ...
28class FormatterRegistry:
29 """Registry for looking up formatters by name."""
31 def __init__(self) -> None:
32 self._formatters: dict[str, type[Formatter]] = {}
34 def register(self, name: str, formatter_class: type[Formatter]) -> None:
35 self._formatters[name] = formatter_class
37 def get(self, name: str, **kwargs: object) -> Formatter:
38 """Return a formatter instance by name.
40 Raises KeyError if the format name is not registered.
41 """
42 if name not in self._formatters:
43 available = ", ".join(sorted(self._formatters))
44 msg = f"Unknown format {name!r}. Available: {available}"
45 raise KeyError(msg)
46 return self._formatters[name](**kwargs)
48 @property
49 def available(self) -> list[str]:
50 return sorted(self._formatters)
53# Global registry instance populated by formatter modules.
54registry = FormatterRegistry()