Applying ANSI Escape Sequences on Unix-like Systems
Terminal text styling on Linux and macOS relies on ANSI escape sequences. These sequences begin with an ESC character (represented as \033 or \x1b) followed by display parameters and ending with m. The format is \033[mode;foreground;backgroundm.
| Foreground | Background | Color | Display Mode | Effect |
|---|---|---|---|---|
| 30 | 40 | Black | 0 | Default |
| 31 | 41 | Red | 1 | Bold / Bright |
| 32 | 42 | Green | 22 | Normal intensity |
| 33 | 43 | Yellow | 4 | Underline |
| 34 | 44 | Blue | 24 | Not underlined |
| 35 | 45 | Magenta | 5 | Blink |
| 36 | 46 | Cyan | 25 | Steady |
| 37 | 47 | White | 7 | Invert |
| 27 | Not inverted | |||
| 8 | Hidden | |||
| 28 | Visible |
Foreground and background parameters are optional. By wrapping strings with these sequences, you can apply rich formatting.
class AnsiFormatter:
PALETTE = {
'fg': {'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37},
'bg': {'black': 40, 'red': 41, 'green': 42, 'yellow': 43, 'blue': 44, 'magenta': 45, 'cyan': 46, 'white': 47},
'style': {'default': 0, 'bold': 1, 'dim': 2, 'underline': 4, 'blink': 5, 'invert': 7, 'hidden': 8}
}
@staticmethod
def paint(text, style='', fg='', bg=''):
parts = []
if style in AnsiFormatter.PALETTE['style']:
parts.append(str(AnsiFormatter.PALETTE['style'][style]))
if fg in AnsiFormatter.PALETTE['fg']:
parts.append(str(AnsiFormatter.PALETTE['fg'][fg]))
if bg in AnsiFormatter.PALETTE['bg']:
parts.append(str(AnsiFormatter.PALETTE['bg'][bg]))
if not parts:
return text
prefix = f"\033[{';'.join(parts)}m"
suffix = "\033[0m"
return f"{prefix}{text}{suffix}"
if __name__ == '__main__':
print(AnsiFormatter.paint("Alert!", style='bold', fg='red'))
print(AnsiFormatter.paint("Success", fg='green', bg='black'))
Windows Console API Integration
Legacy Windows command prompts do not natively parse ANSI codes. Instead, the ctypes module can invoke the Windows API to modify console attributes. The SetConsoleTextAttribute function accepts a 16-bit integer where the lower 4 bits represent the foreground and the higher 4 bits represent the background.
import ctypes
import sys
class WinConsolePainter:
STD_OUTPUT = -11
FG = {
'black': 0x00, 'blue': 0x01, 'green': 0x02, 'cyan': 0x03,
'red': 0x04, 'magenta': 0x05, 'yellow': 0x06, 'white': 0x07,
'intense_white': 0x0f
}
BG = {
'black': 0x00, 'blue': 0x10, 'green': 0x20, 'cyan': 0x30,
'red': 0x40, 'magenta': 0x50, 'yellow': 0x60, 'white': 0x70,
'intense_white': 0xf0
}
def __init__(self):
self.handle = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT)
def _set_color(self, color_val):
ctypes.windll.kernel32.SetConsoleTextAttribute(self.handle, color_val)
def _reset(self):
self._set_color(self.FG['white'])
def write(self, text, fg='white', bg='black'):
color_val = self.FG.get(fg, 0x07) | self.BG.get(bg, 0x00)
self._set_color(color_val)
sys.stdout.write(text + '\n')
self._reset()
if __name__ == '__main__':
painter = WinConsolePainter()
painter.write("Critical Error", fg='red', bg='white')
painter.write("Information", fg='cyan')
Cross-Platform Solution Using Colorama
The colorama library provides a straightforward, cross-platform approach. It wraps standard output, translating ANSI codes into appropriate Windows API calls when necessary, while passing them through unchanged on Unix systems.
pip install colorama
from colorama import init, Fore, Back, Style
init(autoreset=True)
print(Fore.RED + "Error detected")
print(Back.CYAN + "Highlighting important context")
print(Style.BRIGHT + "High visibility text")
print("Standard output resumed")
Unified Cross-Platform Wrapper
If avoiding third-party dependencies is required, a custom wrapper can detect the operating system and route the formatting logic to either the ANSI formatter or the Windows API.
import platform
import sys
import ctypes
class ConsolePainter:
def __init__(self):
self.os_name = platform.system()
if self.os_name == 'Windows':
self._handle = ctypes.windll.kernel32.GetStdHandle(-11)
self._win_fg = {'red': 0x04, 'green': 0x02, 'blue': 0x01, 'yellow': 0x06}
self._win_bg = {'red': 0x40, 'green': 0x20, 'blue': 0x10, 'yellow': 0x60}
else:
self._ansi_fg = {'red': 31, 'green': 32, 'blue': 34, 'yellow': 33}
def _win_set_attr(self, color_code):
ctypes.windll.kernel32.SetConsoleTextAttribute(self._handle, color_code)
def print_text(self, message, fg='white', bg='black'):
if self.os_name == 'Windows':
fg_code = self._win_fg.get(fg, 0x07)
bg_code = self._win_bg.get(bg, 0x00)
self._win_set_attr(fg_code | bg_code)
sys.stdout.write(message + '\n')
self._win_set_attr(0x07) # Reset
else:
fg_code = self._ansi_fg.get(fg, 37)
print(f"\033[{fg_code}m{message}\033[0m")
if __name__ == '__main__':
ui = ConsolePainter()
ui.print_text("Operation Successful", fg='green')
ui.print_text("Warning Issued", fg='yellow')