diff options
author | Walter Purcaro <vuolter@users.noreply.github.com> | 2015-04-13 10:21:32 +0200 |
---|---|---|
committer | Walter Purcaro <vuolter@users.noreply.github.com> | 2015-04-13 10:21:32 +0200 |
commit | eca20b701c411046e7ededb0462b310124ce3c18 (patch) | |
tree | c5eb36261cfc935f001b816f28f15d1f5afbf7e0 /lib | |
parent | Merge branch 'stable' into 0.4.10 (diff) | |
download | pyload-eca20b701c411046e7ededb0462b310124ce3c18.tar.xz |
Cleanup + fixup + new lib
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) |