Unbound method must be called with instance as first argument
Consider following testcase: class C: pass class D(C): pass def m(self): return self C.m = m D.m = C.m print C().m() print D().m() CPython runs it okay, but PyPy baffles and raises TypeError, with message ${SUBJ}. Changing 5th line to "D.m = m" and it runs fine. The point is, "D.m = C.m" should transform C.m from unbound method of class C to unbound method of class D... This breaks mailbox.py, among other things. Read how PortableUnixMailbox is implemented there. Regards,
Hi Seo, Seems the problem is that Method does not have a __get__ (since C.m returns a Method not a Function via __get__ while assigning to D). In other words mm = C.m D.m = mm assert D.m == mm # True since no __get__() assert C.m != mm # True since __get__() creates a new method Some simple fixes are 1. add a __get__() method to interpreter.function.Method which forwards request onto w_function attribute. 2. put a hack in objspace/descrooperation.DescrOperation in get() to handle special case for Method (yuk!) The CPython implementation seems to be doing some hairy caching instead of creating new method each __get__(), so it hard to tell if this is correct behaviour. And the above fixes are only based on my understanding of desciptors, which has a high probability to be wrong. If someone backs me up I can commit fix 1. :-) Cheers, Richard On Sat, 3 Jul 2004, Seo Sanghyeon wrote:
Consider following testcase:
class C: pass class D(C): pass def m(self): return self C.m = m D.m = C.m print C().m() print D().m()
CPython runs it okay, but PyPy baffles and raises TypeError, with message ${SUBJ}.
Changing 5th line to "D.m = m" and it runs fine. The point is, "D.m = C.m" should transform C.m from unbound method of class C to unbound method of class D...
This breaks mailbox.py, among other things. Read how PortableUnixMailbox is implemented there.
Regards, _______________________________________________ pypy-dev@codespeak.net http://codespeak.net/mailman/listinfo/pypy-dev
Hey Richard, [Richard Emslie Sat, Jul 03, 2004 at 05:54:23PM +0100]
Seems the problem is that Method does not have a __get__ (since C.m returns a Method not a Function via __get__ while assigning to D). In other words
mm = C.m D.m = mm assert D.m == mm # True since no __get__() assert C.m != mm # True since __get__() creates a new method
Some simple fixes are
1. add a __get__() method to interpreter.function.Method which forwards request onto w_function attribute.
yes, i think this is the right way.
2. put a hack in objspace/descrooperation.DescrOperation in get() to handle special case for Method (yuk!)
yuk indeed!
The CPython implementation seems to be doing some hairy caching instead of creating new method each __get__(), so it hard to tell if this is correct behaviour. And the above fixes are only based on my understanding of desciptors, which has a high probability to be wrong. If someone backs me up I can commit fix 1. :-)
I might have a lower probabiliy of beeing wrong but Samuele probably has one close to zero. I suggest you go with your first solution (and also write some tests for it :-). Btw, it would be really nice to be able to run app-level tests against CPython apart from PyPy/std basically ensuring us we are beeing compatible. However, this probably can only be implemented nicely after we do the utest-switch. cheers, holger
Hi Holger, On Mon, 5 Jul 2004, holger krekel wrote:
The CPython implementation seems to be doing some hairy caching instead of creating new method each __get__(), so it hard to tell if this is correct behaviour. And the above fixes are only based on my understanding of desciptors, which has a high probability to be wrong. If someone backs me up I can commit fix 1. :-)
I might have a lower probabiliy of beeing wrong but Samuele probably has one close to zero. I suggest you go with your first solution (and also write some tests for it :-).
Thanks for feedback! Done, even with some tests. ;-)
Btw, it would be really nice to be able to run app-level tests against CPython apart from PyPy/std basically ensuring us we are beeing compatible. However, this probably can only be implemented nicely after we do the utest-switch.
Makes a lot of sense if we are in murky grounds with what the correct behaviour should be. Looking forward to the new testing framework. :-) Cheers, Richard
Hello Richard, On Tue, Jul 06, 2004 at 02:12:04PM +0100, Richard Emslie wrote:
The CPython implementation seems to be doing some hairy caching instead of creating new method each __get__(), so it hard to tell if this is correct behaviour.
I believe we should closely follow CPython's behavior unless there is a reason not to. The algorithm appears to look like: * already bound methods are never re-bound * unbound method objects can get bound but only to something more specific than before. For example if you do: class C: pass class D: pass # not a subclass C.m = ... D.m = C.m then D().m doesn't give you a bound method in CPython either. I'm not sure why exactly it is so, but I guess it makes some sense. Also note that in your patch you shouldn't assume that w_function is a wrapped Function instance (the name is bad, it should be w_callable). A bientot, Armin.
Hi Armin, On Wed, 7 Jul 2004, Armin Rigo wrote:
Hello Richard,
On Tue, Jul 06, 2004 at 02:12:04PM +0100, Richard Emslie wrote:
The CPython implementation seems to be doing some hairy caching instead of creating new method each __get__(), so it hard to tell if this is correct behaviour.
I believe we should closely follow CPython's behavior unless there is a reason not to. The algorithm appears to look like:
* already bound methods are never re-bound
* unbound method objects can get bound but only to something more specific than before.
I got you checkins which do this - thanks. I did notice that def f(): pass f.__get__() crashes the pypy interpreter. Sticking a try/except around performance_shortcut_call() in DescrOperation.call_args() fixes - but is not a solution. I'm not sure why it goes wrong. Cheers, Richard
Hello Richard, On Sun, Jul 11, 2004 at 01:57:29PM +0100, Richard Emslie wrote:
I did notice that def f(): pass f.__get__()
crashes the pypy interpreter.
Yuk. The performance_shortcut_call() optimization is buggy: it doesn't check the number of arguments before calling the interp-level implementation. Fixed. (But I still think that the whole gateway.py mess needs simplification...) Thanks, Armin
participants (4)
-
Armin Rigo -
holger krekel -
Richard Emslie -
Seo Sanghyeon