Source code for fitzzftw.patch.base

# File: src/fitzzftw/patch/base.py
# Author: Fitzz TeXnik Welt
# Email: FitzzTeXnikWelt@t-online.de
# License: LGPLv2 or above
"""
base
===============================

This module provides the core visual and behavioral foundations for the
fitzzftw patch framework, focusing on terminal output and protocol safety.

Core Features:
--------------
* **TerminalColorMixin**:
  A reusable component that adds ANSI color support
  to any class. It includes a global toggle (:attr:`~.base.TerminalColorMixin.use_colors`)
  and a specialized
  **Test-Mode** for predictable assertions in automated environments.

* **Protocol-Enforced Printing**:
  The :meth:`~.base.TerminalColorMixin.print` method validates the host
  class against the :class:`~fitzzftw.patch.protocols.LineLike` protocol. 
  If requirements (like `prefix`,
  `orig_line`, or `_color_map`) are missing, it raises a detailed
  :exc:`~.exceptions.FtwProtocolError` with implementation guidance.

* **Visual Diagnostics**:
  Includes :func:`~.base.color_terminal_check`, a utility to
  verify ANSI support and semantic color mapping directly from the CLI.

The module ensures that terminal output is not only visually rich but also
structurally sound and testable.
"""
from pathlib import Path
from typing import ClassVar, cast

from fitzzftw.patch.exceptions import FtwProtocolError
from fitzzftw.patch.protocols import LineLike
from fitzzftw.patch.static import Color, ColorKey, colors

#SECTION - MixinClasses

#CLASS - ColorMixin
[docs] class TerminalColorMixin: """ Provides colorization capabilities for CLI output. The mixin provides a :meth:`~ColorMixin.colorize` method to encapsulate strings with ANSI color codes and bold styling. """ # ANSI Terminal Codes _ANSI:Color = colors """Internal mapping of semantic names to ANSI escape sequences. Can be overridden for testing purposes. """ use_colors: ClassVar[bool] = True """Global toggle to enable or disable colorized output. Defaults to True; should be set explicitly based on CLI flags. """
[docs] def colorize(self, text: str, color_key: ColorKey , bold:bool=False, **kwargs) -> None: """ Colorizes the text using ANSI escape sequences. :param text: The string to colorize. :param color_key: The color to use ('red', 'green', 'cyan'). :param bold: Whether to make the output bold. :param kwargs: Arbitrary keyword arguments forwarded to the built-in print() function. This allows for fine-grained control over the output, such as specifying a custom 'file' stream or changing the line termination via 'end' (e.g., end="|"). :returns: Colorized string or plain text if colors are disabled. """ kwargs["flush"]= True if not self.use_colors: print(text, **kwargs) else: prefix = self._ANSI.get("bold", "\033[1m") if bold else "" prefix += self._ANSI.get(color_key, "") suffix = self._ANSI.get("reset", "\033[0m") if prefix else "" print(f"{prefix}{text}{suffix}", **kwargs)
[docs] def print(self, **kwargs) -> None: """ Prints the line with color mapping based on its prefix. :raises FtwProtocolError: If the class does not satisfy the LineLike protocol. :param kwargs: Passed to colorize/print (e.g., end, file). """ if not isinstance(self, LineLike): raise FtwProtocolError(self.print, self.__class__.__name__, (LineLike,)) # raise TypeError( # f"Class '{self.__class__.__name__}' is not 'LineLike'. " # "To use this mixin, you must provide 'prefix', 'content', " # "and '_color_map', or override the 'print' method." # ) is_bold = kwargs.pop("bold", False) self.colorize(self.orig_line, self._color_map.get(cast(str, self.prefix), "terminal"), bold=is_bold, **kwargs)
#!CLASS #!SECTION
[docs] def color_terminal_check() -> None: """ Performs a visual diagnostic of ANSI color support in the current terminal. This function prints a structured test pattern showcasing 'green', 'red', and 'cyan' in both standard and bold variations. It then repeats the pattern with colors disabled to verify the fallback mechanism. **Usage via CLI:** .. code-block:: bash $ ftw-terminal-color **Visual Output:** - A header 'Visual Terminal Color Check' centered in a 39-character block. - An 'Enabled' row showing colorized and bold tags. - A 'Disabled' row showing plain text tags. """ row_length=49 print("=" * row_length) print(" Visual Terminal Color Check ".center(row_length, "=")) colmix = TerminalColorMixin() # Testreihe 1: Colors ON print("Enabled :", end=" ") colmix.colorize("GRN", "green", end="|") colmix.colorize("GRN-B", "green", True, end="|") colmix.colorize("RED", "red", end="|") colmix.colorize("RED-B", "red", True, end="|") colmix.colorize("CYN", "cyan", end="|") colmix.colorize("CYN-B", "cyan", True, end="|") colmix.colorize("YLW", "yellow", end="|") colmix.colorize("YLW-B", "yellow", True) # Testreihe 2: Colors OFF colmix.use_colors = False # pyright: ignore[reportAttributeAccessIssue] print("Disabled:", end=" ") colmix.colorize("GRN", "green", end="|") colmix.colorize("GRN-B", "green", True, end="|") colmix.colorize("RED", "red", end="|") colmix.colorize("RED-B", "red", True, end="|") colmix.colorize("CYN", "cyan", end="|") colmix.colorize("CYN-B", "cyan", True, end="|") colmix.colorize("YLW", "yellow", end="|") colmix.colorize("YLW-B", "yellow", True) print("=" * row_length)
if __name__ == "__main__": # pragma: no cover from doctest import FAIL_FAST, testfile be_verbose = False # be_verbose = True option_flags = 0 option_flags = FAIL_FAST test_sum = 0 test_failed = 0 # Pfad zu den dokumentierenden Tests testfiles_dir = Path(__file__).parents[3] / "doc/source/devel" test_file = testfiles_dir / "get_started_base.rst" # test_file = testfiles_dir / "get_started_ftw_patch.rst" if test_file.exists(): print(f"--- Running Doctest for {test_file.name} ---") doctestresult = testfile( str(test_file), module_relative=False, verbose=be_verbose, optionflags=option_flags, ) test_failed += doctestresult.failed test_sum += doctestresult.attempted if test_failed == 0: print(f"\nDocTests passed without errors, {test_sum} tests.") else: print(f"\nDocTests failed: {test_failed} tests.") else: print(f"⚠️ Warning: Test file {test_file.name} not found.")