Skip to content

Commit af8d895

Browse files
committed
Split _cli.util.console
1 parent 3205fcf commit af8d895

File tree

3 files changed

+125
-115
lines changed

3 files changed

+125
-115
lines changed

sphinx/_cli/util/colour.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Format colored console output."""
2+
3+
from __future__ import annotations
4+
5+
import os
6+
import sys
7+
8+
try:
9+
# check if colorama is installed to support color on Windows
10+
import colorama
11+
except ImportError:
12+
colorama = None
13+
14+
15+
codes: dict[str, str] = {}
16+
17+
18+
def color_terminal() -> bool:
19+
if 'NO_COLOR' in os.environ:
20+
return False
21+
if sys.platform == 'win32' and colorama is not None:
22+
colorama.init()
23+
return True
24+
if 'FORCE_COLOR' in os.environ:
25+
return True
26+
if not hasattr(sys.stdout, 'isatty'):
27+
return False
28+
if not sys.stdout.isatty():
29+
return False
30+
if 'COLORTERM' in os.environ:
31+
return True
32+
term = os.environ.get('TERM', 'dumb').lower()
33+
if term in ('xterm', 'linux') or 'color' in term:
34+
return True
35+
return False
36+
37+
38+
def nocolor() -> None:
39+
if sys.platform == 'win32' and colorama is not None:
40+
colorama.deinit()
41+
codes.clear()
42+
43+
44+
def coloron() -> None:
45+
codes.update(_orig_codes)
46+
47+
48+
def colorize(name: str, text: str, input_mode: bool = False) -> str:
49+
def escseq(name: str) -> str:
50+
# Wrap escape sequence with ``\1`` and ``\2`` to let readline know
51+
# it is non-printable characters
52+
# ref: https://tiswww.case.edu/php/chet/readline/readline.html
53+
#
54+
# Note: This hack does not work well in Windows (see #5059)
55+
escape = codes.get(name, '')
56+
if input_mode and escape and sys.platform != 'win32':
57+
return '\1' + escape + '\2'
58+
else:
59+
return escape
60+
61+
return escseq(name) + text + escseq('reset')
62+
63+
64+
def create_color_func(name: str) -> None:
65+
def inner(text: str) -> str:
66+
return colorize(name, text)
67+
globals()[name] = inner
68+
69+
70+
_attrs = {
71+
'reset': '39;49;00m',
72+
'bold': '01m',
73+
'faint': '02m',
74+
'standout': '03m',
75+
'underline': '04m',
76+
'blink': '05m',
77+
}
78+
79+
for _name, _value in _attrs.items():
80+
codes[_name] = '\x1b[' + _value
81+
82+
_colors = [
83+
('black', 'darkgray'),
84+
('darkred', 'red'),
85+
('darkgreen', 'green'),
86+
('brown', 'yellow'),
87+
('darkblue', 'blue'),
88+
('purple', 'fuchsia'),
89+
('turquoise', 'teal'),
90+
('lightgray', 'white'),
91+
]
92+
93+
for i, (dark, light) in enumerate(_colors, 30):
94+
codes[dark] = '\x1b[%im' % i
95+
codes[light] = '\x1b[%im' % (i + 60)
96+
97+
_orig_codes = codes.copy()
98+
99+
for _name in codes:
100+
create_color_func(_name)

sphinx/_cli/util/console.py

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,128 +2,13 @@
22

33
from __future__ import annotations
44

5-
import os
65
import re
7-
import shutil
8-
import sys
9-
10-
try:
11-
# check if colorama is installed to support color on Windows
12-
import colorama
13-
except ImportError:
14-
colorama = None
15-
16-
17-
_ansi_re: re.Pattern = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm')
18-
codes: dict[str, str] = {}
196

207

218
def terminal_safe(s: str) -> str:
229
"""Safely encode a string for printing to the terminal."""
2310
return s.encode('ascii', 'backslashreplace').decode('ascii')
2411

2512

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

sphinx/_cli/util/terminal_width.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Format colored console output."""
2+
3+
from __future__ import annotations
4+
5+
import re
6+
import shutil
7+
8+
_ansi_re: re.Pattern = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm')
9+
10+
11+
def get_terminal_width() -> int:
12+
"""Return the width of the terminal in columns."""
13+
return shutil.get_terminal_size().columns - 1
14+
15+
16+
_tw: int = get_terminal_width()
17+
18+
19+
def term_width_line(text: str) -> str:
20+
if not codes:
21+
# if no coloring, don't output fancy backspaces
22+
return text + '\n'
23+
else:
24+
# codes are not displayed, this must be taken into account
25+
return text.ljust(_tw + len(text) - len(_ansi_re.sub('', text))) + '\r'

0 commit comments

Comments
 (0)