1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
"""Format colored console output."""
from __future__ import annotations
import os
import re
import shutil
import sys
try:
# check if colorama is installed to support color on Windows
import colorama
except ImportError:
colorama = None
_ansi_re: re.Pattern[str] = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm')
codes: dict[str, str] = {}
def terminal_safe(s: str) -> str:
"""Safely encode a string for printing to the terminal."""
return s.encode('ascii', 'backslashreplace').decode('ascii')
def get_terminal_width() -> int:
"""Return the width of the terminal in columns."""
return shutil.get_terminal_size().columns - 1
_tw: int = get_terminal_width()
def term_width_line(text: str) -> str:
if not codes:
# if no coloring, don't output fancy backspaces
return text + '\n'
else:
# codes are not displayed, this must be taken into account
return text.ljust(_tw + len(text) - len(_ansi_re.sub('', text))) + '\r'
def color_terminal() -> bool:
if 'NO_COLOR' in os.environ:
return False
if sys.platform == 'win32' and colorama is not None:
colorama.init()
return True
if 'FORCE_COLOR' in os.environ:
return True
if not hasattr(sys.stdout, 'isatty'):
return False
if not sys.stdout.isatty():
return False
if 'COLORTERM' in os.environ:
return True
term = os.environ.get('TERM', 'dumb').lower()
if term in ('xterm', 'linux') or 'color' in term:
return True
return False
def nocolor() -> None:
if sys.platform == 'win32' and colorama is not None:
colorama.deinit()
codes.clear()
def coloron() -> None:
codes.update(_orig_codes)
def colorize(name: str, text: str, input_mode: bool = False) -> str:
def escseq(name: str) -> str:
# Wrap escape sequence with ``\1`` and ``\2`` to let readline know
# it is non-printable characters
# ref: https://tiswww.case.edu/php/chet/readline/readline.html
#
# Note: This hack does not work well in Windows (see #5059)
escape = codes.get(name, '')
if input_mode and escape and sys.platform != 'win32':
return '\1' + escape + '\2'
else:
return escape
return escseq(name) + text + escseq('reset')
def strip_colors(s: str) -> str:
return re.compile('\x1b.*?m').sub('', s)
def create_color_func(name: str) -> None:
def inner(text: str) -> str:
return colorize(name, text)
globals()[name] = inner
_attrs = {
'reset': '39;49;00m',
'bold': '01m',
'faint': '02m',
'standout': '03m',
'underline': '04m',
'blink': '05m',
}
for _name, _value in _attrs.items():
codes[_name] = '\x1b[' + _value
_colors = [
('black', 'darkgray'),
('darkred', 'red'),
('darkgreen', 'green'),
('brown', 'yellow'),
('darkblue', 'blue'),
('purple', 'fuchsia'),
('turquoise', 'teal'),
('lightgray', 'white'),
]
for i, (dark, light) in enumerate(_colors, 30):
codes[dark] = '\x1b[%im' % i
codes[light] = '\x1b[%im' % (i + 60)
_orig_codes = codes.copy()
for _name in codes:
create_color_func(_name)
|