annotate genshi/builder.py @ 745:74b5c5476ddb trunk

Preparing for [milestone:0.5] release.
author cmlenz
date Mon, 09 Jun 2008 09:50:03 +0000
parents f4f8ffefbd49
children 52219748e5c1
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
741
f4f8ffefbd49 Updated copyright years.
cmlenz
parents: 737
diff changeset
3 # Copyright (C) 2006-2008 Edgewall Software
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
5 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 182
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
9 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 182
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
13
430
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
14 """Support for programmatically generating markup streams from Python code using
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
15 a very simple syntax. The main entry point to this module is the `tag` object
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
16 (which is actually an instance of the ``ElementFactory`` class). You should
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
17 rarely (if ever) need to directly import and use any of the other classes in
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
18 this module.
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
19
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
20 Elements can be created using the `tag` object using attribute access. For
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
21 example:
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
22
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
23 >>> doc = tag.p('Some text and ', tag.a('a link', href='http://example.org/'), '.')
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
24 >>> doc
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
25 <Element "p">
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
26
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
27 This produces an `Element` instance which can be further modified to add child
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
28 nodes and attributes. This is done by "calling" the element: positional
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
29 arguments are added as child nodes (alternatively, the `Element.append` method
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
30 can be used for that purpose), whereas keywords arguments are added as
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
31 attributes:
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
32
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
33 >>> doc(tag.br)
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
34 <Element "p">
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
35 >>> print doc
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
36 <p>Some text and <a href="http://example.org/">a link</a>.<br/></p>
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
37
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
38 If an attribute name collides with a Python keyword, simply append an underscore
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
39 to the name:
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
40
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
41 >>> doc(class_='intro')
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
42 <Element "p">
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
43 >>> print doc
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
44 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
45
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
46 As shown above, an `Element` can easily be directly rendered to XML text by
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
47 printing it or using the Python ``str()`` function. This is basically a
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
48 shortcut for converting the `Element` to a stream and serializing that
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
49 stream:
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
50
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
51 >>> stream = doc.generate()
431
ad01564e87f2 * Don't allow `style` attributes by default in the `HTMLSanitizer`. Closes #97.
cmlenz
parents: 430
diff changeset
52 >>> stream #doctest: +ELLIPSIS
430
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
53 <genshi.core.Stream object at ...>
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
54 >>> print stream
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
55 <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
56
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
57
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
58 The `tag` object also allows creating "fragments", which are basically lists
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
59 of nodes (elements or text) that don't have a parent element. This can be useful
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
60 for creating snippets of markup that are attached to a parent element later (for
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
61 example in a template). Fragments are created by calling the `tag` object, which
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
62 returns an object of type `Fragment`:
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
63
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
64 >>> fragment = tag('Hello, ', tag.em('world'), '!')
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
65 >>> fragment
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
66 <Fragment>
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
67 >>> print fragment
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
68 Hello, <em>world</em>!
77e99857b351 Moved the `builder` document into the API docs.
cmlenz
parents: 425
diff changeset
69 """
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 410
diff changeset
70
730
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
71 try:
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
72 set
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
73 except NameError:
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
74 from sets import Set as set
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
75
737
ca72e3dc443d Implement the `__html__` protocol as suggested in #202. This would allow Genshi to be used in combination with other markup generating tools, as long as they support the same protocol.
cmlenz
parents: 735
diff changeset
76 from genshi.core import Attrs, Markup, Namespace, QName, Stream, \
ca72e3dc443d Implement the `__html__` protocol as suggested in #202. This would allow Genshi to be used in combination with other markup generating tools, as long as they support the same protocol.
cmlenz
parents: 735
diff changeset
77 START, END, TEXT
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
78
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
79 __all__ = ['Fragment', 'Element', 'ElementFactory', 'tag']
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 410
diff changeset
80 __docformat__ = 'restructuredtext en'
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
81
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
82
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
83 class Fragment(object):
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
84 """Represents a markup fragment, which is basically just a list of element
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
85 or text nodes.
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
86 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
87 __slots__ = ['children']
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
88
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
89 def __init__(self):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
90 """Create a new fragment."""
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
91 self.children = []
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
92
65
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
93 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
94 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
95
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
96 def __call__(self, *args):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
97 """Append any positional arguments as child nodes.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
98
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
99 :see: `append`
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
100 """
133
79f445396cd7 Minor cleanup and performance improvement for the builder module.
cmlenz
parents: 119
diff changeset
101 map(self.append, args)
65
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
102 return self
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
103
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
104 def __iter__(self):
98
44af12832c5a Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents: 94
diff changeset
105 return self._generate()
65
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
106
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
107 def __repr__(self):
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
108 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
109
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
110 def __str__(self):
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
111 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
112
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
113 def __unicode__(self):
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
114 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
115
737
ca72e3dc443d Implement the `__html__` protocol as suggested in #202. This would allow Genshi to be used in combination with other markup generating tools, as long as they support the same protocol.
cmlenz
parents: 735
diff changeset
116 def __html__(self):
ca72e3dc443d Implement the `__html__` protocol as suggested in #202. This would allow Genshi to be used in combination with other markup generating tools, as long as they support the same protocol.
cmlenz
parents: 735
diff changeset
117 return Markup(self.generate())
ca72e3dc443d Implement the `__html__` protocol as suggested in #202. This would allow Genshi to be used in combination with other markup generating tools, as long as they support the same protocol.
cmlenz
parents: 735
diff changeset
118
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
119 def append(self, node):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
120 """Append an element or string as child node.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
121
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
122 :param node: the node to append; can be an `Element`, `Fragment`, or a
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
123 `Stream`, or a Python string or number
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
124 """
379
e1d659c87ddf The builder API now accepts streams as children of elements and fragments.
cmlenz
parents: 345
diff changeset
125 if isinstance(node, (Stream, Element, basestring, int, float, long)):
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
126 # For objects of a known/primitive type, we avoid the check for
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
127 # whether it is iterable for better performance
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
128 self.children.append(node)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
129 elif isinstance(node, Fragment):
133
79f445396cd7 Minor cleanup and performance improvement for the builder module.
cmlenz
parents: 119
diff changeset
130 self.children.extend(node.children)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
131 elif node is not None:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
132 try:
506
0ea38a6cf173 Use explicit buffer class in transformer filter, and revert the breaking change to the builder module.
cmlenz
parents: 501
diff changeset
133 map(self.append, iter(node))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
134 except TypeError:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
135 self.children.append(node)
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
136
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
137 def _generate(self):
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
138 for child in self.children:
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
139 if isinstance(child, Fragment):
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
140 for event in child._generate():
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
141 yield event
379
e1d659c87ddf The builder API now accepts streams as children of elements and fragments.
cmlenz
parents: 345
diff changeset
142 elif isinstance(child, Stream):
e1d659c87ddf The builder API now accepts streams as children of elements and fragments.
cmlenz
parents: 345
diff changeset
143 for event in child:
e1d659c87ddf The builder API now accepts streams as children of elements and fragments.
cmlenz
parents: 345
diff changeset
144 yield event
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
145 else:
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
146 if not isinstance(child, basestring):
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
147 child = unicode(child)
133
79f445396cd7 Minor cleanup and performance improvement for the builder module.
cmlenz
parents: 119
diff changeset
148 yield TEXT, child, (None, -1, -1)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
149
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
150 def generate(self):
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
151 """Return a markup event stream for the fragment.
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
152
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
153 :rtype: `Stream`
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
154 """
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
155 return Stream(self._generate())
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
156
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
157
345
2aa7ca37ae6a Make `Attrs` instances immutable.
cmlenz
parents: 230
diff changeset
158 def _kwargs_to_attrs(kwargs):
735
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
159 attrs = []
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
160 names = set()
730
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
161 for name, value in kwargs.items():
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
162 name = name.rstrip('_').replace('_', '-')
735
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
163 if value is not None and name not in names:
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
164 attrs.append((QName(name), unicode(value)))
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
165 names.add(name)
7e428b22dbaa Tweak to [854] to not change the ordering of attributes compared to how they were previously ordered, to avoid breaking any tests that rely on the order. See #216.
cmlenz
parents: 730
diff changeset
166 return Attrs(attrs)
345
2aa7ca37ae6a Make `Attrs` instances immutable.
cmlenz
parents: 230
diff changeset
167
2aa7ca37ae6a Make `Attrs` instances immutable.
cmlenz
parents: 230
diff changeset
168
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
169 class Element(Fragment):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
170 """Simple XML output generator based on the builder pattern.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
171
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
172 Construct XML elements by passing the tag name to the constructor:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
173
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
174 >>> print Element('strong')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
175 <strong/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
176
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
177 Attributes can be specified using keyword arguments. The values of the
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
178 arguments will be converted to strings and any special XML characters
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
179 escaped:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
180
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
181 >>> print Element('textarea', rows=10, cols=60)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
182 <textarea rows="10" cols="60"/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
183 >>> print Element('span', title='1 < 2')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
184 <span title="1 &lt; 2"/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
185 >>> print Element('span', title='"baz"')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
186 <span title="&#34;baz&#34;"/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
187
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
188 The " character is escaped using a numerical entity.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
189 The order in which attributes are rendered is undefined.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
190
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
191 If an attribute value evaluates to `None`, that attribute is not included
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
192 in the output:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
193
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
194 >>> print Element('a', name=None)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
195 <a/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
196
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
197 Attribute names that conflict with Python keywords can be specified by
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
198 appending an underscore:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
199
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
200 >>> print Element('div', class_='warning')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
201 <div class="warning"/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
202
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
203 Nested elements can be added to an element using item access notation.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
204 The call notation can also be used for this and for adding attributes
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
205 using keyword arguments, as one would do in the constructor.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
206
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
207 >>> print Element('ul')(Element('li'), Element('li'))
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
208 <ul><li/><li/></ul>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
209 >>> print Element('a')('Label')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
210 <a>Label</a>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
211 >>> print Element('a')('Label', href="target")
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
212 <a href="target">Label</a>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
213
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
214 Text nodes can be nested in an element by adding strings instead of
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
215 elements. Any special characters in the strings are escaped automatically:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
216
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
217 >>> print Element('em')('Hello world')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
218 <em>Hello world</em>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
219 >>> print Element('em')(42)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
220 <em>42</em>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
221 >>> print Element('em')('1 < 2')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
222 <em>1 &lt; 2</em>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
223
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
224 This technique also allows mixed content:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
225
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
226 >>> print Element('p')('Hello ', Element('b')('world'))
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
227 <p>Hello <b>world</b></p>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
228
34
3421dd98f015 quotes should not be escaped inside text nodes
mgood
parents: 28
diff changeset
229 Quotes are not escaped inside text nodes:
3421dd98f015 quotes should not be escaped inside text nodes
mgood
parents: 28
diff changeset
230 >>> print Element('p')('"Hello"')
3421dd98f015 quotes should not be escaped inside text nodes
mgood
parents: 28
diff changeset
231 <p>"Hello"</p>
3421dd98f015 quotes should not be escaped inside text nodes
mgood
parents: 28
diff changeset
232
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
233 Elements can also be combined with other elements or strings using the
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
234 addition operator, which results in a `Fragment` object that contains the
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
235 operands:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
236
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
237 >>> print Element('br') + 'some text' + Element('br')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
238 <br/>some text<br/>
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
239
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
240 Elements with a namespace can be generated using the `Namespace` and/or
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
241 `QName` classes:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
242
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 182
diff changeset
243 >>> from genshi.core import Namespace
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
244 >>> xhtml = Namespace('http://www.w3.org/1999/xhtml')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
245 >>> print Element(xhtml.html, lang='en')
410
d14d89995c29 Improve the handling of namespaces in serialization.
cmlenz
parents: 408
diff changeset
246 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
247 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
248 __slots__ = ['tag', 'attrib']
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
249
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
250 def __init__(self, tag_, **attrib):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
251 Fragment.__init__(self)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
252 self.tag = QName(tag_)
730
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
253 self.attrib = _kwargs_to_attrs(attrib)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
254
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
255 def __call__(self, *args, **kwargs):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
256 """Append any positional arguments as child nodes, and keyword arguments
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
257 as attributes.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
258
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
259 :return: the element itself so that calls can be chained
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
260 :rtype: `Element`
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
261 :see: `Fragment.append`
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
262 """
730
5e9d250ad3ad Fix for potential duplicate attributes making it through the builder API. Closes #216.
cmlenz
parents: 506
diff changeset
263 self.attrib |= _kwargs_to_attrs(kwargs)
98
44af12832c5a Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents: 94
diff changeset
264 Fragment.__call__(self, *args)
44af12832c5a Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents: 94
diff changeset
265 return self
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
266
65
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
267 def __repr__(self):
b3fdf93057ab Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents: 34
diff changeset
268 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
269
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
270 def _generate(self):
133
79f445396cd7 Minor cleanup and performance improvement for the builder module.
cmlenz
parents: 119
diff changeset
271 yield START, (self.tag, self.attrib), (None, -1, -1)
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
272 for kind, data, pos in Fragment._generate(self):
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
273 yield kind, data, pos
133
79f445396cd7 Minor cleanup and performance improvement for the builder module.
cmlenz
parents: 119
diff changeset
274 yield END, self.tag, (None, -1, -1)
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
275
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
276 def generate(self):
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
277 """Return a markup event stream for the fragment.
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
278
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
279 :rtype: `Stream`
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
280 """
94
0f8800c46e21 Some bugfixes and minor performance improvements for the builder module.
cmlenz
parents: 91
diff changeset
281 return Stream(self._generate())
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
282
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
283
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
284 class ElementFactory(object):
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
285 """Factory for `Element` objects.
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
286
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
287 A new element is created simply by accessing a correspondingly named
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
288 attribute of the factory object:
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
289
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
290 >>> factory = ElementFactory()
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
291 >>> print factory.foo
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
292 <foo/>
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
293 >>> print factory.foo(id=2)
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
294 <foo id="2"/>
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
295
119
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
296 Markup fragments (lists of nodes without a parent element) can be created
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
297 by calling the factory:
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
298
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
299 >>> print factory('Hello, ', factory.em('world'), '!')
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
300 Hello, <em>world</em>!
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
301
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
302 A factory can also be bound to a specific namespace:
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
303
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
304 >>> factory = ElementFactory('http://www.w3.org/1999/xhtml')
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
305 >>> print factory.html(lang="en")
410
d14d89995c29 Improve the handling of namespaces in serialization.
cmlenz
parents: 408
diff changeset
306 <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
307
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
308 The namespace for a specific element can be altered on an existing factory
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
309 by specifying the new namespace using item access:
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
310
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
311 >>> factory = ElementFactory()
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
312 >>> print factory.html(factory['http://www.w3.org/2000/svg'].g(id=3))
410
d14d89995c29 Improve the handling of namespaces in serialization.
cmlenz
parents: 408
diff changeset
313 <html><g xmlns="http://www.w3.org/2000/svg" id="3"/></html>
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
314
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
315 Usually, the `ElementFactory` class is not be used directly. Rather, the
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
316 `tag` instance should be used to create elements.
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
317 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
318
19
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
319 def __init__(self, namespace=None):
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
320 """Create the factory, optionally bound to the given namespace.
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
321
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 410
diff changeset
322 :param namespace: the namespace URI for any created elements, or `None`
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 410
diff changeset
323 for no namespace
28
35956040ba6e More docstrings for the builder module.
cmlenz
parents: 27
diff changeset
324 """
20
cc92d74ce9e5 Fix tests broken in [20].
cmlenz
parents: 19
diff changeset
325 if namespace and not isinstance(namespace, Namespace):
19
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
326 namespace = Namespace(namespace)
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
327 self.namespace = namespace
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
328
119
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
329 def __call__(self, *args):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
330 """Create a fragment that has the given positional arguments as child
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
331 nodes.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
332
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
333 :return: the created `Fragment`
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
334 :rtype: `Fragment`
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
335 """
119
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
336 return Fragment()(*args)
cc2aee07f53b Allow creating fragments from the `tag` object in `markup.builder`.
cmlenz
parents: 98
diff changeset
337
19
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
338 def __getitem__(self, namespace):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
339 """Return a new factory that is bound to the specified namespace.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
340
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
341 :param namespace: the namespace URI or `Namespace` object
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
342 :return: an `ElementFactory` that produces elements bound to the given
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
343 namespace
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
344 :rtype: `ElementFactory`
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
345 """
19
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
346 return ElementFactory(namespace)
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
347
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
348 def __getattr__(self, name):
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
349 """Create an `Element` with the given name.
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
350
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
351 :param name: the tag name of the element to create
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
352 :return: an `Element` with the specified name
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
353 :rtype: `Element`
433
bc430fd7c54d More API docs.
cmlenz
parents: 431
diff changeset
354 """
19
704f10b06507 Enable `ElementFactory` to create namespaced elements.
cmlenz
parents: 17
diff changeset
355 return Element(self.namespace and self.namespace[name] or name)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
356
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
357
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
358 tag = ElementFactory()
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
359 """Global `ElementFactory` bound to the default namespace.
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
360
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
361 :type: `ElementFactory`
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 433
diff changeset
362 """
Copyright (C) 2012-2017 Edgewall Software