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