# HG changeset patch # User cmlenz # Date 1170265007 0 # Node ID c199e9b958848d74651f0efdcf9635c8bc53c8a7 # Parent 68772732c89699caaa89df91ccd1dac7576adc94 Fix output of namespace declarations for namespace URLs appearing more than once in a stream. Thanks to Jeff Cutsinger for reporting the problem. diff --git a/genshi/output.py b/genshi/output.py --- a/genshi/output.py +++ b/genshi/output.py @@ -23,8 +23,8 @@ import re from genshi.core import escape, Markup, Namespace, QName, StreamEventKind -from genshi.core import DOCTYPE, START, END, START_NS, TEXT, START_CDATA, \ - END_CDATA, PI, COMMENT, XML_NAMESPACE +from genshi.core import DOCTYPE, START, END, START_NS, END_NS, TEXT, \ + START_CDATA, END_CDATA, PI, COMMENT, XML_NAMESPACE __all__ = ['DocType', 'XMLSerializer', 'XHTMLSerializer', 'HTMLSerializer', 'TextSerializer'] @@ -75,7 +75,7 @@ def __call__(self, stream): ns_attrib = [] - ns_mapping = {XML_NAMESPACE.uri: 'xml'} + ns_mapping = {XML_NAMESPACE.uri: ['xml']} have_doctype = False in_cdata = False @@ -88,14 +88,14 @@ tag, attrib = data tagname = tag.localname - namespace = tag.namespace - if namespace: - if namespace in ns_mapping: - prefix = ns_mapping[namespace] - if prefix: - tagname = '%s:%s' % (prefix, tagname) + tagns = tag.namespace + if tagns: + if tagns in ns_mapping: + prefix = ns_mapping.get(tagns) + if prefix and prefix[-1]: + tagname = '%s:%s' % (prefix[-1], tagname) else: - ns_attrib.append((QName('xmlns'), namespace)) + ns_attrib.append((QName('xmlns'), tagns)) buf = ['<', tagname] if ns_attrib: @@ -105,8 +105,8 @@ attrns = attr.namespace if attrns: prefix = ns_mapping.get(attrns) - if prefix: - attrname = '%s:%s' % (prefix, attrname) + if prefix and prefix[-1]: + attrname = '%s:%s' % (prefix[-1], attrname) buf += [' ', attrname, '="', escape(value), '"'] ns_attrib = [] @@ -119,8 +119,8 @@ tagname = tag.localname if tag.namespace: prefix = ns_mapping.get(tag.namespace) - if prefix: - tagname = '%s:%s' % (prefix, tag.localname) + if prefix and prefix[-1]: + tagname = '%s:%s' % (prefix[-1], tagname) yield Markup('' % tagname) elif kind is TEXT: @@ -148,11 +148,18 @@ elif kind is START_NS: prefix, uri = data if uri not in ns_mapping: - ns_mapping[uri] = prefix if not prefix: ns_attrib.append((QName('xmlns'), uri)) else: ns_attrib.append((QName('xmlns:%s' % prefix), uri)) + ns_mapping.setdefault(uri, []).append(prefix) + + elif kind is END_NS: + for uri, prefix in ns_mapping.items(): + if prefix[-1] == data: + prefix.pop() + if not prefix: + del ns_mapping[uri] elif kind is START_CDATA: yield Markup('' % tagname) elif kind is TEXT: @@ -277,11 +284,18 @@ elif kind is START_NS: prefix, uri = data if uri not in ns_mapping: - ns_mapping[uri] = prefix if not prefix: ns_attrib.append((QName('xmlns'), uri)) else: ns_attrib.append((QName('xmlns:%s' % prefix), uri)) + ns_mapping.setdefault(uri, []).append(prefix) + + elif kind is END_NS: + for uri, prefix in ns_mapping.items(): + if prefix[-1] == data: + prefix.pop() + if not prefix: + del ns_mapping[uri] elif kind is START_CDATA: yield Markup(' + + + """, output) + + def test_multiple_default_namespaces(self): + stream = Stream([ + (Stream.START, (QName('div'), Attrs()), (None, -1, -1)), + (Stream.TEXT, '\n ', (None, -1, -1)), + (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), + (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), + (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), + (Stream.END_NS, '', (None, -1, -1)), + (Stream.TEXT, '\n ', (None, -1, -1)), + (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), + (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), + (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), + (Stream.END_NS, '', (None, -1, -1)), + (Stream.TEXT, '\n ', (None, -1, -1)), + (Stream.END, QName('div'), (None, -1, -1)), + ]) + output = stream.render(XMLSerializer) + self.assertEqual("""
+

+

+

""", output) + + def test_multiple_bound_namespaces(self): + stream = Stream([ + (Stream.START, (QName('div'), Attrs()), (None, -1, -1)), + (Stream.TEXT, '\n ', (None, -1, -1)), + (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), + (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), + (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), + (Stream.END_NS, 'x', (None, -1, -1)), + (Stream.TEXT, '\n ', (None, -1, -1)), + (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), + (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), + (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.END, QName('div'), (None, -1, -1)), - (Stream.END_NS, 'x', (None, -1, -1)) ]) output = stream.render(XMLSerializer) - self.assertEqual("""
-

-

+ self.assertEqual("""

+ +
""", output)