Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > cross-platform coloured text in terminal

Reply
Thread Tools

cross-platform coloured text in terminal

 
 
Jonathan Hartley
Guest
Posts: n/a
 
      04-16-2010
Hi,

It irks me that I know of no simple cross-platform way to print
colored terminal text from Python.

As I understand it, printing ANSI escape codes (as wrapped nicely by
module termcolor and others) works on Macs and *nix, but only works on
Windows if one has installed the ANSI.SYS device driver, which most
users have not. However, on Windows, there is an alternative method,
which is to make win32 calls via ctypes.

I'd like to try and unite these different implementations under a
single cross-platform API. Has this been done already? I understand
that the detailed capabilities of the two implementations (eg. dim/
bright colors) might not map neatly, but at least for simple colored
text, it should be OK.

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:


class ColorStream(object):

def __init__(self, wrapped):
self.wrapped = wrapped

def write(self, text):
# magic goes here
self.wrapped.write(text)

def __getattr__(self, name):
return getattr(self.wrapped, name)

term = ColorTerm(sys.stdout)
print <<term, ANSI.GREEN + "hello"

The idea being that in place of 'magic goes here', there will be code
that, on Windows, searches 'text' for ANSI escape codes, strips them
from the text, and converts them into the appropriate win32 calls.

For extra nasty magic, either the module or the user of the module
could wrap sys.stdout globally:

sys.stdout = ColoredStream(sys.stdout)

Then print statements in the user's code would simply be:

print ANSI.GREEN + "hello"

and this would work on all platforms.

No doubt there are many problems with these ideas. I would love to
hear about them. Many thanks.
 
Reply With Quote
 
 
 
 
Jonathan Hartley
Guest
Posts: n/a
 
      04-16-2010
On Apr 16, 10:28*am, Jonathan Hartley <(E-Mail Removed)> wrote:
> Hi,
>
> It irks me that I know of no simple cross-platform way to print
> colored terminal text from Python.
>
> As I understand it, printing ANSI escape codes (as wrapped nicely by
> module termcolor and others) works on Macs and *nix, but only works on
> Windows if one has installed the ANSI.SYS device driver, which most
> users have not. However, on Windows, there is an alternative method,
> which is to make win32 calls via ctypes.
>
> I'd like to try and unite these different implementations under a
> single cross-platform API. Has this been done already? I understand
> that the detailed capabilities of the two implementations (eg. dim/
> bright colors) might not map neatly, but at least for simple colored
> text, it should be OK.
>
> 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:
>
> class ColorStream(object):
>
> * * def __init__(self, wrapped):
> * * * * self.wrapped = wrapped
>
> * * def write(self, text):
> * * * * # magic goes here
> * * * * self.wrapped.write(text)
>
> * * def __getattr__(self, name):
> * * * * return getattr(self.wrapped, name)
>
> term = ColorTerm(sys.stdout)
> print <<term, ANSI.GREEN + "hello"
>
> The idea being that in place of 'magic goes here', there will be code
> that, on Windows, searches 'text' for ANSI escape codes, strips them
> from the text, and converts them into the appropriate win32 calls.
>
> For extra nasty magic, either the module or the user of the module
> could wrap sys.stdout globally:
>
> sys.stdout = ColoredStream(sys.stdout)
>
> Then print statements in the user's code would simply be:
>
> print ANSI.GREEN + "hello"
>
> and this would work on all platforms.
>
> No doubt there are many problems with these ideas. I would love to
> hear about them. Many thanks.



Sorry, I forgot to mention: The reason I like this idea is that, in
theory, all existing libraries like termcolor will then work,
unmodified, on all platforms.
 
Reply With Quote
 
 
 
 
Lie Ryan
Guest
Posts: n/a
 
      04-16-2010
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.
 
Reply With Quote
 
Jonathan Hartley
Guest
Posts: n/a
 
      04-17-2010
On Apr 16, 5:59*pm, Lie Ryan <(E-Mail Removed)> 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.

Jonathan
 
Reply With Quote
 
Jonathan Hartley
Guest
Posts: n/a
 
      04-20-2010
On Apr 17, 11:52*am, Jonathan Hartley <(E-Mail Removed)> wrote:
> On Apr 16, 5:59*pm, Lie Ryan <(E-Mail Removed)> 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):

>
> > classColor(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 thecolor)
> > * * * * * *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):

>
> > classColor(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.
>
> * Jonathan



I have implemented these ideas here. It seems to work.
http://pypi.python.org/pypi/colorama
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
coloured text in console Ghai C++ 4 04-19-2006 10:06 PM
Blue coloured text beneath folders. g saul Computer Support 10 10-02-2005 02:26 PM
coloured text... sudip C Programming 4 07-07-2005 01:41 AM
Differently Coloured Text in one textual area arun.hallan@gmail.com Java 2 02-11-2005 10:33 AM



Advertisments