Usage Guide

This guide provides detailed examples and explains the core concepts of ascii_colors.

Core Concepts: Direct Print vs. Logging

ascii_colors offers two fundamentally different ways to generate styled terminal output:

Important

Understanding this distinction is key to using the library effectively.

  1. Direct Print Methods (ASCIIColors.red, print, bold, bg_red, etc.) * What they do: Print strings directly to a stream (default: sys.stdout) using Python’s built-in print function. * How they work: Apply ANSI escape codes for colors and styles immediately before printing. * Logging System: They completely bypass the logging system. Levels, Handlers, Formatters, and Context are ignored. * Use Case: Best for simple, immediate visual feedback, status messages, user prompts, banners, or decorative output where structured logging isn’t needed.

  2. Logging System (basicConfig, getLogger, logger.info, Handlers, Formatters) * What it does: Provides a structured, leveled logging framework, similar to Python’s standard logging. * How it works: Log messages are created as LogRecord-like objects. They are filtered by level, processed by Formatters to create strings, and then sent by Handlers to destinations (console, files, etc.). * Styling: Console output (via ConsoleHandler/StreamHandler) is typically colored based on the log level (e.g., Warnings Yellow, Errors Red) by default. Formatting is highly customizable. * Interaction: Use the recommended logging-compatible API (import ascii_colors as logging) or the ASCIIColors class methods (ASCIIColors.info, add_handler). Both control the same underlying global logging state. * Use Case: Ideal for application logs, debugging information, tracing events, and any scenario requiring structured, filterable, and configurable output routing.

Using the Logging Compatibility Layer (import ascii_colors as logging)

The most convenient and recommended way to utilize the logging features is through the compatibility layer. It allows you to use familiar functions and patterns from Python’s standard logging module.

using_compat_layer.py
 1import ascii_colors as logging # The crucial import alias
 2
 3# Configure using basicConfig (similar to standard logging)
 4logging.basicConfig(
 5    level=logging.INFO, # Set the minimum level to log
 6    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 7    datefmt='%H:%M:%S'
 8    # Default handler is StreamHandler to stderr with level-based coloring
 9)
10
11# Get logger instances
12logger = logging.getLogger('MyModule')
13another_logger = logging.getLogger('Another.Component')
14
15# Log messages using standard methods
16logger.debug("This won't be shown (global level is INFO)")
17logger.info("Module started.")
18another_logger.warning("Something looks suspicious.")
19another_logger.error("Failed to process request.")
20
21# Benefit: Console output is automatically colored by level!

This approach provides a smooth transition for projects already using standard logging or a familiar interface for new projects.

Available Colors and Styles (for Direct Printing)

These constants are attributes of the ASCIIColors class. Use them with direct print methods like print(), bold(), bg_red(), multicolor(), etc.

Note

Actual rendering depends on the capabilities of your terminal emulator. Most modern terminals support these styles and basic/bright colors. 256-color support (used for orange) is also common but not universal.

Reset Code

  • ASCIIColors.color_reset: Resets all styles and colors to the terminal default. Automatically appended by most direct print methods.

Text Styles

  • ASCIIColors.style_bold

  • ASCIIColors.style_dim

  • ASCIIColors.style_italic

  • ASCIIColors.style_underline

  • ASCIIColors.style_blink (support varies)

  • ASCIIColors.style_reverse (swaps foreground/background)

  • ASCIIColors.style_hidden (conceals text; support varies)

  • ASCIIColors.style_strikethrough

Foreground Colors (Regular)

  • ASCIIColors.color_black

  • ASCIIColors.color_red

  • ASCIIColors.color_green

  • ASCIIColors.color_yellow

  • ASCIIColors.color_blue

  • ASCIIColors.color_magenta

  • ASCIIColors.color_cyan

  • ASCIIColors.color_white

  • ASCIIColors.color_orange (256-color approximation)

Foreground Colors (Bright)

  • ASCIIColors.color_bright_black (often gray)

  • ASCIIColors.color_bright_red

  • ASCIIColors.color_bright_green

  • ASCIIColors.color_bright_yellow

  • ASCIIColors.color_bright_blue

  • ASCIIColors.color_bright_magenta

  • ASCIIColors.color_bright_cyan

  • ASCIIColors.color_bright_white

