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 The ``E`` Element factory for generating XML documents.
38 """
39
40 from __future__ import absolute_import
41
42 import lxml.etree as ET
43
44 from functools import partial
45
46 try:
47 basestring
48 except NameError:
49 basestring = str
50
51 try:
52 unicode
53 except NameError:
54 unicode = str
55
56
58 """Element generator factory.
59
60 Unlike the ordinary Element factory, the E factory allows you to pass in
61 more than just a tag and some optional attributes; you can also pass in
62 text and other elements. The text is added as either text or tail
63 attributes, and elements are inserted at the right spot. Some small
64 examples::
65
66 >>> from lxml import etree as ET
67 >>> from lxml.builder import E
68
69 >>> ET.tostring(E("tag"))
70 '<tag/>'
71 >>> ET.tostring(E("tag", "text"))
72 '<tag>text</tag>'
73 >>> ET.tostring(E("tag", "text", key="value"))
74 '<tag key="value">text</tag>'
75 >>> ET.tostring(E("tag", E("subtag", "text"), "tail"))
76 '<tag><subtag>text</subtag>tail</tag>'
77
78 For simple tags, the factory also allows you to write ``E.tag(...)`` instead
79 of ``E('tag', ...)``::
80
81 >>> ET.tostring(E.tag())
82 '<tag/>'
83 >>> ET.tostring(E.tag("text"))
84 '<tag>text</tag>'
85 >>> ET.tostring(E.tag(E.subtag("text"), "tail"))
86 '<tag><subtag>text</subtag>tail</tag>'
87
88 Here's a somewhat larger example; this shows how to generate HTML
89 documents, using a mix of prepared factory functions for inline elements,
90 nested ``E.tag`` calls, and embedded XHTML fragments::
91
92 # some common inline elements
93 A = E.a
94 I = E.i
95 B = E.b
96
97 def CLASS(v):
98 # helper function, 'class' is a reserved word
99 return {'class': v}
100
101 page = (
102 E.html(
103 E.head(
104 E.title("This is a sample document")
105 ),
106 E.body(
107 E.h1("Hello!", CLASS("title")),
108 E.p("This is a paragraph with ", B("bold"), " text in it!"),
109 E.p("This is another paragraph, with a ",
110 A("link", href="http://www.python.org"), "."),
111 E.p("Here are some reserved characters: <spam&egg>."),
112 ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),
113 )
114 )
115 )
116
117 print ET.tostring(page)
118
119 Here's a prettyprinted version of the output from the above script::
120
121 <html>
122 <head>
123 <title>This is a sample document</title>
124 </head>
125 <body>
126 <h1 class="title">Hello!</h1>
127 <p>This is a paragraph with <b>bold</b> text in it!</p>
128 <p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p>
129 <p>Here are some reserved characters: <spam&egg>.</p>
130 <p>And finally, here is an embedded XHTML fragment.</p>
131 </body>
132 </html>
133
134 For namespace support, you can pass a namespace map (``nsmap``)
135 and/or a specific target ``namespace`` to the ElementMaker class::
136
137 >>> E = ElementMaker(namespace="http://my.ns/")
138 >>> print(ET.tostring( E.test ))
139 <test xmlns="http://my.ns/"/>
140
141 >>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'})
142 >>> print(ET.tostring( E.test ))
143 <p:test xmlns:p="http://my.ns/"/>
144 """
145
146 - def __init__(self, typemap=None,
147 namespace=None, nsmap=None, makeelement=None):
176
177 def add_cdata(elem, cdata):
178 if elem.text:
179 raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text)
180 elem.text = cdata
181
182 if str not in typemap:
183 typemap[str] = add_text
184 if unicode not in typemap:
185 typemap[unicode] = add_text
186 if ET.CDATA not in typemap:
187 typemap[ET.CDATA] = add_cdata
188
189 def add_dict(elem, item):
190 attrib = elem.attrib
191 for k, v in item.items():
192 if isinstance(v, basestring):
193 attrib[k] = v
194 else:
195 attrib[k] = typemap[type(v)](None, v)
196 if dict not in typemap:
197 typemap[dict] = add_dict
198
199 self._typemap = typemap
200
201 - def __call__(self, tag, *children, **attrib):
202 typemap = self._typemap
203
204 if self._namespace is not None and tag[0] != '{':
205 tag = self._namespace + tag
206 elem = self._makeelement(tag, nsmap=self._nsmap)
207 if attrib:
208 typemap[dict](elem, attrib)
209
210 for item in children:
211 if callable(item):
212 item = item()
213 t = typemap.get(type(item))
214 if t is None:
215 if ET.iselement(item):
216 elem.append(item)
217 continue
218 for basetype in type(item).__mro__:
219
220 t = typemap.get(basetype)
221 if t is not None:
222 break
223 else:
224 raise TypeError("bad argument type: %s(%r)" %
225 (type(item).__name__, item))
226 v = t(elem, item)
227 if v:
228 typemap.get(type(v))(elem, v)
229
230 return elem
231
233 return partial(self, tag)
234
235
236
237 E = ElementMaker()
238