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