Skip to content

EzPrinter

Rich-based console output handler.

Overview

The EzPrinter class (alias: Printer) provides Rich-formatted console logging with pattern-based formatting. It wraps Rich Console to provide a simple and consistent API for console output.

Class Reference

EzPrinter

EzPrinter(level: str = 'INFO', indent_step: int = 3, indent_symbol: str = '>', base_indent_symbol: str = '~')

Bases: LoggingHandler, IndentationManager

Console printer handler with advanced formatting and indentation support using Rich.

This handler provides console-based logging with: - Color-coded log levels using Rich - Indentation management - Robust character handling (Rich handles special characters automatically) - Context manager support - Pattern-based logging (SUCCESS, ERROR, WARN, TIP, etc.) - Access to RichWizard for advanced display features

Initialize the console printer handler.

PARAMETER DESCRIPTION
level

The desired logging level

TYPE: str DEFAULT: 'INFO'

indent_step

Number of spaces for each indentation level

TYPE: int DEFAULT: 3

indent_symbol

Symbol for indentation levels

TYPE: str DEFAULT: '>'

base_indent_symbol

Symbol for the base indentation

TYPE: str DEFAULT: '~'

RAISES DESCRIPTION
ValidationError

If the provided level is invalid

Source code in src/ezpl/handlers/console.py
def __init__(
    self,
    level: str = "INFO",
    indent_step: int = 3,
    indent_symbol: str = ">",
    base_indent_symbol: str = "~",
) -> None:
    """
    Initialize the console printer handler.

    Args:
        level: The desired logging level
        indent_step: Number of spaces for each indentation level
        indent_symbol: Symbol for indentation levels
        base_indent_symbol: Symbol for the base indentation

    Raises:
        ValidationError: If the provided level is invalid
    """
    if not LogLevel.is_valid_level(level):
        raise ValidationError(f"Invalid log level: {level}", "level", level)

    self._level = level.upper()
    self._level_manually_set = False
    self._indent = 0
    self._indent_step = indent_step
    self._indent_symbol = indent_symbol
    self._base_indent_symbol = base_indent_symbol

    # Initialize Rich Console
    self._console = Console()
    self._level_numeric = LogLevel.get_no(self._level)

    # Initialize Rich Wizard for advanced display features
    self._wizard = RichWizard(self._console)

Attributes

level property

level: str

Return the current logging level.

level_manually_set property

level_manually_set: bool

Return whether level was set manually at runtime.

indent_step property

indent_step: int

Return the configured indentation step.

indent_symbol property

indent_symbol: str

Return the configured indentation symbol.

base_indent_symbol property

base_indent_symbol: str

Return the configured base indentation symbol.

wizard property

wizard: RichWizard

Get the Rich Wizard instance for advanced display features.

RETURNS DESCRIPTION
RichWizard

RichWizard instance for panels, tables, JSON, etc.

Example

printer.wizard.success_panel("Success", "Operation completed") printer.wizard.status_table("Status", data) printer.wizard.dependency_table({"tool": "1.0.0"})

Functions

mark_level_as_configured

mark_level_as_configured() -> None

Mark the current level as coming from configuration (not manual set).

Source code in src/ezpl/handlers/console.py
def mark_level_as_configured(self) -> None:
    """Mark the current level as coming from configuration (not manual set)."""
    self._level_manually_set = False

set_level

set_level(level: str) -> None

Set the logging level.

PARAMETER DESCRIPTION
level

The desired logging level

TYPE: str

RAISES DESCRIPTION
ValidationError

If the provided level is invalid

Source code in src/ezpl/handlers/console.py
def set_level(self, level: str) -> None:
    """
    Set the logging level.

    Args:
        level: The desired logging level

    Raises:
        ValidationError: If the provided level is invalid
    """
    if not LogLevel.is_valid_level(level):
        raise ValidationError(f"Invalid log level: {level}", "level", level)

    self._level = level.upper()
    self._level_numeric = LogLevel.get_no(self._level)
    self._level_manually_set = True

log

log(level: str, message: Any) -> None

Log a message with the specified level.

PARAMETER DESCRIPTION
level

The log level

TYPE: str

message

The message to log (any type, will be safely converted to string)

TYPE: Any

RAISES DESCRIPTION
ValidationError

