ColourText
.

**Purpose**. This is a simple pattern that shows how to output colour in a
console. If you run the tool, you can see test cases run, much like in the
screenshot above this post.
**Usage**. To get blue text like you see in the example, you'd create an
object called cstring like this:
```python
ob = ColourText.cstring("This has |blue:blue* text")
```
Then render it:
```python
ColourText.to_ascii(ob)
```
**Code.**
```python
#!/usr/bin/python
#
# Provides a mechanism for creating text with arbitrary formatting.
#
# Example
#
# raw = "|red:this text comes out red* Now we're outside of formatting"
# ob = cstring(raw)
# print to_ascii(ob)
#
# Backslash acts as an escape character.
#
# There's a simple to_ascii function further down that returns a stream
# of symbols that will render to colour in ascii, and there's test
# cases at the end showing more examples
#
# cturner, 20101121
#
class cstring(object):
"""Colour string. Star marks end of a block.
Example: "Text |red:I am red|blue:I'm blue* This bit has no formatting"."""
# Indicates a string
MARKER_S = 's'
# Indicates start of a format block
MARKER_F = 'f'
# Indicates end of a format block
MARKER_X = 'x'
def __init__(self, text):
(self._sequence, self._length) = self._parse(text)
def _parse(self, text):
"""Returns a sequence of segments, and a length."""
cb = []
wb = []
_str = 0
_tag = 1
_str = 2
b_escape = False
state = _str
length = 0
for c in text:
if b_escape:
wb.append(c)
else:
if state == _str:
if c == '|':
length += len(wb)
cb.append( (self.MARKER_S, ''.join(wb)) )
wb = []
state = _tag
elif c == '*':
length += len(wb)
cb.append( (self.MARKER_S, ''.join(wb)) )
cb.append( (self.MARKER_X, '') )
wb = []
else:
wb.append(c)
else:
if c == ':':
format_option = ''.join(wb)
cb.append( (self.MARKER_F, format_option) )
wb = []
state = _str
else:
wb.append(c)
if state != _str:
er = ' '.join( [ "Invalid format string. Still in format option"
, "at end of string. '%s'"%(''.join(wb))
] )
raise Exception(er)
if 0 < len(wb):
length += len(wb)
cb.append( (self.MARKER_S, ''.join(wb)) )
if not cb[-1][0] == self.MARKER_X:
cb.append( (self.MARKER_X, '') )
return (cb, length)
def __iter__(self):
return (x for x in self._sequence)
def __len__(self):
return self._length
class CstringException(Exception):
def __init__(self, msg):
self.message = msg
# --------------------------------------------------------
# struct
# --------------------------------------------------------
COLOURS = { 'blue': '\033[94m'
, 'green': '\033[92m'
, 'yellow': '\033[93m'
, 'red': '\033[91m'
}
ENDC = '\033[0m'
def to_ascii(cs):
sb = []
for code, value in cs:
if code == 's':
sb.append(value)
elif code == 'f':
if value in COLOURS:
sb.append(COLOURS[value])
else:
er = "Don't have a handler for format option '%s'"%value
raise Exception(er)
elif code == 'x':
sb.append(ENDC)
sb.append(value)
else:
raise Exception("cstring is invalid stream. got code %s."%code)
return ''.join(sb)
# --------------------------------------------------------
# test
# --------------------------------------------------------
def test():
lst_test_string = [ "This is simple text."
, u"This is simple text, but unicode"
, "This has |blue:blue* text"
, u"Unicode |green:green |red:and red* text"
, "|yellow:neglect to close tap here"
, ''
, "That last line was empty deliberately"
]
for c in COLOURS:
lst_test_string.append("|%s:%s*"%(c, c))
for s in lst_test_string:
print "> %s"%s
cs = cstring(s)
#print cs.sequence
print 'length', len(cs)
print to_ascii(cs)
#print
# --------------------------------------------------------
# loader
# --------------------------------------------------------
if __name__ == '__main__':
test()
```