[Patches] [ python-Patches-490402 ] Improper PyMethodObject->im_class fix
noreply@sourceforge.net
noreply@sourceforge.net
Fri, 07 Dec 2001 13:49:32 -0800
Patches item #490402, was opened at 2001-12-07 13:09
You can respond by visiting:
http://sourceforge.net/tracker/?func=detail&atid=305470&aid=490402&group_id=5470
Category: Core (C code)
Group: None
Status: Closed
Resolution: Invalid
Priority: 5
Submitted By: Robin Dunn (robind)
Assigned to: Guido van Rossum (gvanrossum)
Summary: Improper PyMethodObject->im_class fix
Initial Comment:
In wxPython's code for reflecting virtual method calls
to Python methods I have code that looks up the method
by name and checks the class it comes from. If it's
in the base Python shadow class then I just call the
base C++ method directly to save time, otherwise I
call the Python method.
In the Python 2.2 betas I found that the class object
returned from PyMethod_GET_CLASS is now the class of
the instance, not the class where the method was
defined as it was before, and as documented in
classobject.h. This causes my code to recurse until
the max stack depth is reached, and I've been unable
to find a way to work around this change.
The differences can be further illustrated by this
little Python program:
class A:
def spam(self):
print "spam"
class B(A):
pass
import sys
print sys.version
b = B()
print b.spam
print B.spam
When run with various version of Python I get this:
[C:\temp] p15 method_test.py
1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)]
<method A.spam of B instance at 86c958>
<unbound method A.spam>
[C:\temp] p20 method_test.py
2.0 (#8, Oct 16 2000, 17:27:58) [MSC 32 bit (Intel)]
<method A.spam of B instance at 007F4734>
<unbound method A.spam>
[C:\temp] p21 method_test.py
2.1.1 (#20, Jul 20 2001, 01:19:29) [MSC 32 bit (Intel)]
<method A.spam of B instance at 007C1A9C>
<unbound method A.spam>
[C:\temp] p22 method_test.py
2.2b2 (#26, Nov 16 2001, 11:44:11) [MSC 32 bit (Intel)]
<bound method B.spam of <__main__.B instance at
0x00778868>>
<unbound method B.spam>
Notice the change from "A.spam" to "B.spam" in the
final case.
Going on the assumption that this is a bug and not
intended behaviour, the attached patch fixes this
issue as can be seen in this run of the test, but I
don't know if there are any other ramifications:
[C:\temp] \PROJECTS\Python-cvs\PCbuild\python_d.exe
method_test.py
Adding parser accelerators ...
Done.
2.2b2+ (#26, Dec 7 2001, 11:46:31) [MSC 32 bit
(Intel)]
<bound method A.spam of <__main__.B instance at
0x007DCF80>>
<unbound method A.spam>
[3306 refs]
If this is not a bug then please let me know what the
proper way is via the API to find the class that
defined a particular method.
----------------------------------------------------------------------
>Comment By: Robin Dunn (robind)
Date: 2001-12-07 13:49
Message:
Logged In: YES
user_id=53955
Thanks.
BTW, the comment on line 34 of classobject.h (about
im_class) should probably be changed.
----------------------------------------------------------------------
Comment By: Guido van Rossum (gvanrossum)
Date: 2001-12-07 13:45
Message:
Logged In: YES
user_id=6380
I haven't had the need to do this elsewhere, so there's no C
API.
To do it for new-style classes, look at the implementation
of _PyType_Lookup() in typeobject.c; at the point where it
returns res, the defining class is base. (Please look at the
current CVS or at least 2.2b2; there's a new special case in
the code to allow classic classes in the MRO of a new-style
class.)
For classic classes, you can copy class_lookup() from
classobject.c.
----------------------------------------------------------------------
Comment By: Robin Dunn (robind)
Date: 2001-12-07 13:38
Message:
Logged In: YES
user_id=53955
Is there an easy way to do that in the C API? If not, can
you give me some hints for how to do it the hard way? <wink>
----------------------------------------------------------------------
Comment By: Guido van Rossum (gvanrossum)
Date: 2001-12-07 13:32
Message:
Logged In: YES
user_id=6380
It's not a bug. This change has acually revealed several
bugs in existing code of the following sort:
class A:
def method(self): ...
class B(A): ... # does not override method (yet)
class C(A):
def method(self):
...do something else...
B.method(self) # invoke base class method
Note that C doesn't inherit from B, yet the call to
B.method(self) succeeded (in prior Python versions), because
im_class was set to the defining class instead of the class
used in the getattr operation. This kind of bug occurs when
the class hierarchy is restructured but not all upcalls are
fixed, and is waiting for problems (when B grows its own
implementation of method(), C which is unrelated to B
suddenly stops working).
To find the class that defines a method, for new-style
classes you can walk the __mro__ tuple looking in the
__dict__ of each class found for the method; for classic
classes you have to walk the hierarchy using __bases__. I
hope this is not too much of an inconvenience.
Let me know if further action is required; I'd like to close
this bug report as "Won't fix".
----------------------------------------------------------------------
Comment By: Tim Peters (tim_one)
Date: 2001-12-07 13:15
Message:
Logged In: YES
user_id=31435
This one's for Guido.
----------------------------------------------------------------------
You can respond by visiting:
http://sourceforge.net/tracker/?func=detail&atid=305470&aid=490402&group_id=5470