annotate markup/output.py @ 63:70c805c78796

Use a more neutral gray for the background color in the diagram.
author cmlenz
date Fri, 07 Jul 2006 17:58:52 +0000
parents b8456279c444
children 822089ae65ce
rev   line source
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
2 #
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
3 # Copyright (C) 2006 Christopher Lenz
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
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 26
diff changeset
8 # are also available at http://markup.cmlenz.net/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
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 26
diff changeset
12 # history and logs, available at http://markup.cmlenz.net/log/.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
13
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
14 """This module provides different kinds of serialization methods for XML event
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
15 streams.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
16 """
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
17
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
18 try:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
19 frozenset
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
20 except NameError:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
21 from sets import ImmutableSet as frozenset
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
22
18
4cbebb15a834 Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents: 1
diff changeset
23 from markup.core import Markup, Namespace, QName, Stream
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
24
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
25 __all__ = ['Serializer', 'XMLSerializer', 'HTMLSerializer']
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
26
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
27
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
28 class Serializer(object):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
29 """Base class for serializers."""
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
30
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
31 def serialize(self, stream):
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
32 """Must be implemented by concrete subclasses to serialize the given
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
33 stream.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
34
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
35 This method must be implemented as a generator, producing the
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
36 serialized output incrementally as unicode strings.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
37 """
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
38 raise NotImplementedError
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
39
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
40
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
41 class XMLSerializer(Serializer):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
42 """Produces XML text from an event stream.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
43
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
44 >>> from markup.builder import tag
20
e3d3c1d8c98a Fix tests broken in [20].
cmlenz
parents: 19
diff changeset
45 >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
46 >>> print ''.join(XMLSerializer().serialize(elem.generate()))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
47 <div><a href="foo"/><br/><hr noshade="True"/></div>
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
48 """
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
49
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
50 def serialize(self, stream):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
51 ns_attrib = []
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
52 ns_mapping = {}
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
53
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
54 stream = _PushbackIterator(stream)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
55 for kind, data, pos in stream:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
56
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
57 if kind is Stream.DOCTYPE:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
58 # FIXME: what if there's no system or public ID in the input?
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
59 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
60
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
61 elif kind is Stream.START_NS:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
62 prefix, uri = data
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
63 if uri not in ns_mapping:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
64 ns_mapping[uri] = prefix
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
65 if not prefix:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
66 ns_attrib.append((QName('xmlns'), uri))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
67 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
68 ns_attrib.append((QName('xmlns:%s' % prefix), uri))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
69
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
70 elif kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
71 tag, attrib = data
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
72
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
73 tagname = tag.localname
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
74 if tag.namespace:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
75 try:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
76 prefix = ns_mapping[tag.namespace]
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
77 if prefix:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
78 tagname = prefix + ':' + tag.localname
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
79 except KeyError:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
80 ns_attrib.append((QName('xmlns'), tag.namespace))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
81 buf = ['<', tagname]
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
82
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
83 if ns_attrib:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
84 attrib.extend(ns_attrib)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
85 ns_attrib = []
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
86 for attr, value in attrib:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
87 attrname = attr.localname
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
88 if attr.namespace:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
89 prefix = ns_mapping.get(attr.namespace)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
90 if prefix:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
91 attrname = prefix + ':' + attrname
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
92 buf.append(' %s="%s"' % (attrname, Markup.escape(value)))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
93
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
94 kind, data, pos = stream.next()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
95 if kind is Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
96 buf.append('/>')
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
97 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
98 buf.append('>')
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
99 stream.pushback((kind, data, pos))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
100
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
101 yield Markup(''.join(buf))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
102
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
103 elif kind is Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
104 tag = data
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
105 tagname = tag.localname
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
106 if tag.namespace:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
107 prefix = ns_mapping.get(tag.namespace)
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
108 if prefix:
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
109 tagname = prefix + ':' + tag.localname
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
110 yield Markup('</%s>' % tagname)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
111
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
112 elif kind is Stream.TEXT:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
113 yield Markup.escape(data, quotes=False)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
114
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
115
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
116 class HTMLSerializer(Serializer):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
117 """Produces HTML text from an event stream.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
118
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
119 >>> from markup.builder import tag
20
e3d3c1d8c98a Fix tests broken in [20].
cmlenz
parents: 19
diff changeset
120 >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
121 >>> print ''.join(HTMLSerializer().serialize(elem.generate()))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
122 <div><a href="foo"></a><br><hr noshade></div>
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
123 """
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
124
18
4cbebb15a834 Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents: 1
diff changeset
125 NAMESPACE = Namespace('http://www.w3.org/1999/xhtml')
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
126
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
127 _EMPTY_ELEMS = frozenset(['area', 'base', 'basefont', 'br', 'col', 'frame',
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
128 'hr', 'img', 'input', 'isindex', 'link', 'meta',
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
129 'param'])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
130 _BOOLEAN_ATTRS = frozenset(['selected', 'checked', 'compact', 'declare',
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
131 'defer', 'disabled', 'ismap', 'multiple',
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
132 'nohref', 'noresize', 'noshade', 'nowrap'])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
133
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
134 def serialize(self, stream):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
135 ns_mapping = {}
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
136
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
137 stream = _PushbackIterator(stream)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
138 for kind, data, pos in stream:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
139
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
140 if kind is Stream.DOCTYPE:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
141 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
142
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
143 elif kind is Stream.START_NS:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
144 prefix, uri = data
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
145 if uri not in ns_mapping:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
146 ns_mapping[uri] = prefix
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
147
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
148 elif kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
149 tag, attrib = data
18
4cbebb15a834 Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents: 1
diff changeset
150 if tag.namespace and tag not in self.NAMESPACE:
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
151 continue # not in the HTML namespace, so don't emit
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
152 buf = ['<', tag.localname]
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
153 for attr, value in attrib:
18
4cbebb15a834 Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents: 1
diff changeset
154 if attr.namespace and attr not in self.NAMESPACE:
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
155 continue # not in the HTML namespace, so don't emit
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
156 if attr.localname in self._BOOLEAN_ATTRS:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
157 if value:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
158 buf.append(' %s' % attr.localname)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
159 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
160 buf.append(' %s="%s"' % (attr.localname,
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
161 Markup.escape(value)))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
162
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
163 if tag.localname in self._EMPTY_ELEMS:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
164 kind, data, pos = stream.next()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
165 if kind is not Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
166 stream.pushback((kind, data, pos))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
167
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
168 yield Markup(''.join(buf + ['>']))
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
169
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
170 elif kind is Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
171 tag = data
18
4cbebb15a834 Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents: 1
diff changeset
172 if tag.namespace and tag not in self.NAMESPACE:
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
173 continue # not in the HTML namespace, so don't emit
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
174 yield Markup('</%s>' % tag.localname)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
175
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
176 elif kind is Stream.TEXT:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
177 yield Markup.escape(data, quotes=False)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
178
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
179
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 20
diff changeset
180 class _PushbackIterator(object):
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
181 """A simple wrapper for iterators that allows pushing items back on the
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
182 queue via the `pushback()` method.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
183
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
184 That can effectively be used to peek at the next item."""
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
185 __slots__ = ['iterable', 'buf']
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
186
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
187 def __init__(self, iterable):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
188 self.iterable = iter(iterable)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
189 self.buf = []
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
190
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
191 def __iter__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
192 return self
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
193
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
194 def next(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
195 if self.buf:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
196 return self.buf.pop(0)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
197 return self.iterable.next()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
198
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
199 def pushback(self, item):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
200 self.buf.append(item)
Copyright (C) 2012-2017 Edgewall Software