efro.terminal
Functionality related to terminal IO.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Functionality related to terminal IO.""" 4from __future__ import annotations 5 6import sys 7import os 8from enum import Enum, unique 9from typing import TYPE_CHECKING 10 11if TYPE_CHECKING: 12 from typing import Any, ClassVar 13 14 15@unique 16class TerminalColor(Enum): 17 """Color codes for printing to terminals. 18 19 Generally the Clr class should be used when incorporating color into 20 terminal output, as it handles non-color-supporting terminals/etc. 21 """ 22 23 # Styles 24 RESET = '\033[0m' 25 BOLD = '\033[1m' 26 UNDERLINE = '\033[4m' 27 INVERSE = '\033[7m' 28 29 # Normal foreground colors 30 BLACK = '\033[30m' 31 RED = '\033[31m' 32 GREEN = '\033[32m' 33 YELLOW = '\033[33m' 34 BLUE = '\033[34m' 35 MAGENTA = '\033[35m' 36 CYAN = '\033[36m' 37 WHITE = '\033[37m' 38 39 # Normal background colors. 40 BG_BLACK = '\033[40m' 41 BG_RED = '\033[41m' 42 BG_GREEN = '\033[42m' 43 BG_YELLOW = '\033[43m' 44 BG_BLUE = '\033[44m' 45 BG_MAGENTA = '\033[45m' 46 BG_CYAN = '\033[46m' 47 BG_WHITE = '\033[47m' 48 49 # Strong foreground colors 50 STRONG_BLACK = '\033[90m' 51 STRONG_RED = '\033[91m' 52 STRONG_GREEN = '\033[92m' 53 STRONG_YELLOW = '\033[93m' 54 STRONG_BLUE = '\033[94m' 55 STRONG_MAGENTA = '\033[95m' 56 STRONG_CYAN = '\033[96m' 57 STRONG_WHITE = '\033[97m' 58 59 # Strong background colors. 60 STRONG_BG_BLACK = '\033[100m' 61 STRONG_BG_RED = '\033[101m' 62 STRONG_BG_GREEN = '\033[102m' 63 STRONG_BG_YELLOW = '\033[103m' 64 STRONG_BG_BLUE = '\033[104m' 65 STRONG_BG_MAGENTA = '\033[105m' 66 STRONG_BG_CYAN = '\033[106m' 67 STRONG_BG_WHITE = '\033[107m' 68 69 70def _default_color_enabled() -> bool: 71 """Return whether we enable ANSI color codes by default.""" 72 import platform 73 74 # If we're not attached to a terminal, go with no-color. 75 if not sys.__stdout__.isatty(): 76 return False 77 78 # Another common way to say the terminal can't do fancy stuff like color: 79 if os.environ.get('TERM') == 'dumb': 80 return False 81 82 # On windows, try to enable ANSI color mode. 83 if platform.system() == 'Windows': 84 return _windows_enable_color() 85 86 # We seem to be a terminal with color support; let's do it! 87 return True 88 89 90# noinspection PyPep8Naming 91def _windows_enable_color() -> bool: 92 """Attempt to enable ANSI color on windows terminal; return success.""" 93 # pylint: disable=invalid-name, import-error, undefined-variable 94 # Pulled from: https://bugs.python.org/issue30075 95 import msvcrt 96 import ctypes 97 from ctypes import wintypes 98 99 kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # type: ignore 100 101 ERROR_INVALID_PARAMETER = 0x0057 102 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 103 104 def _check_bool(result: Any, _func: Any, args: Any) -> Any: 105 if not result: 106 raise ctypes.WinError(ctypes.get_last_error()) # type: ignore 107 return args 108 109 LPDWORD = ctypes.POINTER(wintypes.DWORD) 110 kernel32.GetConsoleMode.errcheck = _check_bool 111 kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD) 112 kernel32.SetConsoleMode.errcheck = _check_bool 113 kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD) 114 115 def set_conout_mode(new_mode: int, mask: int = 0xFFFFFFFF) -> int: 116 # don't assume StandardOutput is a console. 117 # open CONOUT$ instead 118 fdout = os.open('CONOUT$', os.O_RDWR) 119 try: 120 hout = msvcrt.get_osfhandle(fdout) # type: ignore 121 # pylint: disable=useless-suppression 122 # pylint: disable=no-value-for-parameter 123 old_mode = wintypes.DWORD() 124 # pylint: enable=useless-suppression 125 kernel32.GetConsoleMode(hout, ctypes.byref(old_mode)) 126 mode = (new_mode & mask) | (old_mode.value & ~mask) 127 kernel32.SetConsoleMode(hout, mode) 128 return old_mode.value 129 finally: 130 os.close(fdout) 131 132 def enable_vt_mode() -> int: 133 mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING 134 try: 135 return set_conout_mode(mode, mask) 136 except WindowsError as exc: # type: ignore 137 if exc.winerror == ERROR_INVALID_PARAMETER: 138 raise NotImplementedError from exc 139 raise 140 141 try: 142 enable_vt_mode() 143 return True 144 except NotImplementedError: 145 return False 146 147 148class ClrBase: 149 """Base class for color convenience class.""" 150 151 RST: ClassVar[str] 152 BLD: ClassVar[str] 153 UND: ClassVar[str] 154 INV: ClassVar[str] 155 156 # Normal foreground colors 157 BLK: ClassVar[str] 158 RED: ClassVar[str] 159 GRN: ClassVar[str] 160 YLW: ClassVar[str] 161 BLU: ClassVar[str] 162 MAG: ClassVar[str] 163 CYN: ClassVar[str] 164 WHT: ClassVar[str] 165 166 # Normal background colors. 167 BBLK: ClassVar[str] 168 BRED: ClassVar[str] 169 BGRN: ClassVar[str] 170 BYLW: ClassVar[str] 171 BBLU: ClassVar[str] 172 BMAG: ClassVar[str] 173 BCYN: ClassVar[str] 174 BWHT: ClassVar[str] 175 176 # Strong foreground colors 177 SBLK: ClassVar[str] 178 SRED: ClassVar[str] 179 SGRN: ClassVar[str] 180 SYLW: ClassVar[str] 181 SBLU: ClassVar[str] 182 SMAG: ClassVar[str] 183 SCYN: ClassVar[str] 184 SWHT: ClassVar[str] 185 186 # Strong background colors. 187 SBBLK: ClassVar[str] 188 SBRED: ClassVar[str] 189 SBGRN: ClassVar[str] 190 SBYLW: ClassVar[str] 191 SBBLU: ClassVar[str] 192 SBMAG: ClassVar[str] 193 SBCYN: ClassVar[str] 194 SBWHT: ClassVar[str] 195 196 197class ClrAlways(ClrBase): 198 """Convenience class for color terminal output. 199 200 This version has colors always enabled. Generally you should use Clr which 201 points to the correct enabled/disabled class depending on the environment. 202 """ 203 204 color_enabled = True 205 206 # Styles 207 RST = TerminalColor.RESET.value 208 BLD = TerminalColor.BOLD.value 209 UND = TerminalColor.UNDERLINE.value 210 INV = TerminalColor.INVERSE.value 211 212 # Normal foreground colors 213 BLK = TerminalColor.BLACK.value 214 RED = TerminalColor.RED.value 215 GRN = TerminalColor.GREEN.value 216 YLW = TerminalColor.YELLOW.value 217 BLU = TerminalColor.BLUE.value 218 MAG = TerminalColor.MAGENTA.value 219 CYN = TerminalColor.CYAN.value 220 WHT = TerminalColor.WHITE.value 221 222 # Normal background colors. 223 BBLK = TerminalColor.BG_BLACK.value 224 BRED = TerminalColor.BG_RED.value 225 BGRN = TerminalColor.BG_GREEN.value 226 BYLW = TerminalColor.BG_YELLOW.value 227 BBLU = TerminalColor.BG_BLUE.value 228 BMAG = TerminalColor.BG_MAGENTA.value 229 BCYN = TerminalColor.BG_CYAN.value 230 BWHT = TerminalColor.BG_WHITE.value 231 232 # Strong foreground colors 233 SBLK = TerminalColor.STRONG_BLACK.value 234 SRED = TerminalColor.STRONG_RED.value 235 SGRN = TerminalColor.STRONG_GREEN.value 236 SYLW = TerminalColor.STRONG_YELLOW.value 237 SBLU = TerminalColor.STRONG_BLUE.value 238 SMAG = TerminalColor.STRONG_MAGENTA.value 239 SCYN = TerminalColor.STRONG_CYAN.value 240 SWHT = TerminalColor.STRONG_WHITE.value 241 242 # Strong background colors. 243 SBBLK = TerminalColor.STRONG_BG_BLACK.value 244 SBRED = TerminalColor.STRONG_BG_RED.value 245 SBGRN = TerminalColor.STRONG_BG_GREEN.value 246 SBYLW = TerminalColor.STRONG_BG_YELLOW.value 247 SBBLU = TerminalColor.STRONG_BG_BLUE.value 248 SBMAG = TerminalColor.STRONG_BG_MAGENTA.value 249 SBCYN = TerminalColor.STRONG_BG_CYAN.value 250 SBWHT = TerminalColor.STRONG_BG_WHITE.value 251 252 253class ClrNever(ClrBase): 254 """Convenience class for color terminal output. 255 256 This version has colors disabled. Generally you should use Clr which 257 points to the correct enabled/disabled class depending on the environment. 258 """ 259 260 color_enabled = False 261 262 # Styles 263 RST = '' 264 BLD = '' 265 UND = '' 266 INV = '' 267 268 # Normal foreground colors 269 BLK = '' 270 RED = '' 271 GRN = '' 272 YLW = '' 273 BLU = '' 274 MAG = '' 275 CYN = '' 276 WHT = '' 277 278 # Normal background colors. 279 BBLK = '' 280 BRED = '' 281 BGRN = '' 282 BYLW = '' 283 BBLU = '' 284 BMAG = '' 285 BCYN = '' 286 BWHT = '' 287 288 # Strong foreground colors 289 SBLK = '' 290 SRED = '' 291 SGRN = '' 292 SYLW = '' 293 SBLU = '' 294 SMAG = '' 295 SCYN = '' 296 SWHT = '' 297 298 # Strong background colors. 299 SBBLK = '' 300 SBRED = '' 301 SBGRN = '' 302 SBYLW = '' 303 SBBLU = '' 304 SBMAG = '' 305 SBCYN = '' 306 SBWHT = '' 307 308 309_envval = os.environ.get('EFRO_TERMCOLORS') 310color_enabled: bool = ( 311 True 312 if _envval == '1' 313 else False 314 if _envval == '0' 315 else _default_color_enabled() 316) 317Clr: type[ClrBase] = ClrAlways if color_enabled else ClrNever
@unique
class
TerminalColor16@unique 17class TerminalColor(Enum): 18 """Color codes for printing to terminals. 19 20 Generally the Clr class should be used when incorporating color into 21 terminal output, as it handles non-color-supporting terminals/etc. 22 """ 23 24 # Styles 25 RESET = '\033[0m' 26 BOLD = '\033[1m' 27 UNDERLINE = '\033[4m' 28 INVERSE = '\033[7m' 29 30 # Normal foreground colors 31 BLACK = '\033[30m' 32 RED = '\033[31m' 33 GREEN = '\033[32m' 34 YELLOW = '\033[33m' 35 BLUE = '\033[34m' 36 MAGENTA = '\033[35m' 37 CYAN = '\033[36m' 38 WHITE = '\033[37m' 39 40 # Normal background colors. 41 BG_BLACK = '\033[40m' 42 BG_RED = '\033[41m' 43 BG_GREEN = '\033[42m' 44 BG_YELLOW = '\033[43m' 45 BG_BLUE = '\033[44m' 46 BG_MAGENTA = '\033[45m' 47 BG_CYAN = '\033[46m' 48 BG_WHITE = '\033[47m' 49 50 # Strong foreground colors 51 STRONG_BLACK = '\033[90m' 52 STRONG_RED = '\033[91m' 53 STRONG_GREEN = '\033[92m' 54 STRONG_YELLOW = '\033[93m' 55 STRONG_BLUE = '\033[94m' 56 STRONG_MAGENTA = '\033[95m' 57 STRONG_CYAN = '\033[96m' 58 STRONG_WHITE = '\033[97m' 59 60 # Strong background colors. 61 STRONG_BG_BLACK = '\033[100m' 62 STRONG_BG_RED = '\033[101m' 63 STRONG_BG_GREEN = '\033[102m' 64 STRONG_BG_YELLOW = '\033[103m' 65 STRONG_BG_BLUE = '\033[104m' 66 STRONG_BG_MAGENTA = '\033[105m' 67 STRONG_BG_CYAN = '\033[106m' 68 STRONG_BG_WHITE = '\033[107m'
Color codes for printing to terminals.
Generally the Clr class should be used when incorporating color into terminal output, as it handles non-color-supporting terminals/etc.
RESET =
<TerminalColor.RESET: '\x1b[0m'>
BOLD =
<TerminalColor.BOLD: '\x1b[1m'>
UNDERLINE =
<TerminalColor.UNDERLINE: '\x1b[4m'>
INVERSE =
<TerminalColor.INVERSE: '\x1b[7m'>
BLACK =
<TerminalColor.BLACK: '\x1b[30m'>
RED =
<TerminalColor.RED: '\x1b[31m'>
GREEN =
<TerminalColor.GREEN: '\x1b[32m'>
YELLOW =
<TerminalColor.YELLOW: '\x1b[33m'>
BLUE =
<TerminalColor.BLUE: '\x1b[34m'>
MAGENTA =
<TerminalColor.MAGENTA: '\x1b[35m'>
CYAN =
<TerminalColor.CYAN: '\x1b[36m'>
WHITE =
<TerminalColor.WHITE: '\x1b[37m'>
BG_BLACK =
<TerminalColor.BG_BLACK: '\x1b[40m'>
BG_RED =
<TerminalColor.BG_RED: '\x1b[41m'>
BG_GREEN =
<TerminalColor.BG_GREEN: '\x1b[42m'>
BG_YELLOW =
<TerminalColor.BG_YELLOW: '\x1b[43m'>
BG_BLUE =
<TerminalColor.BG_BLUE: '\x1b[44m'>
BG_MAGENTA =
<TerminalColor.BG_MAGENTA: '\x1b[45m'>
BG_CYAN =
<TerminalColor.BG_CYAN: '\x1b[46m'>
BG_WHITE =
<TerminalColor.BG_WHITE: '\x1b[47m'>
STRONG_BLACK =
<TerminalColor.STRONG_BLACK: '\x1b[90m'>
STRONG_RED =
<TerminalColor.STRONG_RED: '\x1b[91m'>
STRONG_GREEN =
<TerminalColor.STRONG_GREEN: '\x1b[92m'>
STRONG_YELLOW =
<TerminalColor.STRONG_YELLOW: '\x1b[93m'>
STRONG_BLUE =
<TerminalColor.STRONG_BLUE: '\x1b[94m'>
STRONG_MAGENTA =
<TerminalColor.STRONG_MAGENTA: '\x1b[95m'>
STRONG_CYAN =
<TerminalColor.STRONG_CYAN: '\x1b[96m'>
STRONG_WHITE =
<TerminalColor.STRONG_WHITE: '\x1b[97m'>
STRONG_BG_BLACK =
<TerminalColor.STRONG_BG_BLACK: '\x1b[100m'>
STRONG_BG_RED =
<TerminalColor.STRONG_BG_RED: '\x1b[101m'>
STRONG_BG_GREEN =
<TerminalColor.STRONG_BG_GREEN: '\x1b[102m'>
STRONG_BG_YELLOW =
<TerminalColor.STRONG_BG_YELLOW: '\x1b[103m'>
STRONG_BG_BLUE =
<TerminalColor.STRONG_BG_BLUE: '\x1b[104m'>
STRONG_BG_MAGENTA =
<TerminalColor.STRONG_BG_MAGENTA: '\x1b[105m'>
STRONG_BG_CYAN =
<TerminalColor.STRONG_BG_CYAN: '\x1b[106m'>
STRONG_BG_WHITE =
<TerminalColor.STRONG_BG_WHITE: '\x1b[107m'>
Inherited Members
- enum.Enum
- name
- value
class
ClrBase:
149class ClrBase: 150 """Base class for color convenience class.""" 151 152 RST: ClassVar[str] 153 BLD: ClassVar[str] 154 UND: ClassVar[str] 155 INV: ClassVar[str] 156 157 # Normal foreground colors 158 BLK: ClassVar[str] 159 RED: ClassVar[str] 160 GRN: ClassVar[str] 161 YLW: ClassVar[str] 162 BLU: ClassVar[str] 163 MAG: ClassVar[str] 164 CYN: ClassVar[str] 165 WHT: ClassVar[str] 166 167 # Normal background colors. 168 BBLK: ClassVar[str] 169 BRED: ClassVar[str] 170 BGRN: ClassVar[str] 171 BYLW: ClassVar[str] 172 BBLU: ClassVar[str] 173 BMAG: ClassVar[str] 174 BCYN: ClassVar[str] 175 BWHT: ClassVar[str] 176 177 # Strong foreground colors 178 SBLK: ClassVar[str] 179 SRED: ClassVar[str] 180 SGRN: ClassVar[str] 181 SYLW: ClassVar[str] 182 SBLU: ClassVar[str] 183 SMAG: ClassVar[str] 184 SCYN: ClassVar[str] 185 SWHT: ClassVar[str] 186 187 # Strong background colors. 188 SBBLK: ClassVar[str] 189 SBRED: ClassVar[str] 190 SBGRN: ClassVar[str] 191 SBYLW: ClassVar[str] 192 SBBLU: ClassVar[str] 193 SBMAG: ClassVar[str] 194 SBCYN: ClassVar[str] 195 SBWHT: ClassVar[str]
Base class for color convenience class.
198class ClrAlways(ClrBase): 199 """Convenience class for color terminal output. 200 201 This version has colors always enabled. Generally you should use Clr which 202 points to the correct enabled/disabled class depending on the environment. 203 """ 204 205 color_enabled = True 206 207 # Styles 208 RST = TerminalColor.RESET.value 209 BLD = TerminalColor.BOLD.value 210 UND = TerminalColor.UNDERLINE.value 211 INV = TerminalColor.INVERSE.value 212 213 # Normal foreground colors 214 BLK = TerminalColor.BLACK.value 215 RED = TerminalColor.RED.value 216 GRN = TerminalColor.GREEN.value 217 YLW = TerminalColor.YELLOW.value 218 BLU = TerminalColor.BLUE.value 219 MAG = TerminalColor.MAGENTA.value 220 CYN = TerminalColor.CYAN.value 221 WHT = TerminalColor.WHITE.value 222 223 # Normal background colors. 224 BBLK = TerminalColor.BG_BLACK.value 225 BRED = TerminalColor.BG_RED.value 226 BGRN = TerminalColor.BG_GREEN.value 227 BYLW = TerminalColor.BG_YELLOW.value 228 BBLU = TerminalColor.BG_BLUE.value 229 BMAG = TerminalColor.BG_MAGENTA.value 230 BCYN = TerminalColor.BG_CYAN.value 231 BWHT = TerminalColor.BG_WHITE.value 232 233 # Strong foreground colors 234 SBLK = TerminalColor.STRONG_BLACK.value 235 SRED = TerminalColor.STRONG_RED.value 236 SGRN = TerminalColor.STRONG_GREEN.value 237 SYLW = TerminalColor.STRONG_YELLOW.value 238 SBLU = TerminalColor.STRONG_BLUE.value 239 SMAG = TerminalColor.STRONG_MAGENTA.value 240 SCYN = TerminalColor.STRONG_CYAN.value 241 SWHT = TerminalColor.STRONG_WHITE.value 242 243 # Strong background colors. 244 SBBLK = TerminalColor.STRONG_BG_BLACK.value 245 SBRED = TerminalColor.STRONG_BG_RED.value 246 SBGRN = TerminalColor.STRONG_BG_GREEN.value 247 SBYLW = TerminalColor.STRONG_BG_YELLOW.value 248 SBBLU = TerminalColor.STRONG_BG_BLUE.value 249 SBMAG = TerminalColor.STRONG_BG_MAGENTA.value 250 SBCYN = TerminalColor.STRONG_BG_CYAN.value 251 SBWHT = TerminalColor.STRONG_BG_WHITE.value
Convenience class for color terminal output.
This version has colors always enabled. Generally you should use Clr which points to the correct enabled/disabled class depending on the environment.
254class ClrNever(ClrBase): 255 """Convenience class for color terminal output. 256 257 This version has colors disabled. Generally you should use Clr which 258 points to the correct enabled/disabled class depending on the environment. 259 """ 260 261 color_enabled = False 262 263 # Styles 264 RST = '' 265 BLD = '' 266 UND = '' 267 INV = '' 268 269 # Normal foreground colors 270 BLK = '' 271 RED = '' 272 GRN = '' 273 YLW = '' 274 BLU = '' 275 MAG = '' 276 CYN = '' 277 WHT = '' 278 279 # Normal background colors. 280 BBLK = '' 281 BRED = '' 282 BGRN = '' 283 BYLW = '' 284 BBLU = '' 285 BMAG = '' 286 BCYN = '' 287 BWHT = '' 288 289 # Strong foreground colors 290 SBLK = '' 291 SRED = '' 292 SGRN = '' 293 SYLW = '' 294 SBLU = '' 295 SMAG = '' 296 SCYN = '' 297 SWHT = '' 298 299 # Strong background colors. 300 SBBLK = '' 301 SBRED = '' 302 SBGRN = '' 303 SBYLW = '' 304 SBBLU = '' 305 SBMAG = '' 306 SBCYN = '' 307 SBWHT = ''
Convenience class for color terminal output.
This version has colors disabled. Generally you should use Clr which points to the correct enabled/disabled class depending on the environment.
color_enabled: bool =
False