1
# -*- test-case-name: twisted.conch.test.test_text -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Character attribute manipulation API
8
This module provides a domain-specific language (using Python syntax)
9
for the creation of text with additional display attributes associated
10
with it. It is intended as an alternative to manually building up
11
strings containing ECMA 48 character attribute control codes. It
12
currently supports foreground and background colors (black, red,
13
green, yellow, blue, magenta, cyan, and white), intensity selection,
14
underlining, blinking and reverse video. Character set selection
17
Character attributes are specified by using two Python operations:
18
attribute lookup and indexing. For example, the string \"Hello
19
world\" with red foreground and all other attributes set to their
20
defaults, assuming the name twisted.conch.insults.text.attributes has
21
been imported and bound to the name \"A\" (with the statement C{from
22
twisted.conch.insults.text import attributes as A}, for example) one
23
uses this expression::
25
| A.fg.red[\"Hello world\"]
27
Other foreground colors are set by substituting their name for
28
\"red\". To set both a foreground and a background color, this
31
| A.fg.red[A.bg.green[\"Hello world\"]]
33
Note that either A.bg.green can be nested within A.fg.red or vice
34
versa. Also note that multiple items can be nested within a single
35
index operation by separating them with commas::
37
| A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]
39
Other character attributes are set in a similar fashion. To specify a
40
blinking version of the previous expression::
42
| A.blink[A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]]
44
C{A.reverseVideo}, C{A.underline}, and C{A.bold} are also valid.
46
A third operation is actually supported: unary negation. This turns
47
off an attribute when an enclosing expression would otherwise have
48
caused it to be on. For example::
50
| A.underline[A.fg.red[\"Hello\", -A.underline[\" world\"]]]
55
from twisted.conch.insults import helper, insults
57
class _Attribute(object):
61
def __getitem__(self, item):
62
assert isinstance(item, (list, tuple, _Attribute, str))
63
if isinstance(item, (list, tuple)):
64
self.children.extend(item)
66
self.children.append(item)
69
def serialize(self, write, attrs=None):
71
attrs = helper.CharacterAttribute()
72
for ch in self.children:
73
if isinstance(ch, _Attribute):
74
ch.serialize(write, attrs.copy())
76
write(attrs.toVT102())
79
class _NormalAttr(_Attribute):
80
def serialize(self, write, attrs):
82
super(_NormalAttr, self).serialize(write, attrs)
84
class _OtherAttr(_Attribute):
85
def __init__(self, attrname, attrvalue):
86
self.attrname = attrname
87
self.attrvalue = attrvalue
91
result = _OtherAttr(self.attrname, not self.attrvalue)
92
result.children.extend(self.children)
95
def serialize(self, write, attrs):
96
attrs = attrs.wantOne(**{self.attrname: self.attrvalue})
97
super(_OtherAttr, self).serialize(write, attrs)
99
class _ColorAttr(_Attribute):
100
def __init__(self, color, ground):
105
def serialize(self, write, attrs):
106
attrs = attrs.wantOne(**{self.ground: self.color})
107
super(_ColorAttr, self).serialize(write, attrs)
109
class _ForegroundColorAttr(_ColorAttr):
110
def __init__(self, color):
111
super(_ForegroundColorAttr, self).__init__(color, 'foreground')
113
class _BackgroundColorAttr(_ColorAttr):
114
def __init__(self, color):
115
super(_BackgroundColorAttr, self).__init__(color, 'background')
117
class CharacterAttributes(object):
118
class _ColorAttribute(object):
119
def __init__(self, ground):
123
'black': helper.BLACK,
125
'green': helper.GREEN,
126
'yellow': helper.YELLOW,
128
'magenta': helper.MAGENTA,
130
'white': helper.WHITE}
132
def __getattr__(self, name):
134
return self.ground(self.attrs[name])
136
raise AttributeError(name)
138
fg = _ColorAttribute(_ForegroundColorAttr)
139
bg = _ColorAttribute(_BackgroundColorAttr)
142
'bold': insults.BOLD,
143
'blink': insults.BLINK,
144
'underline': insults.UNDERLINE,
145
'reverseVideo': insults.REVERSE_VIDEO}
147
def __getattr__(self, name):
150
if name in self.attrs:
151
return _OtherAttr(name, True)
152
raise AttributeError(name)
154
def flatten(output, attrs):
155
"""Serialize a sequence of characters with attribute information
157
The resulting string can be interpreted by VT102-compatible
158
terminals so that the contained characters are displayed and, for
159
those attributes which the terminal supports, have the attributes
160
specified in the input.
162
For example, if your terminal is VT102 compatible, you might run
163
this for a colorful variation on the \"hello world\" theme::
165
| from twisted.conch.insults.text import flatten, attributes as A
166
| from twisted.conch.insults.helper import CharacterAttribute
168
| A.normal[A.bold[A.fg.red['He'], A.fg.green['ll'], A.fg.magenta['o'], ' ',
169
| A.fg.yellow['Wo'], A.fg.blue['rl'], A.fg.cyan['d!']]],
170
| CharacterAttribute())
172
@param output: Object returned by accessing attributes of the
173
module-level attributes object.
175
@param attrs: A L{twisted.conch.insults.helper.CharacterAttribute}
178
@return: A VT102-friendly string
181
output.serialize(L.append, attrs)
184
attributes = CharacterAttributes()
186
__all__ = ['attributes', 'flatten']