If the level is invalid

Source code in src/ezpl/handlers/console.py
def log(self, level: str, message: Any) -> None:
    """
    Log a message with the specified level.

    Args:
        level: The log level
        message: The message to log (any type, will be safely converted to string)

    Raises:
        ValidationError: If the level is invalid
    """
    if not LogLevel.is_valid_level(level):
        raise ValidationError(f"Invalid log level: {level}", "level", level)

    # Convert message to string robustly
    message = safe_str_convert(message)
    message = sanitize_for_console(message)

    try:
        level_numeric = LogLevel.get_no(level)
        if level_numeric < self._level_numeric:
            return  # Level too low, skip

        # Map log levels to patterns for consistent output
        pattern_map = {
            "DEBUG": Pattern.DEBUG,
            "INFO": Pattern.INFO,
            "SUCCESS": Pattern.SUCCESS,
            "WARNING": Pattern.WARN,
            "ERROR": Pattern.ERROR,
            "CRITICAL": Pattern.ERROR,  # Critical also uses ERROR pattern
        }
        pattern_enum = pattern_map.get(level.upper(), Pattern.INFO)
        self.print_pattern(pattern_enum, message, level)

    except Exception as e:
        # Never raise an exception, just log the error safely
        try:
            self._console.print(
                f"[bold red]LOGGING ERROR:[/bold red] {type(e).__name__}"
            )
        except Exception as e:
            raise ValueError(f"Failed to print logging error: {e}") from e

info

info(message: Any) -> None

Log an informational message with pattern format.

Source code in src/ezpl/handlers/console.py
def info(self, message: Any) -> None:
    """Log an informational message with pattern format."""
    self.print_pattern(Pattern.INFO, message, "INFO")

debug

debug(message: Any) -> None

Log a debug message with pattern format.

Source code in src/ezpl/handlers/console.py
def debug(self, message: Any) -> None:
    """Log a debug message with pattern format."""
    self.print_pattern(Pattern.DEBUG, message, "DEBUG")

success

success(message: Any) -> None

Log a success message with pattern format.

Source code in src/ezpl/handlers/console.py
def success(self, message: Any) -> None:
    """Log a success message with pattern format."""
    self.print_pattern(Pattern.SUCCESS, message, "INFO")

warning

warning(message: Any) -> None

Log a warning message with pattern format.

Source code in src/ezpl/handlers/console.py
def warning(self, message: Any) -> None:
    """Log a warning message with pattern format."""
    self.print_pattern(Pattern.WARN, message, "WARNING")

warn

warn(message: Any) -> None

Alias for warning(). Log a warning message with pattern format.

Source code in src/ezpl/handlers/console.py
def warn(self, message: Any) -> None:
    """Alias for warning(). Log a warning message with pattern format."""
    self.warning(message)

error

error(message: Any) -> None

Log an error message with pattern format.

Source code in src/ezpl/handlers/console.py
def error(self, message: Any) -> None:
    """Log an error message with pattern format."""
    self.print_pattern(Pattern.ERROR, message, "ERROR")

critical

critical(message: Any) -> None

Log a critical message with pattern format.

Source code in src/ezpl/handlers/console.py
def critical(self, message: Any) -> None:
    """Log a critical message with pattern format."""
    self.print_pattern(Pattern.ERROR, message, "CRITICAL")

tip

tip(message: Any) -> None

Display a tip message with pattern format.

Source code in src/ezpl/handlers/console.py
def tip(self, message: Any) -> None:
    """Display a tip message with pattern format."""
    self.print_pattern(Pattern.TIP, message, "INFO")

system

system(message: Any) -> None

Display a system message with pattern format.

Source code in src/ezpl/handlers/console.py
def system(self, message: Any) -> None:
    """Display a system message with pattern format."""
    self.print_pattern(Pattern.SYSTEM, message, "INFO")

install

install(message: Any) -> None

Display an installation message with pattern format.

Source code in src/ezpl/handlers/console.py
def install(self, message: Any) -> None:
    """Display an installation message with pattern format."""
    self.print_pattern(Pattern.INSTALL, message, "INFO")

detect

detect(message: Any) -> None

Display a detection message with pattern format.

