Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 19050af

Browse filesBrowse files
authored
Merge pull request #5520 from arihant2math/colorize
Add _colorize at 3.13.2
2 parents a46ce8e + e96557b commit 19050af
Copy full SHA for 19050af

File tree

3 files changed

+208
-0
lines changed
Filter options

3 files changed

+208
-0
lines changed

‎Lib/_colorize.py

Copy file name to clipboard
+67Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import io
2+
import os
3+
import sys
4+
5+
COLORIZE = True
6+
7+
8+
class ANSIColors:
9+
BOLD_GREEN = "\x1b[1;32m"
10+
BOLD_MAGENTA = "\x1b[1;35m"
11+
BOLD_RED = "\x1b[1;31m"
12+
GREEN = "\x1b[32m"
13+
GREY = "\x1b[90m"
14+
MAGENTA = "\x1b[35m"
15+
RED = "\x1b[31m"
16+
RESET = "\x1b[0m"
17+
YELLOW = "\x1b[33m"
18+
19+
20+
NoColors = ANSIColors()
21+
22+
for attr in dir(NoColors):
23+
if not attr.startswith("__"):
24+
setattr(NoColors, attr, "")
25+
26+
27+
def get_colors(colorize: bool = False, *, file=None) -> ANSIColors:
28+
if colorize or can_colorize(file=file):
29+
return ANSIColors()
30+
else:
31+
return NoColors
32+
33+
34+
def can_colorize(*, file=None) -> bool:
35+
if file is None:
36+
file = sys.stdout
37+
38+
if not sys.flags.ignore_environment:
39+
if os.environ.get("PYTHON_COLORS") == "0":
40+
return False
41+
if os.environ.get("PYTHON_COLORS") == "1":
42+
return True
43+
if os.environ.get("NO_COLOR"):
44+
return False
45+
if not COLORIZE:
46+
return False
47+
if os.environ.get("FORCE_COLOR"):
48+
return True
49+
if os.environ.get("TERM") == "dumb":
50+
return False
51+
52+
if not hasattr(file, "fileno"):
53+
return False
54+
55+
if sys.platform == "win32":
56+
try:
57+
import nt
58+
59+
if not nt._supports_virtual_terminal():
60+
return False
61+
except (ImportError, AttributeError):
62+
return False
63+
64+
try:
65+
return os.isatty(file.fileno())
66+
except io.UnsupportedOperation:
67+
return file.isatty()

‎Lib/test/test__colorize.py

