Mercurial > genshi > genshi-test
comparison genshi/builder.py @ 230:24757b771651
Renamed Markup to Genshi in repository.
author | cmlenz |
---|---|
date | Mon, 11 Sep 2006 15:07:07 +0000 |
parents | markup/builder.py@41db0260ebb1 |
children | 8e75b83d3e71 676b78a0c5a0 |
comparison
equal
deleted
inserted
replaced
229:110d69dbbda3 | 230:24757b771651 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 # | |
3 # Copyright (C) 2006 Edgewall Software | |
4 # All rights reserved. | |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
8 # are also available at http://genshi.edgewall.org/wiki/License. | |
9 # | |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
12 # history and logs, available at http://genshi.edgewall.org/log/. | |
13 | |
14 from genshi.core import Attrs, Namespace, QName, Stream, START, END, TEXT | |
15 | |
16 __all__ = ['Fragment', 'Element', 'tag'] | |
17 | |
18 | |
19 class Fragment(object): | |
20 """Represents a markup fragment, which is basically just a list of element | |
21 or text nodes. | |
22 """ | |
23 __slots__ = ['children'] | |
24 | |
25 def __init__(self): | |
26 self.children = [] | |
27 | |
28 def __add__(self, other): | |
29 return Fragment()(self, other) | |
30 | |
31 def __call__(self, *args): | |
32 map(self.append, args) | |
33 return self | |
34 | |
35 def __iter__(self): | |
36 return self._generate() | |
37 | |
38 def __repr__(self): | |
39 return '<%s>' % self.__class__.__name__ | |
40 | |
41 def __str__(self): | |
42 return str(self.generate()) | |
43 | |
44 def __unicode__(self): | |
45 return unicode(self.generate()) | |
46 | |
47 def append(self, node): | |
48 """Append an element or string as child node.""" | |
49 if isinstance(node, (Element, basestring, int, float, long)): | |
50 # For objects of a known/primitive type, we avoid the check for | |
51 # whether it is iterable for better performance | |
52 self.children.append(node) | |
53 elif isinstance(node, Fragment): | |
54 self.children.extend(node.children) | |
55 elif node is not None: | |
56 try: | |
57 map(self.append, iter(node)) | |
58 except TypeError: | |
59 self.children.append(node) | |
60 | |
61 def _generate(self): | |
62 for child in self.children: | |
63 if isinstance(child, Fragment): | |
64 for event in child._generate(): | |
65 yield event | |
66 else: | |
67 if not isinstance(child, basestring): | |
68 child = unicode(child) | |
69 yield TEXT, child, (None, -1, -1) | |
70 | |
71 def generate(self): | |
72 """Return a markup event stream for the fragment.""" | |
73 return Stream(self._generate()) | |
74 | |
75 | |
76 class Element(Fragment): | |
77 """Simple XML output generator based on the builder pattern. | |
78 | |
79 Construct XML elements by passing the tag name to the constructor: | |
80 | |
81 >>> print Element('strong') | |
82 <strong/> | |
83 | |
84 Attributes can be specified using keyword arguments. The values of the | |
85 arguments will be converted to strings and any special XML characters | |
86 escaped: | |
87 | |
88 >>> print Element('textarea', rows=10, cols=60) | |
89 <textarea rows="10" cols="60"/> | |
90 >>> print Element('span', title='1 < 2') | |
91 <span title="1 < 2"/> | |
92 >>> print Element('span', title='"baz"') | |
93 <span title=""baz""/> | |
94 | |
95 The " character is escaped using a numerical entity. | |
96 The order in which attributes are rendered is undefined. | |
97 | |
98 If an attribute value evaluates to `None`, that attribute is not included | |
99 in the output: | |
100 | |
101 >>> print Element('a', name=None) | |
102 <a/> | |
103 | |
104 Attribute names that conflict with Python keywords can be specified by | |
105 appending an underscore: | |
106 | |
107 >>> print Element('div', class_='warning') | |
108 <div class="warning"/> | |
109 | |
110 Nested elements can be added to an element using item access notation. | |
111 The call notation can also be used for this and for adding attributes | |
112 using keyword arguments, as one would do in the constructor. | |
113 | |
114 >>> print Element('ul')(Element('li'), Element('li')) | |
115 <ul><li/><li/></ul> | |
116 >>> print Element('a')('Label') | |
117 <a>Label</a> | |
118 >>> print Element('a')('Label', href="target") | |
119 <a href="target">Label</a> | |
120 | |
121 Text nodes can be nested in an element by adding strings instead of | |
122 elements. Any special characters in the strings are escaped automatically: | |
123 | |
124 >>> print Element('em')('Hello world') | |
125 <em>Hello world</em> | |
126 >>> print Element('em')(42) | |
127 <em>42</em> | |
128 >>> print Element('em')('1 < 2') | |
129 <em>1 < 2</em> | |
130 | |
131 This technique also allows mixed content: | |
132 | |
133 >>> print Element('p')('Hello ', Element('b')('world')) | |
134 <p>Hello <b>world</b></p> | |
135 | |
136 Quotes are not escaped inside text nodes: | |
137 >>> print Element('p')('"Hello"') | |
138 <p>"Hello"</p> | |
139 | |
140 Elements can also be combined with other elements or strings using the | |
141 addition operator, which results in a `Fragment` object that contains the | |
142 operands: | |
143 | |
144 >>> print Element('br') + 'some text' + Element('br') | |
145 <br/>some text<br/> | |
146 | |
147 Elements with a namespace can be generated using the `Namespace` and/or | |
148 `QName` classes: | |
149 | |
150 >>> from genshi.core import Namespace | |
151 >>> xhtml = Namespace('http://www.w3.org/1999/xhtml') | |
152 >>> print Element(xhtml.html, lang='en') | |
153 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"/> | |
154 """ | |
155 __slots__ = ['tag', 'attrib'] | |
156 | |
157 def __init__(self, tag_, **attrib): | |
158 Fragment.__init__(self) | |
159 self.tag = QName(tag_) | |
160 self.attrib = Attrs() | |
161 for attr, value in attrib.items(): | |
162 if value is not None: | |
163 if not isinstance(value, basestring): | |
164 value = unicode(value) | |
165 self.attrib.append((QName(attr.rstrip('_').replace('_', '-')), | |
166 value)) | |
167 | |
168 def __call__(self, *args, **kwargs): | |
169 for attr, value in kwargs.items(): | |
170 if value is not None: | |
171 if not isinstance(value, basestring): | |
172 value = unicode(value) | |
173 self.attrib.set(attr.rstrip('_').replace('_', '-'), value) | |
174 Fragment.__call__(self, *args) | |
175 return self | |
176 | |
177 def __repr__(self): | |
178 return '<%s "%s">' % (self.__class__.__name__, self.tag) | |
179 | |
180 def _generate(self): | |
181 yield START, (self.tag, self.attrib), (None, -1, -1) | |
182 for kind, data, pos in Fragment._generate(self): | |
183 yield kind, data, pos | |
184 yield END, self.tag, (None, -1, -1) | |
185 | |
186 def generate(self): | |
187 """Return a markup event stream for the fragment.""" | |
188 return Stream(self._generate()) | |
189 | |
190 | |
191 class ElementFactory(object): | |
192 """Factory for `Element` objects. | |
193 | |
194 A new element is created simply by accessing a correspondingly named | |
195 attribute of the factory object: | |
196 | |
197 >>> factory = ElementFactory() | |
198 >>> print factory.foo | |
199 <foo/> | |
200 >>> print factory.foo(id=2) | |
201 <foo id="2"/> | |
202 | |
203 Markup fragments (lists of nodes without a parent element) can be created | |
204 by calling the factory: | |
205 | |
206 >>> print factory('Hello, ', factory.em('world'), '!') | |
207 Hello, <em>world</em>! | |
208 | |
209 A factory can also be bound to a specific namespace: | |
210 | |
211 >>> factory = ElementFactory('http://www.w3.org/1999/xhtml') | |
212 >>> print factory.html(lang="en") | |
213 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"/> | |
214 | |
215 The namespace for a specific element can be altered on an existing factory | |
216 by specifying the new namespace using item access: | |
217 | |
218 >>> factory = ElementFactory() | |
219 >>> print factory.html(factory['http://www.w3.org/2000/svg'].g(id=3)) | |
220 <html><g id="3" xmlns="http://www.w3.org/2000/svg"/></html> | |
221 | |
222 Usually, the `ElementFactory` class is not be used directly. Rather, the | |
223 `tag` instance should be used to create elements. | |
224 """ | |
225 | |
226 def __init__(self, namespace=None): | |
227 """Create the factory, optionally bound to the given namespace. | |
228 | |
229 @param namespace: the namespace URI for any created elements, or `None` | |
230 for no namespace | |
231 """ | |
232 if namespace and not isinstance(namespace, Namespace): | |
233 namespace = Namespace(namespace) | |
234 self.namespace = namespace | |
235 | |
236 def __call__(self, *args): | |
237 return Fragment()(*args) | |
238 | |
239 def __getitem__(self, namespace): | |
240 """Return a new factory that is bound to the specified namespace.""" | |
241 return ElementFactory(namespace) | |
242 | |
243 def __getattr__(self, name): | |
244 """Create an `Element` with the given name.""" | |
245 return Element(self.namespace and self.namespace[name] or name) | |
246 | |
247 | |
248 tag = ElementFactory() |