comparison genshi/builder.py @ 902:09cc3627654c experimental-inline

Sync `experimental/inline` branch with [source:trunk@1126].
author cmlenz
date Fri, 23 Apr 2010 21:08:26 +0000
parents 1837f39efd6f
children
comparison
equal deleted inserted replaced
830:de82830f8816 902:09cc3627654c
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 # 2 #
3 # Copyright (C) 2006-2008 Edgewall Software 3 # Copyright (C) 2006-2009 Edgewall Software
4 # All rights reserved. 4 # All rights reserved.
5 # 5 #
6 # This software is licensed as described in the file COPYING, which 6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms 7 # you should have received as part of this distribution. The terms
8 # are also available at http://genshi.edgewall.org/wiki/License. 8 # are also available at http://genshi.edgewall.org/wiki/License.
30 can be used for that purpose), whereas keywords arguments are added as 30 can be used for that purpose), whereas keywords arguments are added as
31 attributes: 31 attributes:
32 32
33 >>> doc(tag.br) 33 >>> doc(tag.br)
34 <Element "p"> 34 <Element "p">
35 >>> print doc 35 >>> print(doc)
36 <p>Some text and <a href="http://example.org/">a link</a>.<br/></p> 36 <p>Some text and <a href="http://example.org/">a link</a>.<br/></p>
37 37
38 If an attribute name collides with a Python keyword, simply append an underscore 38 If an attribute name collides with a Python keyword, simply append an underscore
39 to the name: 39 to the name:
40 40
41 >>> doc(class_='intro') 41 >>> doc(class_='intro')
42 <Element "p"> 42 <Element "p">
43 >>> print doc 43 >>> print(doc)
44 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p> 44 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
45 45
46 As shown above, an `Element` can easily be directly rendered to XML text by 46 As shown above, an `Element` can easily be directly rendered to XML text by
47 printing it or using the Python ``str()`` function. This is basically a 47 printing it or using the Python ``str()`` function. This is basically a
48 shortcut for converting the `Element` to a stream and serializing that 48 shortcut for converting the `Element` to a stream and serializing that
49 stream: 49 stream:
50 50
51 >>> stream = doc.generate() 51 >>> stream = doc.generate()
52 >>> stream #doctest: +ELLIPSIS 52 >>> stream #doctest: +ELLIPSIS
53 <genshi.core.Stream object at ...> 53 <genshi.core.Stream object at ...>
54 >>> print stream 54 >>> print(stream)
55 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p> 55 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
56 56
57 57
58 The `tag` object also allows creating "fragments", which are basically lists 58 The `tag` object also allows creating "fragments", which are basically lists
59 of nodes (elements or text) that don't have a parent element. This can be useful 59 of nodes (elements or text) that don't have a parent element. This can be useful
62 returns an object of type `Fragment`: 62 returns an object of type `Fragment`:
63 63
64 >>> fragment = tag('Hello, ', tag.em('world'), '!') 64 >>> fragment = tag('Hello, ', tag.em('world'), '!')
65 >>> fragment 65 >>> fragment
66 <Fragment> 66 <Fragment>
67 >>> print fragment 67 >>> print(fragment)
68 Hello, <em>world</em>! 68 Hello, <em>world</em>!
69 """ 69 """
70 70
71 from genshi.core import Attrs, Markup, Namespace, QName, Stream, \ 71 from genshi.core import Attrs, Markup, Namespace, QName, Stream, \
72 START, END, TEXT 72 START, END, TEXT
91 def __call__(self, *args): 91 def __call__(self, *args):
92 """Append any positional arguments as child nodes. 92 """Append any positional arguments as child nodes.
93 93
94 :see: `append` 94 :see: `append`
95 """ 95 """
96 map(self.append, args) 96 for arg in args:
97 self.append(arg)
97 return self 98 return self
98 99
99 def __iter__(self): 100 def __iter__(self):
100 return self._generate() 101 return self._generate()
101 102
102 def __repr__(self): 103 def __repr__(self):
103 return '<%s>' % self.__class__.__name__ 104 return '<%s>' % type(self).__name__
104 105
105 def __str__(self): 106 def __str__(self):
106 return str(self.generate()) 107 return str(self.generate())
107 108
108 def __unicode__(self): 109 def __unicode__(self):
123 self.children.append(node) 124 self.children.append(node)
124 elif isinstance(node, Fragment): 125 elif isinstance(node, Fragment):
125 self.children.extend(node.children) 126 self.children.extend(node.children)
126 elif node is not None: 127 elif node is not None:
127 try: 128 try:
128 map(self.append, iter(node)) 129 for child in node:
130 self.append(child)
129 except TypeError: 131 except TypeError:
130 self.children.append(node) 132 self.children.append(node)
131 133
132 def _generate(self): 134 def _generate(self):
133 for child in self.children: 135 for child in self.children:
164 class Element(Fragment): 166 class Element(Fragment):
165 """Simple XML output generator based on the builder pattern. 167 """Simple XML output generator based on the builder pattern.
166 168
167 Construct XML elements by passing the tag name to the constructor: 169 Construct XML elements by passing the tag name to the constructor:
168 170
169 >>> print Element('strong') 171 >>> print(Element('strong'))
170 <strong/> 172 <strong/>
171 173
172 Attributes can be specified using keyword arguments. The values of the 174 Attributes can be specified using keyword arguments. The values of the
173 arguments will be converted to strings and any special XML characters 175 arguments will be converted to strings and any special XML characters
174 escaped: 176 escaped:
175 177
176 >>> print Element('textarea', rows=10, cols=60) 178 >>> print(Element('textarea', rows=10, cols=60))
177 <textarea rows="10" cols="60"/> 179 <textarea rows="10" cols="60"/>
178 >>> print Element('span', title='1 < 2') 180 >>> print(Element('span', title='1 < 2'))
179 <span title="1 &lt; 2"/> 181 <span title="1 &lt; 2"/>
180 >>> print Element('span', title='"baz"') 182 >>> print(Element('span', title='"baz"'))
181 <span title="&#34;baz&#34;"/> 183 <span title="&#34;baz&#34;"/>
182 184
183 The " character is escaped using a numerical entity. 185 The " character is escaped using a numerical entity.
184 The order in which attributes are rendered is undefined. 186 The order in which attributes are rendered is undefined.
185 187
186 If an attribute value evaluates to `None`, that attribute is not included 188 If an attribute value evaluates to `None`, that attribute is not included
187 in the output: 189 in the output:
188 190
189 >>> print Element('a', name=None) 191 >>> print(Element('a', name=None))
190 <a/> 192 <a/>
191 193
192 Attribute names that conflict with Python keywords can be specified by 194 Attribute names that conflict with Python keywords can be specified by
193 appending an underscore: 195 appending an underscore:
194 196
195 >>> print Element('div', class_='warning') 197 >>> print(Element('div', class_='warning'))
196 <div class="warning"/> 198 <div class="warning"/>
197 199
198 Nested elements can be added to an element using item access notation. 200 Nested elements can be added to an element using item access notation.
199 The call notation can also be used for this and for adding attributes 201 The call notation can also be used for this and for adding attributes
200 using keyword arguments, as one would do in the constructor. 202 using keyword arguments, as one would do in the constructor.
201 203
202 >>> print Element('ul')(Element('li'), Element('li')) 204 >>> print(Element('ul')(Element('li'), Element('li')))
203 <ul><li/><li/></ul> 205 <ul><li/><li/></ul>
204 >>> print Element('a')('Label') 206 >>> print(Element('a')('Label'))
205 <a>Label</a> 207 <a>Label</a>
206 >>> print Element('a')('Label', href="target") 208 >>> print(Element('a')('Label', href="target"))
207 <a href="target">Label</a> 209 <a href="target">Label</a>
208 210
209 Text nodes can be nested in an element by adding strings instead of 211 Text nodes can be nested in an element by adding strings instead of
210 elements. Any special characters in the strings are escaped automatically: 212 elements. Any special characters in the strings are escaped automatically:
211 213
212 >>> print Element('em')('Hello world') 214 >>> print(Element('em')('Hello world'))
213 <em>Hello world</em> 215 <em>Hello world</em>
214 >>> print Element('em')(42) 216 >>> print(Element('em')(42))
215 <em>42</em> 217 <em>42</em>
216 >>> print Element('em')('1 < 2') 218 >>> print(Element('em')('1 < 2'))
217 <em>1 &lt; 2</em> 219 <em>1 &lt; 2</em>
218 220
219 This technique also allows mixed content: 221 This technique also allows mixed content:
220 222
221 >>> print Element('p')('Hello ', Element('b')('world')) 223 >>> print(Element('p')('Hello ', Element('b')('world')))
222 <p>Hello <b>world</b></p> 224 <p>Hello <b>world</b></p>
223 225
224 Quotes are not escaped inside text nodes: 226 Quotes are not escaped inside text nodes:
225 >>> print Element('p')('"Hello"') 227 >>> print(Element('p')('"Hello"'))
226 <p>"Hello"</p> 228 <p>"Hello"</p>
227 229
228 Elements can also be combined with other elements or strings using the 230 Elements can also be combined with other elements or strings using the
229 addition operator, which results in a `Fragment` object that contains the 231 addition operator, which results in a `Fragment` object that contains the
230 operands: 232 operands:
231 233
232 >>> print Element('br') + 'some text' + Element('br') 234 >>> print(Element('br') + 'some text' + Element('br'))
233 <br/>some text<br/> 235 <br/>some text<br/>
234 236
235 Elements with a namespace can be generated using the `Namespace` and/or 237 Elements with a namespace can be generated using the `Namespace` and/or
236 `QName` classes: 238 `QName` classes:
237 239
238 >>> from genshi.core import Namespace 240 >>> from genshi.core import Namespace
239 >>> xhtml = Namespace('http://www.w3.org/1999/xhtml') 241 >>> xhtml = Namespace('http://www.w3.org/1999/xhtml')
240 >>> print Element(xhtml.html, lang='en') 242 >>> print(Element(xhtml.html, lang='en'))
241 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/> 243 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
242 """ 244 """
243 __slots__ = ['tag', 'attrib'] 245 __slots__ = ['tag', 'attrib']
244 246
245 def __init__(self, tag_, **attrib): 247 def __init__(self, tag_, **attrib):
258 self.attrib |= _kwargs_to_attrs(kwargs) 260 self.attrib |= _kwargs_to_attrs(kwargs)
259 Fragment.__call__(self, *args) 261 Fragment.__call__(self, *args)
260 return self 262 return self
261 263
262 def __repr__(self): 264 def __repr__(self):
263 return '<%s "%s">' % (self.__class__.__name__, self.tag) 265 return '<%s "%s">' % (type(self).__name__, self.tag)
264 266
265 def _generate(self): 267 def _generate(self):
266 yield START, (self.tag, self.attrib), (None, -1, -1) 268 yield START, (self.tag, self.attrib), (None, -1, -1)
267 for kind, data, pos in Fragment._generate(self): 269 for kind, data, pos in Fragment._generate(self):
268 yield kind, data, pos 270 yield kind, data, pos
281 283
282 A new element is created simply by accessing a correspondingly named 284 A new element is created simply by accessing a correspondingly named
283 attribute of the factory object: 285 attribute of the factory object:
284 286
285 >>> factory = ElementFactory() 287 >>> factory = ElementFactory()
286 >>> print factory.foo 288 >>> print(factory.foo)
287 <foo/> 289 <foo/>
288 >>> print factory.foo(id=2) 290 >>> print(factory.foo(id=2))
289 <foo id="2"/> 291 <foo id="2"/>
290 292
291 Markup fragments (lists of nodes without a parent element) can be created 293 Markup fragments (lists of nodes without a parent element) can be created
292 by calling the factory: 294 by calling the factory:
293 295
294 >>> print factory('Hello, ', factory.em('world'), '!') 296 >>> print(factory('Hello, ', factory.em('world'), '!'))
295 Hello, <em>world</em>! 297 Hello, <em>world</em>!
296 298
297 A factory can also be bound to a specific namespace: 299 A factory can also be bound to a specific namespace:
298 300
299 >>> factory = ElementFactory('http://www.w3.org/1999/xhtml') 301 >>> factory = ElementFactory('http://www.w3.org/1999/xhtml')
300 >>> print factory.html(lang="en") 302 >>> print(factory.html(lang="en"))
301 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/> 303 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
302 304
303 The namespace for a specific element can be altered on an existing factory 305 The namespace for a specific element can be altered on an existing factory
304 by specifying the new namespace using item access: 306 by specifying the new namespace using item access:
305 307
306 >>> factory = ElementFactory() 308 >>> factory = ElementFactory()
307 >>> print factory.html(factory['http://www.w3.org/2000/svg'].g(id=3)) 309 >>> print(factory.html(factory['http://www.w3.org/2000/svg'].g(id=3)))
308 <html><g xmlns="http://www.w3.org/2000/svg" id="3"/></html> 310 <html><g xmlns="http://www.w3.org/2000/svg" id="3"/></html>
309 311
310 Usually, the `ElementFactory` class is not be used directly. Rather, the 312 Usually, the `ElementFactory` class is not be used directly. Rather, the
311 `tag` instance should be used to create elements. 313 `tag` instance should be used to create elements.
312 """ 314 """
Copyright (C) 2012-2017 Edgewall Software