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 our stdout is not attached to a terminal, go with no-color. 75 if not sys.__stdout__.isatty(): 76 return False 77 78 termenv = os.environ.get('TERM') 79 80 # If TERM is unset, don't attempt color (this is currently the case 81 # in xcode). 82 if termenv is None: 83 return False 84 85 # A common way to say the terminal can't do fancy stuff like color. 86 if termenv == 'dumb': 87 return False 88 89 # On windows, try to enable ANSI color mode. 90 if platform.system() == 'Windows': 91 return _windows_enable_color() 92 93 # We seem to be a terminal with color support; let's do it! 94 return True 95 96 97# noinspection PyPep8Naming 98def _windows_enable_color() -> bool: 99 """Attempt to enable ANSI color on windows terminal; return success.""" 100 # pylint: disable=invalid-name, import-error, undefined-variable 101 # Pulled from: https://bugs.python.org/issue30075 102 import msvcrt 103 import ctypes 104 from ctypes import wintypes 105 106 kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # type: ignore 107 108 ERROR_INVALID_PARAMETER = 0x0057 109 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 110 111 def _check_bool(result: Any, _func: Any, args: Any) -> Any: 112 if not result: 113 raise ctypes.WinError(ctypes.get_last_error()) # type: ignore 114 return args 115 116 LPDWORD = ctypes.POINTER(wintypes.DWORD) 117 kernel32.GetConsoleMode.errcheck = _check_bool 118 kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD) 119 kernel32.SetConsoleMode.errcheck = _check_bool 120 kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD) 121 122 def set_conout_mode(new_mode: int, mask: int = 0xFFFFFFFF) -> int: 123 # don't assume StandardOutput is a console. 124 # open CONOUT$ instead 125 fdout = os.open('CONOUT$', os.O_RDWR) 126 try: 127 hout = msvcrt.get_osfhandle(fdout) # type: ignore 128 # pylint: disable=useless-suppression 129 # pylint: disable=no-value-for-parameter 130 old_mode = wintypes.DWORD() 131 # pylint: enable=useless-suppression 132 kernel32.GetConsoleMode(hout, ctypes.byref(old_mode)) 133 mode = (new_mode & mask) | (old_mode.value & ~mask) 134 kernel32.SetConsoleMode(hout, mode) 135 return old_mode.value 136 finally: 137 os.close(fdout) 138 139 def enable_vt_mode() -> int: 140 mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING 141 try: 142 return set_conout_mode(mode, mask) 143 except WindowsError as exc: # type: ignore 144 if exc.winerror == ERROR_INVALID_PARAMETER: 145 raise NotImplementedError from exc 146 raise 147 148 try: 149 enable_vt_mode() 150 return True 151 except NotImplementedError: 152 return False 153 154 155class ClrBase: 156 """Base class for color convenience class.""" 157 158 RST: ClassVar[str] 159 BLD: ClassVar[str] 160 UND: ClassVar[str] 161 INV: ClassVar[str] 162 163 # Normal foreground colors 164 BLK: ClassVar[str] 165 RED: ClassVar[str] 166 GRN: ClassVar[str] 167 YLW: ClassVar[str] 168 BLU: ClassVar[str] 169 MAG: ClassVar[str] 170 CYN: ClassVar[str] 171 WHT: ClassVar[str] 172 173 # Normal background colors. 174 BBLK: ClassVar[str] 175 BRED: ClassVar[str] 176 BGRN: ClassVar[str] 177 BYLW: ClassVar[str] 178 BBLU: ClassVar[str] 179 BMAG: ClassVar[str] 180 BCYN: ClassVar[str] 181 BWHT: ClassVar[str] 182 183 # Strong foreground colors 184 SBLK: ClassVar[str] 185 SRED: ClassVar[str] 186 SGRN: ClassVar[str] 187 SYLW: ClassVar[str] 188 SBLU: ClassVar[str] 189 SMAG: ClassVar[str] 190 SCYN: ClassVar[str] 191 SWHT: ClassVar[str] 192 193 # Strong background colors. 194 SBBLK: ClassVar[str] 195 SBRED: ClassVar[str] 196 SBGRN: ClassVar[str] 197 SBYLW: ClassVar[str] 198 SBBLU: ClassVar[str] 199 SBMAG: ClassVar[str] 200 SBCYN: ClassVar[str] 201 SBWHT: ClassVar[str] 202 203 204class ClrAlways(ClrBase): 205 """Convenience class for color terminal output. 206 207 This version has colors always enabled. Generally you should use Clr which 208 points to the correct enabled/disabled class depending on the environment. 209 """ 210 211 color_enabled = True 212 213 # Styles 214 RST = TerminalColor.RESET.value 215 BLD = TerminalColor.BOLD.value 216 UND = TerminalColor.UNDERLINE.value 217 INV = TerminalColor.INVERSE.value 218 219 # Normal foreground colors 220 BLK = TerminalColor.BLACK.value 221 RED = TerminalColor.RED.value 222 GRN = TerminalColor.GREEN.value 223 YLW = TerminalColor.YELLOW.value 224 BLU = TerminalColor.BLUE.value 225 MAG = TerminalColor.MAGENTA.value 226 CYN = TerminalColor.CYAN.value 227 WHT = TerminalColor.WHITE.value 228 229 # Normal background colors. 230 BBLK = TerminalColor.BG_BLACK.value 231 BRED = TerminalColor.BG_RED.value 232 BGRN = TerminalColor.BG_GREEN.value 233 BYLW = TerminalColor.BG_YELLOW.value 234 BBLU = TerminalColor.BG_BLUE.value 235 BMAG = TerminalColor.BG_MAGENTA.value 236 BCYN = TerminalColor.BG_CYAN.value 237 BWHT = TerminalColor.BG_WHITE.value 238 239 # Strong foreground colors 240 SBLK = TerminalColor.STRONG_BLACK.value 241 SRED = TerminalColor.STRONG_RED.value 242 SGRN = TerminalColor.STRONG_GREEN.value 243 SYLW = TerminalColor.STRONG_YELLOW.value 244 SBLU = TerminalColor.STRONG_BLUE.value 245 SMAG = TerminalColor.STRONG_MAGENTA.value 246 SCYN = TerminalColor.STRONG_CYAN.value 247 SWHT = TerminalColor.STRONG_WHITE.value 248 249 # Strong background colors. 250 SBBLK = TerminalColor.STRONG_BG_BLACK.value 251 SBRED = TerminalColor.STRONG_BG_RED.value 252 SBGRN = TerminalColor.STRONG_BG_GREEN.value 253 SBYLW = TerminalColor.STRONG_BG_YELLOW.value 254 SBBLU = TerminalColor.STRONG_BG_BLUE.value 255 SBMAG = TerminalColor.STRONG_BG_MAGENTA.value 256 SBCYN = TerminalColor.STRONG_BG_CYAN.value 257 SBWHT = TerminalColor.STRONG_BG_WHITE.value 258 259 260class ClrNever(ClrBase): 261 """Convenience class for color terminal output. 262 263 This version has colors disabled. Generally you should use Clr which 264 points to the correct enabled/disabled class depending on the environment. 265 """ 266 267 color_enabled = False 268 269 # Styles 270 RST = '' 271 BLD = '' 272 UND = '' 273 INV = '' 274 275 # Normal foreground colors 276 BLK = '' 277 RED = '' 278 GRN = '' 279 YLW = '' 280 BLU = '' 281 MAG = '' 282 CYN = '' 283 WHT = '' 284 285 # Normal background colors. 286 BBLK = '' 287 BRED = '' 288 BGRN = '' 289 BYLW = '' 290 BBLU = '' 291 BMAG = '' 292 BCYN = '' 293 BWHT = '' 294 295 # Strong foreground colors 296 SBLK = '' 297 SRED = '' 298 SGRN = '' 299 SYLW = '' 300 SBLU = '' 301 SMAG = '' 302 SCYN = '' 303 SWHT = '' 304 305 # Strong background colors. 306 SBBLK = '' 307 SBRED = '' 308 SBGRN = '' 309 SBYLW = '' 310 SBBLU = '' 311 SBMAG = '' 312 SBCYN = '' 313 SBWHT = '' 314 315 316_envval = os.environ.get('EFRO_TERMCOLORS') 317color_enabled: bool = ( 318 True 319 if _envval == '1' 320 else False 321 if _envval == '0' 322 else _default_color_enabled() 323) 324Clr: 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:
156class ClrBase: 157 """Base class for color convenience class.""" 158 159 RST: ClassVar[str] 160 BLD: ClassVar[str] 161 UND: ClassVar[str] 162 INV: ClassVar[str] 163 164 # Normal foreground colors 165 BLK: ClassVar[str] 166 RED: ClassVar[str] 167 GRN: ClassVar[str] 168 YLW: ClassVar[str] 169 BLU: ClassVar[str] 170 MAG: ClassVar[str] 171 CYN: ClassVar[str] 172 WHT: ClassVar[str] 173 174 # Normal background colors. 175 BBLK: ClassVar[str] 176 BRED: ClassVar[str] 177 BGRN: ClassVar[str] 178 BYLW: ClassVar[str] 179 BBLU: ClassVar[str] 180 BMAG: ClassVar[str] 181 BCYN: ClassVar[str] 182 BWHT: ClassVar[str] 183 184 # Strong foreground colors 185 SBLK: ClassVar[str] 186 SRED: ClassVar[str] 187 SGRN: ClassVar[str] 188 SYLW: ClassVar[str] 189 SBLU: ClassVar[str] 190 SMAG: ClassVar[str] 191 SCYN: ClassVar[str] 192 SWHT: ClassVar[str] 193 194 # Strong background colors. 195 SBBLK: ClassVar[str] 196 SBRED: ClassVar[str] 197 SBGRN: ClassVar[str] 198 SBYLW: ClassVar[str] 199 SBBLU: ClassVar[str] 200 SBMAG: ClassVar[str] 201 SBCYN: ClassVar[str] 202 SBWHT: ClassVar[str]
Base class for color convenience class.
205class ClrAlways(ClrBase): 206 """Convenience class for color terminal output. 207 208 This version has colors always enabled. Generally you should use Clr which 209 points to the correct enabled/disabled class depending on the environment. 210 """ 211 212 color_enabled = True 213 214 # Styles 215 RST = TerminalColor.RESET.value 216 BLD = TerminalColor.BOLD.value 217 UND = TerminalColor.UNDERLINE.value 218 INV = TerminalColor.INVERSE.value 219 220 # Normal foreground colors 221 BLK = TerminalColor.BLACK.value 222 RED = TerminalColor.RED.value 223 GRN = TerminalColor.GREEN.value 224 YLW = TerminalColor.YELLOW.value 225 BLU = TerminalColor.BLUE.value 226 MAG = TerminalColor.MAGENTA.value 227 CYN = TerminalColor.CYAN.value 228 WHT = TerminalColor.WHITE.value 229 230 # Normal background colors. 231 BBLK = TerminalColor.BG_BLACK.value 232 BRED = TerminalColor.BG_RED.value 233 BGRN = TerminalColor.BG_GREEN.value 234 BYLW = TerminalColor.BG_YELLOW.value 235 BBLU = TerminalColor.BG_BLUE.value 236 BMAG = TerminalColor.BG_MAGENTA.value 237 BCYN = TerminalColor.BG_CYAN.value 238 BWHT = TerminalColor.BG_WHITE.value 239 240 # Strong foreground colors 241 SBLK = TerminalColor.STRONG_BLACK.value 242 SRED = TerminalColor.STRONG_RED.value 243 SGRN = TerminalColor.STRONG_GREEN.value 244 SYLW = TerminalColor.STRONG_YELLOW.value 245 SBLU = TerminalColor.STRONG_BLUE.value 246 SMAG = TerminalColor.STRONG_MAGENTA.value 247 SCYN = TerminalColor.STRONG_CYAN.value 248 SWHT = TerminalColor.STRONG_WHITE.value 249 250 # Strong background colors. 251 SBBLK = TerminalColor.STRONG_BG_BLACK.value 252 SBRED = TerminalColor.STRONG_BG_RED.value 253 SBGRN = TerminalColor.STRONG_BG_GREEN.value 254 SBYLW = TerminalColor.STRONG_BG_YELLOW.value 255 SBBLU = TerminalColor.STRONG_BG_BLUE.value 256 SBMAG = TerminalColor.STRONG_BG_MAGENTA.value 257 SBCYN = TerminalColor.STRONG_BG_CYAN.value 258 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.
261class ClrNever(ClrBase): 262 """Convenience class for color terminal output. 263 264 This version has colors disabled. Generally you should use Clr which 265 points to the correct enabled/disabled class depending on the environment. 266 """ 267 268 color_enabled = False 269 270 # Styles 271 RST = '' 272 BLD = '' 273 UND = '' 274 INV = '' 275 276 # Normal foreground colors 277 BLK = '' 278 RED = '' 279 GRN = '' 280 YLW = '' 281 BLU = '' 282 MAG = '' 283 CYN = '' 284 WHT = '' 285 286 # Normal background colors. 287 BBLK = '' 288 BRED = '' 289 BGRN = '' 290 BYLW = '' 291 BBLU = '' 292 BMAG = '' 293 BCYN = '' 294 BWHT = '' 295 296 # Strong foreground colors 297 SBLK = '' 298 SRED = '' 299 SGRN = '' 300 SYLW = '' 301 SBLU = '' 302 SMAG = '' 303 SCYN = '' 304 SWHT = '' 305 306 # Strong background colors. 307 SBBLK = '' 308 SBRED = '' 309 SBGRN = '' 310 SBYLW = '' 311 SBBLU = '' 312 SBMAG = '' 313 SBCYN = '' 314 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