Thursday, March 8, 2018¶
Today I worked for #2330 (which is part of our porting to Python 3 project).
It all started with this error message TypeError: append() argument must be xml.etree.ElementTree.Element, not lxml.etree._Element.
Until now we have been working with two different implementations of
ElementTree: my self-made implementation based on
etgen.utils.Namespace
, and the lxml implementation. In
Python 3 this now causes above problem, probably because there is some
type checking somewhere.
I think the best solution is to completely move over to the lxml implementation.
Though not the easy way. It has a few consequences.
We cannot use E.tostring() any more because the E
defined in
lxml.etree
doesn’t have that method. My extended version of
tostring()
is now as a global function in etgen.html
.
Old code:
from etgen.html import E
...
E.tostring()
New code:
from etgen.html import E, tostring
...
tostring()
Same problem for E.iselement and E.to_rst and E.raw.
Note that there was no direct error message because expressions E.tostring, E.iselement and E.to_rst are valid: they return an element with that tag. Which is of course not at all our expected behaviour. One possible consequence was:
FutureWarning: The behavior of this method will change in future
versions. Use specific 'len(elem)' or 'elem is not None' test
instead.
elif isinstance(text, six.string_types) or E.iselement(text):
In lxml we don’t have the hack of adding an underscore to attributes like class which are a reserved in Python. We must convert these cases. Before:
return E.li(a, class_="active")
After:
return E.li(a, **{'class': "active"})
There is also the trick described in the lxml tutorial:
def CLASS(*args): # class is a reserved word in Python
return {"class":' '.join(args)}
But I don’t like it because it adds unnecessary complexity.
There were lots of failures saying TypeError: bad argument type: __proxy__(u’ by ‘). These are because lxml elements don’t like Django translatable strings. Old code:
return E.div(E.h2(self.actor.label), e)
New code:
return E.div(E.h2(str(self.actor.label)), e)
I tried to add the str()
in the core, i.e. to have as least
impact as possible to application code.
Another failure was TypeError: update() takes no keyword
arguments
. Old code:
e.attrib.update(align='right')
New code:
e.set('align', 'right')
I also changed the message:
Your printable document (filename sales.VatProductInvoice-135.pdf) should now open in a new browser window. If it doesn’t, please consult <a href=”http://www.lino-framework.org/help/print.html” target=”_blank”>the documentation</a> or ask your system administrator.
to:
Your printable document (<a href=”/media/cache/appypdf/sales.VatProductInvoice-135.pdf”>sales.VatProductInvoice-135.pdf</a>) should now open in a new browser window. If it doesn’t, please ask your system administrator.
Because the a
tag with two attributes (href and target) caused
doctest failures in Python 3 where the order of these attribues is
arbitrary.