ExtensionClasses and class compatibility

Balazs Scheidler bazsi at balabit.hu
Sat May 12 15:07:49 EDT 2001


Hi,

(First I meant to send this to the interpreter developers, but I was unable
to find a developer mailing list, so I send it here in the hope it'll find
the intended recipients)

I have a problem with ExtensionClasses and class compatibility. I have a
program embedding the Python interpreter and exporting some functions and
extensionclasses so that Python can use it. I want to use mixin classes the
following way:

class A:
	def config(self):
		print "A.config"

class B:
	def config(self):
		print "B.config"
		
class Base:
	def config(self):
		print "Base.config"

class C(Base, A, B):
	def config(self):
		Base.config(self)
		A.config(self)
		B.config(self)

c = C()
c.config()

It does work if everything is a normal Python object. If `Base' is an
ExtensionClass it fails with an exception stating that A.config(self) must
be called with instance as first parameter. I'm using Python 2.1 (first I
used 1.5.2 and upgraded in the hope the problem goes away) and the
extensionclass lib bundled with Zope 2.3.2

Looking through the Python sources the culprit seems to be in the
PyObject_IsInstance() function. If Base is an extensionclass its instance is
not a PyInstanceObject, but the class we are checking against (A in the case
above) is. The code from PyObject_IsInstance:

        if (PyClass_Check(cls)) {
                if (PyInstance_Check(inst)) {
                        PyObject *inclass = (PyObject*)((PyInstanceObject*)inst)->in_class;
                        retval = PyClass_IsSubclass(inclass, cls);
                }
        }
        else if (PyType_Check(cls)) {
                retval = ((PyObject *)(inst->ob_type) == cls);
        }
        else if (!PyInstance_Check(inst)) {
		... general case, when cls is not a class and inst is not an instance ...


I patched the interpreter so that my case is handled too, and I certainly
don't want to maintain separate patches myself. So I would be glad if it
could be integrated into the main tree. Given of course that it's not an
awful idea.

BTW: I'm not on the list so please Cc: any possible replies to my post.

--- abstract.c.old	Wed Mar 21 19:40:58 2001
+++ abstract.c	Sat May 12 20:54:51 2001
@@ -1678,39 +1678,54 @@
 	return r;
 }
 
-int
-PyObject_IsInstance(PyObject *inst, PyObject *cls)
+static int
+abstract_isderived(PyObject *inst, PyObject *cls)
 {
 	PyObject *icls;
 	static PyObject *__class__ = NULL;
 	int retval = 0;
 
+	if (__class__ == NULL) {
+		__class__ = PyString_FromString("__class__");
+		if (__class__ == NULL)
+			return -1;
+	}
+	icls = PyObject_GetAttr(inst, __class__);
+	if (icls != NULL) {
+		retval = abstract_issubclass(icls, cls, 1);
+		Py_DECREF(icls);
+		if (retval < 0 &&
+		    !PyErr_ExceptionMatches(PyExc_TypeError))
+			return -1;
+	}
+	else
+		retval = -1;
+		
+	return retval;
+}
+
+int
+PyObject_IsInstance(PyObject *inst, PyObject *cls)
+{
+	int retval = 0;
+
         if (PyClass_Check(cls)) {
-		if (PyInstance_Check(inst)) {
+        	if (PyInstance_Check(inst)) {
 			PyObject *inclass =
 				(PyObject*)((PyInstanceObject*)inst)->in_class;
 			retval = PyClass_IsSubclass(inclass, cls);
 		}
+		else {
+			retval = abstract_isderived(inst, cls);
+			if (retval < 0)
+				retval = 0;
+		}
 	}
 	else if (PyType_Check(cls)) {
 		retval = ((PyObject *)(inst->ob_type) == cls);
 	}
 	else if (!PyInstance_Check(inst)) {
-		if (__class__ == NULL) {
-			__class__ = PyString_FromString("__class__");
-			if (__class__ == NULL)
-				return -1;
-		}
-		icls = PyObject_GetAttr(inst, __class__);
-		if (icls != NULL) {
-			retval = abstract_issubclass(icls, cls, 1);
-			Py_DECREF(icls);
-			if (retval < 0 &&
-			    !PyErr_ExceptionMatches(PyExc_TypeError))
-				return -1;
-		}
-		else
-			retval = -1;
+		retval = abstract_isderived(inst, cls);
 	}
 	else
 		retval = -1;


-- 
Bazsi




More information about the Python-list mailing list