diff Python/bltinmodule.c @ 10995:0d186c10a70e legacy-trunk

Patch by Jim Fulton (code style tweaked a bit) to support ExtensionClasses in isinstance() and issubclass(). - abstract instance and class protocols are used *only* in those cases that would generate errors before the patch. That is, there's no penalty for the normal case. - instance protocol: an object smells like an instance if it has a __class__ attribute that smells like a class. - class protocol: an object smells like a class if it has a __bases__ attribute that is a tuple with elements that smell like classes (although not all elements may actually get sniffed ;).
author Guido van Rossum <guido@python.org>
date Wed, 16 Jun 1999 17:28:37 +0000
parents 540c8173e4fd
children e9442734bc49
line wrap: on
line diff
--- a/Python/bltinmodule.c	Wed Jun 16 12:28:12 1999 +0000
+++ b/Python/bltinmodule.c	Wed Jun 16 17:28:37 1999 +0000
@@ -2003,6 +2003,56 @@
 Without arguments, equivalent to locals().\n\
 With an argument, equivalent to object.__dict__.";
 
+static int
+abstract_issubclass(derived, cls, err, first)
+	PyObject *derived;
+	PyObject *cls;
+	char *err;
+	int first;
+{
+	static PyObject *__bases__ = NULL;
+	PyObject *bases;
+	int i, l, n;
+	int r = 0;
+
+	if (__bases__ == NULL) {
+		__bases__ = PyString_FromString("__bases__");
+		if (__bases__ == NULL)
+			return -1;
+	}
+
+	if (first) {
+		bases = PyObject_GetAttr(cls, __bases__);
+		if (bases == NULL || !PyTuple_Check(bases)) {
+		        Py_XDECREF(bases);
+	        	PyErr_SetString(PyExc_TypeError, err);
+			return -1;
+		}
+		Py_DECREF(bases);
+	}
+
+	if (derived == cls)
+		return 1;
+
+	bases = PyObject_GetAttr(derived, __bases__);
+	if (bases == NULL || !PyTuple_Check(bases)) {
+	        Py_XDECREF(bases);
+	        PyErr_SetString(PyExc_TypeError, err);
+		return -1;
+	}
+
+	n = PyTuple_GET_SIZE(bases);
+	for (i = 0; i < n; i++) {
+		r = abstract_issubclass(PyTuple_GET_ITEM(bases, i),
+					cls, err, 0);
+		if (r != 0)
+			break;
+	}
+
+	Py_DECREF(bases);
+
+	return r;
+}
 
 static PyObject *
 builtin_isinstance(self, args)
@@ -2011,27 +2061,49 @@
 {
 	PyObject *inst;
 	PyObject *cls;
-	int retval;
+	PyObject *icls;
+	static PyObject *__class__ = NULL;
+	int retval = 0;
 
 	if (!PyArg_ParseTuple(args, "OO", &inst, &cls))
 		return NULL;
-	if (PyType_Check(cls)) {
+
+        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 (!PyClass_Check(cls)) {
+	else if (!PyInstance_Check(inst)) {
+	        if (__class__ == NULL) {
+			__class__ = PyString_FromString("__class__");
+			if (__class__ == NULL)
+				return NULL;
+		}
+		icls = PyObject_GetAttr(inst, __class__);
+		if (icls != NULL) {
+			retval = abstract_issubclass(
+				icls, cls,
+				"second argument must be a class", 
+				1);
+			Py_DECREF(icls);
+			if (retval < 0)
+				return NULL;
+		}
+		else {
 			PyErr_SetString(PyExc_TypeError,
 					"second argument must be a class");
 			return NULL;
 		}
-
-		if (!PyInstance_Check(inst))
-			retval = 0;
-		else {
-			PyObject *inclass =
-				(PyObject*)((PyInstanceObject*)inst)->in_class;
-			retval = PyClass_IsSubclass(inclass, cls);
-		}
+	}
+        else {
+		PyErr_SetString(PyExc_TypeError,
+				"second argument must be a class");
+		return NULL;
 	}
 	return PyInt_FromLong(retval);
 }
@@ -2054,13 +2126,18 @@
 
 	if (!PyArg_ParseTuple(args, "OO", &derived, &cls))
 		return NULL;
+
 	if (!PyClass_Check(derived) || !PyClass_Check(cls)) {
-		PyErr_SetString(PyExc_TypeError, "arguments must be classes");
-		return NULL;
+		retval = abstract_issubclass(
+				derived, cls, "arguments must be classes", 1);
+		if (retval < 0) 
+			return NULL;
 	}
-	/* shortcut */
-	if (!(retval = (derived == cls)))
-		retval = PyClass_IsSubclass(derived, cls);
+	else {
+		/* shortcut */
+	  	if (!(retval = (derived == cls)))
+			retval = PyClass_IsSubclass(derived, cls);
+	}
 
 	return PyInt_FromLong(retval);
 }