mirror of
https://github.com/python/cpython.git
synced 2025-07-30 22:54:16 +00:00
Issue #2534: speed up isinstance() and issubclass() by 50-70%, so as to
match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__ mechanism. In the process, fix a bug where isinstance() and issubclass(), when given a tuple of classes as second argument, were looking up __instancecheck__ / __subclasscheck__ on the tuple rather than on each type object. Reviewed by Benjamin Peterson and Raymond Hettinger.
This commit is contained in:
parent
14cb6bcf2b
commit
0668c62677
8 changed files with 211 additions and 88 deletions
|
@ -1377,6 +1377,11 @@ PyAPI_FUNC(int) PyObject_IsSubclass(PyObject *object, PyObject *typeorclass);
|
||||||
/* issubclass(object, typeorclass) */
|
/* issubclass(object, typeorclass) */
|
||||||
|
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) _PyObject_RealIsInstance(PyObject *inst, PyObject *cls);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -88,15 +88,21 @@ class TestABC(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
b = B()
|
b = B()
|
||||||
self.assertEqual(issubclass(B, A), False)
|
self.assertEqual(issubclass(B, A), False)
|
||||||
|
self.assertEqual(issubclass(B, (A,)), False)
|
||||||
self.assertEqual(isinstance(b, A), False)
|
self.assertEqual(isinstance(b, A), False)
|
||||||
|
self.assertEqual(isinstance(b, (A,)), False)
|
||||||
A.register(B)
|
A.register(B)
|
||||||
self.assertEqual(issubclass(B, A), True)
|
self.assertEqual(issubclass(B, A), True)
|
||||||
|
self.assertEqual(issubclass(B, (A,)), True)
|
||||||
self.assertEqual(isinstance(b, A), True)
|
self.assertEqual(isinstance(b, A), True)
|
||||||
|
self.assertEqual(isinstance(b, (A,)), True)
|
||||||
class C(B):
|
class C(B):
|
||||||
pass
|
pass
|
||||||
c = C()
|
c = C()
|
||||||
self.assertEqual(issubclass(C, A), True)
|
self.assertEqual(issubclass(C, A), True)
|
||||||
|
self.assertEqual(issubclass(C, (A,)), True)
|
||||||
self.assertEqual(isinstance(c, A), True)
|
self.assertEqual(isinstance(c, A), True)
|
||||||
|
self.assertEqual(isinstance(c, (A,)), True)
|
||||||
|
|
||||||
def test_isinstance_invalidation(self):
|
def test_isinstance_invalidation(self):
|
||||||
class A:
|
class A:
|
||||||
|
@ -105,20 +111,26 @@ class TestABC(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
b = B()
|
b = B()
|
||||||
self.assertEqual(isinstance(b, A), False)
|
self.assertEqual(isinstance(b, A), False)
|
||||||
|
self.assertEqual(isinstance(b, (A,)), False)
|
||||||
A.register(B)
|
A.register(B)
|
||||||
self.assertEqual(isinstance(b, A), True)
|
self.assertEqual(isinstance(b, A), True)
|
||||||
|
self.assertEqual(isinstance(b, (A,)), True)
|
||||||
|
|
||||||
def test_registration_builtins(self):
|
def test_registration_builtins(self):
|
||||||
class A:
|
class A:
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
A.register(int)
|
A.register(int)
|
||||||
self.assertEqual(isinstance(42, A), True)
|
self.assertEqual(isinstance(42, A), True)
|
||||||
|
self.assertEqual(isinstance(42, (A,)), True)
|
||||||
self.assertEqual(issubclass(int, A), True)
|
self.assertEqual(issubclass(int, A), True)
|
||||||
|
self.assertEqual(issubclass(int, (A,)), True)
|
||||||
class B(A):
|
class B(A):
|
||||||
pass
|
pass
|
||||||
B.register(basestring)
|
B.register(basestring)
|
||||||
self.assertEqual(isinstance("", A), True)
|
self.assertEqual(isinstance("", A), True)
|
||||||
|
self.assertEqual(isinstance("", (A,)), True)
|
||||||
self.assertEqual(issubclass(str, A), True)
|
self.assertEqual(issubclass(str, A), True)
|
||||||
|
self.assertEqual(issubclass(str, (A,)), True)
|
||||||
|
|
||||||
def test_registration_edge_cases(self):
|
def test_registration_edge_cases(self):
|
||||||
class A:
|
class A:
|
||||||
|
@ -141,29 +153,40 @@ class TestABC(unittest.TestCase):
|
||||||
class A:
|
class A:
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
self.failUnless(issubclass(A, A))
|
self.failUnless(issubclass(A, A))
|
||||||
|
self.failUnless(issubclass(A, (A,)))
|
||||||
class B:
|
class B:
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
self.failIf(issubclass(A, B))
|
self.failIf(issubclass(A, B))
|
||||||
|
self.failIf(issubclass(A, (B,)))
|
||||||
self.failIf(issubclass(B, A))
|
self.failIf(issubclass(B, A))
|
||||||
|
self.failIf(issubclass(B, (A,)))
|
||||||
class C:
|
class C:
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
A.register(B)
|
A.register(B)
|
||||||
class B1(B):
|
class B1(B):
|
||||||
pass
|
pass
|
||||||
self.failUnless(issubclass(B1, A))
|
self.failUnless(issubclass(B1, A))
|
||||||
|
self.failUnless(issubclass(B1, (A,)))
|
||||||
class C1(C):
|
class C1(C):
|
||||||
pass
|
pass
|
||||||
B1.register(C1)
|
B1.register(C1)
|
||||||
self.failIf(issubclass(C, B))
|
self.failIf(issubclass(C, B))
|
||||||
|
self.failIf(issubclass(C, (B,)))
|
||||||
self.failIf(issubclass(C, B1))
|
self.failIf(issubclass(C, B1))
|
||||||
|
self.failIf(issubclass(C, (B1,)))
|
||||||
self.failUnless(issubclass(C1, A))
|
self.failUnless(issubclass(C1, A))
|
||||||
|
self.failUnless(issubclass(C1, (A,)))
|
||||||
self.failUnless(issubclass(C1, B))
|
self.failUnless(issubclass(C1, B))
|
||||||
|
self.failUnless(issubclass(C1, (B,)))
|
||||||
self.failUnless(issubclass(C1, B1))
|
self.failUnless(issubclass(C1, B1))
|
||||||
|
self.failUnless(issubclass(C1, (B1,)))
|
||||||
C1.register(int)
|
C1.register(int)
|
||||||
class MyInt(int):
|
class MyInt(int):
|
||||||
pass
|
pass
|
||||||
self.failUnless(issubclass(MyInt, A))
|
self.failUnless(issubclass(MyInt, A))
|
||||||
|
self.failUnless(issubclass(MyInt, (A,)))
|
||||||
self.failUnless(isinstance(42, A))
|
self.failUnless(isinstance(42, A))
|
||||||
|
self.failUnless(isinstance(42, (A,)))
|
||||||
|
|
||||||
def test_all_new_methods_are_called(self):
|
def test_all_new_methods_are_called(self):
|
||||||
class A:
|
class A:
|
||||||
|
|
|
@ -333,7 +333,19 @@ class ExceptionTests(unittest.TestCase):
|
||||||
return g()
|
return g()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return -1
|
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):
|
def testUnicodeStrUsage(self):
|
||||||
# Make sure both instances and classes have a str and unicode
|
# Make sure both instances and classes have a str and unicode
|
||||||
|
@ -363,12 +375,20 @@ class ExceptionTests(unittest.TestCase):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
except:
|
except:
|
||||||
self.fail("Should have raised TypeError")
|
self.fail("Should have raised KeyError")
|
||||||
else:
|
else:
|
||||||
self.fail("Should have raised TypeError")
|
self.fail("Should have raised KeyError")
|
||||||
self.assertEqual(stderr.getvalue(),
|
|
||||||
"Exception ValueError: ValueError() in "
|
with captured_output("stderr") as stderr:
|
||||||
"<type 'exceptions.KeyError'> ignored\n")
|
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():
|
def test_main():
|
||||||
run_unittest(ExceptionTests)
|
run_unittest(ExceptionTests)
|
||||||
|
|
|
@ -41,26 +41,39 @@ class TypeChecksTest(unittest.TestCase):
|
||||||
|
|
||||||
def testIsSubclassBuiltin(self):
|
def testIsSubclassBuiltin(self):
|
||||||
self.assertEqual(issubclass(int, Integer), True)
|
self.assertEqual(issubclass(int, Integer), True)
|
||||||
|
self.assertEqual(issubclass(int, (Integer,)), True)
|
||||||
self.assertEqual(issubclass(float, Integer), False)
|
self.assertEqual(issubclass(float, Integer), False)
|
||||||
|
self.assertEqual(issubclass(float, (Integer,)), False)
|
||||||
|
|
||||||
def testIsInstanceBuiltin(self):
|
def testIsInstanceBuiltin(self):
|
||||||
self.assertEqual(isinstance(42, Integer), True)
|
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)
|
||||||
|
self.assertEqual(isinstance(3.14, (Integer,)), False)
|
||||||
|
|
||||||
def testIsInstanceActual(self):
|
def testIsInstanceActual(self):
|
||||||
self.assertEqual(isinstance(Integer(), Integer), True)
|
self.assertEqual(isinstance(Integer(), Integer), True)
|
||||||
|
self.assertEqual(isinstance(Integer(), (Integer,)), True)
|
||||||
|
|
||||||
def testIsSubclassActual(self):
|
def testIsSubclassActual(self):
|
||||||
self.assertEqual(issubclass(Integer, Integer), True)
|
self.assertEqual(issubclass(Integer, Integer), True)
|
||||||
|
self.assertEqual(issubclass(Integer, (Integer,)), True)
|
||||||
|
|
||||||
def testSubclassBehavior(self):
|
def testSubclassBehavior(self):
|
||||||
self.assertEqual(issubclass(SubInt, Integer), True)
|
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(SubInt, (SubInt,)), True)
|
||||||
self.assertEqual(issubclass(Integer, SubInt), False)
|
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(issubclass(int, (SubInt,)), False)
|
||||||
self.assertEqual(isinstance(SubInt(), Integer), True)
|
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(SubInt(), (SubInt,)), True)
|
||||||
self.assertEqual(isinstance(42, SubInt), False)
|
self.assertEqual(isinstance(42, SubInt), False)
|
||||||
|
self.assertEqual(isinstance(42, (SubInt,)), False)
|
||||||
|
|
||||||
def testInfiniteRecursionCaughtProperly(self):
|
def testInfiniteRecursionCaughtProperly(self):
|
||||||
e = Evil()
|
e = Evil()
|
||||||
|
|
|
@ -12,6 +12,13 @@ What's New in Python 2.6 release candidate 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #2534: speed up isinstance() and issubclass() by 50-70%, so as to
|
||||||
|
match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__
|
||||||
|
mechanism. In the process, fix a bug where isinstance() and issubclass(),
|
||||||
|
when given a tuple of classes as second argument, were looking up
|
||||||
|
__instancecheck__ / __subclasscheck__ on the tuple rather than on each
|
||||||
|
type object.
|
||||||
|
|
||||||
- Fix crashes on memory allocation failure found with failmalloc.
|
- Fix crashes on memory allocation failure found with failmalloc.
|
||||||
|
|
||||||
- Fix memory leaks found with valgrind and update suppressions file.
|
- Fix memory leaks found with valgrind and update suppressions file.
|
||||||
|
|
|
@ -2778,39 +2778,38 @@ abstract_get_bases(PyObject *cls)
|
||||||
static int
|
static int
|
||||||
abstract_issubclass(PyObject *derived, PyObject *cls)
|
abstract_issubclass(PyObject *derived, PyObject *cls)
|
||||||
{
|
{
|
||||||
PyObject *bases;
|
PyObject *bases = NULL;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
if (derived == cls)
|
if (derived == cls)
|
||||||
return 1;
|
return 1;
|
||||||
|
bases = abstract_get_bases(derived);
|
||||||
if (PyTuple_Check(cls)) {
|
if (bases == NULL) {
|
||||||
/* Not a general sequence -- that opens up the road to
|
if (PyErr_Occurred())
|
||||||
recursion and stack overflow. */
|
return -1;
|
||||||
n = PyTuple_GET_SIZE(cls);
|
return 0;
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
if (derived == PyTuple_GET_ITEM(cls, i))
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
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++) {
|
||||||
|
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
|
static int
|
||||||
|
@ -2828,7 +2827,7 @@ check_class(PyObject *cls, const char *error)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
recursive_isinstance(PyObject *inst, PyObject *cls, int recursion_depth)
|
recursive_isinstance(PyObject *inst, PyObject *cls)
|
||||||
{
|
{
|
||||||
PyObject *icls;
|
PyObject *icls;
|
||||||
static PyObject *__class__ = NULL;
|
static PyObject *__class__ = NULL;
|
||||||
|
@ -2862,25 +2861,6 @@ recursive_isinstance(PyObject *inst, PyObject *cls, int recursion_depth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
else {
|
||||||
if (!check_class(cls,
|
if (!check_class(cls,
|
||||||
"isinstance() arg 2 must be a class, type,"
|
"isinstance() arg 2 must be a class, type,"
|
||||||
|
@ -2910,6 +2890,24 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
|
||||||
if (Py_TYPE(inst) == (PyTypeObject *)cls)
|
if (Py_TYPE(inst) == (PyTypeObject *)cls)
|
||||||
return 1;
|
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) {
|
if (name == NULL) {
|
||||||
name = PyString_InternFromString("__instancecheck__");
|
name = PyString_InternFromString("__instancecheck__");
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
|
@ -2934,47 +2932,28 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
return recursive_isinstance(inst, cls, Py_GetRecursionLimit());
|
return recursive_isinstance(inst, cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
recursive_issubclass(PyObject *derived, PyObject *cls, int recursion_depth)
|
recursive_issubclass(PyObject *derived, PyObject *cls)
|
||||||
{
|
{
|
||||||
int retval;
|
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 (!PyClass_Check(derived) || !PyClass_Check(cls)) {
|
||||||
if (!check_class(derived,
|
if (!check_class(derived,
|
||||||
"issubclass() arg 1 must be a class"))
|
"issubclass() arg 1 must be a class"))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (PyTuple_Check(cls)) {
|
if (!check_class(cls,
|
||||||
Py_ssize_t i;
|
"issubclass() arg 2 must be a class"
|
||||||
Py_ssize_t n = PyTuple_GET_SIZE(cls);
|
" or tuple of classes"))
|
||||||
|
return -1;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = abstract_issubclass(derived, cls);
|
retval = abstract_issubclass(derived, cls);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2992,20 +2971,40 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
|
||||||
static PyObject *name = NULL;
|
static PyObject *name = NULL;
|
||||||
PyObject *t, *v, *tb;
|
PyObject *t, *v, *tb;
|
||||||
PyObject *checker;
|
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) {
|
if (name == NULL) {
|
||||||
name = PyString_InternFromString("__subclasscheck__");
|
name = PyString_InternFromString("__subclasscheck__");
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
PyErr_Fetch(&t, &v, &tb);
|
||||||
checker = PyObject_GetAttr(cls, name);
|
checker = PyObject_GetAttr(cls, name);
|
||||||
PyErr_Restore(t, v, tb);
|
PyErr_Restore(t, v, tb);
|
||||||
if (checker != NULL) {
|
if (checker != NULL) {
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
int ok = -1;
|
int ok = -1;
|
||||||
if (Py_EnterRecursiveCall(" in __subclasscheck__"))
|
if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
|
||||||
|
Py_DECREF(checker);
|
||||||
return ok;
|
return ok;
|
||||||
|
}
|
||||||
res = PyObject_CallFunctionObjArgs(checker, derived, NULL);
|
res = PyObject_CallFunctionObjArgs(checker, derived, NULL);
|
||||||
Py_LeaveRecursiveCall();
|
Py_LeaveRecursiveCall();
|
||||||
Py_DECREF(checker);
|
Py_DECREF(checker);
|
||||||
|
@ -3015,7 +3014,19 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
|
||||||
}
|
}
|
||||||
return ok;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -571,6 +571,49 @@ type_get_doc(PyTypeObject *type, void *context)
|
||||||
return result;
|
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[] = {
|
static PyGetSetDef type_getsets[] = {
|
||||||
{"__name__", (getter)type_name, (setter)type_set_name, NULL},
|
{"__name__", (getter)type_name, (setter)type_set_name, NULL},
|
||||||
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
|
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
|
||||||
|
@ -579,6 +622,8 @@ static PyGetSetDef type_getsets[] = {
|
||||||
(setter)type_set_abstractmethods, NULL},
|
(setter)type_set_abstractmethods, NULL},
|
||||||
{"__dict__", (getter)type_dict, NULL, NULL},
|
{"__dict__", (getter)type_dict, NULL, NULL},
|
||||||
{"__doc__", (getter)type_get_doc, NULL, NULL},
|
{"__doc__", (getter)type_get_doc, NULL, NULL},
|
||||||
|
{"__instancecheck__", (getter)type_get_instancecheck, NULL, NULL},
|
||||||
|
{"__subclasscheck__", (getter)type_get_subclasscheck, NULL, NULL},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,6 @@ PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc)
|
||||||
/* This function must not fail, so print the error here */
|
/* This function must not fail, so print the error here */
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
PyErr_WriteUnraisable(err);
|
PyErr_WriteUnraisable(err);
|
||||||
/* issubclass did not succeed */
|
|
||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
PyErr_Restore(exception, value, tb);
|
PyErr_Restore(exception, value, tb);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue