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