diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/colorama/__init__.py | 7 | ||||
| -rw-r--r-- | lib/colorama/ansi.py | 99 | ||||
| -rw-r--r-- | lib/colorama/ansitowin32.py | 228 | ||||
| -rw-r--r-- | lib/colorama/initialise.py | 66 | ||||
| -rw-r--r-- | lib/colorama/win32.py | 146 | ||||
| -rw-r--r-- | lib/colorama/winterm.py | 151 | ||||
| -rw-r--r-- | lib/colorlog/__init__.py | 14 | ||||
| -rw-r--r-- | lib/colorlog/colorlog.py | 137 | ||||
| -rw-r--r-- | lib/colorlog/escape_codes.py | 57 | ||||
| -rw-r--r-- | lib/colorlog/logging.py | 44 | 
10 files changed, 949 insertions, 0 deletions
| diff --git a/lib/colorama/__init__.py b/lib/colorama/__init__.py new file mode 100644 index 000000000..4af0c1e11 --- /dev/null +++ b/lib/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.3.3' + diff --git a/lib/colorama/ansi.py b/lib/colorama/ansi.py new file mode 100644 index 000000000..1cc722500 --- /dev/null +++ b/lib/colorama/ansi.py @@ -0,0 +1,99 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\007' + + +def code_to_chars(code): +    return CSI + str(code) + 'm' + + +class AnsiCodes(object): +    def __init__(self, codes): +        for name in dir(codes): +            if not name.startswith('_'): +                value = getattr(codes, name) +                setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): +    def UP(self, n=1): +        return CSI + str(n) + "A" +    def DOWN(self, n=1): +        return CSI + str(n) + "B" +    def FORWARD(self, n=1): +        return CSI + str(n) + "C" +    def BACK(self, n=1): +        return CSI + str(n) + "D" +    def POS(self, x=1, y=1): +        return CSI + str(y) + ";" + str(x) + "H" + +def set_title(title): +    return OSC + "2;" + title + BEL + +def clear_screen(mode=2): +    return CSI + str(mode) + "J" + +def clear_line(mode=2): +    return CSI + str(mode) + "K" + + +class AnsiFore: +    BLACK           = 30 +    RED             = 31 +    GREEN           = 32 +    YELLOW          = 33 +    BLUE            = 34 +    MAGENTA         = 35 +    CYAN            = 36 +    WHITE           = 37 +    RESET           = 39 + +    # These are fairly well supported, but not part of the standard. +    LIGHTBLACK_EX   = 90 +    LIGHTRED_EX     = 91 +    LIGHTGREEN_EX   = 92 +    LIGHTYELLOW_EX  = 93 +    LIGHTBLUE_EX    = 94 +    LIGHTMAGENTA_EX = 95 +    LIGHTCYAN_EX    = 96 +    LIGHTWHITE_EX   = 97 + + +class AnsiBack: +    BLACK           = 40 +    RED             = 41 +    GREEN           = 42 +    YELLOW          = 43 +    BLUE            = 44 +    MAGENTA         = 45 +    CYAN            = 46 +    WHITE           = 47 +    RESET           = 49 + +    # These are fairly well supported, but not part of the standard. +    LIGHTBLACK_EX   = 100 +    LIGHTRED_EX     = 101 +    LIGHTGREEN_EX   = 102 +    LIGHTYELLOW_EX  = 103 +    LIGHTBLUE_EX    = 104 +    LIGHTMAGENTA_EX = 105 +    LIGHTCYAN_EX    = 106 +    LIGHTWHITE_EX   = 107 + + +class AnsiStyle: +    BRIGHT    = 1 +    DIM       = 2 +    NORMAL    = 22 +    RESET_ALL = 0 + +Fore = AnsiCodes( AnsiFore ) +Back = AnsiCodes( AnsiBack ) +Style = AnsiCodes( AnsiStyle ) +Cursor = AnsiCursor() diff --git a/lib/colorama/ansitowin32.py b/lib/colorama/ansitowin32.py new file mode 100644 index 000000000..62e770c86 --- /dev/null +++ b/lib/colorama/ansitowin32.py @@ -0,0 +1,228 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll + + +winterm = None +if windll is not None: +    winterm = WinTerm() + + +def is_a_tty(stream): +    return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): +    ''' +    Wraps a stream (such as stdout), acting as a transparent proxy for all +    attribute access apart from method 'write()', which is delegated to our +    Converter instance. +    ''' +    def __init__(self, wrapped, converter): +        # double-underscore everything to prevent clashes with names of +        # attributes on the wrapped stream object. +        self.__wrapped = wrapped +        self.__convertor = converter + +    def __getattr__(self, name): +        return getattr(self.__wrapped, name) + +    def write(self, text): +        self.__convertor.write(text) + + +class AnsiToWin32(object): +    ''' +    Implements a 'write()' method which, on Windows, will strip ANSI character +    sequences from the text, and if outputting to a tty, will convert them into +    win32 function calls. +    ''' +    ANSI_CSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')     # Control Sequence Introducer +    ANSI_OSC_RE = re.compile('\033\]((?:.|;)*?)(\x07)')         # Operating System Command + +    def __init__(self, wrapped, convert=None, strip=None, autoreset=False): +        # The wrapped stream (normally sys.stdout or sys.stderr) +        self.wrapped = wrapped + +        # should we reset colors to defaults after every .write() +        self.autoreset = autoreset + +        # create the proxy wrapping our output stream +        self.stream = StreamWrapper(wrapped, self) + +        on_windows = os.name == 'nt' +        on_emulated_windows = on_windows and 'TERM' in os.environ + +        # should we strip ANSI sequences from our output? +        if strip is None: +            strip = on_windows and not on_emulated_windows +        self.strip = strip + +        # should we should convert ANSI sequences into win32 calls? +        if convert is None: +            convert = on_windows and not wrapped.closed and not on_emulated_windows and is_a_tty(wrapped) +        self.convert = convert + +        # dict of ansi codes to win32 functions and parameters +        self.win32_calls = self.get_win32_calls() + +        # are we wrapping stderr? +        self.on_stderr = self.wrapped is sys.stderr + +    def should_wrap(self): +        ''' +        True if this class is actually needed. If false, then the output +        stream will not be affected, nor will win32 calls be issued, so +        wrapping stdout is not actually required. This will generally be +        False on non-Windows platforms, unless optional functionality like +        autoreset has been requested using kwargs to init() +        ''' +        return self.convert or self.strip or self.autoreset + +    def get_win32_calls(self): +        if self.convert and winterm: +            return { +                AnsiStyle.RESET_ALL: (winterm.reset_all, ), +                AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), +                AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), +                AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), +                AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), +                AnsiFore.RED: (winterm.fore, WinColor.RED), +                AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), +                AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), +                AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), +                AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), +                AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), +                AnsiFore.WHITE: (winterm.fore, WinColor.GREY), +                AnsiFore.RESET: (winterm.fore, ), +                AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), +                AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), +                AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), +                AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), +                AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), +                AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), +                AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), +                AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), +                AnsiBack.BLACK: (winterm.back, WinColor.BLACK), +                AnsiBack.RED: (winterm.back, WinColor.RED), +                AnsiBack.GREEN: (winterm.back, WinColor.GREEN), +                AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), +                AnsiBack.BLUE: (winterm.back, WinColor.BLUE), +                AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), +                AnsiBack.CYAN: (winterm.back, WinColor.CYAN), +                AnsiBack.WHITE: (winterm.back, WinColor.GREY), +                AnsiBack.RESET: (winterm.back, ), +                AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), +                AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), +                AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), +                AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), +                AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), +                AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), +                AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), +                AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), +            } +        return dict() + +    def write(self, text): +        if self.strip or self.convert: +            self.write_and_convert(text) +        else: +            self.wrapped.write(text) +            self.wrapped.flush() +        if self.autoreset: +            self.reset_all() + + +    def reset_all(self): +        if self.convert: +            self.call_win32('m', (0,)) +        elif not self.wrapped.closed and is_a_tty(self.wrapped): +            self.wrapped.write(Style.RESET_ALL) + + +    def write_and_convert(self, text): +        ''' +        Write the given text to our wrapped stream, stripping any ANSI +        sequences from the text, and optionally converting them into win32 +        calls. +        ''' +        cursor = 0 +        text = self.convert_osc(text) +        for match in self.ANSI_CSI_RE.finditer(text): +            start, end = match.span() +            self.write_plain_text(text, cursor, start) +            self.convert_ansi(*match.groups()) +            cursor = end +        self.write_plain_text(text, cursor, len(text)) + + +    def write_plain_text(self, text, start, end): +        if start < end: +            self.wrapped.write(text[start:end]) +            self.wrapped.flush() + + +    def convert_ansi(self, paramstring, command): +        if self.convert: +            params = self.extract_params(command, paramstring) +            self.call_win32(command, params) + + +    def extract_params(self, command, paramstring): +        if command in 'Hf': +            params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) +            while len(params) < 2: +                # defaults: +                params = params + (1,) +        else: +            params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) +            if len(params) == 0: +                # defaults: +                if command in 'JKm': +                    params = (0,) +                elif command in 'ABCD': +                    params = (1,) + +        return params + + +    def call_win32(self, command, params): +        if command == 'm': +            for param in params: +                if param in self.win32_calls: +                    func_args = self.win32_calls[param] +                    func = func_args[0] +                    args = func_args[1:] +                    kwargs = dict(on_stderr=self.on_stderr) +                    func(*args, **kwargs) +        elif command in 'J': +            winterm.erase_screen(params[0], on_stderr=self.on_stderr) +        elif command in 'K': +            winterm.erase_line(params[0], on_stderr=self.on_stderr) +        elif command in 'Hf':     # cursor position - absolute +            winterm.set_cursor_position(params, on_stderr=self.on_stderr) +        elif command in 'ABCD':   # cursor position - relative +            n = params[0] +            # A - up, B - down, C - forward, D - back +            x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] +            winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + +    def convert_osc(self, text): +        for match in self.ANSI_OSC_RE.finditer(text): +            start, end = match.span() +            text = text[:start] + text[end:] +            paramstring, command = match.groups() +            if command in '\x07':       # \x07 = BEL +                params = paramstring.split(";") +                # 0 - change title and icon (we will only change title) +                # 1 - change icon (we don't support this) +                # 2 - change title +                if params[0] in '02': +                    winterm.set_title(params[1]) +        return text diff --git a/lib/colorama/initialise.py b/lib/colorama/initialise.py new file mode 100644 index 000000000..7e27f84f8 --- /dev/null +++ b/lib/colorama/initialise.py @@ -0,0 +1,66 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + +wrapped_stdout = sys.stdout +wrapped_stderr = sys.stderr + +atexit_done = False + + +def reset_all(): +    AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + +    if not wrap and any([autoreset, convert, strip]): +        raise ValueError('wrap=False conflicts with any other arg=True') + +    global wrapped_stdout, wrapped_stderr +    if sys.stdout is None: +        wrapped_stdout = None +    else: +        sys.stdout = wrapped_stdout = \ +            wrap_stream(orig_stdout, convert, strip, autoreset, wrap) +    if sys.stderr is None: +        wrapped_stderr = None +    else: +        sys.stderr = wrapped_stderr = \ +            wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + +    global atexit_done +    if not atexit_done: +        atexit.register(reset_all) +        atexit_done = True + + +def deinit(): +    if orig_stdout is not None: +        sys.stdout = orig_stdout +    if orig_stderr is not None: +        sys.stderr = orig_stderr + + +def reinit(): +    if wrapped_stdout is not None: +        sys.stdout = wrapped_stdout +    if wrapped_stderr is not None: +        sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): +    if wrap: +        wrapper = AnsiToWin32(stream, +            convert=convert, strip=strip, autoreset=autoreset) +        if wrapper.should_wrap(): +            stream = wrapper.stream +    return stream + + diff --git a/lib/colorama/win32.py b/lib/colorama/win32.py new file mode 100644 index 000000000..c604f3721 --- /dev/null +++ b/lib/colorama/win32.py @@ -0,0 +1,146 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: +    import ctypes +    from ctypes import LibraryLoader +    windll = LibraryLoader(ctypes.WinDLL) +    from ctypes import wintypes +except (AttributeError, ImportError): +    windll = None +    SetConsoleTextAttribute = lambda *_: None +else: +    from ctypes import byref, Structure, c_char, POINTER + +    COORD = wintypes._COORD + +    class CONSOLE_SCREEN_BUFFER_INFO(Structure): +        """struct in wincon.h.""" +        _fields_ = [ +            ("dwSize", COORD), +            ("dwCursorPosition", COORD), +            ("wAttributes", wintypes.WORD), +            ("srWindow", wintypes.SMALL_RECT), +            ("dwMaximumWindowSize", COORD), +        ] +        def __str__(self): +            return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( +                self.dwSize.Y, self.dwSize.X +                , self.dwCursorPosition.Y, self.dwCursorPosition.X +                , self.wAttributes +                , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right +                , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X +            ) + +    _GetStdHandle = windll.kernel32.GetStdHandle +    _GetStdHandle.argtypes = [ +        wintypes.DWORD, +    ] +    _GetStdHandle.restype = wintypes.HANDLE + +    _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo +    _GetConsoleScreenBufferInfo.argtypes = [ +        wintypes.HANDLE, +        POINTER(CONSOLE_SCREEN_BUFFER_INFO), +    ] +    _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + +    _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute +    _SetConsoleTextAttribute.argtypes = [ +        wintypes.HANDLE, +        wintypes.WORD, +    ] +    _SetConsoleTextAttribute.restype = wintypes.BOOL + +    _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition +    _SetConsoleCursorPosition.argtypes = [ +        wintypes.HANDLE, +        COORD, +    ] +    _SetConsoleCursorPosition.restype = wintypes.BOOL + +    _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA +    _FillConsoleOutputCharacterA.argtypes = [ +        wintypes.HANDLE, +        c_char, +        wintypes.DWORD, +        COORD, +        POINTER(wintypes.DWORD), +    ] +    _FillConsoleOutputCharacterA.restype = wintypes.BOOL + +    _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute +    _FillConsoleOutputAttribute.argtypes = [ +        wintypes.HANDLE, +        wintypes.WORD, +        wintypes.DWORD, +        COORD, +        POINTER(wintypes.DWORD), +    ] +    _FillConsoleOutputAttribute.restype = wintypes.BOOL + +    _SetConsoleTitleW = windll.kernel32.SetConsoleTitleA +    _SetConsoleTitleW.argtypes = [ +        wintypes.LPCSTR +    ] +    _SetConsoleTitleW.restype = wintypes.BOOL + +    handles = { +        STDOUT: _GetStdHandle(STDOUT), +        STDERR: _GetStdHandle(STDERR), +    } + +    def GetConsoleScreenBufferInfo(stream_id=STDOUT): +        handle = handles[stream_id] +        csbi = CONSOLE_SCREEN_BUFFER_INFO() +        success = _GetConsoleScreenBufferInfo( +            handle, byref(csbi)) +        return csbi + +    def SetConsoleTextAttribute(stream_id, attrs): +        handle = handles[stream_id] +        return _SetConsoleTextAttribute(handle, attrs) + +    def SetConsoleCursorPosition(stream_id, position, adjust=True): +        position = COORD(*position) +        # If the position is out of range, do nothing. +        if position.Y <= 0 or position.X <= 0: +            return +        # Adjust for Windows' SetConsoleCursorPosition: +        #    1. being 0-based, while ANSI is 1-based. +        #    2. expecting (x,y), while ANSI uses (y,x). +        adjusted_position = COORD(position.Y - 1, position.X - 1) +        if adjust: +            # Adjust for viewport's scroll position +            sr = GetConsoleScreenBufferInfo(STDOUT).srWindow +            adjusted_position.Y += sr.Top +            adjusted_position.X += sr.Left +        # Resume normal processing +        handle = handles[stream_id] +        return _SetConsoleCursorPosition(handle, adjusted_position) + +    def FillConsoleOutputCharacter(stream_id, char, length, start): +        handle = handles[stream_id] +        char = c_char(char.encode()) +        length = wintypes.DWORD(length) +        num_written = wintypes.DWORD(0) +        # Note that this is hard-coded for ANSI (vs wide) bytes. +        success = _FillConsoleOutputCharacterA( +            handle, char, length, start, byref(num_written)) +        return num_written.value + +    def FillConsoleOutputAttribute(stream_id, attr, length, start): +        ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' +        handle = handles[stream_id] +        attribute = wintypes.WORD(attr) +        length = wintypes.DWORD(length) +        num_written = wintypes.DWORD(0) +        # Note that this is hard-coded for ANSI (vs wide) bytes. +        return _FillConsoleOutputAttribute( +            handle, attribute, length, start, byref(num_written)) + +    def SetConsoleTitle(title): +        return _SetConsoleTitleW(title) diff --git a/lib/colorama/winterm.py b/lib/colorama/winterm.py new file mode 100644 index 000000000..fcc774ffa --- /dev/null +++ b/lib/colorama/winterm.py @@ -0,0 +1,151 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): +    BLACK   = 0 +    BLUE    = 1 +    GREEN   = 2 +    CYAN    = 3 +    RED     = 4 +    MAGENTA = 5 +    YELLOW  = 6 +    GREY    = 7 + +# from wincon.h +class WinStyle(object): +    NORMAL              = 0x00 # dim text, dim background +    BRIGHT              = 0x08 # bright text, dim background +    BRIGHT_BACKGROUND   = 0x80 # dim text, bright background + +class WinTerm(object): + +    def __init__(self): +        self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes +        self.set_attrs(self._default) +        self._default_fore = self._fore +        self._default_back = self._back +        self._default_style = self._style + +    def get_attrs(self): +        return self._fore + self._back * 16 + self._style + +    def set_attrs(self, value): +        self._fore = value & 7 +        self._back = (value >> 4) & 7 +        self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + +    def reset_all(self, on_stderr=None): +        self.set_attrs(self._default) +        self.set_console(attrs=self._default) + +    def fore(self, fore=None, light=False, on_stderr=False): +        if fore is None: +            fore = self._default_fore +        self._fore = fore +        if light: +            self._style |= WinStyle.BRIGHT +        self.set_console(on_stderr=on_stderr) + +    def back(self, back=None, light=False, on_stderr=False): +        if back is None: +            back = self._default_back +        self._back = back +        if light: +            self._style |= WinStyle.BRIGHT_BACKGROUND +        self.set_console(on_stderr=on_stderr) + +    def style(self, style=None, on_stderr=False): +        if style is None: +            style = self._default_style +        self._style = style +        self.set_console(on_stderr=on_stderr) + +    def set_console(self, attrs=None, on_stderr=False): +        if attrs is None: +            attrs = self.get_attrs() +        handle = win32.STDOUT +        if on_stderr: +            handle = win32.STDERR +        win32.SetConsoleTextAttribute(handle, attrs) + +    def get_position(self, handle): +        position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition +        # Because Windows coordinates are 0-based, +        # and win32.SetConsoleCursorPosition expects 1-based. +        position.X += 1 +        position.Y += 1 +        return position + +    def set_cursor_position(self, position=None, on_stderr=False): +        if position is None: +            #I'm not currently tracking the position, so there is no default. +            #position = self.get_position() +            return +        handle = win32.STDOUT +        if on_stderr: +            handle = win32.STDERR +        win32.SetConsoleCursorPosition(handle, position) + +    def cursor_adjust(self, x, y, on_stderr=False): +        handle = win32.STDOUT +        if on_stderr: +            handle = win32.STDERR +        position = self.get_position(handle) +        adjusted_position = (position.Y + y, position.X + x) +        win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + +    def erase_screen(self, mode=0, on_stderr=False): +        # 0 should clear from the cursor to the end of the screen. +        # 1 should clear from the cursor to the beginning of the screen. +        # 2 should clear the entire screen, and move cursor to (1,1) +        handle = win32.STDOUT +        if on_stderr: +            handle = win32.STDERR +        csbi = win32.GetConsoleScreenBufferInfo(handle) +        # get the number of character cells in the current buffer +        cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y +        # get number of character cells before current cursor position +        cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X +        if mode == 0: +            from_coord = csbi.dwCursorPosition +            cells_to_erase = cells_in_screen - cells_before_cursor +        if mode == 1: +            from_coord = win32.COORD(0, 0) +            cells_to_erase = cells_before_cursor +        elif mode == 2: +            from_coord = win32.COORD(0, 0) +            cells_to_erase = cells_in_screen +        # fill the entire screen with blanks +        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) +        # now set the buffer's attributes accordingly +        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) +        if mode == 2: +            # put the cursor where needed +            win32.SetConsoleCursorPosition(handle, (1, 1)) + +    def erase_line(self, mode=0, on_stderr=False): +        # 0 should clear from the cursor to the end of the line. +        # 1 should clear from the cursor to the beginning of the line. +        # 2 should clear the entire line. +        handle = win32.STDOUT +        if on_stderr: +            handle = win32.STDERR +        csbi = win32.GetConsoleScreenBufferInfo(handle) +        if mode == 0: +            from_coord = csbi.dwCursorPosition +            cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X +        if mode == 1: +            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) +            cells_to_erase = csbi.dwCursorPosition.X +        elif mode == 2: +            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) +            cells_to_erase = csbi.dwSize.X +        # fill the entire screen with blanks +        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) +        # now set the buffer's attributes accordingly +        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + +    def set_title(self, title): +        win32.SetConsoleTitle(title) diff --git a/lib/colorlog/__init__.py b/lib/colorlog/__init__.py new file mode 100644 index 000000000..1d57f586d --- /dev/null +++ b/lib/colorlog/__init__.py @@ -0,0 +1,14 @@ +"""A logging formatter for colored output.""" + +from __future__ import absolute_import + +from colorlog.colorlog import ( +    ColoredFormatter, escape_codes, default_log_colors) + +from colorlog.logging import ( +    basicConfig, root, getLogger, log, +    debug, info, warning, error, exception, critical) + +__all__ = ('ColoredFormatter', 'default_log_colors', 'escape_codes', +           'basicConfig', 'root', 'getLogger', 'debug', 'info', 'warning', +           'error', 'exception', 'critical', 'log', 'exception') diff --git a/lib/colorlog/colorlog.py b/lib/colorlog/colorlog.py new file mode 100644 index 000000000..49860dd50 --- /dev/null +++ b/lib/colorlog/colorlog.py @@ -0,0 +1,137 @@ +"""The ColoredFormatter class.""" + +from __future__ import absolute_import + +import logging +import collections +import sys + +from colorlog.escape_codes import escape_codes, parse_colors + +__all__ = ('escape_codes', 'default_log_colors', 'ColoredFormatter') + +# The default colors to use for the debug levels +default_log_colors = { +    'DEBUG': 'white', +    'INFO': 'green', +    'WARNING': 'yellow', +    'ERROR': 'red', +    'CRITICAL': 'bold_red', +} + +# The default format to use for each style +default_formats = { +    '%': '%(log_color)s%(levelname)s:%(name)s:%(message)s', +    '{': '{log_color}{levelname}:{name}:{message}', +    '$': '${log_color}${levelname}:${name}:${message}' +} + + +class ColoredRecord(object): +    """ +    Wraps a LogRecord and attempts to parse missing keys as escape codes. + +    When the record is formatted, the logging library uses ``record.__dict__`` +    directly - so this class replaced the dict with a ``defaultdict`` that +    checks if a missing key is an escape code. +    """ + +    class __dict(collections.defaultdict): +        def __missing__(self, name): +            try: +                return parse_colors(name) +            except Exception: +                raise KeyError("{} is not a valid record attribute " +                               "or color sequence".format(name)) + +    def __init__(self, record): +        # Replace the internal dict with one that can handle missing keys +        self.__dict__ = self.__dict() +        self.__dict__.update(record.__dict__) + +        # Keep a refrence to the original refrence so ``__getattr__`` can +        # access functions that are not in ``__dict__`` +        self.__record = record + +    def __getattr__(self, name): +        return getattr(self.__record, name) + + +class ColoredFormatter(logging.Formatter): +    """ +    A formatter that allows colors to be placed in the format string. + +    Intended to help in creating more readable logging output. +    """ + +    def __init__(self, fmt=None, datefmt=None, +                 log_colors=None, reset=True, style='%', +                 secondary_log_colors=None): +        """ +        Set the format and colors the ColoredFormatter will use. + +        The ``fmt``, ``datefmt`` and ``style`` args are passed on to the +        ``logging.Formatter`` constructor. + +        The ``secondary_log_colors`` argument can be used to create additional +        ``log_color`` attributes. Each key in the dictionary will set +        ``log_color_{key}``, using the value to select from a different +        ``log_colors`` set. + +        :Parameters: +        - fmt (str): The format string to use +        - datefmt (str): A format string for the date +        - log_colors (dict): +            A mapping of log level names to color names +        - reset (bool): +            Implictly append a color reset to all records unless False +        - style ('%' or '{' or '$'): +            The format style to use. (*No meaning prior to Python 3.2.*) +        - secondary_log_colors (dict): +            Map secondary ``log_color`` attributes. (*New in version 2.6.*) +        """ +        if fmt is None: +            if sys.version_info > (3, 2): +                fmt = default_formats[style] +            else: +                fmt = default_formats['%'] + +        if sys.version_info > (3, 2): +            super(ColoredFormatter, self).__init__(fmt, datefmt, style) +        elif sys.version_info > (2, 7): +            super(ColoredFormatter, self).__init__(fmt, datefmt) +        else: +            logging.Formatter.__init__(self, fmt, datefmt) + +        self.log_colors = ( +            log_colors if log_colors is not None else default_log_colors) +        self.secondary_log_colors = secondary_log_colors +        self.reset = reset + +    def color(self, log_colors, name): +        """Return escape codes from a ``log_colors`` dict.""" +        return parse_colors(log_colors.get(name, "")) + +    def format(self, record): +        """Format a message from a record object.""" +        record = ColoredRecord(record) +        record.log_color = self.color(self.log_colors, record.levelname) + +        # Set secondary log colors +        if self.secondary_log_colors: +            for name, log_colors in self.secondary_log_colors.items(): +                color = self.color(log_colors, record.levelname) +                setattr(record, name + '_log_color', color) + +        # Format the message +        if sys.version_info > (2, 7): +            message = super(ColoredFormatter, self).format(record) +        else: +            message = logging.Formatter.format(self, record) + +        # Add a reset code to the end of the message +        # (if it wasn't explicitly added in format str) +        if self.reset and not message.endswith(escape_codes['reset']): +            message += escape_codes['reset'] + +        return message diff --git a/lib/colorlog/escape_codes.py b/lib/colorlog/escape_codes.py new file mode 100644 index 000000000..848eb6489 --- /dev/null +++ b/lib/colorlog/escape_codes.py @@ -0,0 +1,57 @@ +""" +Generates a dictionary of ANSI escape codes. + +http://en.wikipedia.org/wiki/ANSI_escape_code + +Uses colorama as an optional dependancy to support color on Windows +""" + +try: +    import colorama +except ImportError: +    pass +else: +    colorama.init() + +__all__ = ('escape_codes', 'parse_colors') + +# Returns escape codes from format codes +esc = lambda *x: '\033[' + ';'.join(x) + 'm' + +# The initial list of escape codes +escape_codes = { +    'reset': esc('0'), +    'bold': esc('01'), +} + +# The color names +COLORS = [ +    'black', +    'red', +    'green', +    'yellow', +    'blue', +    'purple', +    'cyan', +    'white' +] + +PREFIXES = [ +    # Foreground without prefix +    ('3', ''), ('01;3', 'bold_'), + +    # Foreground with fg_ prefix +    ('3', 'fg_'), ('01;3', 'fg_bold_'), + +    # Background with bg_ prefix - bold/light works differently +    ('4', 'bg_'), ('10', 'bg_bold_'), +] + +for prefix, prefix_name in PREFIXES: +    for code, name in enumerate(COLORS): +        escape_codes[prefix_name + name] = esc(prefix + str(code)) + + +def parse_colors(sequence): +    """Return escape codes from a color sequence.""" +    return ''.join(escape_codes[n] for n in sequence.split(',') if n) diff --git a/lib/colorlog/logging.py b/lib/colorlog/logging.py new file mode 100644 index 000000000..13f0c4ffb --- /dev/null +++ b/lib/colorlog/logging.py @@ -0,0 +1,44 @@ +"""Wrappers around the logging module.""" + +from __future__ import absolute_import + +import functools +import logging + +from colorlog.colorlog import ColoredFormatter + +BASIC_FORMAT = "%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s" + + +def basicConfig(**kwargs): +    """Call ``logging.basicConfig`` and override the formatter it creates.""" +    logging.basicConfig(**kwargs) +    logging._acquireLock() +    try: +        stream = logging.root.handlers[0] +        stream.setFormatter( +            ColoredFormatter( +                fmt=kwargs.get('format', BASIC_FORMAT), +                datefmt=kwargs.get('datefmt', None))) +    finally: +        logging._releaseLock() + + +def ensure_configured(func): +    """Modify a function to call ``basicConfig`` first if no handlers exist.""" +    @functools.wraps(func) +    def wrapper(*args, **kwargs): +        if len(logging.root.handlers) == 0: +            basicConfig() +        return func(*args, **kwargs) +    return wrapper + +root = logging.root +getLogger = logging.getLogger +debug = ensure_configured(logging.debug) +info = ensure_configured(logging.info) +warning = ensure_configured(logging.warning) +error = ensure_configured(logging.error) +critical = ensure_configured(logging.critical) +log = ensure_configured(logging.log) +exception = ensure_configured(logging.exception) | 
