0001"""Secondary abstractions for terminal control
0002
0003The code here is mostly a front-end to the ``control`` module. The OutputStream class
0004acts as a wrapper for tty pseudo files like sys.stdout and sys.stder and can 
0005handle several ANSI control codes on multiple platforms.
0006
0007Here is an example usage.
0008
0009.. code-block:: Python
0010
0011    import os
0012    stdout.write('spam' + 
0013        color('bright','yellow','white') + 
0014        'eggs' + 
0015        color('default') + os.linesep
0016    )
0017
0018Warning: on IPython setting sys.stdout to the stdout object in this
0019module will break readline.
0020"""
0021
0022__all__ = ['color', 'OutputStream', 'stdout', 'stderr']
0023
0024import sys, os, re, control
0025
0026escape_parts = re.compile('\x01?\x1b\\[([0-9;]*)m\x02?')
0027
0028def color(codes=[], fg=None, bg=None):
0029    """Returns an ANSI control code. This is useful when writing to an OutputStream.
0030    
0031    codes
0032        A list containing strings. The strings should one of the keys in
0033        the DISPLAY_CODES constant in the ``control`` module. It can also
0034        be just a single string.
0035    fg, bg
0036        A string. Explicitly for setting the foreground or background. Use
0037        one of the keys in the COLORS constant in the ``control`` module.
0038        
0039    color(('bright','underline'),'blue','white')
0040        give bright blue foreground and white background with underline
0041    color(fg='blue')
0042        gives a blue foreground
0043    color('default')
0044        resets the color to the default.
0045    
0046    Avoid using black or white. Depending on the situation the default
0047    background/foreground is normally black or white, but it's hard to
0048    tell which. Bare terminals are normally white on black, but virtual
0049    terminals run from X or another GUI system are often black on white.
0050    This can lead to unpredicatble results. If you want reversed
0051    colours, use the 'reverse' code, and if you want to set the
0052    colors back to their original colors, use the 'default' code.
0053    
0054    Also, be prudent with your use of 'hidden' and 'blink'. Several terminals
0055    do not support them (and for good reason too), they can be really
0056    annoying and make reading difficult.
0057    """
0058    return control.ANSI.displaycode(codes, fg, bg)
0059
0060class OutputStream(object):
0061
0062    def __init__(self, stream):
0063        self.stream = stream
0064
0065    def raw_write(self, text):
0066        self.stream.write(text)
0067
0068    # methods below here are also methods of the file object
0069
0070    def write(self, text):
0071        chunks = escape_parts.split(text)
0072        i = 0
0073        for chunk in chunks:
0074            if chunk != '':
0075                if i % 2 == 0:
0076                    self.stream.write(chunk)
0077                else:
0078                    c=chunk.split(';')
0079                    r=control.ANSI.readcodes(c)
0080                    control.display(**r)
0081                # failure to flush after ouput can cause weird ordering behaviour
0082                # when writting to stdout and stderr simutaniously. This should
0083                # fix the worst of it, but application developers should be warned
0084                # not to really on the state of things between call between one method
0085                # call and another
0086                self.flush()
0087            i += 1
0088
0089    def writelines(self, lines):
0090        self.write(lines.join(os.linesep))
0091
0092    def flush(self):
0093        return self.stream.flush()
0094
0095    def isatty(self,*args,**kwargs):
0096        return self.stream.isatty(*args,**kwargs)
0097
0098    # these should really never be called, they are here for API
0099    # compatibility puposes (although I doubt it makes any difference)
0100    def tell(self,*args,**kwargs):
0101        return self.stream.tell(*args,**kwargs)
0102    def truncate(self,*args,**kwargs):
0103        return self.stream.truncate(*args,**kwargs)
0104    def readinto(self,*args,**kwargs):
0105        return self.stream.readinto(*args,**kwargs)
0106    def readline(self,*args,**kwargs):
0107        return self.stream.readline(*args,**kwargs)
0108    def readlines(self,*args,**kwargs):
0109        return self.stream.readlines(*args,**kwargs)
0110    def next(self,*args,**kwargs):
0111        return self.stream.next(*args,**kwargs)
0112    def read(self,*args,**kwargs):
0113        return self.stream.read(*args,**kwargs)
0114    def close(self,*args,**kwargs):
0115        return self.stream.close(*args,**kwargs)
0116    def fileno(self,*args,**kwargs):
0117        return self.stream.fileno(*args,**kwargs)
0118    def xreadlines(self,*args,**kwargs):
0119        return self.stream.xreadlines(*args,**kwargs)
0120
0121    def _get_mode(self): return self.stream.mode
0122    def _set_mode(self, value): self.stream.mode = value
0123    mode = property(_get_mode, _set_mode)
0124
0125    def _get_newlines(self): return self.stream.newlines
0126    def _set_newlines(self, value): self.stream.newlines = value
0127    newlines = property(_get_newlines, _set_newlines)
0128
0129    def _get_encoding(self): return self.stream.encoding
0130    def _set_encoding(self, value): self.stream.encoding = value
0131    encoding = property(_get_encoding, _set_encoding)
0132
0133    def _get_softspace(self): return self.stream.softspace
0134    def _set_softspace(self, value): self.stream.softspace = value
0135    softspace = property(_get_softspace, _set_softspace)
0136
0137    def _get_name(self): return self.stream.name
0138    def _set_name(self, value): self.stream.name = value
0139    name = property(_get_name, _set_name)
0140
0141# wrapper for sys.stdout
0142stdout = OutputStream(sys.stdout)
0143
0144# wrapper for sys.stderr
0145stderr = OutputStream(sys.stderr)