Difference in behaviour: bug or bad usage?

The following is a simplified example of the behaviour we're seeing the Sunburnt library, which uses lxml: Python 2.4.3 (#1, Sep 21 2011, 19:55:41) [GCC 4.1.2 20080704 (Red Hat 4.1.2-51)] on linux2 Type "help", "copyright", "credits" or "license" for more information.
import lxml.etree from lxml.builder import E
class Foo(object): ... QUERY = E.query ... ... def test(self, q): ... return self.QUERY(q) ... foo = Foo() bar = foo.test('a') Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 5, in test File "/opt/define/lib/python2.4/site-packages/lxml-2.2.7_r0-py2.4-linux-x86_64.egg/lxml/builder.py", line 47, in <lambda> return lambda *args, **kwargs: func(tag, *args, **kwargs) File "/opt/define/lib/python2.4/site-packages/lxml-2.2.7_r0-py2.4-linux-x86_64.egg/lxml/builder.py", line 220, in __call__ raise TypeError("bad argument type: %r" % item) TypeError: bad argument type: <__main__.Foo object at 0x2b1a8a79ee10>
Python 2.7, lxml 2.7.7
import lxml.etree from lxml.builder import E
class Foo(object): ... QUERY = E.query ... ... def test(self, q): ... return self.QUERY(q) ... foo = Foo() bar = foo.test('a') print lxml.etree.tostring(bar) <query>a</query>
I'm looking for guidance on whether the exception in Python 2.4 is due to a bug in lxml, incorrect usage in binding E.query to a class attribute, or perhaps an error on our part when building lxml on Redhat. The Sunburnt code in which this occurred is SoldrDelete.delete_queries() https://github.com/tow/sunburnt/blob/master/sunburnt/schema.py#L617 With thanks, Alex Think green - keep it on the screen. This e-mail and any attachment is for authorised use by the intended recipient(s) only. It may contain proprietary material, confidential information and/or be subject to legal privilege. It should not be copied, disclosed to, retained or used by, any other party. If you are not an intended recipient then please promptly delete this e-mail and any attachment and all copies and inform the sender. Thank you.

On Tue, Jan 24, 2012 at 01:21:28PM +0000, Willmer, Alex (PTS) wrote:
The following is a simplified example of the behaviour we're seeing the Sunburnt library, which uses lxml:
Python 2.4.3 (#1, Sep 21 2011, 19:55:41) [GCC 4.1.2 20080704 (Red Hat 4.1.2-51)] on linux2 Type "help", "copyright", "credits" or "license" for more information.
import lxml.etree from lxml.builder import E
class Foo(object): ... QUERY = E.query ... ... def test(self, q): ... return self.QUERY(q) ... foo = Foo() bar = foo.test('a') Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 5, in test File "/opt/define/lib/python2.4/site-packages/lxml-2.2.7_r0-py2.4-linux-x86_64.egg/lxml/builder.py", line 47, in <lambda> return lambda *args, **kwargs: func(tag, *args, **kwargs) File "/opt/define/lib/python2.4/site-packages/lxml-2.2.7_r0-py2.4-linux-x86_64.egg/lxml/builder.py", line 220, in __call__ raise TypeError("bad argument type: %r" % item) TypeError: bad argument type: <__main__.Foo object at 0x2b1a8a79ee10>
On Python 2.4 lxml.builder.E.query is a plain function (a lambda). When you assign it to a class attribute it becomes an unbound method -- that's how Python works. When you access it through an instance attribute, it becomes a bound method, and when you call it it passes self as the first argument. Here's a simplified example (I was too lazy to type yours into a Python REPL): >>> import lxml.builder >>> class Foo(object): ... QUERY = lxml.builder.E.query ... >>> Foo().QUERY('a') Traceback (most recent call last): ... TypeError: bad argument type: <__main__.Foo object at 0xb7336ccc> and a workaround: >>> class Foo(object): ... QUERY = staticmethod(lxml.builder.E.query) ... >>> Foo().QUERY('a') <Element query at 0x-4892278c>
Python 2.7, lxml 2.7.7
import lxml.etree from lxml.builder import E
class Foo(object): ... QUERY = E.query ... ... def test(self, q): ... return self.QUERY(q) ... foo = Foo() bar = foo.test('a') print lxml.etree.tostring(bar) <query>a</query>
On Python 2.7 lxml.builder.E.query is a <functools.partial object>. It does not become a bound method. Incidentally, my workaround still works: >>> class Foo(object): ... QUERY = staticmethod(lxml.builder.E.query) ... >>> Foo().QUERY('a') <Element query at 0x8bdbd74> so you can use it if you want to be compatible with Python 2.4 and 2.7 in the same codebase.
I'm looking for guidance on whether the exception in Python 2.4 is due to a bug in lxml,
No.
incorrect usage in binding E.query to a class attribute,
Yes.
or perhaps an error on our part when building lxml on Redhat.
No. Regards, Marius Gedminas -- Isn't air travel wonderful? Breakfast in London, dinner in New York, luggage in Brazil.
participants (2)
-
Marius Gedminas
-
Willmer, Alex (PTS)