Mercurial > genshi > genshi-test
annotate markup/builder.py @ 21:eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
* The filename is now included as first item in the `pos` tuple of stream events.
* Simplified the "basic" example so that it actually ''is'' basic.
* Added a more complex example using nested relative includes in [source:/trunk/examples/includes/ examples/includes].
author | cmlenz |
---|---|
date | Tue, 20 Jun 2006 13:05:37 +0000 |
parents | e3d3c1d8c98a |
children | b8456279c444 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
3 # Copyright (C) 2006 Christopher Lenz | |
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://trac.edgewall.com/license.html. | |
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://projects.edgewall.com/trac/. | |
13 | |
19
7b589453b797
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): | |
20 __slots__ = ['children'] | |
21 | |
22 def __init__(self): | |
23 self.children = [] | |
24 | |
25 def append(self, node): | |
26 """Append an element or string as child node.""" | |
27 if isinstance(node, (Element, basestring, int, float, long)): | |
28 # For objects of a known/primitive type, we avoid the check for | |
29 # whether it is iterable for better performance | |
30 self.children.append(node) | |
31 elif isinstance(node, Fragment): | |
32 self.children += node.children | |
33 elif node is not None: | |
34 try: | |
35 children = iter(node) | |
36 except TypeError: | |
37 self.children.append(node) | |
38 else: | |
39 for child in node: | |
40 self.append(children) | |
41 | |
42 def __add__(self, other): | |
43 return Fragment()(self, other) | |
44 | |
45 def __call__(self, *args): | |
46 for arg in args: | |
47 self.append(arg) | |
48 return self | |
49 | |
50 def generate(self): | |
51 """Generator that yield tags and text nodes as strings.""" | |
52 def _generate(): | |
53 for child in self.children: | |
54 if isinstance(child, Fragment): | |
55 for event in child.generate(): | |
56 yield event | |
57 else: | |
17
ad63ad459524
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
|
58 yield Stream.TEXT, unicode(child), (-1, -1) |
1 | 59 return Stream(_generate()) |
60 | |
61 def __iter__(self): | |
62 return iter(self.generate()) | |
63 | |
64 def __str__(self): | |
65 return str(self.generate()) | |
66 | |
67 def __unicode__(self): | |
68 return unicode(self.generate()) | |
69 | |
70 | |
71 class Element(Fragment): | |
72 """Simple XML output generator based on the builder pattern. | |
73 | |
74 Construct XML elements by passing the tag name to the constructor: | |
75 | |
76 >>> print Element('strong') | |
77 <strong/> | |
78 | |
79 Attributes can be specified using keyword arguments. The values of the | |
80 arguments will be converted to strings and any special XML characters | |
81 escaped: | |
82 | |
83 >>> print Element('textarea', rows=10, cols=60) | |
84 <textarea rows="10" cols="60"/> | |
85 >>> print Element('span', title='1 < 2') | |
86 <span title="1 < 2"/> | |
87 >>> print Element('span', title='"baz"') | |
88 <span title=""baz""/> | |
89 | |
90 The " character is escaped using a numerical entity. | |
91 The order in which attributes are rendered is undefined. | |
92 | |
93 If an attribute value evaluates to `None`, that attribute is not included | |
94 in the output: | |
95 | |
96 >>> print Element('a', name=None) | |
97 <a/> | |
98 | |
99 Attribute names that conflict with Python keywords can be specified by | |
100 appending an underscore: | |
101 | |
102 >>> print Element('div', class_='warning') | |
103 <div class="warning"/> | |
104 | |
105 Nested elements can be added to an element using item access notation. | |
106 The call notation can also be used for this and for adding attributes | |
107 using keyword arguments, as one would do in the constructor. | |
108 | |
109 >>> print Element('ul')(Element('li'), Element('li')) | |
110 <ul><li/><li/></ul> | |
111 >>> print Element('a')('Label') | |
112 <a>Label</a> | |
113 >>> print Element('a')('Label', href="target") | |
114 <a href="target">Label</a> | |
115 | |
116 Text nodes can be nested in an element by adding strings instead of | |
117 elements. Any special characters in the strings are escaped automatically: | |
118 | |
119 >>> print Element('em')('Hello world') | |
120 <em>Hello world</em> | |
121 >>> print Element('em')(42) | |
122 <em>42</em> | |
123 >>> print Element('em')('1 < 2') | |
124 <em>1 < 2</em> | |
125 | |
126 This technique also allows mixed content: | |
127 | |
128 >>> print Element('p')('Hello ', Element('b')('world')) | |
129 <p>Hello <b>world</b></p> | |
130 | |
131 Elements can also be combined with other elements or strings using the | |
132 addition operator, which results in a `Fragment` object that contains the | |
133 operands: | |
134 | |
135 >>> print Element('br') + 'some text' + Element('br') | |
136 <br/>some text<br/> | |
137 | |
138 Elements with a namespace can be generated using the `Namespace` and/or | |
139 `QName` classes: | |
140 | |
141 >>> from markup.core import Namespace | |
142 >>> xhtml = Namespace('http://www.w3.org/1999/xhtml') | |
143 >>> print Element(xhtml.html, lang='en') | |
144 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"/> | |
145 """ | |
146 __slots__ = ['tag', 'attrib'] | |
147 | |
148 def __init__(self, tag_, **attrib): | |
149 Fragment.__init__(self) | |
150 self.tag = QName(tag_) | |
151 self.attrib = Attributes() | |
152 self(**attrib) | |
153 | |
154 def __call__(self, *args, **kwargs): | |
155 for attr, value in kwargs.items(): | |
156 if value is None: | |
157 continue | |
158 attr = attr.rstrip('_').replace('_', '-') | |
159 self.attrib.set(attr, value) | |
160 return Fragment.__call__(self, *args) | |
161 | |
162 def generate(self): | |
163 """Generator that yield tags and text nodes as strings.""" | |
164 def _generate(): | |
165 yield Stream.START, (self.tag, self.attrib), (-1, -1) | |
166 for kind, data, pos in Fragment.generate(self): | |
167 yield kind, data, pos | |
168 yield Stream.END, self.tag, (-1, -1) | |
169 return Stream(_generate()) | |
170 | |
171 | |
172 class ElementFactory(object): | |
173 | |
19
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
174 def __init__(self, namespace=None): |
20 | 175 if namespace and not isinstance(namespace, Namespace): |
19
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
176 namespace = Namespace(namespace) |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
177 self.namespace = namespace |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
178 |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
179 def __getitem__(self, namespace): |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
180 return ElementFactory(namespace) |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
181 |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
182 def __getattr__(self, name): |
7b589453b797
Enable `ElementFactory` to create namespaced elements.
cmlenz
parents:
17
diff
changeset
|
183 return Element(self.namespace and self.namespace[name] or name) |
1 | 184 |
185 | |
186 tag = ElementFactory() |