Background Colors (Regular)

  • ASCIIColors.bg_black

  • ASCIIColors.bg_red

  • ASCIIColors.bg_green

  • ASCIIColors.bg_yellow

  • ASCIIColors.bg_blue

  • ASCIIColors.bg_magenta

  • ASCIIColors.bg_cyan

  • ASCIIColors.bg_white

  • ASCIIColors.bg_orange (256-color approximation)

Background Colors (Bright)

  • ASCIIColors.bg_bright_black

  • ASCIIColors.bg_bright_red

  • ASCIIColors.bg_bright_green

  • ASCIIColors.bg_bright_yellow

  • ASCIIColors.bg_bright_blue

  • ASCIIColors.bg_bright_magenta

  • ASCIIColors.bg_bright_cyan

  • ASCIIColors.bg_bright_white

Direct Printing Examples

direct_print_examples.py
 1from ascii_colors import ASCIIColors
 2
 3# --- Simple Colors ---
 4ASCIIColors.red("Error: File not found.")
 5ASCIIColors.green("Success: Configuration saved.")
 6ASCIIColors.blue("Info: Processing request...")
 7
 8# --- Styles ---
 9ASCIIColors.bold("Important Announcement")
10ASCIIColors.underline("Section Header", color=ASCIIColors.color_yellow)
11ASCIIColors.italic("Note: This feature is experimental.", color=ASCIIColors.color_magenta)
12ASCIIColors.dim("Less important details.")
13ASCIIColors.strikethrough("Deprecated option.")
14
15# --- Backgrounds ---
16ASCIIColors.bg_yellow("WARNING", color=ASCIIColors.color_black) # Black text on yellow BG
17ASCIIColors.print_with_bg(
18    " Critical Failure! ",
19    color=ASCIIColors.color_bright_white,
20    background=ASCIIColors.bg_bright_red
21)
22
23# --- Combining ---
24ASCIIColors.print(
25    " Status: OK ",
26    color=ASCIIColors.color_black, # Text color
27    style=ASCIIColors.style_bold + ASCIIColors.style_reverse, # Bold and Reverse video
28    background=ASCIIColors.bg_bright_green # Base background (reversed)
29)
30
31# --- Multicolor ---
32ASCIIColors.multicolor(
33    ["Task: ", "Upload", " | Progress: ", "100%", " | Status: ", "DONE"],
34    [
35        ASCIIColors.color_white, ASCIIColors.color_cyan,           # Task
36        ASCIIColors.color_white, ASCIIColors.color_bright_yellow, # Progress
37        ASCIIColors.color_white, ASCIIColors.color_bright_green   # Status
38    ]
39)
40
41# --- Highlight ---
42log_line = "INFO: User 'admin' logged in from 127.0.0.1"
43ASCIIColors.highlight(
44    text=log_line,
45    subtext=["INFO", "admin", "127.0.0.1"], # Words/phrases to highlight
46    color=ASCIIColors.color_white,           # Default text color
47    highlight_color=ASCIIColors.bg_blue + ASCIIColors.color_bright_white # Style for highlights
48)

Logging System Examples

Setup with `basicConfig`

The simplest way to configure logging. Ideal for scripts or basic applications.

logging_setup_basic.py
 1import ascii_colors as logging
 2import sys
 3
 4logging.basicConfig(
 5    level=logging.DEBUG, # Log everything from DEBUG upwards
 6    stream=sys.stdout,   # Send logs to standard output
 7    format='%(levelname)s:%(name)s:%(message)s' # Simple format
 8)
 9
10logger = logging.getLogger("BasicApp")
11logger.debug("Starting process...")
12logger.info("User interaction needed.")

Manual Setup for Advanced Control

Provides full control over handlers, formatters, and levels.

logging_setup_manual.py
 1import ascii_colors as logging
 2from ascii_colors import handlers # Access RotatingFileHandler
 3import sys
 4from pathlib import Path
 5
 6# --- Get the root logger to configure ---
 7root_logger = logging.getLogger()
 8
 9# --- Clear existing handlers (important if re-configuring) ---
