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