Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Incremental serializer: fix namespaces #79

wants to merge 5 commits into from

2 participants

  • Reject invalid prefixes (only None, unicode or ASCII-only bytes are valid)
  • Pick a prefix for namespaces without one
  • Write prefix declarations with attributes.

I don't think this is going to work. Just because you add the declaration to the (reversed) nsmap doesn't mean that the element that originally owned that particular nsmap wrote the declaration into the file. So subsequent siblings may end up with a missing prefix declaration. It should work to put it into the nsmap that was originally passed into this function, though.

_find_prefix() is only used in _write_start_element() and _write_attributes() and the latter is only used in the former, which in turn is only used when a element is being written to the file.

A declaration does not apply to siblings.


What I meant was that this code is potentially changing the nsmap of an ancestor element instead of the local one. If that happens, a subsequent lookup from a sibling will pick up a prefix that is not declared in its scope.

Oh, I missed that. Of course this should only changed the dict being passed as a parameter. Switching now to a different variable name. Thanks!


This looks a bit, well, funny. I think namespace declarations should be written out explicitly as part of the element writing and should appear before the attributes (which might need them).

We can easily switch to serialize prefix declarations before "normal" attributes, but I don’t think it is supposed to change anything.

The scope of a namespace declaration declaring a prefix extends from the beginning of the start-tag in which it appears to the end of the corresponding end-tag

And the syntax of a declaration really is the same as that of attributes.


Sure. I'm fine with doing it that way, just switch the order.


Right, I mixed this up. Thanks for catching it.

Two things here: test if prefix is None rather than just if prefix (do not accept 0 or the empty string), and add the _prefixValidOrRaise check.


Merged. Thanks!

@scoder scoder closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 21, 2012
  1. @SimonSapin
  2. @SimonSapin

    Incremental serializer: fix prefixes

    SimonSapin authored
    * Pick a prefix for namespaces without one
    * Write prefix declarations with attributes.
  3. @SimonSapin
Commits on Nov 22, 2012
  1. @SimonSapin
  2. @SimonSapin
This page is out of date. Refresh to see the latest.
Showing with 57 additions and 15 deletions.
  1. +31 −15 src/lxml/serializer.pxi
  2. +26 −0 src/lxml/tests/
46 src/lxml/serializer.pxi
@@ -711,13 +711,15 @@ cdef class _IncrementalFileWriter:
for name, value in _extra.iteritems():
ns, tag = _getNsTag(name)
attributes.append((ns, tag, value))
+ reversed_nsmap = {}
if nsmap:
- nsmap = { _utf8(ns) : (_utf8(prefix) if prefix else None)
- for prefix, ns in nsmap.items() }
- else:
- nsmap = None
+ for prefix, ns in nsmap.items():
+ if prefix is not None:
+ prefix = _utf8(prefix)
+ _prefixValidOrRaise(prefix)
+ reversed_nsmap[_utf8(ns)] = prefix
ns, name = _getNsTag(tag)
- return _FileWriterElement(self, (ns, name, attributes, nsmap))
+ return _FileWriterElement(self, (ns, name, attributes, reversed_nsmap))
cdef _write_qname(self, bytes name, bytes prefix):
if prefix is not None:
@@ -732,17 +734,25 @@ cdef class _IncrementalFileWriter:
prefix = self._find_prefix(ns, nsmap)
tree.xmlOutputBufferWrite(self._c_out, 1, '<')
self._write_qname(name, prefix)
- self._write_attributes(attributes)
+ self._write_attributes(attributes, nsmap)
tree.xmlOutputBufferWrite(self._c_out, 1, '>')
- self._element_stack.append((ns, name, prefix, nsmap))
+ # _find_prefix() changes nsmap, so making a copy can be important
+ # if the same _FileWriterElement is used more than onec.
+ self._element_stack.append((ns, name, prefix, nsmap.copy()))
self._status = WRITER_IN_ELEMENT
- cdef _write_attributes(self, list attributes):
- for ns, name, value in attributes:
+ cdef _write_attributes(self, list attributes, dict nsmap):
+ attributes = [
+ (self._find_prefix(ns, nsmap), name, value)
+ for ns, name, value in attributes]
+ # _find_prefix may change nsmap, do this afterwards.
+ ns_declarations = [
+ (b'xmlns', prefix, href) for href, prefix in nsmap.items()]
+ for prefix, name, value in (ns_declarations + attributes):
tree.xmlOutputBufferWrite(self._c_out, 1, ' ')
- self._write_qname(name, self._find_prefix(ns))
+ self._write_qname(name, prefix)
tree.xmlOutputBufferWrite(self._c_out, 2, '="')
tree.xmlOutputBufferWriteEscape(self._c_out, _xcstr(value), NULL)
tree.xmlOutputBufferWrite(self._c_out, 1, '"')
@@ -765,16 +775,22 @@ cdef class _IncrementalFileWriter:
self._status = WRITER_FINISHED
- cdef _find_prefix(self, bytes href, nsmap=None):
+ cdef _find_prefix(self, bytes href, nsmap):
if href is None:
return None
if nsmap is not None and href in nsmap:
return nsmap[href]
for element_config in reversed(self._element_stack):
- nsmap = element_config[-1]
- if href in nsmap:
- return nsmap[href]
- return None
+ ancestor_nsmap = element_config[-1]
+ if href in ancestor_nsmap:
+ return ancestor_nsmap[href]
+ i = 0
+ while 1:
+ prefix = _utf8('ns%d' % i)
+ if prefix not in nsmap:
+ nsmap[href] = prefix
+ return prefix
+ i += 1
def write(self, *args, bint with_tail=True, bint pretty_print=False):
"""write(self, *args, with_tail=True, pretty_print=False)
26 src/lxml/tests/
@@ -69,6 +69,32 @@ def test_write_Element_repeatedly(self):
self.assertEqual(100, len(tree.getroot()))
self.assertEqual(set(['test']), set(el.tag for el in tree.getroot()))
+ def test_namespace_nsmap(self):
+ with etree.xmlfile(self._file) as xf:
+ with xf.element('{nsURI}test', nsmap={'x': 'nsURI'}):
+ pass
+ self.assertXml('<x:test xmlns:x="nsURI"></x:test>')
+ def test_namespace_nested_nsmap(self):
+ with etree.xmlfile(self._file) as xf:
+ with xf.element('test', nsmap={'x': 'nsURI'}):
+ with xf.element('{nsURI}toast'):
+ pass
+ self.assertXml('<test xmlns:x="nsURI"><x:toast></x:toast></test>')
+ def test_anonymous_namespace(self):
+ with etree.xmlfile(self._file) as xf:
+ with xf.element('{nsURI}test'):
+ pass
+ self.assertXml('<ns0:test xmlns:ns0="nsURI"></ns0:test>')
+ def test_namespace_nested_anonymous(self):
+ with etree.xmlfile(self._file) as xf:
+ with xf.element('test'):
+ with xf.element('{nsURI}toast'):
+ pass
+ self.assertXml('<test><ns0:toast xmlns:ns0="nsURI"></ns0:toast></test>')
def test_pi(self):
with etree.xmlfile(self._file) as xf:
Something went wrong with that request. Please try again.