~ibmcharmers/charms/xenial/ibm-cinder-storwize-svc/trunk

3 by Ankammarao
Marked tests folder executable
1
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
2
import re
3
import sys
4
import os
5
6
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
7
from .winterm import WinTerm, WinColor, WinStyle
8
from .win32 import windll, winapi_test
9
10
11
winterm = None
12
if windll is not None:
13
    winterm = WinTerm()
14
15
16
def is_stream_closed(stream):
17
    return not hasattr(stream, 'closed') or stream.closed
18
19
20
def is_a_tty(stream):
21
    return hasattr(stream, 'isatty') and stream.isatty()
22
23
24
class StreamWrapper(object):
25
    '''
26
    Wraps a stream (such as stdout), acting as a transparent proxy for all
27
    attribute access apart from method 'write()', which is delegated to our
28
    Converter instance.
29
    '''
30
    def __init__(self, wrapped, converter):
31
        # double-underscore everything to prevent clashes with names of
32
        # attributes on the wrapped stream object.
33
        self.__wrapped = wrapped
34
        self.__convertor = converter
35
36
    def __getattr__(self, name):
37
        return getattr(self.__wrapped, name)
38
39
    def write(self, text):
40
        self.__convertor.write(text)
41
42
43
class AnsiToWin32(object):
44
    '''
45
    Implements a 'write()' method which, on Windows, will strip ANSI character
46
    sequences from the text, and if outputting to a tty, will convert them into
47
    win32 function calls.
48
    '''
49
    ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?')     # Control Sequence Introducer
50
    ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?')         # Operating System Command
51
52
    def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
53
        # The wrapped stream (normally sys.stdout or sys.stderr)
54
        self.wrapped = wrapped
55
56
        # should we reset colors to defaults after every .write()
57
        self.autoreset = autoreset
58
59
        # create the proxy wrapping our output stream
60
        self.stream = StreamWrapper(wrapped, self)
61
62
        on_windows = os.name == 'nt'
63
        # We test if the WinAPI works, because even if we are on Windows
64
        # we may be using a terminal that doesn't support the WinAPI
65
        # (e.g. Cygwin Terminal). In this case it's up to the terminal
66
        # to support the ANSI codes.
67
        conversion_supported = on_windows and winapi_test()
68
69
        # should we strip ANSI sequences from our output?
70
        if strip is None:
71
            strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped))
72
        self.strip = strip
73
74
        # should we should convert ANSI sequences into win32 calls?
75
        if convert is None:
76
            convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped)
77
        self.convert = convert
78
79
        # dict of ansi codes to win32 functions and parameters
80
        self.win32_calls = self.get_win32_calls()
81
82
        # are we wrapping stderr?
83
        self.on_stderr = self.wrapped is sys.stderr
84
85
    def should_wrap(self):
86
        '''
87
        True if this class is actually needed. If false, then the output
88
        stream will not be affected, nor will win32 calls be issued, so
89
        wrapping stdout is not actually required. This will generally be
90
        False on non-Windows platforms, unless optional functionality like
91
        autoreset has been requested using kwargs to init()
92
        '''
93
        return self.convert or self.strip or self.autoreset
94
95
    def get_win32_calls(self):
96
        if self.convert and winterm:
97
            return {
98
                AnsiStyle.RESET_ALL: (winterm.reset_all, ),
99
                AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
100
                AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
101
                AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
102
                AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
103
                AnsiFore.RED: (winterm.fore, WinColor.RED),
104
                AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
105
                AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
106
                AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
107
                AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
108
                AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
109
                AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
110
                AnsiFore.RESET: (winterm.fore, ),
111
                AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
112
                AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
113
                AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
114
                AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
115
                AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
116
                AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
117
                AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
118
                AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
119
                AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
120
                AnsiBack.RED: (winterm.back, WinColor.RED),
121
                AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
122
                AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
123
                AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
124
                AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
125
                AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
126
                AnsiBack.WHITE: (winterm.back, WinColor.GREY),
127
                AnsiBack.RESET: (winterm.back, ),
128
                AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
129
                AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
130
                AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
131
                AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
132
                AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
133
                AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
134
                AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
135
                AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
136
            }
137
        return dict()
138
139
    def write(self, text):
140
        if self.strip or self.convert:
141
            self.write_and_convert(text)
142
        else:
143
            self.wrapped.write(text)
144
            self.wrapped.flush()
145
        if self.autoreset:
146
            self.reset_all()
147
148
149
    def reset_all(self):
150
        if self.convert:
151
            self.call_win32('m', (0,))
152
        elif not self.strip and not is_stream_closed(self.wrapped):
153
            self.wrapped.write(Style.RESET_ALL)
154
155
156
    def write_and_convert(self, text):
157
        '''
158
        Write the given text to our wrapped stream, stripping any ANSI
159
        sequences from the text, and optionally converting them into win32
160
        calls.
161
        '''
162
        cursor = 0
163
        text = self.convert_osc(text)
164
        for match in self.ANSI_CSI_RE.finditer(text):
165
            start, end = match.span()
166
            self.write_plain_text(text, cursor, start)
167
            self.convert_ansi(*match.groups())
168
            cursor = end
169
        self.write_plain_text(text, cursor, len(text))
170
171
172
    def write_plain_text(self, text, start, end):
173
        if start < end:
174
            self.wrapped.write(text[start:end])
175
            self.wrapped.flush()
176
177
178
    def convert_ansi(self, paramstring, command):
179
        if self.convert:
180
            params = self.extract_params(command, paramstring)
181
            self.call_win32(command, params)
182
183
184
    def extract_params(self, command, paramstring):
185
        if command in 'Hf':
186
            params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
187
            while len(params) < 2:
188
                # defaults:
189
                params = params + (1,)
190
        else:
191
            params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
192
            if len(params) == 0:
193
                # defaults:
194
                if command in 'JKm':
195
                    params = (0,)
196
                elif command in 'ABCD':
197
                    params = (1,)
198
199
        return params
200
201
202
    def call_win32(self, command, params):
203
        if command == 'm':
204
            for param in params:
205
                if param in self.win32_calls:
206
                    func_args = self.win32_calls[param]
207
                    func = func_args[0]
208
                    args = func_args[1:]
209
                    kwargs = dict(on_stderr=self.on_stderr)
210
                    func(*args, **kwargs)
211
        elif command in 'J':
212
            winterm.erase_screen(params[0], on_stderr=self.on_stderr)
213
        elif command in 'K':
214
            winterm.erase_line(params[0], on_stderr=self.on_stderr)
215
        elif command in 'Hf':     # cursor position - absolute
216
            winterm.set_cursor_position(params, on_stderr=self.on_stderr)
217
        elif command in 'ABCD':   # cursor position - relative
218
            n = params[0]
219
            # A - up, B - down, C - forward, D - back
220
            x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
221
            winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
222
223
224
    def convert_osc(self, text):
225
        for match in self.ANSI_OSC_RE.finditer(text):
226
            start, end = match.span()
227
            text = text[:start] + text[end:]
228
            paramstring, command = match.groups()
229
            if command in '\x07':       # \x07 = BEL
230
                params = paramstring.split(";")
231
                # 0 - change title and icon (we will only change title)
232
                # 1 - change icon (we don't support this)
233
                # 2 - change title
234
                if params[0] in '02':
235
                    winterm.set_title(params[1])
236
        return text