OrderedDict attributes no longer work in python 2.7

Bug #1838252 reported by Cosimo Lupo
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
lxml
Fix Released
Low
scoder

Bug Description

Up until lxml 4.3.5, I could pass an OrderedDict as an etree.Element's attributes to ensure that when serialized to XML, it would keep the order of the attributes:

    In [1]: from lxml import etree

    In [2]: from collections import OrderedDict

    In [3]: e = etree.Element("glyph", OrderedDict([("name", "a"), ("format", "2")]))

    In [4]: etree.tostring(e)
    Out[4]: '<glyph name="a" format="2"/>'

After upgrading to lxml 4.4.0, I know always get the attributes sorted alphabetically:

    Out[4]: '<glyph format="2" name="a"/>'

Note that this only happens with version 4.4.0 under python 2.7. It does _not_ also happen with python 3. In the latter case, I am still able to preserve the attributes order when passing an OrderedDict to Element constructor.

Is this a regression or a deliberate change?
I still maintain a py2.py3 codebase and would like to keep the old behavior if possible.
Thank you.

## -- LXML SYSTEM INFO

Python : sys.version_info(major=2, minor=7, micro=16, releaselevel='final', serial=0)
lxml.etree : (4, 4, 0, 0)
libxml used : (2, 9, 9)
libxml compiled : (2, 9, 9)
libxslt used : (1, 1, 33)
libxslt compiled : (1, 1, 33)
## ------

Revision history for this message
Cosimo Lupo (lupocos) wrote :

I just noticed the issue happens on python 3.5 as well, not only python 2.7.
I suspect that in the latest lxml 4.4.0, the attributes that are passed to the Element constructor are casted to a built-in dict, and the latter remembers the insertion order from >= python 3.6.

Revision history for this message
scoder (scoder) wrote :

Thanks for the report. It's not intended. The bug is here:

https://github.com/lxml/lxml/blob/master/src/lxml/apihelpers.pxi#L290-L305

Note how the inheritance test first checks for "dict", then for "OrderedDict". Sadly, "OrderedDict" inherits from "dict", so the second branch is never taken for it.

Changed in lxml:
milestone: none → 4.4.1
status: New → Confirmed
Revision history for this message
scoder (scoder) wrote :
Changed in lxml:
assignee: nobody → scoder (scoder)
status: Confirmed → Fix Committed
Revision history for this message
Cosimo Lupo (lupocos) wrote :

Thank you very much for the quick fix! :)
I look forward to the next release.

scoder (scoder)
Changed in lxml:
importance: Undecided → Low
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.