This repository has been archived by the owner on Apr 18, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
taglib.py
152 lines (116 loc) · 3.91 KB
/
taglib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import cgi
__author__ = 'Jeremy Latt <jlatt@yelp.com>'
__all__ = ('T', 'Literal', 'Text', 'XHTML')
def iterflat(args):
for arg in args:
if hasattr(arg, '__iter__'):
for a in iterflat(arg):
yield a
else:
yield arg
def translate(attrs):
for key, value in attrs.iteritems():
if key.endswith('_'):
key = key[:-1]
yield (key, value)
def iterattrs(attrs):
for key, value in attrs.iteritems():
if isinstance(value, bool):
if value:
value = key
else: # skip False
continue
else:
value = unicode(value)
yield (key, value)
class StrSerializable(object):
def __unicode__(self):
from StringIO import StringIO
f = self.serialize(StringIO())
strval = f.getvalue()
f.close()
return strval
def __str__(self):
return unicode(self).encode('utf-8')
__repr__ = __str__
def serialize(self, f):
raise NotImplemented
class _Tag(StrSerializable):
empty = set(('link', 'input', 'hr', 'meta'))
def __init__(self, tagname, *children, **attrs):
assert tagname == cgi.escape(tagname), 'illegal tag name %s' % tagname
self.tagname = tagname
self.children = list(iterflat(children))
self.attrs = dict(translate(attrs))
def __call__(self, *children, **attrs):
if children:
self.children.extend(iterflat(children))
if attrs:
self.attrs.update(translate(attrs))
return self
def serialize(self, f):
is_empty = self.tagname in self.empty
open_tag_end = '>'
if is_empty:
assert not self.children, 'empty tag %s has children' % self.tagname
open_tag_end = '/>'
# open tag
formatted_attrs = ''.join([' %s="%s"' % (cgi.escape(key), cgi.escape(value, quote=True)) for key, value in iterattrs(self.attrs)])
f.write('<%s%s%s' % (self.tagname, formatted_attrs, open_tag_end))
for child in self.children:
if hasattr(child, 'serialize'):
child.serialize(f)
else:
f.write(cgi.escape(unicode(child)))
if not is_empty:
f.write('</%s>' % self.tagname)
return f
class Literal(StrSerializable):
def __init__(self, html):
self.html = unicode(html)
def serialize(self, f):
f.write(self.html)
return f
class Text(StrSerializable):
def __init__(self, text):
self.text = unicode(text)
def serialize(self, f):
f.write(cgi.escape(self.text))
return f
class CData(StrSerializable):
begin = '<![CDATA['
end = ']]>'
def __init__(self, value):
self.value = unicode(value)
def serialize(self, f):
f.write(self.begin)
f.write(self.value)
f.write(self.end)
return f
class ScriptCData(CData):
begin = '/* %s */' % CData.begin
end = '/* %s */' % CData.end
class XHTML(StrSerializable):
preamble = '<?xml version="1.0" encoding="UTF-8"?>'
namespace = 'http://www.w3.org/1999/xhtml'
strict_doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
def __init__(self):
self.html = T.html(xmlns=self.namespace)
def serialize(self, f):
f.write(self.preamble)
f.write(self.strict_doctype)
self.html.serialize(f)
return f
def __call__(self, *args, **kw):
self.html(*args, **kw)
return self
class TagFactory(object):
"""Tag wrapper that lets you use normal Tag syntax (i.e. T('head')(...)) as
well as "manifested" syntax like T.head(...).
"""
tag_cls = _Tag
def __getattr__(self, name):
return self.tag_cls(name)
def __call__(self, *args, **kwargs):
return self.tag_cls(*args, **kwargs)
T = TagFactory()