Mercurial > genshi > genshi-test
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 < 2"/> | 181 <span title="1 < 2"/> |
180 >>> print Element('span', title='"baz"') | 182 >>> print(Element('span', title='"baz"')) |
181 <span title=""baz""/> | 183 <span title=""baz""/> |
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 < 2</em> | 219 <em>1 < 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 """ |