10root_logger.handlers.clear()
11# Alternatively, use: logging.basicConfig(force=True, ...) to reset
12
13# --- Set overall level for the logger ---
14root_logger.setLevel(logging.DEBUG) # Allow all messages to pass to handlers
15
16# --- Configure Console Handler ---
17console_formatter = logging.Formatter(
18    # Using {}-style formatting
19    fmt='[{asctime}] <{name}> {levelname:^8s}: {message}',
20    style='{',
21    datefmt='%H:%M:%S'
22)
23console_handler = logging.StreamHandler(stream=sys.stdout)
24console_handler.setLevel(logging.INFO) # Console shows INFO and above
25console_handler.setFormatter(console_formatter)
26root_logger.addHandler(console_handler)
27
28# --- Configure File Handler ---
29log_file = Path("app_detailed.log")
30file_formatter = logging.Formatter(
31    # Using %-style formatting with source info
32    fmt='%(asctime)s|%(levelname)-8s|%(name)s:%(lineno)d|%(message)s',
33    style='%',
34    datefmt='%Y-%m-%d %H:%M:%S',
35    include_source=True # Capture file/line number (adds overhead)
36)
37file_handler = logging.FileHandler(log_file, mode='w', encoding='utf-8')
38file_handler.setLevel(logging.DEBUG) # File logs everything (DEBUG and above)
39file_handler.setFormatter(file_formatter)
40root_logger.addHandler(file_handler)
41
42# --- Configure Rotating JSON File Handler ---
43json_log_file = Path("audit.jsonl")
44json_formatter = logging.JSONFormatter(
45    # Define the structure of the JSON output
46    fmt={
47        "ts": "asctime",
48        "level": "levelname",
49        "logger": "name",
50        "msg": "message",
51        "req_id": "request_id", # Include custom context
52        "user": "user_name",   # Include custom context
53        "file": "filename",
54        "line": "lineno",
55    },
56    datefmt="iso", # ISO8601 timestamp format
57    style='%', # Style applies to how fmt values are looked up
58    json_ensure_ascii=False
59)
60# Use the handlers namespace
61rotating_json_handler = handlers.RotatingFileHandler(
62    json_log_file,
63    maxBytes=5 * 1024 * 1024, # 5 MB
64    backupCount=5,
65    encoding='utf-8'
66)
67rotating_json_handler.setLevel(logging.WARNING) # Log WARNING and above as JSON
68rotating_json_handler.setFormatter(json_formatter)
69root_logger.addHandler(rotating_json_handler)
70
71
72# --- Now, log messages using any logger ---
73main_logger = logging.getLogger("MainApp")
74util_logger = logging.getLogger("MainApp.Utils")
75
76main_logger.debug("Low-level detail.") # File only
77util_logger.info("Utility function called.") # Console and File
78main_logger.warning(
79    "Possible issue detected.",
80    extra={"request_id": "xyz789", "user_name": "guest"} # Add context
81) # Console, File, and JSON
82main_logger.error(
83    "Operation failed!",
84    extra={"request_id": "xyz789", "user_name": "admin"}
85) # Console, File, and JSON

Logging Messages

Use standard logger methods.

logging_messages.py
 1import ascii_colors as logging
 2
 3# Assume logging is configured (e.g., via basicConfig or manual setup)
 4logger = logging.getLogger("DataProcessor")
 5
 6# Standard levels
 7logger.debug("Starting data validation for batch %d.", 101)
 8logger.info("Processing %d records.", 5000)
 9logger.warning("Record %d has missing field 'email'. Skipping.", 1234)
10logger.error("Failed to connect to database '%s'.", "prod_db")
11logger.critical("Data corruption detected! Halting process.")
12
13# Logging exceptions
14try:
15    data = {}
16    value = data['required_key']
17except KeyError as e:
18    # Option 1: Log error with traceback automatically
19    logger.exception("Missing required data key!")
20    # Option 2: Log error manually including traceback
21    # logger.error("Missing required data key!", exc_info=True)
22    # Option 3: Use the utility function (logs at ERROR level)
23    # from ascii_colors import trace_exception
24    # trace_exception(e)

