diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 2125666..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto \ No newline at end of file diff --git a/pyzen/_colorama/README.txt b/pyzen/_colorama/README.txt index 15b4701..55fe171 100644 --- a/pyzen/_colorama/README.txt +++ b/pyzen/_colorama/README.txt @@ -1,307 +1,307 @@ -Download and docs: - http://pypi.python.org/pypi/colorama -Development: - http://code.google.com/p/colorama - -Description -=========== - -Makes ANSI escape character sequences for producing colored terminal text work -under MS Windows. - -ANSI escape character sequences have long been used to produce colored terminal -text on Unix and Macs. Colorama makes this work on Windows, too. It also -provides some shortcuts to help generate ANSI sequences, and works fine in -conjunction with any other ANSI sequence generation library, such as Termcolor -(http://pypi.python.org/pypi/termcolor.) - -This has the upshot of providing a simple cross-platform API for printing -colored terminal text from Python, and has the happy side-effect that existing -applications or libraries which use ANSI sequences to produce colored output on -Linux or Macs can now also work on Windows, simply by calling -``colorama.init()``. - -A demo script in the source code repository prints some colored text using -ANSI sequences. Compare its output under Gnome-terminal's built in ANSI -handling, versus on Windows Command-Prompt using Colorama: - -.. image:: http://colorama.googlecode.com/hg/screenshots/ubuntu-demo.png - :width: 661 - :height: 357 - :alt: ANSI sequences on Ubuntu under gnome-terminal. - -.. image:: http://colorama.googlecode.com/hg/screenshots/windows-demo.png - :width: 668 - :height: 325 - :alt: Same ANSI sequences on Windows, using Colorama. - -These screengrabs make it clear that Colorama on Windows does not support -ANSI 'dim text': it looks the same as 'normal text'. - - -Dependencies -============ - -None, other than Python. Tested on Python 2.5.5, 2.6.5, 2.7 & 3.1.2. - - -Usage -===== - -Initialisation --------------- - -Applications should initialise Colorama using:: - - from colorama import init - init() - -If you are on Windows, the call to ``init()`` will start filtering ANSI escape -sequences out of any text sent to stdout or stderr, and will replace them with -equivalent Win32 calls. - -Calling ``init()`` has no effect on other platforms (unless you request other -optional functionality, see keyword args below.) The intention is that -applications can call ``init()`` unconditionally on all platforms, after which -ANSI output should just work. - - -Colored Output --------------- - -Cross-platform printing of colored text can then be done using Colorama's -constant shorthand for ANSI escape sequences:: - - from colorama import Fore, Back, Style - print Fore.RED + 'some red text' - print Back.GREEN + and with a green background' - print Style.DIM + 'and in dim text' - print + Fore.RESET + Back.RESET + Style.RESET_ALL - print 'back to normal now' - -or simply by manually printing ANSI sequences from your own code:: - - print '/033[31m' + 'some red text' - print '/033[30m' # and reset to default color - -or Colorama can be used happily in conjunction with existing ANSI libraries -such as Termcolor:: - - from colorama import init - from termcolor import colored - - # use Colorama to make Termcolor work on Windows too - init() - - # then use Termcolor for all colored text output - print colored('Hello, World!', 'green', 'on_red') - -Available formatting constants are:: - - Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Style: DIM, NORMAL, BRIGHT, RESET_ALL - -Style.RESET_ALL resets foreground, background and brightness. Colorama will -perform this reset automatically on program exit. - - -Init Keyword Args ------------------ - -``init()`` accepts some kwargs to override default behaviour. - -init(autoreset=False): - If you find yourself repeatedly sending reset sequences to turn off color - changes at the end of every print, then ``init(autoreset=True)`` will - automate that:: - - from colorama import init - init(autoreset=True) - print Fore.RED + 'some red text' - print 'automatically back to default color again' - -init(strip=None): - Pass ``True`` or ``False`` to override whether ansi codes should be - stripped from the output. The default behaviour is to strip if on Windows. - -init(convert=None): - Pass ``True`` or ``False`` to override whether to convert ansi codes in the - output into win32 calls. The default behaviour is to convert if on Windows - and output is to a tty (terminal). - -init(wrap=True): - On Windows, colorama works by replacing ``sys.stdout`` and ``sys.stderr`` - with proxy objects, which override the .write() method to do their work. If - this wrapping causes you problems, then this can be disabled by passing - ``init(wrap=False)``. The default behaviour is to wrap if autoreset or - strip or convert are True. - - When wrapping is disabled, colored printing on non-Windows platforms will - continue to work as normal. To do cross-platform colored output, you can - use Colorama's ``AnsiToWin32`` proxy directly:: - - from colorama import init, AnsiToWin32 - init(wrap=False) - stream = AnsiToWin32(sys.stderr).stream - print >>stream, Fore.BLUE + 'blue text on stderr' - - -Status & Known Problems -======================= - -Feature complete as far as colored text goes, but still finding bugs and -occasionally making small changes to the API (such as new keyword arguments -to ``init()``). - -Only tested on WinXP (CMD, Console2) and Ubuntu (gnome-terminal, xterm). Much -obliged if anyone can let me know how it fares elsewhere, in particular on -Macs. - -I'd like to add the ability to handle ANSI codes which position the text cursor -and clear the terminal. - -See outstanding issues and wishlist at: -http://code.google.com/p/colorama/issues/list - -If anything doesn't work for you, or doesn't do what you expected or hoped for, -I'd *love* to hear about it on that issues list. - - -Recognised ANSI Sequences -========================= - -ANSI sequences generally take the form: - - ESC [ ; ... - -Where is an integer, and is a single letter. Zero or more -params are passed to a . If no params are passed, it is generally -synonymous with passing a single zero. No spaces exist in the sequence, they -have just been inserted here to make it easy to read. - -The only ANSI sequences that colorama converts into win32 calls are:: - - ESC [ 0 m # reset all (colors and brightness) - ESC [ 1 m # bright - ESC [ 2 m # dim (looks same as normal brightness) - ESC [ 22 m # normal brightness - - # FOREGROUND: - ESC [ 30 m # black - ESC [ 31 m # red - ESC [ 32 m # green - ESC [ 33 m # yellow - ESC [ 34 m # blue - ESC [ 35 m # magenta - ESC [ 36 m # cyan - ESC [ 37 m # white - ESC [ 39 m # reset - - # BACKGROUND - ESC [ 40 m # black - ESC [ 41 m # red - ESC [ 42 m # green - ESC [ 43 m # yellow - ESC [ 44 m # blue - ESC [ 45 m # magenta - ESC [ 46 m # cyan - ESC [ 47 m # white - ESC [ 49 m # reset - -Multiple numeric params to the 'm' command can be combined into a single -sequence, eg:: - - ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background - -All other ANSI sequences of the form ``ESC [ ; ... `` -are silently stripped from the output on Windows. - -Any other form of ANSI sequence, such as single-character codes or alternative -initial characters, are not recognised nor stripped. - - -Development -=========== - -Running tests requires: - -- Michael Foord's 'mock' module to be installed. -- Either to be run under Python2.7 or 3.1 stdlib unittest, or to have Michael - Foord's 'unittest2' module to be installed. - -unittest2 test discovery doesn't work for colorama, so I use 'nose':: - - nosetests -s - -The -s is required because 'nosetests' otherwise applies a proxy of its own to -stdout, which confuses the unit tests. - - -Thanks -====== -Roger Binns, for many suggestions, valuable feedback, & bug reports. -Tim Golden for thought and much appreciated feedback on the initial idea. - - -Changes -======= - -0.1.17 - Prevent printing of garbage ANSI codes upon installing with pip -0.1.16 - Re-upload to fix previous error. Make clean now removes old MANIFEST. -0.1.15 - Completely broken. Distribution was empty due to leftover invalid MANIFEST - file from building on a different platform. - Fix python3 incompatibility kindly reported by G |uumlaut| nter Kolousek -0.1.14 - Fix hard-coded reset to white-on-black colors. Fore.RESET, Back.RESET - and Style.RESET_ALL now revert to the colors as they were when init() - was called. Some lessons hopefully learned about testing prior to release. -0.1.13 - Completely broken: barfed when installed using pip. -0.1.12 - Completely broken: contained no source code. double oops. -0.1.11 - Completely broken: fatal import errors on Ubuntu. oops. -0.1.10 - Stop emulating 'bright' text with bright backgrounds. - Display 'normal' text using win32 normal foreground instead of bright. - Drop support for 'dim' text. -0.1.9 - Fix incompatibility with Python 2.5 and earlier. - Remove setup.py dependency on setuptools, now uses stdlib distutils. -0.1.8 - Fix ghastly errors all over the place on Ubuntu. - Add init kwargs 'convert' and 'strip', which supercede the old 'wrap'. -0.1.7 - Python 3 compatible. - Fix: Now strips ansi on windows without necessarily converting it to - win32 calls (eg. if output is not a tty.) - Fix: Flaky interaction of interleaved ansi sent to stdout and stderr. - Improved demo.sh (hg checkout only.) -0.1.6 - Fix ansi sequences with no params now default to parmlist of [0]. - Fix flaky behaviour of autoreset and reset_all atexit. - Fix stacking of repeated atexit calls - now just called once. - Fix ghastly import problems while running tests. - 'demo.py' (hg checkout only) now demonstrates autoreset and reset atexit. - Provide colorama.VERSION, used by setup.py. - Tests defanged so they no longer actually change terminal color when run. -0.1.5 - Now works on Ubuntu. -0.1.4 - Implemented RESET_ALL on application exit -0.1.3 - Implemented init(wrap=False) -0.1.2 - Implemented init(autoreset=True) -0.1.1 - Minor tidy -0.1 - Works on Windows for foreground color, background color, bright or dim - -.. |uumlaut| unicode:: U+00FC .. u with umlaut - :trim: - +Download and docs: + http://pypi.python.org/pypi/colorama +Development: + http://code.google.com/p/colorama + +Description +=========== + +Makes ANSI escape character sequences for producing colored terminal text work +under MS Windows. + +ANSI escape character sequences have long been used to produce colored terminal +text on Unix and Macs. Colorama makes this work on Windows, too. It also +provides some shortcuts to help generate ANSI sequences, and works fine in +conjunction with any other ANSI sequence generation library, such as Termcolor +(http://pypi.python.org/pypi/termcolor.) + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.init()``. + +A demo script in the source code repository prints some colored text using +ANSI sequences. Compare its output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: http://colorama.googlecode.com/hg/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: http://colorama.googlecode.com/hg/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screengrabs make it clear that Colorama on Windows does not support +ANSI 'dim text': it looks the same as 'normal text'. + + +Dependencies +============ + +None, other than Python. Tested on Python 2.5.5, 2.6.5, 2.7 & 3.1.2. + + +Usage +===== + +Initialisation +-------------- + +Applications should initialise Colorama using:: + + from colorama import init + init() + +If you are on Windows, the call to ``init()`` will start filtering ANSI escape +sequences out of any text sent to stdout or stderr, and will replace them with +equivalent Win32 calls. + +Calling ``init()`` has no effect on other platforms (unless you request other +optional functionality, see keyword args below.) The intention is that +applications can call ``init()`` unconditionally on all platforms, after which +ANSI output should just work. + + +Colored Output +-------------- + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences:: + + from colorama import Fore, Back, Style + print Fore.RED + 'some red text' + print Back.GREEN + and with a green background' + print Style.DIM + 'and in dim text' + print + Fore.RESET + Back.RESET + Style.RESET_ALL + print 'back to normal now' + +or simply by manually printing ANSI sequences from your own code:: + + print '/033[31m' + 'some red text' + print '/033[30m' # and reset to default color + +or Colorama can be used happily in conjunction with existing ANSI libraries +such as Termcolor:: + + from colorama import init + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + init() + + # then use Termcolor for all colored text output + print colored('Hello, World!', 'green', 'on_red') + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +Style.RESET_ALL resets foreground, background and brightness. Colorama will +perform this reset automatically on program exit. + + +Init Keyword Args +----------------- + +``init()`` accepts some kwargs to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that:: + + from colorama import init + init(autoreset=True) + print Fore.RED + 'some red text' + print 'automatically back to default color again' + +init(strip=None): + Pass ``True`` or ``False`` to override whether ansi codes should be + stripped from the output. The default behaviour is to strip if on Windows. + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ansi codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the .write() method to do their work. If + this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if autoreset or + strip or convert are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly:: + + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + print >>stream, Fore.BLUE + 'blue text on stderr' + + +Status & Known Problems +======================= + +Feature complete as far as colored text goes, but still finding bugs and +occasionally making small changes to the API (such as new keyword arguments +to ``init()``). + +Only tested on WinXP (CMD, Console2) and Ubuntu (gnome-terminal, xterm). Much +obliged if anyone can let me know how it fares elsewhere, in particular on +Macs. + +I'd like to add the ability to handle ANSI codes which position the text cursor +and clear the terminal. + +See outstanding issues and wishlist at: +http://code.google.com/p/colorama/issues/list + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd *love* to hear about it on that issues list. + + +Recognised ANSI Sequences +========================= + +ANSI sequences generally take the form: + + ESC [ ; ... + +Where is an integer, and is a single letter. Zero or more +params are passed to a . If no params are passed, it is generally +synonymous with passing a single zero. No spaces exist in the sequence, they +have just been inserted here to make it easy to read. + +The only ANSI sequences that colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + +Multiple numeric params to the 'm' command can be combined into a single +sequence, eg:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised nor stripped. + + +Development +=========== + +Running tests requires: + +- Michael Foord's 'mock' module to be installed. +- Either to be run under Python2.7 or 3.1 stdlib unittest, or to have Michael + Foord's 'unittest2' module to be installed. + +unittest2 test discovery doesn't work for colorama, so I use 'nose':: + + nosetests -s + +The -s is required because 'nosetests' otherwise applies a proxy of its own to +stdout, which confuses the unit tests. + + +Thanks +====== +Roger Binns, for many suggestions, valuable feedback, & bug reports. +Tim Golden for thought and much appreciated feedback on the initial idea. + + +Changes +======= + +0.1.17 + Prevent printing of garbage ANSI codes upon installing with pip +0.1.16 + Re-upload to fix previous error. Make clean now removes old MANIFEST. +0.1.15 + Completely broken. Distribution was empty due to leftover invalid MANIFEST + file from building on a different platform. + Fix python3 incompatibility kindly reported by G |uumlaut| nter Kolousek +0.1.14 + Fix hard-coded reset to white-on-black colors. Fore.RESET, Back.RESET + and Style.RESET_ALL now revert to the colors as they were when init() + was called. Some lessons hopefully learned about testing prior to release. +0.1.13 + Completely broken: barfed when installed using pip. +0.1.12 + Completely broken: contained no source code. double oops. +0.1.11 + Completely broken: fatal import errors on Ubuntu. oops. +0.1.10 + Stop emulating 'bright' text with bright backgrounds. + Display 'normal' text using win32 normal foreground instead of bright. + Drop support for 'dim' text. +0.1.9 + Fix incompatibility with Python 2.5 and earlier. + Remove setup.py dependency on setuptools, now uses stdlib distutils. +0.1.8 + Fix ghastly errors all over the place on Ubuntu. + Add init kwargs 'convert' and 'strip', which supercede the old 'wrap'. +0.1.7 + Python 3 compatible. + Fix: Now strips ansi on windows without necessarily converting it to + win32 calls (eg. if output is not a tty.) + Fix: Flaky interaction of interleaved ansi sent to stdout and stderr. + Improved demo.sh (hg checkout only.) +0.1.6 + Fix ansi sequences with no params now default to parmlist of [0]. + Fix flaky behaviour of autoreset and reset_all atexit. + Fix stacking of repeated atexit calls - now just called once. + Fix ghastly import problems while running tests. + 'demo.py' (hg checkout only) now demonstrates autoreset and reset atexit. + Provide colorama.VERSION, used by setup.py. + Tests defanged so they no longer actually change terminal color when run. +0.1.5 + Now works on Ubuntu. +0.1.4 + Implemented RESET_ALL on application exit +0.1.3 + Implemented init(wrap=False) +0.1.2 + Implemented init(autoreset=True) +0.1.1 + Minor tidy +0.1 + Works on Windows for foreground color, background color, bright or dim + +.. |uumlaut| unicode:: U+00FC .. u with umlaut + :trim: + diff --git a/pyzen/_colorama/ansi.py b/pyzen/_colorama/ansi.py index 7c45983..7b818e1 100644 --- a/pyzen/_colorama/ansi.py +++ b/pyzen/_colorama/ansi.py @@ -1,49 +1,49 @@ -''' -This module generates ANSI character codes to printing colors to terminals. -See: http://en.wikipedia.org/wiki/ANSI_escape_code -''' - -CSI = '\033[' - -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 AnsiFore: - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - RESET = 39 - -class AnsiBack: - BLACK = 40 - RED = 41 - GREEN = 42 - YELLOW = 43 - BLUE = 44 - MAGENTA = 45 - CYAN = 46 - WHITE = 47 - RESET = 49 - -class AnsiStyle: - BRIGHT = 1 - DIM = 2 - NORMAL = 22 - RESET_ALL = 0 - -Fore = AnsiCodes( AnsiFore ) -Back = AnsiCodes( AnsiBack ) -Style = AnsiCodes( AnsiStyle ) - +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' + +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 AnsiFore: + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + +class AnsiBack: + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + +class AnsiStyle: + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiCodes( AnsiFore ) +Back = AnsiCodes( AnsiBack ) +Style = AnsiCodes( AnsiStyle ) + diff --git a/pyzen/_colorama/ansitowin32.py b/pyzen/_colorama/ansitowin32.py index 363061d..9ce77dd 100644 --- a/pyzen/_colorama/ansitowin32.py +++ b/pyzen/_colorama/ansitowin32.py @@ -1,176 +1,176 @@ - -import re -import sys - -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style -from .winterm import WinTerm, WinColor, WinStyle -from .win32 import windll - - -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_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') - - 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 = sys.platform.startswith('win') - - # should we strip ANSI sequences from our output? - if strip is None: - strip = on_windows - self.strip = strip - - # should we should convert ANSI sequences into win32 calls? - if convert is None: - convert = on_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, ), - 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, ), - } - - - 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,)) - else: - 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 - for match in self.ANSI_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(paramstring) - self.call_win32(command, params) - - - def extract_params(self, paramstring): - def split(paramstring): - for p in paramstring.split(';'): - if p != '': - yield int(p) - return tuple(split(paramstring)) - - - def call_win32(self, command, params): - if params == []: - params = [0] - 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) - + +import re +import sys + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll + + +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_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') + + 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 = sys.platform.startswith('win') + + # should we strip ANSI sequences from our output? + if strip is None: + strip = on_windows + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = on_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, ), + 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, ), + } + + + 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,)) + else: + 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 + for match in self.ANSI_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(paramstring) + self.call_win32(command, params) + + + def extract_params(self, paramstring): + def split(paramstring): + for p in paramstring.split(';'): + if p != '': + yield int(p) + return tuple(split(paramstring)) + + + def call_win32(self, command, params): + if params == []: + params = [0] + 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) + diff --git a/pyzen/_colorama/initialise.py b/pyzen/_colorama/initialise.py index 4df5c3e..eca2bea 100644 --- a/pyzen/_colorama/initialise.py +++ b/pyzen/_colorama/initialise.py @@ -1,38 +1,38 @@ -import atexit -import sys - -from .ansitowin32 import AnsiToWin32 - - -orig_stdout = sys.stdout -orig_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 wrap==False and (autoreset==True or convert==True or strip==True): - raise ValueError('wrap=False conflicts with any other arg=True') - - sys.stdout = wrap_stream(orig_stdout, convert, strip, autoreset, wrap) - sys.stderr = wrap_stream(orig_stderr, convert, strip, autoreset, wrap) - - global atexit_done - if not atexit_done: - atexit.register(reset_all) - atexit_done = True - - -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 - +import atexit +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = sys.stdout +orig_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 wrap==False and (autoreset==True or convert==True or strip==True): + raise ValueError('wrap=False conflicts with any other arg=True') + + sys.stdout = wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + sys.stderr = wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +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/pyzen/_colorama/win32.py b/pyzen/_colorama/win32.py index 2a6fc94..5f49e6c 100644 --- a/pyzen/_colorama/win32.py +++ b/pyzen/_colorama/win32.py @@ -1,95 +1,95 @@ - -# from winbase.h -STDOUT = -11 -STDERR = -12 - -try: - from ctypes import windll -except ImportError: - windll = None - SetConsoleTextAttribute = lambda *_: None -else: - from ctypes import ( - byref, Structure, c_char, c_short, c_uint32, c_ushort - ) - - handles = { - STDOUT: windll.kernel32.GetStdHandle(STDOUT), - STDERR: windll.kernel32.GetStdHandle(STDERR), - } - - SHORT = c_short - WORD = c_ushort - DWORD = c_uint32 - TCHAR = c_char - - class COORD(Structure): - """struct in wincon.h""" - _fields_ = [ - ('X', SHORT), - ('Y', SHORT), - ] - - class SMALL_RECT(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("Left", SHORT), - ("Top", SHORT), - ("Right", SHORT), - ("Bottom", SHORT), - ] - - class CONSOLE_SCREEN_BUFFER_INFO(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", WORD), - ("srWindow", SMALL_RECT), - ("dwMaximumWindowSize", COORD), - ] - - def GetConsoleScreenBufferInfo(stream_id): - handle = handles[stream_id] - csbi = CONSOLE_SCREEN_BUFFER_INFO() - success = windll.kernel32.GetConsoleScreenBufferInfo( - handle, byref(csbi)) - # This fails when imported via setup.py when installing using 'pip' - # presumably the fix is that running setup.py should not trigger all - # this activity. - # assert success - return csbi - - def SetConsoleTextAttribute(stream_id, attrs): - handle = handles[stream_id] - success = windll.kernel32.SetConsoleTextAttribute(handle, attrs) - assert success - - def SetConsoleCursorPosition(stream_id, position): - handle = handles[stream_id] - position = COORD(*position) - success = windll.kernel32.SetConsoleCursorPosition(handle, position) - assert success - - def FillConsoleOutputCharacter(stream_id, char, length, start): - handle = handles[stream_id] - char = TCHAR(char) - length = DWORD(length) - start = COORD(*start) - num_written = DWORD(0) - # AttributeError: function 'FillConsoleOutputCharacter' not found - # could it just be that my types are wrong? - success = windll.kernel32.FillConsoleOutputCharacter( - handle, char, length, start, byref(num_written)) - assert success - return num_written.value - - -if __name__=='__main__': - x = GetConsoleScreenBufferInfo(STDOUT) - print(x.dwSize) - print(x.dwCursorPosition) - print(x.wAttributes) - print(x.srWindow) - print(x.dwMaximumWindowSize) - + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + from ctypes import windll +except ImportError: + windll = None + SetConsoleTextAttribute = lambda *_: None +else: + from ctypes import ( + byref, Structure, c_char, c_short, c_uint32, c_ushort + ) + + handles = { + STDOUT: windll.kernel32.GetStdHandle(STDOUT), + STDERR: windll.kernel32.GetStdHandle(STDERR), + } + + SHORT = c_short + WORD = c_ushort + DWORD = c_uint32 + TCHAR = c_char + + class COORD(Structure): + """struct in wincon.h""" + _fields_ = [ + ('X', SHORT), + ('Y', SHORT), + ] + + class SMALL_RECT(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("Left", SHORT), + ("Top", SHORT), + ("Right", SHORT), + ("Bottom", SHORT), + ] + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", WORD), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + + def GetConsoleScreenBufferInfo(stream_id): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = windll.kernel32.GetConsoleScreenBufferInfo( + handle, byref(csbi)) + # This fails when imported via setup.py when installing using 'pip' + # presumably the fix is that running setup.py should not trigger all + # this activity. + # assert success + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + success = windll.kernel32.SetConsoleTextAttribute(handle, attrs) + assert success + + def SetConsoleCursorPosition(stream_id, position): + handle = handles[stream_id] + position = COORD(*position) + success = windll.kernel32.SetConsoleCursorPosition(handle, position) + assert success + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = TCHAR(char) + length = DWORD(length) + start = COORD(*start) + num_written = DWORD(0) + # AttributeError: function 'FillConsoleOutputCharacter' not found + # could it just be that my types are wrong? + success = windll.kernel32.FillConsoleOutputCharacter( + handle, char, length, start, byref(num_written)) + assert success + return num_written.value + + +if __name__=='__main__': + x = GetConsoleScreenBufferInfo(STDOUT) + print(x.dwSize) + print(x.dwCursorPosition) + print(x.wAttributes) + print(x.srWindow) + print(x.dwMaximumWindowSize) + diff --git a/pyzen/_colorama/winterm.py b/pyzen/_colorama/winterm.py index 4326c21..63f4469 100644 --- a/pyzen/_colorama/winterm.py +++ b/pyzen/_colorama/winterm.py @@ -1,69 +1,69 @@ - -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 - - -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 - - def reset_all(self, on_stderr=None): - self.set_attrs(self._default) - self.set_console(attrs=self._default) - - def fore(self, fore=None, on_stderr=False): - if fore is None: - fore = self._default_fore - self._fore = fore - self.set_console(on_stderr=on_stderr) - - def back(self, back=None, on_stderr=False): - if back is None: - back = self._default_back - self._back = back - 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) - + +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 + + +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 + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + 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) + diff --git a/pyzen/ui/osx.py b/pyzen/ui/osx.py index cd2ad81..64a40ae 100644 --- a/pyzen/ui/osx.py +++ b/pyzen/ui/osx.py @@ -1,16 +1,43 @@ import subprocess +import os from pyzen.ui.base import img_path, PyZenUI + class AppleScriptError(Exception): pass +class TerminalNotifierError(Exception): + pass + + def run_script(script): proc = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(script) if proc.returncode != 0: - raise AppleScriptError('osascript failure: %s'%err) + raise AppleScriptError('osascript failure: %s' % err) + out = out.strip() + if out == 'true': + return True + elif out == 'false': + return False + elif out.isdigit(): + return int(out) + else: + return out + + +def run_notifier(**kwargs): + args = list() + for (k, v) in kwargs.iteritems(): + if (k in ['message', 'title', 'subtitle', 'sound', 'group', 'list', 'activate', 'sender', 'open', 'execute']): + args += ['-' + k, v] + proc = subprocess.Popen(['terminal-notifier'] + args, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode != 0: + raise TerminalNotifierError('terminal-notifier failure: %s' % err) out = out.strip() if out == 'true': return True @@ -21,6 +48,7 @@ def run_script(script): else: return out + def is_growl_running(): """Check if Growl is running. Run this before any other Growl functions.""" script = """ @@ -30,46 +58,62 @@ def is_growl_running(): """ return run_script(script) + +def is_notifier_installed(): + """Check if terminal-notifier is installed. Run this before any other functions.""" + return os.system("which terminal-notifier") == 0 + + def register_app(): - script= """ + script = """ tell application "GrowlHelperApp" - -- Make a list of all the notification types + -- Make a list of all the notification types -- that this script will ever send: set the notificationsList to {"Test Successful" , "Test Failure"} -- Register our script with growl. - -- You can optionally (as here) set a default icon + -- You can optionally (as here) set a default icon -- for this script's notifications. register as application "ZenTest" all notifications notificationsList default notifications notificationsList end tell""" run_script(script) + def notify(type, title, msg, img='logo.png'): - script= """ + script = """ tell application "GrowlHelperApp" notify with name "%s" title "%s" description "%s" application name "ZenTest" image from location "file://%s" - end tell"""%(type, title, msg, img_path(img)) + end tell""" % (type, title, msg, img_path(img)) run_script(script) -class GrowlUI(PyZenUI): + +class OSXUI(PyZenUI): + """A PyZen UI that uses Growl. Only supported on OS X.""" - + name = 'osx' platform = 'darwin' - + def __init__(self): + self.has_notifier = is_notifier_installed() self.has_growl = is_growl_running() if self.has_growl: register_app() - + elif self.has_notifier: + pass + def notify(self, failure, title, msg, icon): + type = failure and 'Test Failure' or 'Test Successful' if self.has_growl: - type = failure and 'Test Failure' or 'Test Successful' - notify(type, title, msg, icon+'.png') + notify(type, title, msg, icon + '.png') + elif self.has_notifier: + run_notifier(message=msg, title=title, subtitle=type) # Random test stuff if __name__ == '__main__': + if is_notifier_installed(): + run_notifier(subtitle='Test Successful', title='Test Successful', message='Run 1 test(s) in 0.001 seconds') if is_growl_running(): register_app() - notify('Test Successful', 'Test Successful', 'Run 1 test(s) in 0.001 seconds', 'red.png') \ No newline at end of file + notify('Test Successful', 'Test Successful', 'Run 1 test(s) in 0.001 seconds', 'red.png')