Edgewall Software

Ticket #253: genshi-namespace-flattening.patch

File genshi-namespace-flattening.patch, 10.3 KB (added by cmlenz, 4 years ago)

Added patch by Marshall Vandegrift

  • genshi/tests/output.py

     
    185185          <x:p xmlns:x="http://example.org/"/> 
    186186        </div>""", output) 
    187187 
     188    def test_rebound_namespace(self): 
     189        stream = Stream([ 
     190            (Stream.START_NS, ('a', 'http://example.org/'), (None, -1, -1)), 
     191            (Stream.START, (QName('http://example.org/}div'), Attrs()), (None, -1, -1)), 
     192            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     193            (Stream.START_NS, ('b', 'http://example.org/'), (None, -1, -1)), 
     194            (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), 
     195            (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), 
     196            (Stream.END_NS, 'b', (None, -1, -1)), 
     197            (Stream.TEXT, '\n        ', (None, -1, -1)), 
     198            (Stream.END, QName('http://example.org/}div'), (None, -1, -1)), 
     199            (Stream.END_NS, 'a', (None, -1, -1)), 
     200        ]) 
     201        output = stream.render(XMLSerializer) 
     202        self.assertEqual("""<a:div xmlns:a="http://example.org/"> 
     203          <a:p/> 
     204        </a:div>""", output) 
     205 
     206    def test_rebound_namespace_prefix(self): 
     207        stream = Stream([ 
     208            (Stream.START_NS, ('x', 'http://example.org/a'), (None, -1, -1)), 
     209            (Stream.START, (QName('http://example.org/a}div'), Attrs()), (None, -1, -1)), 
     210            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     211            (Stream.START_NS, ('x', 'http://example.org/b'), (None, -1, -1)), 
     212            (Stream.START, (QName('http://example.org/b}p'), Attrs()), (None, -1, -1)), 
     213            (Stream.END, QName('http://example.org/b}p'), (None, -1, -1)), 
     214            (Stream.END_NS, 'x', (None, -1, -1)), 
     215            (Stream.TEXT, '\n        ', (None, -1, -1)), 
     216            (Stream.END, QName('http://example.org/a}div'), (None, -1, -1)), 
     217            (Stream.END_NS, 'x', (None, -1, -1)), 
     218        ]) 
     219        output = stream.render(XMLSerializer) 
     220        self.assertEqual("""<x:div xmlns:x="http://example.org/a"> 
     221          <x:p xmlns:x="http://example.org/b"/> 
     222        </x:div>""", output) 
     223         
     224    def test_rebound_namespace_with_rebound_namespace_prefix(self): 
     225        stream = Stream([ 
     226            (Stream.START_NS, ('x', 'http://example.org/a'), (None, -1, -1)), 
     227            (Stream.START_NS, ('y', 'http://example.org/a'), (None, -1, -1)), 
     228            (Stream.START, (QName('http://example.org/a}div'), Attrs()), (None, -1, -1)), 
     229            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     230            (Stream.START_NS, ('x', 'http://example.org/b'), (None, -1, -1)), 
     231            (Stream.START, (QName('http://example.org/b}p'), Attrs()), (None, -1, -1)), 
     232            (Stream.TEXT, '\n            ', (None, -1, -1)), 
     233            (Stream.START, (QName('http://example.org/a}span'), Attrs()), (None, -1, -1)), 
     234            (Stream.END, QName('http://example.org/a}span'), (None, -1, -1)), 
     235            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     236            (Stream.END, QName('http://example.org/b}p'), (None, -1, -1)), 
     237            (Stream.END_NS, 'x', (None, -1, -1)), 
     238            (Stream.TEXT, '\n        ', (None, -1, -1)), 
     239            (Stream.END, QName('http://example.org/a}div'), (None, -1, -1)), 
     240            (Stream.END_NS, 'y', (None, -1, -1)), 
     241            (Stream.END_NS, 'x', (None, -1, -1)), 
     242        ]) 
     243        output = stream.render(XMLSerializer) 
     244        self.assertEqual("""<x:div xmlns:x="http://example.org/a"> 
     245          <x:p xmlns:x="http://example.org/b"> 
     246            <y:span xmlns:y="http://example.org/a"/> 
     247          </x:p> 
     248        </x:div>""", output) 
     249         
     250    def test_multiple_bound_default_namespace_with_attribute(self): 
     251        stream = Stream([ 
     252            (Stream.START_NS, ('', 'http://example.org/a'), (None, -1, -1)), 
     253            (Stream.START, (QName('http://example.org/a}div'), Attrs()), (None, -1, -1)), 
     254            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     255            (Stream.START_NS, ('a', 'http://example.org/a'), (None, -1, -1)), 
     256            (Stream.START_NS, ('b', 'http://example.org/b'), (None, -1, -1)), 
     257            (Stream.START, (QName('http://example.org/a}div'), Attrs()), (None, -1, -1)), 
     258            (Stream.TEXT, '\n            ', (None, -1, -1)), 
     259            (Stream.START, (QName('http://example.org/b}p'), Attrs([(QName('http://example.org/a}class'), 'name')])), (None, -1, -1)), 
     260            (Stream.END, QName('http://example.org/b}p'), (None, -1, -1)), 
     261            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     262            (Stream.END, QName('http://example.org/a}div'), (None, -1, -1)), 
     263            (Stream.END_NS, 'b', (None, -1, -1)), 
     264            (Stream.END_NS, 'a', (None, -1, -1)),             
     265            (Stream.TEXT, '\n        ', (None, -1, -1)), 
     266            (Stream.END, QName('http://example.org/a}div'), (None, -1, -1)), 
     267            (Stream.END_NS, '', (None, -1, -1)), 
     268        ]) 
     269        output = stream.render(XMLSerializer) 
     270        self.assertEqual("""<div xmlns="http://example.org/a"> 
     271          <div xmlns:a="http://example.org/a" xmlns:b="http://example.org/b"> 
     272            <b:p a:class="name"/> 
     273          </div> 
     274        </div>""", output) 
     275     
     276    def test_prefer_default_namespace(self): 
     277        stream = Stream([ 
     278            (Stream.START_NS, ('a', 'http://example.org/'), (None, -1, -1)), 
     279            (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), 
     280            (Stream.START, (QName('http://example.org/}div'), Attrs()), (None, -1, -1)), 
     281            (Stream.TEXT, '\n          ', (None, -1, -1)), 
     282            (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), 
     283            (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), 
     284            (Stream.TEXT, '\n        ', (None, -1, -1)), 
     285            (Stream.END, QName('http://example.org/}div'), (None, -1, -1)), 
     286            (Stream.END_NS, '', (None, -1, -1)), 
     287            (Stream.END_NS, 'a', (None, -1, -1)), 
     288        ]) 
     289        output = stream.render(XMLSerializer) 
     290        self.assertEqual("""<div xmlns:a="http://example.org/" xmlns="http://example.org/"> 
     291          <p/> 
     292        </div>""", output) 
     293     
    188294    def test_atom_with_xhtml(self): 
    189295        text = """<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"> 
    190296            <id>urn:uuid:c60843aa-0da8-4fa6-bbe5-98007bc6774e</id> 
  • genshi/output.py

     
    1717 
    1818from itertools import chain 
    1919import re 
     20import copy 
    2021 
    2122from genshi.core import escape, Attrs, Markup, Namespace, QName, StreamEventKind 
    2223from genshi.core import START, END, TEXT, XML_DECL, DOCTYPE, START_NS, END_NS, \ 
     
    573574            namespaces.setdefault(uri, []).append(prefix) 
    574575            prefixes.setdefault(prefix, []).append(uri) 
    575576 
     577        active = [set(['xml'])] 
    576578        ns_attrs = [] 
    577         _push_ns_attr = ns_attrs.append 
    578579        def _make_ns_attr(prefix, uri): 
    579580            return u'xmlns%s' % (prefix and ':%s' % prefix or ''), uri 
     581        def _push_ns_attr(prefix, uri): 
     582            ns_attrs.append(_make_ns_attr(prefix, uri)) 
     583            active[-1].add(prefix) 
    580584 
    581585        def _gen_prefix(): 
    582586            val = 0 
     
    585589                yield 'ns%d' % val 
    586590        _gen_prefix = _gen_prefix().next 
    587591 
     592        def _ns_prefix(uri, attr=False): 
     593            for prefix in chain(('',), namespaces.get(uri, [])): 
     594                if attr and prefix == '': continue 
     595                if prefixes.get(prefix, [None])[-1] == uri: 
     596                    if prefix not in active[-1]: 
     597                        _push_ns_attr(prefix, uri) 
     598                    return prefix 
     599            return None 
     600         
    588601        for kind, data, pos in stream: 
    589602 
    590603            if kind is START or kind is EMPTY: 
     
    593606                tagname = tag.localname 
    594607                tagns = tag.namespace 
    595608                if tagns: 
    596                     if tagns in namespaces: 
    597                         prefix = namespaces[tagns][-1] 
     609                    prefix = _ns_prefix(tagns) 
     610                    if prefix is not None: 
    598611                        if prefix: 
    599612                            tagname = u'%s:%s' % (prefix, tagname) 
    600613                    else: 
    601                         _push_ns_attr((u'xmlns', tagns)) 
    602614                        _push_ns('', tagns) 
     615                        _push_ns_attr('', tagns) 
    603616 
    604617                new_attrs = [] 
    605618                for attr, value in attrs: 
    606619                    attrname = attr.localname 
    607620                    attrns = attr.namespace 
    608621                    if attrns: 
    609                         if attrns not in namespaces: 
     622                        prefix = _ns_prefix(attrns, attr=True) 
     623                        if prefix is None: 
    610624                            prefix = _gen_prefix() 
    611625                            _push_ns(prefix, attrns) 
    612                             _push_ns_attr(('xmlns:%s' % prefix, attrns)) 
    613                         else: 
    614                             prefix = namespaces[attrns][-1] 
     626                            _push_ns_attr(prefix, attrns) 
    615627                        if prefix: 
    616628                            attrname = u'%s:%s' % (prefix, attrname) 
    617629                    new_attrs.append((attrname, value)) 
    618630 
    619631                yield kind, (tagname, Attrs(ns_attrs + new_attrs)), pos 
    620632                del ns_attrs[:] 
     633                active.append(copy.copy(active[-1])) 
    621634 
    622635            elif kind is END: 
    623636                tagname = data.localname 
    624637                tagns = data.namespace 
    625638                if tagns: 
    626                     prefix = namespaces[tagns][-1] 
     639                    prefix = _ns_prefix(tagns) 
    627640                    if prefix: 
    628641                        tagname = u'%s:%s' % (prefix, tagname) 
    629642                yield kind, tagname, pos 
    630  
     643                active.pop() 
     644                 
    631645            elif kind is START_NS: 
    632646                prefix, uri = data 
    633                 if uri not in namespaces: 
    634                     prefix = prefixes.get(uri, [prefix])[-1] 
    635                     _push_ns_attr(_make_ns_attr(prefix, uri)) 
     647                existing = _ns_prefix(uri) 
     648                if existing is None or (existing == '' and prefix != ''): 
     649                    _push_ns_attr(prefix, uri) 
    636650                _push_ns(prefix, uri) 
    637651 
    638652            elif kind is END_NS: