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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
import os
import sys
import getpass
import subprocess
class TerminalPlatformUnsupported(Exception):
"""Platform-specific functionality is not supported
Raised by code that can not be used to interact with the terminal on this
platform.
"""
pass
class Colors:
def __wrap_with(raw_code):
@staticmethod
def inner(text, bold=False):
code = raw_code
if bold:
code = "1;{}".format(code)
return "\033[{}m{}\033[0m".format(code, text)
return inner
red = __wrap_with("31")
green = __wrap_with("32")
yellow = __wrap_with("33")
blue = __wrap_with("34")
magenta = __wrap_with("35")
cyan = __wrap_with("36")
white = __wrap_with("37")
class PosixEchoControl:
"""Posix Console Echo Control Driver
Uses termios on POSIX compliant platforms to control console echo. Is not
supported on Windows as termios is not available and will throw a
TerminalPlatformUnsupported exception if contructed on Windows.
"""
def __init__(self):
try:
import termios
self.termios = termios
except ImportError:
raise TerminalPlatformUnsupported("POSIX not supported")
def set_echo(self, enabled):
handle = sys.stdin.fileno()
if not os.isatty(handle):
return
attrs = self.termios.tcgetattr(handle)
if enabled:
attrs[3] |= self.termios.ECHO
else:
attrs[3] &= ~self.termios.ECHO
self.termios.tcsetattr(handle, self.termios.TCSANOW, attrs)
class Win32EchoControl:
"""Windows Console Echo Control Driver
This uses the console API from WinCon.h and ctypes to control console echo
on Windows clients. It is not possible to construct this class on
non-Windows systems, on those systems it will throw a
TerminalPlatformUnsupported exception.
"""
STD_INPUT_HANDLE = -10
ENABLE_ECHO_INPUT = 0x4
DISABLE_ECHO_INPUT = ~ENABLE_ECHO_INPUT
def __init__(self):
import ctypes
if not hasattr(ctypes, "windll"):
raise TerminalPlatformUnsupported("Windows not supported")
from ctypes import wintypes
self.ctypes = ctypes
self.wintypes = wintypes
self.kernel32 = ctypes.windll.kernel32
def _GetStdHandle(self, handle):
return self.kernel32.GetStdHandle(handle)
def _GetConsoleMode(self, handle):
mode = self.wintypes.DWORD()
self.kernel32.GetConsoleMode(handle, self.ctypes.byref(mode))
return mode.value
def _SetConsoleMode(self, handle, value):
self.kernel32.SetConsoleMode(handle, value)
def set_echo(self, enabled):
stdin = self._GetStdHandle(self.STD_INPUT_HANDLE)
mode = self._GetConsoleMode(stdin)
if enabled:
self._SetConsoleMode(stdin, mode | self.ENABLE_ECHO_INPUT)
else:
self._SetConsoleMode(stdin, mode & self.DISABLE_ECHO_INPUT)
class Screen:
def __init__(self):
try:
self._echo_driver = PosixEchoControl()
except TerminalPlatformUnsupported:
pass
try:
self._echo_driver = Win32EchoControl()
except TerminalPlatformUnsupported:
pass
if not self._echo_driver:
raise TerminalPlatformUnsupported("No supported terminal driver")
def set_echo(self, enabled):
self._echo_driver.set_echo(enabled)
@staticmethod
def clear():
sys.stdout.write("\x1b[2J\x1b[H")
sys.stdout.flush()
@staticmethod
def print_error(msg):
print(Colors.red(msg))
@staticmethod
def print_success(msg):
print(Colors.green(msg))
@staticmethod
def get_string(prompt):
while True:
value = input(prompt).strip()
if not value:
print(Colors.red("Value Required!"))
else:
return value
@staticmethod
def get_password(prompt="Password: "):
while True:
value = getpass.getpass(prompt)
if not value:
print(Colors.red("Value Required!"))
else:
return value
@staticmethod
def get_integer(prompt):
"""Gather user input and convert it to an integer
Will keep trying till the user enters an interger or until they ^C the
program.
"""
while True:
try:
return int(input(prompt).strip())
except ValueError:
print(Colors.red("Invalid Input!"))
def iterate_forever(func, *args, **kwargs):
"""Iterate over a finite iterator forever
When the iterator is exhausted will call the function again to generate a
new iterator and keep iterating.
"""
output = func(*args, **kwargs)
while True:
try:
playlist_item = next(output)
playlist_item.prepare_playback()
yield playlist_item
except StopIteration:
output = func(*args, **kwargs)
class SilentPopen(subprocess.Popen):
"""A Popen varient that dumps it's output and error
"""
def __init__(self, *args, **kwargs):
self._dev_null = open(os.devnull, "w")
kwargs["stdin"] = subprocess.PIPE
kwargs["stdout"] = subprocess.PIPE
kwargs["stderr"] = self._dev_null
super().__init__(*args, **kwargs)
def __del__(self):
self._dev_null.close()
super().__del__()
|