diff -r 6a78335ca820 Include/abstract.h --- a/Include/abstract.h Mon Aug 25 05:52:40 2008 +0200 +++ b/Include/abstract.h Tue Aug 26 13:23:33 2008 +0200 @@ -1377,6 +1377,11 @@ /* issubclass(object, typeorclass) */ +PyAPI_FUNC(int) _PyObject_RealIsInstance(PyObject *inst, PyObject *cls); + +PyAPI_FUNC(int) _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls); + + #ifdef __cplusplus } #endif diff -r 6a78335ca820 Lib/test/test_abc.py --- a/Lib/test/test_abc.py Mon Aug 25 05:52:40 2008 +0200 +++ b/Lib/test/test_abc.py Tue Aug 26 13:23:34 2008 +0200 @@ -88,15 +88,21 @@ pass b = B() self.assertEqual(issubclass(B, A), False) + self.assertEqual(issubclass(B, (A,)), False) self.assertEqual(isinstance(b, A), False) + self.assertEqual(isinstance(b, (A,)), False) A.register(B) self.assertEqual(issubclass(B, A), True) + self.assertEqual(issubclass(B, (A,)), True) self.assertEqual(isinstance(b, A), True) + self.assertEqual(isinstance(b, (A,)), True) class C(B): pass c = C() self.assertEqual(issubclass(C, A), True) + self.assertEqual(issubclass(C, (A,)), True) self.assertEqual(isinstance(c, A), True) + self.assertEqual(isinstance(c, (A,)), True) def test_isinstance_invalidation(self): class A: @@ -105,20 +111,26 @@ pass b = B() self.assertEqual(isinstance(b, A), False) + self.assertEqual(isinstance(b, (A,)), False) A.register(B) self.assertEqual(isinstance(b, A), True) + self.assertEqual(isinstance(b, (A,)), True) def test_registration_builtins(self): class A: __metaclass__ = abc.ABCMeta A.register(int) self.assertEqual(isinstance(42, A), True) + self.assertEqual(isinstance(42, (A,)), True) self.assertEqual(issubclass(int, A), True) + self.assertEqual(issubclass(int, (A,)), True) class B(A): pass B.register(basestring) self.assertEqual(isinstance("", A), True) + self.assertEqual(isinstance("", (A,)), True) self.assertEqual(issubclass(str, A), True) + self.assertEqual(issubclass(str, (A,)), True) def test_registration_edge_cases(self): class A: @@ -141,29 +153,40 @@ class A: __metaclass__ = abc.ABCMeta self.failUnless(issubclass(A, A)) + self.failUnless(issubclass(A, (A,))) class B: __metaclass__ = abc.ABCMeta self.failIf(issubclass(A, B)) + self.failIf(issubclass(A, (B,))) self.failIf(issubclass(B, A)) + self.failIf(issubclass(B, (A,))) class C: __metaclass__ = abc.ABCMeta A.register(B) class B1(B): pass self.failUnless(issubclass(B1, A)) + self.failUnless(issubclass(B1, (A,))) class C1(C): pass B1.register(C1) self.failIf(issubclass(C, B)) + self.failIf(issubclass(C, (B,))) self.failIf(issubclass(C, B1)) + self.failIf(issubclass(C, (B1,))) self.failUnless(issubclass(C1, A)) + self.failUnless(issubclass(C1, (A,))) self.failUnless(issubclass(C1, B)) + self.failUnless(issubclass(C1, (B,))) self.failUnless(issubclass(C1, B1)) + self.failUnless(issubclass(C1, (B1,))) C1.register(int) class MyInt(int): pass self.failUnless(issubclass(MyInt, A)) + self.failUnless(issubclass(MyInt, (A,))) self.failUnless(isinstance(42, A)) + self.failUnless(isinstance(42, (A,))) def test_all_new_methods_are_called(self): class A: diff -r 6a78335ca820 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Mon Aug 25 05:52:40 2008 +0200 +++ b/Lib/test/test_exceptions.py Tue Aug 26 13:23:34 2008 +0200 @@ -333,7 +333,19 @@ return g() except ValueError: return -1 - self.assertRaises(RuntimeError, g) + + # The test prints an unraisable recursion error when + # doing "except ValueError", this is because subclass + # checking has recursion checking too. + with captured_output("stderr"): + try: + g() + except RuntimeError: + pass + except: + self.fail("Should have raised KeyError") + else: + self.fail("Should have raised KeyError") def testUnicodeStrUsage(self): # Make sure both instances and classes have a str and unicode @@ -363,12 +375,20 @@ except KeyError: pass except: - self.fail("Should have raised TypeError") + self.fail("Should have raised KeyError") else: - self.fail("Should have raised TypeError") - self.assertEqual(stderr.getvalue(), - "Exception ValueError: ValueError() in " - " ignored\n") + self.fail("Should have raised KeyError") + + with captured_output("stderr") as stderr: + def g(): + try: + return g() + except RuntimeError: + return sys.exc_info() + e, v, tb = g() + self.assert_(e is RuntimeError, e) + self.assert_("maximum recursion depth exceeded" in str(v), v) + def test_main(): run_unittest(ExceptionTests) diff -r 6a78335ca820 Lib/test/test_typechecks.py --- a/Lib/test/test_typechecks.py Mon Aug 25 05:52:40 2008 +0200 +++ b/Lib/test/test_typechecks.py Tue Aug 26 13:23:34 2008 +0200 @@ -41,26 +41,39 @@ def testIsSubclassBuiltin(self): self.assertEqual(issubclass(int, Integer), True) + self.assertEqual(issubclass(int, (Integer,)), True) self.assertEqual(issubclass(float, Integer), False) + self.assertEqual(issubclass(float, (Integer,)), False) def testIsInstanceBuiltin(self): self.assertEqual(isinstance(42, Integer), True) + self.assertEqual(isinstance(42, (Integer,)), True) self.assertEqual(isinstance(3.14, Integer), False) + self.assertEqual(isinstance(3.14, (Integer,)), False) def testIsInstanceActual(self): self.assertEqual(isinstance(Integer(), Integer), True) + self.assertEqual(isinstance(Integer(), (Integer,)), True) def testIsSubclassActual(self): self.assertEqual(issubclass(Integer, Integer), True) + self.assertEqual(issubclass(Integer, (Integer,)), True) def testSubclassBehavior(self): self.assertEqual(issubclass(SubInt, Integer), True) + self.assertEqual(issubclass(SubInt, (Integer,)), True) self.assertEqual(issubclass(SubInt, SubInt), True) + self.assertEqual(issubclass(SubInt, (SubInt,)), True) self.assertEqual(issubclass(Integer, SubInt), False) + self.assertEqual(issubclass(Integer, (SubInt,)), False) self.assertEqual(issubclass(int, SubInt), False) + self.assertEqual(issubclass(int, (SubInt,)), False) self.assertEqual(isinstance(SubInt(), Integer), True) + self.assertEqual(isinstance(SubInt(), (Integer,)), True) self.assertEqual(isinstance(SubInt(), SubInt), True) + self.assertEqual(isinstance(SubInt(), (SubInt,)), True) self.assertEqual(isinstance(42, SubInt), False) + self.assertEqual(isinstance(42, (SubInt,)), False) def testInfiniteRecursionCaughtProperly(self): e = Evil() diff -r 6a78335ca820 Objects/abstract.c --- a/Objects/abstract.c Mon Aug 25 05:52:40 2008 +0200 +++ b/Objects/abstract.c Tue Aug 26 13:23:34 2008 +0200 @@ -2778,39 +2778,38 @@ static int abstract_issubclass(PyObject *derived, PyObject *cls) { - PyObject *bases; + PyObject *bases = NULL; Py_ssize_t i, n; int r = 0; - - if (derived == cls) - return 1; - - if (PyTuple_Check(cls)) { - /* Not a general sequence -- that opens up the road to - recursion and stack overflow. */ - n = PyTuple_GET_SIZE(cls); + while (1) { + if (derived == cls) + return 1; + bases = abstract_get_bases(derived); + if (bases == NULL) { + if (PyErr_Occurred()) + return -1; + return 0; + } + n = PyTuple_GET_SIZE(bases); + if (n == 0) { + Py_DECREF(bases); + return 0; + } + /* Avoid recursivity in the single inheritance case */ + if (n == 1) { + derived = PyTuple_GET_ITEM(bases, 0); + Py_DECREF(bases); + continue; + } for (i = 0; i < n; i++) { - if (derived == PyTuple_GET_ITEM(cls, i)) - return 1; + r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls); + if (r != 0) + break; } + Py_DECREF(bases); + return r; } - bases = abstract_get_bases(derived); - if (bases == NULL) { - if (PyErr_Occurred()) - return -1; - return 0; - } - n = PyTuple_GET_SIZE(bases); - for (i = 0; i < n; i++) { - r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls); - if (r != 0) - break; - } - - Py_DECREF(bases); - - return r; } static int @@ -2828,7 +2827,7 @@ } static int -recursive_isinstance(PyObject *inst, PyObject *cls, int recursion_depth) +recursive_isinstance(PyObject *inst, PyObject *cls) { PyObject *icls; static PyObject *__class__ = NULL; @@ -2862,25 +2861,6 @@ } } } - else if (PyTuple_Check(cls)) { - Py_ssize_t i, n; - - if (!recursion_depth) { - PyErr_SetString(PyExc_RuntimeError, - "nest level of tuple too deep"); - return -1; - } - - n = PyTuple_GET_SIZE(cls); - for (i = 0; i < n; i++) { - retval = recursive_isinstance( - inst, - PyTuple_GET_ITEM(cls, i), - recursion_depth-1); - if (retval != 0) - break; - } - } else { if (!check_class(cls, "isinstance() arg 2 must be a class, type," @@ -2910,6 +2890,24 @@ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; + if (PyTuple_Check(cls)) { + Py_ssize_t i; + Py_ssize_t n; + int r = 0; + + if (Py_EnterRecursiveCall(" in __instancecheck__")) + return -1; + n = PyTuple_GET_SIZE(cls); + for (i = 0; i < n; ++i) { + PyObject *item = PyTuple_GET_ITEM(cls, i); + r = PyObject_IsInstance(inst, item); + if (r != 0) + /* either found it, or got an error */ + break; + } + Py_LeaveRecursiveCall(); + return r; + } if (name == NULL) { name = PyString_InternFromString("__instancecheck__"); if (name == NULL) @@ -2934,47 +2932,28 @@ } return ok; } - return recursive_isinstance(inst, cls, Py_GetRecursionLimit()); + return recursive_isinstance(inst, cls); } static int -recursive_issubclass(PyObject *derived, PyObject *cls, int recursion_depth) +recursive_issubclass(PyObject *derived, PyObject *cls) { int retval; + if (PyType_Check(cls) && PyType_Check(derived)) { + /* Fast path (non-recursive) */ + return PyType_IsSubtype( + (PyTypeObject *)derived, (PyTypeObject *)cls); + } if (!PyClass_Check(derived) || !PyClass_Check(cls)) { if (!check_class(derived, "issubclass() arg 1 must be a class")) return -1; - if (PyTuple_Check(cls)) { - Py_ssize_t i; - Py_ssize_t n = PyTuple_GET_SIZE(cls); - - if (!recursion_depth) { - PyErr_SetString(PyExc_RuntimeError, - "nest level of tuple too deep"); - return -1; - } - for (i = 0; i < n; ++i) { - retval = recursive_issubclass( - derived, - PyTuple_GET_ITEM(cls, i), - recursion_depth-1); - if (retval != 0) { - /* either found it, or got an error */ - return retval; - } - } - return 0; - } - else { - if (!check_class(cls, - "issubclass() arg 2 must be a class" - " or tuple of classes")) - return -1; - } - + if (!check_class(cls, + "issubclass() arg 2 must be a class" + " or tuple of classes")) + return -1; retval = abstract_issubclass(derived, cls); } else { @@ -2992,20 +2971,40 @@ static PyObject *name = NULL; PyObject *t, *v, *tb; PyObject *checker; - PyErr_Fetch(&t, &v, &tb); + if (PyTuple_Check(cls)) { + Py_ssize_t i; + Py_ssize_t n; + int r = 0; + + if (Py_EnterRecursiveCall(" in __subclasscheck__")) + return -1; + n = PyTuple_GET_SIZE(cls); + for (i = 0; i < n; ++i) { + PyObject *item = PyTuple_GET_ITEM(cls, i); + r = PyObject_IsSubclass(derived, item); + if (r != 0) + /* either found it, or got an error */ + break; + } + Py_LeaveRecursiveCall(); + return r; + } if (name == NULL) { name = PyString_InternFromString("__subclasscheck__"); if (name == NULL) return -1; } + PyErr_Fetch(&t, &v, &tb); checker = PyObject_GetAttr(cls, name); PyErr_Restore(t, v, tb); if (checker != NULL) { PyObject *res; int ok = -1; - if (Py_EnterRecursiveCall(" in __subclasscheck__")) + if (Py_EnterRecursiveCall(" in __subclasscheck__")) { + Py_DECREF(checker); return ok; + } res = PyObject_CallFunctionObjArgs(checker, derived, NULL); Py_LeaveRecursiveCall(); Py_DECREF(checker); @@ -3015,7 +3014,19 @@ } return ok; } - return recursive_issubclass(derived, cls, Py_GetRecursionLimit()); + return recursive_issubclass(derived, cls); +} + +int +_PyObject_RealIsInstance(PyObject *inst, PyObject *cls) +{ + return recursive_isinstance(inst, cls); +} + +int +_PyObject_RealIsSubclass(PyObject *derived, PyObject *cls) +{ + return recursive_issubclass(derived, cls); } diff -r 6a78335ca820 Objects/typeobject.c --- a/Objects/typeobject.c Mon Aug 25 05:52:40 2008 +0200 +++ b/Objects/typeobject.c Tue Aug 26 13:23:35 2008 +0200 @@ -571,6 +571,49 @@ return result; } +static PyObject * +type___instancecheck__(PyObject *type, PyObject *inst) +{ + switch (_PyObject_RealIsInstance(inst, type)) { + case -1: + return NULL; + case 0: + Py_RETURN_FALSE; + default: + Py_RETURN_TRUE; + } +} + + +static PyObject * +type_get_instancecheck(PyObject *type, void *context) +{ + static PyMethodDef ml = {"__instancecheck__", + type___instancecheck__, METH_O }; + return PyCFunction_New(&ml, type); +} + +static PyObject * +type___subclasscheck__(PyObject *type, PyObject *inst) +{ + switch (_PyObject_RealIsSubclass(inst, type)) { + case -1: + return NULL; + case 0: + Py_RETURN_FALSE; + default: + Py_RETURN_TRUE; + } +} + +static PyObject * +type_get_subclasscheck(PyObject *type, void *context) +{ + static PyMethodDef ml = {"__subclasscheck__", + type___subclasscheck__, METH_O }; + return PyCFunction_New(&ml, type); +} + static PyGetSetDef type_getsets[] = { {"__name__", (getter)type_name, (setter)type_set_name, NULL}, {"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL}, @@ -579,6 +622,8 @@ (setter)type_set_abstractmethods, NULL}, {"__dict__", (getter)type_dict, NULL, NULL}, {"__doc__", (getter)type_get_doc, NULL, NULL}, + {"__instancecheck__", (getter)type_get_instancecheck, NULL, NULL}, + {"__subclasscheck__", (getter)type_get_subclasscheck, NULL, NULL}, {0} }; diff -r 6a78335ca820 Python/errors.c --- a/Python/errors.c Mon Aug 25 05:52:40 2008 +0200 +++ b/Python/errors.c Tue Aug 26 13:23:35 2008 +0200 @@ -113,7 +113,6 @@ /* This function must not fail, so print the error here */ if (res == -1) { PyErr_WriteUnraisable(err); - /* issubclass did not succeed */ res = 0; } PyErr_Restore(exception, value, tb);