Source code in src/ezpl/handlers/console.py
def detect(self, message: Any) -> None:
    """Display a detection message with pattern format."""
    self.print_pattern(Pattern.DETECT, message, "INFO")

config

config(message: Any) -> None

Display a configuration message with pattern format.

Source code in src/ezpl/handlers/console.py
def config(self, message: Any) -> None:
    """Display a configuration message with pattern format."""
    self.print_pattern(Pattern.CONFIG, message, "INFO")

deps

deps(message: Any) -> None

Display a dependencies message with pattern format.

Source code in src/ezpl/handlers/console.py
def deps(self, message: Any) -> None:
    """Display a dependencies message with pattern format."""
    self.print_pattern(Pattern.DEPS, message, "INFO")

print_pattern

print_pattern(pattern: str | Pattern, message: Any, level: str = 'INFO') -> None

Display a message with pattern format: • PATTERN :: message

PARAMETER DESCRIPTION
pattern

Pattern name (string) or Pattern enum

TYPE: str | Pattern

message

Message to display

TYPE: Any

level

Log level for filtering (default: INFO)

TYPE: str DEFAULT: 'INFO'

Source code in src/ezpl/handlers/console.py
def print_pattern(
    self, pattern: str | Pattern, message: Any, level: str = "INFO"
) -> None:
    """
    Display a message with pattern format: • PATTERN :: message

    Args:
        pattern: Pattern name (string) or Pattern enum
        message: Message to display
        level: Log level for filtering (default: INFO)
    """
    try:
        # Convert pattern to Pattern enum if string
        if isinstance(pattern, str):
            try:
                pattern_enum = Pattern[pattern.upper()]
            except KeyError:
                # If pattern not found, use INFO as default
                pattern_enum = Pattern.INFO
        else:
            pattern_enum = pattern

        # Check if level should be displayed
        level_numeric = LogLevel.get_no(level)
        if level_numeric < self._level_numeric:
            return  # Level too low, don't display

        # Convert message to string safely and sanitize for console
        message = safe_str_convert(message)
        message = sanitize_for_console(message)

        # Get pattern color
        pattern_color = get_pattern_color(pattern_enum)
        pattern_name = pattern_enum.value

        # Build text with pattern format: • PATTERN :: message
        text = Text()
        text.append("• ", style=pattern_color)
        text.append(pattern_name.ljust(8), style=f"bold {pattern_color}")
        text.append(":: ", style="dim white")

        # Handle indentation - add it just before the message (after ":: ")
        indent_str = self.get_indent()
        if indent_str and indent_str != "~":
            # Add indentation just before the message
            text.append(indent_str, style="dim")
            text.append(" ", style="dim")

        # Add the message
        text.append(str(message), style="white")

        self._console.print(text)

    except Exception as e:
        # Robust error handling: never raise exception
        try:
            error_msg = f"[bold red]PATTERN ERROR:[/bold red] {type(e).__name__}"
            self._console.print(error_msg)
        except Exception as e:
            raise ValueError(f"Failed to print pattern: {e}") from e

get_indent

get_indent() -> str

Get the current indentation string.

RETURNS DESCRIPTION
str

The current indentation string

Source code in src/ezpl/handlers/console.py
def get_indent(self) -> str:
    """
    Get the current indentation string.

    Returns:
        The current indentation string
    """
    try:
        indent_spaces = " " * (self._indent * self._indent_step)
        if self._indent > 0:
            return f"{indent_spaces}{self._indent_symbol}"
        else:
            return self._base_indent_symbol
    except Exception:
        return "~"  # Safe fallback

add_indent

add_indent() -> None

Increase the indentation level by one (with maximum limit).

Source code in src/ezpl/handlers/console.py
def add_indent(self) -> None:
    """Increase the indentation level by one (with maximum limit)."""
    self._indent = min(self._indent + 1, self.MAX_INDENT)

del_indent

del_indent() -> None

Decrease the indentation level by one, ensuring it doesn't go below zero.

Source code in src/ezpl/handlers/console.py
def del_indent(self) -> None:
    """Decrease the indentation level by one, ensuring it doesn't go below zero."""
    self._indent = max(0, self._indent - 1)

reset_indent

reset_indent() -> None

Reset the indentation level to zero.

