This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Meta-class inheritance problem
Type: enhancement Stage:
Components: Interpreter Core Versions:
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: gvanrossum Nosy List: david_abrahams, gvanrossum, loriend, nobody, tim.peters
Priority: low Keywords:

Created on 2001-03-17 10:10 by loriend, last changed 2022-04-10 16:03 by admin. This issue is now closed.

Messages (14)
msg3911 - (view) Author: Lorien Dunn (loriend) Date: 2001-03-17 10:10
I've run into a bug in python 2.0. The real code that
the following pseudo code represents throws a
TypeError.

class MyDerivedClass(MyPurePythonClass,MyBoostClass):
        def __init__(self):
                MyPurePythonClass.__init__(self)
                MyBoostClass.__init__(self)

The problem occurs in MyPurePythonClass- Python says
"unbound method must be called with class instance 1st
argument". This makes sense: I'm passing a meta-class
instead of a class. David Abrahams (the main author of
boost::python) agrees that this is a Python problem,
and that it should affect Zope as well.

I think I've found the point in the python 2.0 sorce
where it occurs:


ceval.c, line 1816 (reformatted for clarity):

/*              BEGIN CODE SAMPLE               */

else
{
/* Unbound methods must be called with an instance of
the class (or a derived class) as first argument */

     if (na > 0 && (self = stack_pointer[-n]) != NULL
                && PyInstance_Check(self)
                && PyClass_IsSubclass((PyObject
*) 		 		   (((PyInstanceObject*)  self)->in_class),
class))
                  /* Handy-dandy */ ;
      else
      {
        PyErr_SetString(PyExc_TypeError, "unbound
method must be 		called with class instance 1st
argument");
        x = NULL;
        break;
      }
}

/*              END CODE SAMPLE         */
msg3912 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2001-03-18 05:29
Logged In: YES 
user_id=31435

Assigned to Guido, because I bet JimF would refuse to take 
blame for this <wink>.
msg3913 - (view) Author: Nobody/Anonymous (nobody) Date: 2001-03-18 13:03
Logged In: NO 

I was a bit surprised to learn that this check was still in 
the code after issubclass and isinstance had been fixed to 
work with extension classes. Suppose you checked for a 
__class__ attribute on the first argument instead (or 
additionally)?
msg3914 - (view) Author: David Abrahams (david_abrahams) Date: 2001-03-18 13:30
Logged In: YES 
user_id=52572

Sorry, that anonymous comment is mine. I'm still a bit 
clumsy with SourceForge.

-Dve
msg3915 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-03-18 22:22
Logged In: YES 
user_id=6380

Zope has several workarounds, so isn't affected.

But I agree it could be fixed.  The test is there because it wants to enforce a concept: 'self' should be an instance 
of the class that defines the method (where a subclass instance is fine too).

But the implementation of the test is based too much on the default implementation of classes.

Could someone please submit a patch that replaces the concrete isinstance() test with an isinstance() test similar 
to that in bltinmodule.c?  (Maybe we need to add a new C API, PyObject_IsInstance().)

If someone could come up with a check that conve
msg3916 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-03-21 19:18
Logged In: YES 
user_id=6380

I *think* I've fixed this now -- please test with the
current CVS.

Please let me know if I goofed up, and I'll reopen the bug
report.
msg3917 - (view) Author: Lorien Dunn (loriend) Date: 2001-03-22 19:59
Logged In: YES 
user_id=175701

I'm afraid it still doesn't work :( 
	the line is (from cvs code):
		ok = PyObject_IsInstance(self, class)
	and the problem is that ok == 0.
Thank you for patching this- I've spent hours trying to
figure out enough about the python internals to do it
myself.

Lorien
	 
msg3918 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-03-22 20:03
Logged In: YES 
user_id=6380

OK, try this.  In your Python code, what does
isinstance(self, MyPurePythonClass) return?
msg3919 - (view) Author: Lorien Dunn (loriend) Date: 2001-03-22 21:58
Logged In: YES 
user_id=175701

In the following code it returns 0.

MyDerivedClass(MyPurePythonClass, MyBoostClass):
       def __init__(self):
            MyBoostClass.__init__(self)
            isinstance(self, MyPurePythonClass)


msg3920 - (view) Author: David Abrahams (david_abrahams) Date: 2001-03-23 03:09
Logged In: YES 
user_id=52572

Though I haven't checked out the latest from CVS, I'm at a 
loss to explain the results Lorien is getting. In 
particular, Boost.Python class instances have a __class__ 
attribute, and Boost.Python classes have a __bases__ 
attribute that is a tuple. Isn't that all that should be 
needed for isinstance() to work properly?...

Aha, now I remember: The first base class in the list 
controls which metaclass object gets control when a derived 
class is created. So the declaration of MyDerivedClass 
below actually creates a regular Python class with a base 
that is a Boost.Python class. At that point, everything is 
beyond my control. To get it to work, at least begin by 
swapping the order of the base classes.

I wonder if it would make sense to always defer to the 
first extension type's type (if any) for subclass creation? 
It's not a general solution (i.e. what if you have multiple 
extension types in the __bases__ tuple?) but at least it 
would handle this case. Surely it's less likely that a 
regular Python class can make any sane use of methods from 
an extension class.

-Dave
msg3921 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-03-23 03:32
Logged In: YES 
user_id=6380

David, I'm not sure I agree with your theory.  At Jim
Fulton's request, when creating a subclass, Python actually
looks through the base classes for one that's not a regular
class, and gives that one control of the subclass creation
process. So MyDerivedClass really should be a Boost.Python
class!  (Lorien can easily verify that by printing
self.__class__.)

Otherwise, if it was a regular class, the __init__ call
woudln't have failed in the first place!

Since I don't have Boost (yet), I can't look into this
further right now, and I fear it will have to remain
unresolved until after the 2.1b2 release.  But I'll try to
look into it again before the final 2.1 release.
msg3922 - (view) Author: Lorien Dunn (loriend) Date: 2001-03-23 03:38
Logged In: YES 
user_id=175701

Guido,
	printing self.__class__ results in the text "<extension
class> MyDerivedClass.MyDerivedClass  at 8121560"

David,
	swapping the order of the base classes makes no difference. 

Thanks.
msg3923 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-04-10 20:14
Logged In: YES 
user_id=6380

Sorry, I'm at a loss with this.

What does isinstance(self, MyPurePythonClass) return? 
(Should be 1)

Lowering the priority -- unless we get some light quickly,
this won't make it into 2.1.
msg3924 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2001-08-11 04:29
Logged In: YES 
user_id=6380

I'm closing this as a Won't Fix.  In 2.2, metaclasses will
work differently anyway.
History
Date User Action Args
2022-04-10 16:03:52adminsetgithub: 34177
2001-03-17 10:10:10loriendcreate