Context Management

Add thread-local context to enrich log records automatically. Formatters must be configured to include the context keys.

logging_context.py
 1import ascii_colors as logging
 2from ascii_colors import ASCIIColors # Needed for context manager
 3import threading
 4import time
 5import sys
 6import random
 7
 8# Setup a formatter that includes 'request_id' and 'user'
 9log_format = "[{asctime}] Req:{request_id}|User:{user} ({name}) {levelname}: {message}"
10formatter = logging.Formatter(log_format, style='{', datefmt='%H:%M:%S')
11handler = logging.StreamHandler(stream=sys.stdout)
12handler.setFormatter(formatter)
13logging.basicConfig(level=logging.INFO, handlers=[handler], force=True) # Reset config
14
15logger = logging.getLogger("WebServer")
16
17def handle_web_request(req_id, user):
18    # Use the context manager: sets context vars for this thread's scope
19    with ASCIIColors.context(request_id=req_id, user=user):
20        logger.info("Request received.")
21        # Simulate work
22        time.sleep(random.uniform(0.1, 0.3))
23        if user == "guest":
24            logger.warning("Guest access has limited permissions.")
25        logger.info("Request processed successfully.")
26        # Context (request_id, user) is automatically cleared upon exiting 'with'
27
28# Simulate multiple concurrent requests
29threads = []
30for i in range(3):
31    user = random.choice(['alice', 'bob', 'guest'])
32    req_id = f"req-{i+1:03d}"
33    thread = threading.Thread(target=handle_web_request, args=(req_id, user))
34    threads.append(thread)
35    thread.start()
36
37for t in threads:
38    t.join()
39
40# Outside the threads/context, the keys are not set
41# logger.info("Finished processing all requests.") # Would cause KeyError if context keys missing

Utilities

Animation Spinner (`execute_with_animation`)

Displays a spinner while a function executes. Uses direct printing for the spinner itself.

utility_animation.py
 1import time
 2from ascii_colors import ASCIIColors
 3import ascii_colors as logging # For logging within the task
 4
 5# Configure logging if the task needs it
 6logging.basicConfig(level=logging.INFO, format='%(message)s')
 7
 8def simulate_database_query(query_id):
 9    logging.info(f"[Task {query_id}] Starting query...")
10    duration = random.uniform(1, 3)
11    time.sleep(duration)
12    if random.random() < 0.2: # Simulate occasional failures
13        raise ConnectionError(f"DB connection lost during query {query_id}")
14    logging.info(f"[Task {query_id}] Query finished.")
15    return f"Query {query_id} results (found {random.randint(10,100)} rows)"
16
17# --- Execute with animation ---
18query_to_run = "Q101"
19try:
20    # result = ASCIIColors.execute_with_animation(
21    #     pending_text=f"Running database query {query_to_run}...",
22    #     func=simulate_database_query,
23    #     # *args for func:
24    #     query_id=query_to_run,
25    #     # Optional color for the pending text:
26    #     color=ASCIIColors.color_cyan
27    # )
28    # Use the direct print methods for the overall status of the animated task
29    # ASCIIColors.success(f"Animation completed: {result}")
30    # Dummy call for example build without randomness
31    ASCIIColors.success("Animation completed: Query Q101 results (...)")
32
33except Exception as e:
34    ASCIIColors.fail(f"Animation failed: {e}")
35    # Optionally log the failure using the logging system
36    # logging.exception(f"Database query {query_to_run} failed")

Trace Exception (`trace_exception`)

A convenience function to log an exception and its traceback using the configured logging system at the ERROR level.

utility_trace_exception.py
 1import ascii_colors as logging
 2from ascii_colors import trace_exception
 3
 4# Assumes logging is configured
 5logging.basicConfig(level=logging.INFO)
 6
 7try:
 8    value = int("not_a_number")
 9except ValueError as e:
10    # Instead of logger.error("...", exc_info=True) or logger.exception(...)
11    trace_exception(e) # Logs the error message and full traceback