Copy file name to clipboard
+135Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import contextlib
2+
import io
3+
import sys
4+
import unittest
5+
import unittest.mock
6+
import _colorize
7+
from test.support.os_helper import EnvironmentVarGuard
8+
9+
10+
@contextlib.contextmanager
11+
def clear_env():
12+
with EnvironmentVarGuard() as mock_env:
13+
for var in "FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS":
14+
mock_env.unset(var)
15+
yield mock_env
16+
17+
18+
def supports_virtual_terminal():
19+
if sys.platform == "win32":
20+
return unittest.mock.patch("nt._supports_virtual_terminal", return_value=True)
21+
else:
22+
return contextlib.nullcontext()
23+
24+
25+
class TestColorizeFunction(unittest.TestCase):
26+
def test_colorized_detection_checks_for_environment_variables(self):
27+
def check(env, fallback, expected):
28+
with (self.subTest(env=env, fallback=fallback),
29+
clear_env() as mock_env):
30+
mock_env.update(env)
31+
isatty_mock.return_value = fallback
32+
stdout_mock.isatty.return_value = fallback
33+
self.assertEqual(_colorize.can_colorize(), expected)
34+
35+
with (unittest.mock.patch("os.isatty") as isatty_mock,
36+
unittest.mock.patch("sys.stdout") as stdout_mock,
37+
supports_virtual_terminal()):
38+
stdout_mock.fileno.return_value = 1
39+
40+
for fallback in False, True:
41+
check({}, fallback, fallback)
42+
check({'TERM': 'dumb'}, fallback, False)
43+
check({'TERM': 'xterm'}, fallback, fallback)
44+
check({'TERM': ''}, fallback, fallback)
45+
check({'FORCE_COLOR': '1'}, fallback, True)
46+
check({'FORCE_COLOR': '0'}, fallback, True)
47+
check({'FORCE_COLOR': ''}, fallback, fallback)
48+
check({'NO_COLOR': '1'}, fallback, False)
49+
check({'NO_COLOR': '0'}, fallback, False)
50+
check({'NO_COLOR': ''}, fallback, fallback)
51+
52+
check({'TERM': 'dumb', 'FORCE_COLOR': '1'}, False, True)
53+
check({'FORCE_COLOR': '1', 'NO_COLOR': '1'}, True, False)
54+
55+
for ignore_environment in False, True:
56+
# Simulate running with or without `-E`.
57+
flags = unittest.mock.MagicMock(ignore_environment=ignore_environment)
58+
with unittest.mock.patch("sys.flags", flags):
59+
check({'PYTHON_COLORS': '1'}, True, True)
60+
check({'PYTHON_COLORS': '1'}, False, not ignore_environment)
61+
check({'PYTHON_COLORS': '0'}, True, ignore_environment)
62+
check({'PYTHON_COLORS': '0'}, False, False)
63+
for fallback in False, True:
64+
check({'PYTHON_COLORS': 'x'}, fallback, fallback)
65+
check({'PYTHON_COLORS': ''}, fallback, fallback)
66+
67+
check({'TERM': 'dumb', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
68+
check({'NO_COLOR': '1', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
69+
check({'FORCE_COLOR': '1', 'PYTHON_COLORS': '0'}, True, ignore_environment)
70+
71+
@unittest.skipUnless(sys.platform == "win32", "requires Windows")
72+
def test_colorized_detection_checks_on_windows(self):
73+
with (clear_env(),
74+
unittest.mock.patch("os.isatty") as isatty_mock,
75+
unittest.mock.patch("sys.stdout") as stdout_mock,
76+
supports_virtual_terminal() as vt_mock):
77+
stdout_mock.fileno.return_value = 1
78+
isatty_mock.return_value = True
79+
stdout_mock.isatty.return_value = True
80+
81+
vt_mock.return_value = True
82+
self.assertEqual(_colorize.can_colorize(), True)
83+
vt_mock.return_value = False
84+
self.assertEqual(_colorize.can_colorize(), False)
85+
import nt
86+
del nt._supports_virtual_terminal
87+
self.assertEqual(_colorize.can_colorize(), False)
88+
89+
def test_colorized_detection_checks_for_std_streams(self):
90+
with (clear_env(),
91+
unittest.mock.patch("os.isatty") as isatty_mock,
92+
unittest.mock.patch("sys.stdout") as stdout_mock,
93+
unittest.mock.patch("sys.stderr") as stderr_mock,
94+
supports_virtual_terminal()):
95+
stdout_mock.fileno.return_value = 1
96+
stderr_mock.fileno.side_effect = ZeroDivisionError
97+
stderr_mock.isatty.side_effect = ZeroDivisionError
98+
99+
isatty_mock.return_value = True
100+
stdout_mock.isatty.return_value = True
101+
self.assertEqual(_colorize.can_colorize(), True)
102+
103+
isatty_mock.return_value = False
104+
stdout_mock.isatty.return_value = False
105+
self.assertEqual(_colorize.can_colorize(), False)
106+
107+
def test_colorized_detection_checks_for_file(self):
108+
with clear_env(), supports_virtual_terminal():
109+
110+
with unittest.mock.patch("os.isatty") as isatty_mock:
111+
file = unittest.mock.MagicMock()
112+
file.fileno.return_value = 1
113+
isatty_mock.return_value = True
114+
self.assertEqual(_colorize.can_colorize(file=file), True)
115+
isatty_mock.return_value = False
116+
self.assertEqual(_colorize.can_colorize(file=file), False)
117+
118+
# No file.fileno.
119+
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
120+
file = unittest.mock.MagicMock(spec=['isatty'])
121+
file.isatty.return_value = True
122+
self.assertEqual(_colorize.can_colorize(file=file), False)
123+
124+
# file.fileno() raises io.UnsupportedOperation.
125+
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
126+
file = unittest.mock.MagicMock()
127+
file.fileno.side_effect = io.UnsupportedOperation
128+
file.isatty.return_value = True
129+
self.assertEqual(_colorize.can_colorize(file=file), True)
130+
file.isatty.return_value = False
131+
self.assertEqual(_colorize.can_colorize(file=file), False)
132+
133+
134+
if __name__ == "__main__":
135+
unittest.main()

‎vm/src/stdlib/nt.rs

Copy file name to clipboardExpand all lines: vm/src/stdlib/nt.rs
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ pub(crate) mod module {
4343
|| attr & FileSystem::FILE_ATTRIBUTE_DIRECTORY != 0))
4444
}
4545

46+
#[pyfunction]
47+
pub(super) fn _supports_virtual_terminal() -> PyResult<bool> {
48+
// TODO: implement this
49+
Ok(true)
50+
}
51+
4652
#[derive(FromArgs)]
4753
pub(super) struct SymlinkArgs {
4854
src: OsPath,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.