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 import etree as ET
37
38 try:
39 from functools import partial
40 except ImportError:
41
43 return lambda *args, **kwargs: func(tag, *args, **kwargs)
44
45
47 """Element generator factory.
48
49 Unlike the ordinary Element factory, the E factory allows you to pass in
50 more than just a tag and some optional attributes; you can also pass in
51 text and other elements. The text is added as either text or tail
52 attributes, and elements are inserted at the right spot. Some small
53 examples::
54
55 >>> from lxml import etree as ET
56 >>> from lxml.builder import E
57
58 >>> ET.tostring(E("tag"))
59 '<tag/>'
60 >>> ET.tostring(E("tag", "text"))
61 '<tag>text</tag>'
62 >>> ET.tostring(E("tag", "text", key="value"))
63 '<tag key="value">text</tag>'
64 >>> ET.tostring(E("tag", E("subtag", "text"), "tail"))
65 '<tag><subtag>text</subtag>tail</tag>'
66
67 For simple tags, the factory also allows you to write ``E.tag(...)`` instead
68 of ``E('tag', ...)``::
69
70 >>> ET.tostring(E.tag())
71 '<tag/>'
72 >>> ET.tostring(E.tag("text"))
73 '<tag>text</tag>'
74 >>> ET.tostring(E.tag(E.subtag("text"), "tail"))
75 '<tag><subtag>text</subtag>tail</tag>'
76
77 Here's a somewhat larger example; this shows how to generate HTML
78 documents, using a mix of prepared factory functions for inline elements,
79 nested ``E.tag`` calls, and embedded XHTML fragments::
80
81 # some common inline elements
82 A = E.a
83 I = E.i
84 B = E.b
85
86 def CLASS(v):
87 # helper function, 'class' is a reserved word
88 return {'class': v}
89
90 page = (
91 E.html(
92 E.head(
93 E.title("This is a sample document")
94 ),
95 E.body(
96 E.h1("Hello!", CLASS("title")),
97 E.p("This is a paragraph with ", B("bold"), " text in it!"),
98 E.p("This is another paragraph, with a ",
99 A("link", href="http://www.python.org"), "."),
100 E.p("Here are some reservered characters: <spam&egg>."),
101 ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),
102 )
103 )
104 )
105
106 print ET.tostring(page)
107
108 Here's a prettyprinted version of the output from the above script::
109
110 <html>
111 <head>
112 <title>This is a sample document</title>
113 </head>
114 <body>
115 <h1 class="title">Hello!</h1>
116 <p>This is a paragraph with <b>bold</b> text in it!</p>
117 <p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p>
118 <p>Here are some reservered characters: <spam&egg>.</p>
119 <p>And finally, here is an embedded XHTML fragment.</p>
120 </body>
121 </html>
122 """
123
124 - def __init__(self, typemap=None,
125 namespace=None, nsmap=None, makeelement=None):
126 if namespace is not None:
127 self._namespace = '{' + namespace + '}'
128 else:
129 self._namespace = None
130
131 if nsmap:
132 self._nsmap = dict(nsmap)
133 else:
134 self._nsmap = None
135
136 if makeelement is not None:
137 assert callable(makeelement)
138 self._makeelement = makeelement
139 else:
140 self._makeelement = ET.Element
141
142
143
144 if typemap:
145 typemap = typemap.copy()
146 else:
147 typemap = {}
148
149 def add_text(elem, item):
150 if len(elem):
151 elem[-1].tail = (elem[-1].tail or "") + item
152 else:
153 elem.text = (elem.text or "") + item
154 if str not in typemap:
155 typemap[str] = add_text
156 if unicode not in typemap:
157 typemap[unicode] = add_text
158
159 def add_dict(elem, item):
160 attrib = elem.attrib
161 for k, v in item.items():
162 if isinstance(v, basestring):
163 attrib[k] = v
164 else:
165 attrib[k] = typemap[type(v)](None, v)
166 if dict not in typemap:
167 typemap[dict] = add_dict
168
169 self._typemap = typemap
170
171 - def __call__(self, tag, *children, **attrib):
172 get = self._typemap.get
173
174 if self._namespace is not None and tag[0] != '{':
175 tag = self._namespace + tag
176 elem = self._makeelement(tag, nsmap=self._nsmap)
177 if attrib:
178 get(dict)(elem, attrib)
179
180 for item in children:
181 if callable(item):
182 item = item()
183 t = get(type(item))
184 if t is None:
185 if ET.iselement(item):
186 elem.append(item)
187 continue
188 raise TypeError("bad argument type: %r" % item)
189 else:
190 v = t(elem, item)
191 if v:
192 get(type(v))(elem, v)
193
194 return elem
195
197 return partial(self, tag)
198
199
200 E = ElementMaker()
201