Source code in src/ezpl/handlers/console.py
def reset_indent(self) -> None:
    """Reset the indentation level to zero."""
    self._indent = 0

manage_indent

manage_indent() -> Generator[None, None, None]

Context manager for temporary indentation.

YIELDS DESCRIPTION
None

None

Source code in src/ezpl/handlers/console.py
@contextmanager
def manage_indent(self) -> Generator[None, None, None]:
    """
    Context manager for temporary indentation.

    Yields:
        None
    """
    try:
        self.add_indent()
        yield
    finally:
        self.del_indent()

print_table

print_table(data: list[dict[str, Any]], title: str | None = None) -> None

Display a table using Rich (delegates to RichWizard).

PARAMETER DESCRIPTION
data

List of dictionaries representing table rows

TYPE: list[dict[str, Any]]

title

Optional table title

TYPE: str | None DEFAULT: None

Source code in src/ezpl/handlers/console.py
def print_table(self, data: list[dict[str, Any]], title: str | None = None) -> None:
    """
    Display a table using Rich (delegates to RichWizard).

    Args:
        data: List of dictionaries representing table rows
        title: Optional table title
    """
    self._wizard.table(data, title=title)

print_panel

print_panel(content: str, title: str | None = None, style: str = 'blue') -> None

Display a panel using Rich (delegates to RichWizard).

PARAMETER DESCRIPTION
content

Panel content

TYPE: str

title

Optional panel title

TYPE: str | None DEFAULT: None

style

Panel style (Rich style string, used as border_style)

TYPE: str DEFAULT: 'blue'

Source code in src/ezpl/handlers/console.py
def print_panel(
    self, content: str, title: str | None = None, style: str = "blue"
) -> None:
    """
    Display a panel using Rich (delegates to RichWizard).

    Args:
        content: Panel content
        title: Optional panel title
        style: Panel style (Rich style string, used as border_style)
    """
    self._wizard.panel(content, title=title, border_style=style)

print_progress

print_progress(*args, **kwargs) -> None

Display a progress bar using Rich.

Note: This is a placeholder. For full progress functionality, users should use Rich's Progress context manager directly.

Source code in src/ezpl/handlers/console.py
def print_progress(self, *args, **kwargs) -> None:
    """
    Display a progress bar using Rich.

    Note: This is a placeholder. For full progress functionality,
    users should use Rich's Progress context manager directly.
    """
    try:
        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            *args,
            **kwargs,
        ):
            # Placeholder - users should use Rich.Progress context manager directly
            pass
    except Exception as e:
        try:
            self._console.print(f"[red]Progress error:[/red] {type(e).__name__}")
        except Exception as e:
            raise ValueError(f"Failed to print progress: {e}") from e

print_json

print_json(data: str | dict | list, title: str | None = None, indent: int | None = None, highlight: bool = True) -> None

Display JSON data in a formatted and syntax-highlighted way using Rich (delegates to RichWizard).

PARAMETER DESCRIPTION
data

JSON data to display (dict, list, or JSON string)

TYPE: str | dict | list

title

Optional title for the JSON display

TYPE: str | None DEFAULT: None

indent

Number of spaces for indentation (default: 2)

TYPE: int | None DEFAULT: None

highlight

Whether to enable syntax highlighting (default: True)

TYPE: bool DEFAULT: True

Examples:

>>> printer.print_json({"name": "Alice", "age": 30})
>>> printer.print_json('{"key": "value"}', title="Config")
>>> printer.print_json([1, 2, 3], indent=4)
Source code in src/ezpl/handlers/console.py
def print_json(
    self,
    data: str | dict | list,
    title: str | None = None,
    indent: int | None = None,
    highlight: bool = True,
) -> None:
    """
    Display JSON data in a formatted and syntax-highlighted way using Rich (delegates to RichWizard).

    Args:
        data: JSON data to display (dict, list, or JSON string)
        title: Optional title for the JSON display
        indent: Number of spaces for indentation (default: 2)
        highlight: Whether to enable syntax highlighting (default: True)

    Examples:
        >>> printer.print_json({"name": "Alice", "age": 30})
        >>> printer.print_json('{"key": "value"}', title="Config")
        >>> printer.print_json([1, 2, 3], indent=4)
    """
    self._wizard.json(data, title=title, indent=indent, highlight=highlight)