Mercurial > genshi > mirror
comparison markup/output.py @ 26:3c1a022be04c trunk
* Split out the XPath tests into a separate `unittest`-based file.
* Added many more docstrings.
* Cleaned up the implementation of the XML/HTML parsers a bit.
* The HTML parser now correctly handles minimized attributes.
* Added `COPYING` and `README` files.
author | cmlenz |
---|---|
date | Wed, 28 Jun 2006 08:55:04 +0000 |
parents | cc92d74ce9e5 |
children | b4f78c05e5c9 |
comparison
equal
deleted
inserted
replaced
25:e3be27f5bcf5 | 26:3c1a022be04c |
---|---|
19 frozenset | 19 frozenset |
20 except NameError: | 20 except NameError: |
21 from sets import ImmutableSet as frozenset | 21 from sets import ImmutableSet as frozenset |
22 | 22 |
23 from markup.core import Markup, Namespace, QName, Stream | 23 from markup.core import Markup, Namespace, QName, Stream |
24 from markup.filters import WhitespaceFilter | |
25 | 24 |
26 __all__ = ['Serializer', 'XMLSerializer', 'HTMLSerializer'] | 25 __all__ = ['Serializer', 'XMLSerializer', 'HTMLSerializer'] |
27 | 26 |
28 | 27 |
29 class Serializer(object): | 28 class Serializer(object): |
30 """Base class for serializers.""" | 29 """Base class for serializers.""" |
31 | 30 |
32 def serialize(self, stream): | 31 def serialize(self, stream): |
32 """Must be implemented by concrete subclasses to serialize the given | |
33 stream. | |
34 | |
35 This method must be implemented as a generator, producing the | |
36 serialized output incrementally as unicode strings. | |
37 """ | |
33 raise NotImplementedError | 38 raise NotImplementedError |
34 | 39 |
35 | 40 |
36 class XMLSerializer(Serializer): | 41 class XMLSerializer(Serializer): |
37 """Produces XML text from an event stream. | 42 """Produces XML text from an event stream. |
44 | 49 |
45 def serialize(self, stream): | 50 def serialize(self, stream): |
46 ns_attrib = [] | 51 ns_attrib = [] |
47 ns_mapping = {} | 52 ns_mapping = {} |
48 | 53 |
49 stream = PushbackIterator(stream) | 54 stream = _PushbackIterator(stream) |
50 for kind, data, pos in stream: | 55 for kind, data, pos in stream: |
51 | 56 |
52 if kind is Stream.DOCTYPE: | 57 if kind is Stream.DOCTYPE: |
53 # FIXME: what if there's no system or public ID in the input? | 58 # FIXME: what if there's no system or public ID in the input? |
54 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data) | 59 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data) |
79 attrib.extend(ns_attrib) | 84 attrib.extend(ns_attrib) |
80 ns_attrib = [] | 85 ns_attrib = [] |
81 for attr, value in attrib: | 86 for attr, value in attrib: |
82 attrname = attr.localname | 87 attrname = attr.localname |
83 if attr.namespace: | 88 if attr.namespace: |
84 try: | 89 prefix = ns_mapping.get(attr.namespace) |
85 prefix = ns_mapping[attr.namespace] | |
86 except KeyError: | |
87 # FIXME: synthesize a prefix for the attribute? | |
88 prefix = '' | |
89 if prefix: | 90 if prefix: |
90 attrname = prefix + ':' + attrname | 91 attrname = prefix + ':' + attrname |
91 buf.append(' %s="%s"' % (attrname, Markup.escape(value))) | 92 buf.append(' %s="%s"' % (attrname, Markup.escape(value))) |
92 | 93 |
93 kind, data, pos = stream.next() | 94 kind, data, pos = stream.next() |
101 | 102 |
102 elif kind is Stream.END: | 103 elif kind is Stream.END: |
103 tag = data | 104 tag = data |
104 tagname = tag.localname | 105 tagname = tag.localname |
105 if tag.namespace: | 106 if tag.namespace: |
106 try: | 107 prefix = ns_mapping.get(tag.namespace) |
107 prefix = ns_mapping[tag.namespace] | 108 if prefix: |
108 if prefix: | 109 tagname = prefix + ':' + tag.localname |
109 tagname = prefix + ':' + tag.localname | |
110 except KeyError: | |
111 pass | |
112 yield Markup('</%s>' % tagname) | 110 yield Markup('</%s>' % tagname) |
113 | 111 |
114 elif kind is Stream.TEXT: | 112 elif kind is Stream.TEXT: |
115 yield Markup.escape(data, quotes=False) | 113 yield Markup.escape(data, quotes=False) |
116 | 114 |
134 'nohref', 'noresize', 'noshade', 'nowrap']) | 132 'nohref', 'noresize', 'noshade', 'nowrap']) |
135 | 133 |
136 def serialize(self, stream): | 134 def serialize(self, stream): |
137 ns_mapping = {} | 135 ns_mapping = {} |
138 | 136 |
139 stream = PushbackIterator(stream) | 137 stream = _PushbackIterator(stream) |
140 for kind, data, pos in stream: | 138 for kind, data, pos in stream: |
141 | 139 |
142 if kind is Stream.DOCTYPE: | 140 if kind is Stream.DOCTYPE: |
143 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data) | 141 yield Markup('<!DOCTYPE %s "%s" "%s">\n' % data) |
144 | 142 |
177 | 175 |
178 elif kind is Stream.TEXT: | 176 elif kind is Stream.TEXT: |
179 yield Markup.escape(data, quotes=False) | 177 yield Markup.escape(data, quotes=False) |
180 | 178 |
181 | 179 |
182 class PushbackIterator(object): | 180 class _PushbackIterator(object): |
183 """A simple wrapper for iterators that allows pushing items back on the | 181 """A simple wrapper for iterators that allows pushing items back on the |
184 queue via the `pushback()` method. | 182 queue via the `pushback()` method. |
185 | 183 |
186 That can effectively be used to peek at the next item.""" | 184 That can effectively be used to peek at the next item.""" |
187 __slots__ = ['iterable', 'buf'] | 185 __slots__ = ['iterable', 'buf'] |