cross-platform coloured text in terminal

Jonathan Hartley tartley at
Sat Apr 17 06:52:45 EDT 2010

On Apr 16, 5:59 pm, Lie Ryan <lie.1... at> wrote:
> On 04/16/10 19:28, Jonathan Hartley wrote:
> > I'm playing with ideas of what API to expose. My favourite one is to
> > simply embed ANSI codes in the stream to be printed. Then this will
> > work as-is on Mac and *nix. To make it work on Windows, printing could
> > be done to a file0-like object which wraps stdout:
> The problem with that is you're simply reinventing ANSI.SYS device driver.
> An alternative API is you could override .__add__(), like so (completely
> untested):
> class Color(object):
>    def __init__(self, color):
>        self.color =  map_the_color(color)
>        self.string = ""
>    def __add__(self, string):
>        self.string += string
>        return self
>    def __str__(self):
>        if terminal_can_do_ansi_color:
>            return ansicolorescape(self.string, self.color)
>        elif windows:
>            syscalltocolor(self.color)
>            print self.string
>            syscalltocolor(reset the color)
>            return ""
> GREEN = Color('green')
> print GREEN + "Great" + "Good"
> you can even go a bit further and allow chained calls (again, completely
> untested, but you get the idea):
> class Color(object):
>    def __init__(self, color):
>        self.color =  map_the_color(color)
>        self.stack = []
>    def __add__(self, string):
>        if isinstance(string, Color):
>            # not a string, chain the calls
>            self.stack.append((string.color, []]))
>        else:
>            # a string,
>            self.stack[-1][1].append(string)
>        return self
>    def __radd__(self, string):
>        self.stack.append([self.default, string])
>        return self
>    def __str__(self):
>        if ansi_capable:
>            return colorescape(format, string)
>        elif windows:
>            for format, string in self.stack:
>                syscalltocolor(color)
>                print string
>                return ""
> GREEN = Color('green')
> RED = Color('red')
> print "Fairly" + GREEN + "Great" + RED + "Poor"
> or something like that, and you will have an API that works
> transparently on all platforms. The downside is that you cannot call
> str(GREEN + "foo") on windows.

Hey Lie,

Thanks heaps for the reply!

>> The problem with that is you're simply reinventing ANSI.SYS device driver.

I don't see that as a problem - in fact I think it's exactly my
goal! :-)

The difference is that the ANSI driver requires installation and a
reboot on the end-user's computer, which is a fiddly and intrusive
thing for a Python developer to achieve. Whereas doing the same job in
a Python module is easy to use for the Python developer - they just
import the module, maybe call an 'init()' function, and then the ANSI
functionality works on all platforms.

Your ideas about generating and chaining the ANSI code strings are
great. I worry though, about intermingling the code that generates
ANSI escape sequences with the code which makes them work on Windows.
The problem is that then, only applications which use your ANSI-
generation library will work on Windows. Whereas if these two things
are kept separate, then applications which use any other ANSI-
generation techniques, such as using 'termcolor', or manaully printing
raw ANSI sequences, these can also all work on Windows too, simply by
adding an import and an 'init()' call to the start of the application.

Am I making sense? Many thanks for your thoughts.


More information about the Python-list mailing list