From 8027c39cd60a40323eb2ffcfac6bbc102e317a53 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 1 Mar 2019 15:11:50 +0100 Subject: Keep the original dict insertion order in Py3.6+ when setting attributes or namespaces from a user provided dict. This follows the ElementTree change in Py3.8, see https://bugs.python.org/issue34160. --- src/lxml/apihelpers.pxi | 17 ++++++++++++++--- src/lxml/objectify.pyx | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src/lxml') diff --git a/src/lxml/apihelpers.pxi b/src/lxml/apihelpers.pxi index cf932d43..5d410e60 100644 --- a/src/lxml/apihelpers.pxi +++ b/src/lxml/apihelpers.pxi @@ -244,6 +244,10 @@ cdef _iter_nsmap(nsmap): The difference to _iter_attrib() is that None doesn't sort with strings in Py3.x. """ + if python.PY_VERSION_HEX >= 0x03060000: + # dicts are insertion-ordered in Py3.6+ => keep the user provided order. + if isinstance(nsmap, dict): + return nsmap.items() if len(nsmap) <= 1: return nsmap.items() # nsmap will usually be a plain unordered dict => avoid type checking overhead @@ -271,7 +275,10 @@ cdef _iter_attrib(attrib): Tries to preserve an existing order and sorts if it assumes no order. """ # attrib will usually be a plain unordered dict - if type(attrib) is dict: + if isinstance(attrib, dict): + if python.PY_VERSION_HEX >= 0x03060000: + # dicts are insertion-ordered in Py3.6+ => keep the user provided order. + return attrib.items() return sorted(attrib.items()) elif isinstance(attrib, (_Attrib, OrderedDict)): return attrib.items() @@ -292,8 +299,12 @@ cdef _initNodeAttributes(xmlNode* c_node, _Document doc, attrib, dict extra): is_html = doc._parser._for_html seen = set() if extra: - for name, value in sorted(extra.items()): - _addAttributeToNode(c_node, doc, is_html, name, value, seen) + if python.PY_VERSION_HEX >= 0x03060000: + for name, value in extra.items(): + _addAttributeToNode(c_node, doc, is_html, name, value, seen) + else: + for name, value in sorted(extra.items()): + _addAttributeToNode(c_node, doc, is_html, name, value, seen) if attrib: for name, value in _iter_attrib(attrib): _addAttributeToNode(c_node, doc, is_html, name, value, seen) diff --git a/src/lxml/objectify.pyx b/src/lxml/objectify.pyx index f5fe7b51..9da49a1c 100644 --- a/src/lxml/objectify.pyx +++ b/src/lxml/objectify.pyx @@ -1327,7 +1327,7 @@ cdef object _dump(_Element element, int indent): result = f"{indentstr}{element.tag} = {value} [{_typename(element)}]\n" xsi_ns = u"{%s}" % XML_SCHEMA_INSTANCE_NS pytype_ns = u"{%s}" % PYTYPE_NAMESPACE - for name, value in cetree.iterattributes(element, 3): + for name, value in sorted(cetree.iterattributes(element, 3)): if u'{' in name: if name == PYTYPE_ATTRIBUTE: if value == TREE_PYTYPE_NAME: -- cgit v1.2.1