terminalwriter.py
Overview
The [terminalwriter.py](/projects/286/67488) module provides utility functions and the `TerminalWriter` class to facilitate writing colored and formatted text output to terminals and files. It supports terminal width detection, markup (color and style) application, source code syntax highlighting, and line width accounting, making terminal output more readable and visually structured.
This module is particularly useful for command-line applications or test runners (such as pytest) that want to enhance output with colors and formatted separators, while gracefully handling different terminal capabilities and environments (including Windows).
Detailed Description of Components
Functions
get_terminal_width() -> int
Purpose: Determines the current terminal width (number of columns).
Returns: Integer representing the terminal width in characters.
Behavior:
Uses
shutil.get_terminal_size()with a fallback of 80 columns.On Windows, where terminal size detection may be unreliable, enforces a minimum width of 40 characters (defaults to 80 if smaller).
Example Usage:
width = get_terminal_width() print(f"Terminal width is {width} columns")
should_do_markup(file: TextIO) -> bool
Purpose: Determines whether markup (color/style escape sequences) should be enabled for the given output file or stream.
Parameters:
file: A text stream (e.g.,sys.stdoutor a file handle).
Returns:
Trueif markup should be used, otherwiseFalse.Logic:
Checks environment variables
PY_COLORS,NO_COLOR, andFORCE_COLOR.Verifies if the file is a TTY (interactive terminal).
Disables markup if the terminal type is
dumb.
Example Usage:
if should_do_markup(sys.stdout): print("Colors enabled") else: print("Colors disabled")
Class: TerminalWriter
A final class providing an interface to write colored and formatted output to terminals or files.
Class Attributes
_esctable: A dictionary mapping color/style names to ANSI escape codes for terminal markup. Supports foreground colors (e.g.,red), background colors (capitalized, e.g.,Red), and text styles (bold,light,blink,invert).
Initialization
TerminalWriter(file: TextIO | None = None) -> None
Parameters:
file: Optional. A text stream to write output to. Defaults tosys.stdout.
Behavior:
On Windows, attempts to import and use
coloramato wrap the stream for ANSI color support.Determines if markup (color/style) should be used via
should_do_markup.Initializes internal state to track the current line and terminal width.
Properties
fullwidth: intGetter: Returns the terminal width, either cached or determined via
get_terminal_width().Setter: Allows overriding the terminal width manually.
width_of_current_line: intReturns the character width of the current line being written (accounts for Unicode character widths using
wcswidth).
Methods
markup(text: str, **markup: bool) -> str
Purpose: Wraps the given text with ANSI escape sequences for specified markup styles/colors if markup is enabled.
Parameters:
text: The string to decorate.**markup: Keyword boolean arguments where keys are style/color names (e.g.,red=True,bold=True).
Returns: The text string with ANSI escape codes applied if markup is enabled; otherwise, returns the original text.
Raises:
ValueErrorif an unknown markup name is provided.Example:
writer = TerminalWriter() print(writer.markup("Error!", red=True, bold=True))
sep(sepchar: str, title: str | None = None, fullwidth: int | None = None, **markup: bool) -> None
Purpose: Writes a separator line composed of repeated characters, optionally with a centered title.
Parameters:
sepchar: The character(s) to repeat for the separator line (e.g.,"-"or"= ").title: Optional string to display in the middle of the separator.fullwidth: Optional terminal width override.**markup: Optional markup styles to apply.
Behavior:
Calculates how many times to repeat the separator character(s) to fill the line up to
fullwidth.Centers the
titleif provided, padded by separator characters.Handles Windows-specific quirks with line endings.
Example:
writer.sep("=", "Section 1", red=True)
write(msg: str, *, flush: bool = False, **markup: bool) -> None
Purpose: Writes a string
msgto the output stream, optionally applying markup and flushing the stream.Parameters:
msg: The message string to write.flush: IfTrue, flushes the output stream after writing.**markup: Optional markup styles to apply.
Behavior:
Tracks the current line for width calculations.
Applies markup if enabled.
Handles Unicode encoding errors by escaping characters.
Example:
writer.write("Hello, World!", red=True, bold=True, flush=True)
line(s: str = "", **markup: bool) -> None
Purpose: Writes a line terminated by a newline character.
Parameters:
s: The string to write.**markup: Optional markup styles.
Example:
writer.line("This is a line", blue=True)
flush() -> None
Purpose: Flushes the underlying file/stream buffer to ensure all output is written.
Example:
writer.flush()
Private/Internal Methods
These methods are intended for internal use and provide advanced functionality, especially for syntax highlighting source code.
_write_source(lines: Sequence[str], indents: Sequence[str] = ()) -> None
Purpose: Writes a sequence of source code lines to the terminal, applying syntax highlighting if enabled.
Parameters:
lines: List or tuple of strings representing lines of source code.indents: Optional list of strings to prepend indentation for each line.
Raises:
ValueErrorif the length ofindentsdoes not match the number of lines.Usage: Internal method; could be extended for more granular colorization needs.
_get_pygments_lexer(lexer: Literal["python", "diff"]) -> Lexer
Purpose: Returns the appropriate Pygments lexer instance for the given language type.
Parameters:
lexer: Either"python"or"diff".
Returns: A
pygments.lexer.Lexerinstance.Raises: Assertion error if an unsupported lexer name is provided.
_get_pygments_formatter() -> TerminalFormatter
Purpose: Returns a Pygments terminal formatter configured with the theme and mode specified by environment variables
PYTEST_THEMEandPYTEST_THEME_MODE.Raises:
UsageErrorif invalid environment variable values are detected.Notes: This method tightly couples with pytest configuration.
_highlight(source: str, lexer: Literal["diff", "python"] = "python") -> str
Purpose: Applies syntax highlighting to the source code string if markup and highlighting are enabled.
Parameters:
source: Source code text to highlight.lexer: Language type for syntax highlighting ("python"or"diff").
Returns: Highlighted source code string with ANSI color escape sequences.
Behavior:
Uses Pygments lexers and terminal formatter.
Adds ANSI reset code at the start to avoid color bleeding.
Removes trailing newline added by Pygments if the original source didn't have one.
Implementation Details & Algorithms
Terminal Width Detection: Uses
shutil.get_terminal_size()with fallbacks and Windows-specific checks to ensure a sane width.Markup Application: Uses ANSI escape codes to apply colors and styles dynamically, controlled by environment variables and TTY checks.
Line Width Calculation: Uses
wcswidthfrom a local module to accurately calculate display width of Unicode strings, important for aligning output in terminals.Syntax Highlighting: Integrates with Pygments to provide colored source code and diff output. Uses environment variables to customize themes.
Windows Compatibility: Uses
coloramaif available to translate ANSI sequences to Windows console API calls, enabling colors on Windows terminals.
Interaction with Other Parts of the Application
..compat.assert_never: Used in_get_pygments_lexerto ensure exhaustive checking of lexer types.wcwidth.wcswidth: Used to calculate the width of Unicode strings for proper alignment.pygments: Heavily used for syntax highlighting support._pytest.config.exceptions.UsageError: Raised when environment-based configuration of the terminal formatter is invalid. This ties the module to pytest's configuration ecosystem.Environment Variables: Controls behavior of markup and theming:
PY_COLORS,NO_COLOR,FORCE_COLORaffect color usage.PYTEST_THEME,PYTEST_THEME_MODEaffect syntax highlighting style.
This module is designed to be used within pytest or similar CLI tools that want enhanced terminal output, especially for displaying source code, diffs, and separators with colors and styles.
Usage Example
from terminalwriter import TerminalWriter
writer = TerminalWriter()
writer.line("Starting test suite", bold=True, green=True)
writer.sep("=", "Test Results", blue=True)
writer.write("Test 1 ... ", flush=True)
writer.line("PASSED", green=True)
writer.line("Test 2 ... ", flush=True)
writer.line("FAILED", red=True, bold=True)
Mermaid Diagram: Class Diagram for TerminalWriter
classDiagram
class TerminalWriter {
-_esctable: dict
-_file: TextIO
-hasmarkup: bool
-_current_line: str
-_terminal_width: int | None
-code_highlight: bool
+__init__(file: TextIO | None)
+fullwidth: int
+fullwidth(value: int)
+width_of_current_line: int
+markup(text: str, **markup: bool) str
+sep(sepchar: str, title: str | None, fullwidth: int | None, **markup: bool) None
+write(msg: str, flush: bool = False, **markup: bool) None
+line(s: str = "", **markup: bool) None
+flush() None
-_write_source(lines: Sequence[str], indents: Sequence[str]) None
-_get_pygments_lexer(lexer: Literal["python", "diff"]) Lexer
-_get_pygments_formatter() TerminalFormatter
-_highlight(source: str, lexer: Literal["diff", "python"]) str
}
Summary
The [terminalwriter.py](/projects/286/67488) module is a robust helper for terminal output formatting, supporting color markup, syntax highlighting, and terminal-aware formatting. It is well suited for test runners and CLI tools needing enhanced visual output. Its design carefully addresses cross-platform issues and integrates with environment-driven configuration for flexible theming.