-
-
Notifications
You must be signed in to change notification settings - Fork 93
/
Copy pathstream_output.py
155 lines (117 loc) · 4.16 KB
/
stream_output.py
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import codecs
import io
import locale
import os
import platform
import sys
from typing import TYPE_CHECKING
from typing import Optional
from typing import TextIO
from cleo.formatters.formatter import Formatter
from .output import Output
from .output import Verbosity
if TYPE_CHECKING:
from .section_output import SectionOutput
class StreamOutput(Output):
def __init__(
self,
stream: TextIO,
verbosity: Verbosity = Verbosity.NORMAL,
decorated: Optional[bool] = None,
formatter: Optional[Formatter] = None,
) -> None:
self._stream = stream
self._supports_utf8 = None
if decorated is None:
decorated = self._has_color_support()
super().__init__(verbosity=verbosity, decorated=decorated, formatter=formatter)
@property
def stream(self) -> TextIO:
return self._stream
def supports_utf8(self) -> bool:
"""
Returns whether the stream supports the UTF-8 encoding.
"""
if self._supports_utf8 is not None:
return self._supports_utf8
encoding = getattr(self._stream, "encoding")
if encoding is None:
encoding = locale.getpreferredencoding(False)
try:
encoding = codecs.lookup(encoding).name
except Exception:
encoding = "utf-8"
self._supports_utf8 = encoding == "utf-8"
return self._supports_utf8
def flush(self) -> None:
self._stream.flush()
def section(self) -> "SectionOutput":
from .section_output import SectionOutput
return SectionOutput(
self._stream,
self._section_outputs,
verbosity=self.verbosity,
decorated=self.is_decorated(),
formatter=self.formatter,
)
def _write(self, message: str, new_line: bool = False) -> None:
if new_line:
message += "\n"
self._stream.write(message)
self._stream.flush()
def _has_color_support(self) -> bool:
# Follow https://no-color.org/
if "NO_COLOR" in os.environ:
return False
if os.getenv("TERM_PROGRAM") == "Hyper":
return True
if platform.system().lower() == "windows":
shell_supported = (
os.getenv("ANSICON") is not None
or "ON" == os.getenv("ConEmuANSI")
or "xterm" == os.getenv("TERM")
)
if shell_supported:
return True
if not hasattr(self._stream, "fileno"):
return False
# Checking for Windows version
# If we have a compatible version
# activate color support
windows_version = sys.getwindowsversion()
major, build = windows_version[0], windows_version[2]
if (major, build) < (10, 14393):
return False
# Activate colors if possible
import ctypes
import ctypes.wintypes
FILE_TYPE_CHAR = 0x0002
FILE_TYPE_REMOTE = 0x8000
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
kernel32 = ctypes.windll.kernel32
fileno = self._stream.fileno()
if fileno == 1:
h = kernel32.GetStdHandle(-11)
elif fileno == 2:
h = kernel32.GetStdHandle(-12)
else:
return False
if h is None or h == ctypes.wintypes.HANDLE(-1):
return False
if (kernel32.GetFileType(h) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR:
return False
mode = ctypes.wintypes.DWORD()
if not kernel32.GetConsoleMode(h, ctypes.byref(mode)):
return False
if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
kernel32.SetConsoleMode(
h, mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING
)
return True
return False
if not hasattr(self._stream, "fileno"):
return False
try:
return os.isatty(self._stream.fileno())
except io.UnsupportedOperation:
return False