mirror of https://github.com/python/cpython
fix many custom mro() edge cases and improve code quality (#22735)
Patch by Eldar Abusalimov.
This commit is contained in:
parent
9125fe2e50
commit
104b9e0cca
|
@ -4996,11 +4996,236 @@ class SharedKeyTests(unittest.TestCase):
|
||||||
self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({}))
|
self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({}))
|
||||||
|
|
||||||
|
|
||||||
|
class DebugHelperMeta(type):
|
||||||
|
"""
|
||||||
|
Sets default __doc__ and simplifies repr() output.
|
||||||
|
"""
|
||||||
|
def __new__(mcls, name, bases, attrs):
|
||||||
|
if attrs.get('__doc__') is None:
|
||||||
|
attrs['__doc__'] = name # helps when debugging with gdb
|
||||||
|
return type.__new__(mcls, name, bases, attrs)
|
||||||
|
def __repr__(cls):
|
||||||
|
return repr(cls.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MroTest(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Regressions for some bugs revealed through
|
||||||
|
mcsl.mro() customization (typeobject.c: mro_internal()) and
|
||||||
|
cls.__bases__ assignment (typeobject.c: type_set_bases()).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.step = 0
|
||||||
|
self.ready = False
|
||||||
|
|
||||||
|
def step_until(self, limit):
|
||||||
|
ret = (self.step < limit)
|
||||||
|
if ret:
|
||||||
|
self.step += 1
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def test_incomplete_set_bases_on_self(self):
|
||||||
|
"""
|
||||||
|
type_set_bases must be aware that type->tp_mro can be NULL.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if self.step_until(1):
|
||||||
|
assert cls.__mro__ is None
|
||||||
|
cls.__bases__ += ()
|
||||||
|
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_reent_set_bases_on_base(self):
|
||||||
|
"""
|
||||||
|
Deep reentrancy must not over-decref old_mro.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if cls.__mro__ is not None and cls.__name__ == 'B':
|
||||||
|
# 4-5 steps are usually enough to make it crash somewhere
|
||||||
|
if self.step_until(10):
|
||||||
|
A.__bases__ += ()
|
||||||
|
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
class B(A):
|
||||||
|
pass
|
||||||
|
B.__bases__ += ()
|
||||||
|
|
||||||
|
def test_reent_set_bases_on_direct_base(self):
|
||||||
|
"""
|
||||||
|
Similar to test_reent_set_bases_on_base, but may crash differently.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
base = cls.__bases__[0]
|
||||||
|
if base is not object:
|
||||||
|
if self.step_until(5):
|
||||||
|
base.__bases__ += ()
|
||||||
|
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
class B(A):
|
||||||
|
pass
|
||||||
|
class C(B):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_reent_set_bases_tp_base_cycle(self):
|
||||||
|
"""
|
||||||
|
type_set_bases must check for an inheritance cycle not only through
|
||||||
|
MRO of the type, which may be not yet updated in case of reentrance,
|
||||||
|
but also through tp_base chain, which is assigned before diving into
|
||||||
|
inner calls to mro().
|
||||||
|
|
||||||
|
Otherwise, the following snippet can loop forever:
|
||||||
|
do {
|
||||||
|
// ...
|
||||||
|
type = type->tp_base;
|
||||||
|
} while (type != NULL);
|
||||||
|
|
||||||
|
Functions that rely on tp_base (like solid_base and PyType_IsSubtype)
|
||||||
|
would not be happy in that case, causing a stack overflow.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if self.ready:
|
||||||
|
if cls.__name__ == 'B1':
|
||||||
|
B2.__bases__ = (B1,)
|
||||||
|
if cls.__name__ == 'B2':
|
||||||
|
B1.__bases__ = (B2,)
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
class B1(A):
|
||||||
|
pass
|
||||||
|
class B2(A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.ready = True
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
B1.__bases__ += ()
|
||||||
|
|
||||||
|
def test_tp_subclasses_cycle_in_update_slots(self):
|
||||||
|
"""
|
||||||
|
type_set_bases must check for reentrancy upon finishing its job
|
||||||
|
by updating tp_subclasses of old/new bases of the type.
|
||||||
|
Otherwise, an implicit inheritance cycle through tp_subclasses
|
||||||
|
can break functions that recurse on elements of that field
|
||||||
|
(like recurse_down_subclasses and mro_hierarchy) eventually
|
||||||
|
leading to a stack overflow.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if self.ready and cls.__name__ == 'C':
|
||||||
|
self.ready = False
|
||||||
|
C.__bases__ = (B2,)
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
class B1(A):
|
||||||
|
pass
|
||||||
|
class B2(A):
|
||||||
|
pass
|
||||||
|
class C(A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.ready = True
|
||||||
|
C.__bases__ = (B1,)
|
||||||
|
B1.__bases__ = (C,)
|
||||||
|
|
||||||
|
self.assertEqual(C.__bases__, (B2,))
|
||||||
|
self.assertEqual(B2.__subclasses__(), [C])
|
||||||
|
self.assertEqual(B1.__subclasses__(), [])
|
||||||
|
|
||||||
|
self.assertEqual(B1.__bases__, (C,))
|
||||||
|
self.assertEqual(C.__subclasses__(), [B1])
|
||||||
|
|
||||||
|
def test_tp_subclasses_cycle_error_return_path(self):
|
||||||
|
"""
|
||||||
|
The same as test_tp_subclasses_cycle_in_update_slots, but tests
|
||||||
|
a code path executed on error (goto bail).
|
||||||
|
"""
|
||||||
|
class E(Exception):
|
||||||
|
pass
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if self.ready and cls.__name__ == 'C':
|
||||||
|
if C.__bases__ == (B2,):
|
||||||
|
self.ready = False
|
||||||
|
else:
|
||||||
|
C.__bases__ = (B2,)
|
||||||
|
raise E
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
class B1(A):
|
||||||
|
pass
|
||||||
|
class B2(A):
|
||||||
|
pass
|
||||||
|
class C(A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.ready = True
|
||||||
|
with self.assertRaises(E):
|
||||||
|
C.__bases__ = (B1,)
|
||||||
|
B1.__bases__ = (C,)
|
||||||
|
|
||||||
|
self.assertEqual(C.__bases__, (B2,))
|
||||||
|
self.assertEqual(C.__mro__, tuple(type.mro(C)))
|
||||||
|
|
||||||
|
def test_incomplete_extend(self):
|
||||||
|
"""
|
||||||
|
Extending an unitialized type with type->tp_mro == NULL must
|
||||||
|
throw a reasonable TypeError exception, instead of failing
|
||||||
|
with PyErr_BadInternalCall.
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if cls.__mro__ is None and cls.__name__ != 'X':
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class X(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_incomplete_super(self):
|
||||||
|
"""
|
||||||
|
Attrubute lookup on a super object must be aware that
|
||||||
|
its target type can be uninitialized (type->tp_mro == NULL).
|
||||||
|
"""
|
||||||
|
class M(DebugHelperMeta):
|
||||||
|
def mro(cls):
|
||||||
|
if cls.__mro__ is None:
|
||||||
|
with self.assertRaises(AttributeError):
|
||||||
|
super(cls, cls).xxx
|
||||||
|
|
||||||
|
return type.mro(cls)
|
||||||
|
|
||||||
|
class A(metaclass=M):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
# Run all local test cases, with PTypesLongInitTest first.
|
# Run all local test cases, with PTypesLongInitTest first.
|
||||||
support.run_unittest(PTypesLongInitTest, OperatorsTest,
|
support.run_unittest(PTypesLongInitTest, OperatorsTest,
|
||||||
ClassPropertiesAndMethods, DictProxyTests,
|
ClassPropertiesAndMethods, DictProxyTests,
|
||||||
MiscTests, PicklingTests, SharedKeyTests)
|
MiscTests, PicklingTests, SharedKeyTests,
|
||||||
|
MroTest)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -16,6 +16,7 @@ Michael Abbott
|
||||||
Rajiv Abraham
|
Rajiv Abraham
|
||||||
David Abrahams
|
David Abrahams
|
||||||
Marc Abramowitz
|
Marc Abramowitz
|
||||||
|
Eldar Abusalimov
|
||||||
Ron Adam
|
Ron Adam
|
||||||
Anton Afanasyev
|
Anton Afanasyev
|
||||||
Ali Afshar
|
Ali Afshar
|
||||||
|
|
|
@ -11,6 +11,9 @@ Release date: TBA
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #22735: Fix many edge cases (including crashes) involving custom mro()
|
||||||
|
implementations.
|
||||||
|
|
||||||
- Issue #22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
|
- Issue #22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
|
||||||
and PyObject_AsWriteBuffer().
|
and PyObject_AsWriteBuffer().
|
||||||
|
|
||||||
|
|
|
@ -539,9 +539,11 @@ type_get_bases(PyTypeObject *type, void *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyTypeObject *best_base(PyObject *);
|
static PyTypeObject *best_base(PyObject *);
|
||||||
static int mro_internal(PyTypeObject *);
|
static int mro_internal(PyTypeObject *, PyObject **);
|
||||||
|
static int type_is_subtype_base_chain(PyTypeObject *, PyTypeObject *);
|
||||||
static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *);
|
static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *);
|
||||||
static int add_subclass(PyTypeObject*, PyTypeObject*);
|
static int add_subclass(PyTypeObject*, PyTypeObject*);
|
||||||
|
static int add_all_subclasses(PyTypeObject *type, PyObject *bases);
|
||||||
static void remove_subclass(PyTypeObject *, PyTypeObject *);
|
static void remove_subclass(PyTypeObject *, PyTypeObject *);
|
||||||
static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
|
static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
|
||||||
static void update_all_slots(PyTypeObject *);
|
static void update_all_slots(PyTypeObject *);
|
||||||
|
@ -551,167 +553,194 @@ static int update_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
update_callback callback, void *data);
|
update_callback callback, void *data);
|
||||||
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
update_callback callback, void *data);
|
update_callback callback, void *data);
|
||||||
|
static PyObject *type_subclasses(PyTypeObject *type, PyObject *ignored);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mro_subclasses(PyTypeObject *type, PyObject* temp)
|
mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
||||||
{
|
{
|
||||||
PyTypeObject *subclass;
|
int res;
|
||||||
PyObject *ref, *subclasses, *old_mro;
|
PyObject *new_mro, *old_mro;
|
||||||
Py_ssize_t i;
|
PyObject *tuple;
|
||||||
|
PyObject *subclasses;
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
subclasses = type->tp_subclasses;
|
res = mro_internal(type, &old_mro);
|
||||||
if (subclasses == NULL)
|
if (res <= 0)
|
||||||
return 0;
|
/* error / reentrance */
|
||||||
assert(PyDict_CheckExact(subclasses));
|
return res;
|
||||||
i = 0;
|
new_mro = type->tp_mro;
|
||||||
|
|
||||||
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
if (old_mro != NULL)
|
||||||
assert(PyWeakref_CheckRef(ref));
|
tuple = PyTuple_Pack(3, type, new_mro, old_mro);
|
||||||
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
|
else
|
||||||
assert(subclass != NULL);
|
tuple = PyTuple_Pack(2, type, new_mro);
|
||||||
if ((PyObject *)subclass == Py_None)
|
|
||||||
continue;
|
if (tuple != NULL)
|
||||||
assert(PyType_Check(subclass));
|
res = PyList_Append(temp, tuple);
|
||||||
old_mro = subclass->tp_mro;
|
else
|
||||||
if (mro_internal(subclass) < 0) {
|
res = -1;
|
||||||
subclass->tp_mro = old_mro;
|
Py_XDECREF(tuple);
|
||||||
return -1;
|
|
||||||
}
|
if (res < 0) {
|
||||||
else {
|
type->tp_mro = old_mro;
|
||||||
PyObject* tuple;
|
Py_DECREF(new_mro);
|
||||||
tuple = PyTuple_Pack(2, subclass, old_mro);
|
return -1;
|
||||||
Py_DECREF(old_mro);
|
|
||||||
if (!tuple)
|
|
||||||
return -1;
|
|
||||||
if (PyList_Append(temp, tuple) < 0)
|
|
||||||
return -1;
|
|
||||||
Py_DECREF(tuple);
|
|
||||||
}
|
|
||||||
if (mro_subclasses(subclass, temp) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
Py_XDECREF(old_mro);
|
||||||
|
|
||||||
|
/* Obtain a copy of subclasses list to iterate over.
|
||||||
|
|
||||||
|
Otherwise type->tp_subclasses might be altered
|
||||||
|
in the middle of the loop, for example, through a custom mro(),
|
||||||
|
by invoking type_set_bases on some subclass of the type
|
||||||
|
which in turn calls remove_subclass/add_subclass on this type.
|
||||||
|
|
||||||
|
Finally, this makes things simple avoiding the need to deal
|
||||||
|
with dictionary iterators and weak references.
|
||||||
|
*/
|
||||||
|
subclasses = type_subclasses(type, NULL);
|
||||||
|
if (subclasses == NULL)
|
||||||
|
return -1;
|
||||||
|
n = PyList_GET_SIZE(subclasses);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
PyTypeObject *subclass;
|
||||||
|
subclass = (PyTypeObject *)PyList_GET_ITEM(subclasses, i);
|
||||||
|
res = mro_hierarchy(subclass, temp);
|
||||||
|
if (res < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Py_DECREF(subclasses);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
type_set_bases(PyTypeObject *type, PyObject *value, void *context)
|
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
||||||
{
|
{
|
||||||
Py_ssize_t i;
|
int res = 0;
|
||||||
int r = 0;
|
PyObject *temp;
|
||||||
PyObject *ob, *temp;
|
PyObject *old_bases;
|
||||||
PyTypeObject *new_base, *old_base;
|
PyTypeObject *new_base, *old_base;
|
||||||
PyObject *old_bases, *old_mro;
|
Py_ssize_t i;
|
||||||
|
|
||||||
if (!check_set_special_type_attr(type, value, "__bases__"))
|
if (!check_set_special_type_attr(type, new_bases, "__bases__"))
|
||||||
return -1;
|
return -1;
|
||||||
if (!PyTuple_Check(value)) {
|
if (!PyTuple_Check(new_bases)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"can only assign tuple to %s.__bases__, not %s",
|
"can only assign tuple to %s.__bases__, not %s",
|
||||||
type->tp_name, Py_TYPE(value)->tp_name);
|
type->tp_name, Py_TYPE(new_bases)->tp_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (PyTuple_GET_SIZE(value) == 0) {
|
if (PyTuple_GET_SIZE(new_bases) == 0) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"can only assign non-empty tuple to %s.__bases__, not ()",
|
"can only assign non-empty tuple to %s.__bases__, not ()",
|
||||||
type->tp_name);
|
type->tp_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (i = 0; i < PyTuple_GET_SIZE(value); i++) {
|
for (i = 0; i < PyTuple_GET_SIZE(new_bases); i++) {
|
||||||
ob = PyTuple_GET_ITEM(value, i);
|
PyObject *ob;
|
||||||
|
PyTypeObject *base;
|
||||||
|
|
||||||
|
ob = PyTuple_GET_ITEM(new_bases, i);
|
||||||
if (!PyType_Check(ob)) {
|
if (!PyType_Check(ob)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"%s.__bases__ must be tuple of classes, not '%s'",
|
"%s.__bases__ must be tuple of classes, not '%s'",
|
||||||
type->tp_name, Py_TYPE(ob)->tp_name);
|
type->tp_name, Py_TYPE(ob)->tp_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (PyType_IsSubtype((PyTypeObject*)ob, type)) {
|
|
||||||
|
base = (PyTypeObject*)ob;
|
||||||
|
if (PyType_IsSubtype(base, type) ||
|
||||||
|
/* In case of reentering here again through a custom mro()
|
||||||
|
the above check is not enough since it relies on
|
||||||
|
base->tp_mro which would gonna be updated inside
|
||||||
|
mro_internal only upon returning from the mro().
|
||||||
|
|
||||||
|
However, base->tp_base has already been assigned (see
|
||||||
|
below), which in turn may cause an inheritance cycle
|
||||||
|
through tp_base chain. And this is definitely
|
||||||
|
not what you want to ever happen. */
|
||||||
|
(base->tp_mro != NULL && type_is_subtype_base_chain(base, type))) {
|
||||||
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"a __bases__ item causes an inheritance cycle");
|
"a __bases__ item causes an inheritance cycle");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_base = best_base(value);
|
new_base = best_base(new_bases);
|
||||||
|
if (new_base == NULL)
|
||||||
if (!new_base)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
|
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
Py_INCREF(new_bases);
|
||||||
Py_INCREF(new_base);
|
Py_INCREF(new_base);
|
||||||
Py_INCREF(value);
|
|
||||||
|
|
||||||
old_bases = type->tp_bases;
|
old_bases = type->tp_bases;
|
||||||
old_base = type->tp_base;
|
old_base = type->tp_base;
|
||||||
old_mro = type->tp_mro;
|
|
||||||
|
|
||||||
type->tp_bases = value;
|
type->tp_bases = new_bases;
|
||||||
type->tp_base = new_base;
|
type->tp_base = new_base;
|
||||||
|
|
||||||
if (mro_internal(type) < 0) {
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = PyList_New(0);
|
temp = PyList_New(0);
|
||||||
if (!temp)
|
if (temp == NULL)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
if (mro_hierarchy(type, temp) < 0)
|
||||||
r = mro_subclasses(type, temp);
|
goto undo;
|
||||||
|
|
||||||
if (r < 0) {
|
|
||||||
for (i = 0; i < PyList_Size(temp); i++) {
|
|
||||||
PyTypeObject* cls;
|
|
||||||
PyObject* mro;
|
|
||||||
PyArg_UnpackTuple(PyList_GET_ITEM(temp, i),
|
|
||||||
"", 2, 2, &cls, &mro);
|
|
||||||
Py_INCREF(mro);
|
|
||||||
ob = cls->tp_mro;
|
|
||||||
cls->tp_mro = mro;
|
|
||||||
Py_DECREF(ob);
|
|
||||||
}
|
|
||||||
Py_DECREF(temp);
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_DECREF(temp);
|
Py_DECREF(temp);
|
||||||
|
|
||||||
/* any base that was in __bases__ but now isn't, we
|
/* Take no action in case if type->tp_bases has been replaced
|
||||||
need to remove |type| from its tp_subclasses.
|
through reentrance. */
|
||||||
conversely, any class now in __bases__ that wasn't
|
if (type->tp_bases == new_bases) {
|
||||||
needs to have |type| added to its subclasses. */
|
/* any base that was in __bases__ but now isn't, we
|
||||||
|
need to remove |type| from its tp_subclasses.
|
||||||
|
conversely, any class now in __bases__ that wasn't
|
||||||
|
needs to have |type| added to its subclasses. */
|
||||||
|
|
||||||
/* for now, sod that: just remove from all old_bases,
|
/* for now, sod that: just remove from all old_bases,
|
||||||
add to all new_bases */
|
add to all new_bases */
|
||||||
|
remove_all_subclasses(type, old_bases);
|
||||||
remove_all_subclasses(type, old_bases);
|
res = add_all_subclasses(type, new_bases);
|
||||||
|
update_all_slots(type);
|
||||||
for (i = PyTuple_GET_SIZE(value) - 1; i >= 0; i--) {
|
|
||||||
ob = PyTuple_GET_ITEM(value, i);
|
|
||||||
if (PyType_Check(ob)) {
|
|
||||||
if (add_subclass((PyTypeObject*)ob, type) < 0)
|
|
||||||
r = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_all_slots(type);
|
|
||||||
|
|
||||||
Py_DECREF(old_bases);
|
Py_DECREF(old_bases);
|
||||||
Py_DECREF(old_base);
|
Py_DECREF(old_base);
|
||||||
Py_DECREF(old_mro);
|
|
||||||
|
|
||||||
return r;
|
return res;
|
||||||
|
|
||||||
|
undo:
|
||||||
|
for (i = PyList_GET_SIZE(temp) - 1; i >= 0; i--) {
|
||||||
|
PyTypeObject *cls;
|
||||||
|
PyObject *new_mro, *old_mro = NULL;
|
||||||
|
|
||||||
|
PyArg_UnpackTuple(PyList_GET_ITEM(temp, i),
|
||||||
|
"", 2, 3, &cls, &new_mro, &old_mro);
|
||||||
|
/* Do not rollback if cls has a newer version of MRO. */
|
||||||
|
if (cls->tp_mro == new_mro) {
|
||||||
|
Py_XINCREF(old_mro);
|
||||||
|
cls->tp_mro = old_mro;
|
||||||
|
Py_DECREF(new_mro);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(temp);
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
Py_DECREF(type->tp_bases);
|
if (type->tp_bases == new_bases) {
|
||||||
Py_DECREF(type->tp_base);
|
assert(type->tp_base == new_base);
|
||||||
if (type->tp_mro != old_mro) {
|
|
||||||
Py_DECREF(type->tp_mro);
|
|
||||||
}
|
|
||||||
|
|
||||||
type->tp_bases = old_bases;
|
type->tp_bases = old_bases;
|
||||||
type->tp_base = old_base;
|
type->tp_base = old_base;
|
||||||
type->tp_mro = old_mro;
|
|
||||||
|
Py_DECREF(new_bases);
|
||||||
|
Py_DECREF(new_base);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_DECREF(old_bases);
|
||||||
|
Py_DECREF(old_base);
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1284,6 +1313,18 @@ static PyTypeObject *solid_base(PyTypeObject *type);
|
||||||
|
|
||||||
/* type test with subclassing support */
|
/* type test with subclassing support */
|
||||||
|
|
||||||
|
Py_LOCAL_INLINE(int)
|
||||||
|
type_is_subtype_base_chain(PyTypeObject *a, PyTypeObject *b)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
if (a == b)
|
||||||
|
return 1;
|
||||||
|
a = a->tp_base;
|
||||||
|
} while (a != NULL);
|
||||||
|
|
||||||
|
return (b == &PyBaseObject_Type);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
|
PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
|
||||||
{
|
{
|
||||||
|
@ -1302,15 +1343,9 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
/* a is not completely initilized yet; follow tp_base */
|
/* a is not completely initilized yet; follow tp_base */
|
||||||
do {
|
return type_is_subtype_base_chain(a, b);
|
||||||
if (a == b)
|
|
||||||
return 1;
|
|
||||||
a = a->tp_base;
|
|
||||||
} while (a != NULL);
|
|
||||||
return b == &PyBaseObject_Type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Internal routines to do a method lookup in the type
|
/* Internal routines to do a method lookup in the type
|
||||||
|
@ -1577,10 +1612,11 @@ consistent method resolution\norder (MRO) for bases");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pmerge(PyObject *acc, PyObject* to_merge) {
|
pmerge(PyObject *acc, PyObject* to_merge)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
Py_ssize_t i, j, to_merge_size, empty_cnt;
|
Py_ssize_t i, j, to_merge_size, empty_cnt;
|
||||||
int *remain;
|
int *remain;
|
||||||
int ok;
|
|
||||||
|
|
||||||
to_merge_size = PyList_GET_SIZE(to_merge);
|
to_merge_size = PyList_GET_SIZE(to_merge);
|
||||||
|
|
||||||
|
@ -1618,15 +1654,13 @@ pmerge(PyObject *acc, PyObject* to_merge) {
|
||||||
candidate = PyList_GET_ITEM(cur_list, remain[i]);
|
candidate = PyList_GET_ITEM(cur_list, remain[i]);
|
||||||
for (j = 0; j < to_merge_size; j++) {
|
for (j = 0; j < to_merge_size; j++) {
|
||||||
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
|
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
|
||||||
if (tail_contains(j_lst, remain[j], candidate)) {
|
if (tail_contains(j_lst, remain[j], candidate))
|
||||||
goto skip; /* continue outer loop */
|
goto skip; /* continue outer loop */
|
||||||
}
|
|
||||||
}
|
|
||||||
ok = PyList_Append(acc, candidate);
|
|
||||||
if (ok < 0) {
|
|
||||||
PyMem_FREE(remain);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
res = PyList_Append(acc, candidate);
|
||||||
|
if (res < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
for (j = 0; j < to_merge_size; j++) {
|
for (j = 0; j < to_merge_size; j++) {
|
||||||
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
|
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
|
||||||
if (remain[j] < PyList_GET_SIZE(j_lst) &&
|
if (remain[j] < PyList_GET_SIZE(j_lst) &&
|
||||||
|
@ -1638,22 +1672,25 @@ pmerge(PyObject *acc, PyObject* to_merge) {
|
||||||
skip: ;
|
skip: ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty_cnt == to_merge_size) {
|
if (empty_cnt != to_merge_size) {
|
||||||
PyMem_FREE(remain);
|
set_mro_error(to_merge, remain);
|
||||||
return 0;
|
res = -1;
|
||||||
}
|
}
|
||||||
set_mro_error(to_merge, remain);
|
|
||||||
|
out:
|
||||||
PyMem_FREE(remain);
|
PyMem_FREE(remain);
|
||||||
return -1;
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mro_implementation(PyTypeObject *type)
|
mro_implementation(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, n;
|
PyObject *result = NULL;
|
||||||
int ok;
|
PyObject *bases;
|
||||||
PyObject *bases, *result;
|
|
||||||
PyObject *to_merge, *bases_aslist;
|
PyObject *to_merge, *bases_aslist;
|
||||||
|
int res;
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
if (type->tp_dict == NULL) {
|
if (type->tp_dict == NULL) {
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0)
|
||||||
|
@ -1677,42 +1714,44 @@ mro_implementation(PyTypeObject *type)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
PyTypeObject *base;
|
||||||
PyObject *parentMRO;
|
PyObject *base_mro_aslist;
|
||||||
parentMRO = PySequence_List(((PyTypeObject*)base)->tp_mro);
|
|
||||||
if (parentMRO == NULL) {
|
base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
|
||||||
Py_DECREF(to_merge);
|
if (base->tp_mro == NULL) {
|
||||||
return NULL;
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"Cannot extend an incomplete type '%.100s'",
|
||||||
|
base->tp_name);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyList_SET_ITEM(to_merge, i, parentMRO);
|
base_mro_aslist = PySequence_List(base->tp_mro);
|
||||||
|
if (base_mro_aslist == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
PyList_SET_ITEM(to_merge, i, base_mro_aslist);
|
||||||
}
|
}
|
||||||
|
|
||||||
bases_aslist = PySequence_List(bases);
|
bases_aslist = PySequence_List(bases);
|
||||||
if (bases_aslist == NULL) {
|
if (bases_aslist == NULL)
|
||||||
Py_DECREF(to_merge);
|
goto out;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* This is just a basic sanity check. */
|
/* This is just a basic sanity check. */
|
||||||
if (check_duplicates(bases_aslist) < 0) {
|
if (check_duplicates(bases_aslist) < 0) {
|
||||||
Py_DECREF(to_merge);
|
|
||||||
Py_DECREF(bases_aslist);
|
Py_DECREF(bases_aslist);
|
||||||
return NULL;
|
goto out;
|
||||||
}
|
}
|
||||||
PyList_SET_ITEM(to_merge, n, bases_aslist);
|
PyList_SET_ITEM(to_merge, n, bases_aslist);
|
||||||
|
|
||||||
result = Py_BuildValue("[O]", (PyObject *)type);
|
result = Py_BuildValue("[O]", (PyObject *)type);
|
||||||
if (result == NULL) {
|
if (result == NULL)
|
||||||
Py_DECREF(to_merge);
|
goto out;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = pmerge(result, to_merge);
|
res = pmerge(result, to_merge);
|
||||||
|
if (res < 0)
|
||||||
|
Py_CLEAR(result);
|
||||||
|
|
||||||
|
out:
|
||||||
Py_DECREF(to_merge);
|
Py_DECREF(to_merge);
|
||||||
if (ok < 0) {
|
|
||||||
Py_DECREF(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1726,59 +1765,133 @@ mro_external(PyObject *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mro_internal(PyTypeObject *type)
|
mro_check(PyTypeObject *type, PyObject *mro)
|
||||||
{
|
{
|
||||||
PyObject *mro, *result, *tuple;
|
PyTypeObject *solid;
|
||||||
int checkit = 0;
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
if (Py_TYPE(type) == &PyType_Type) {
|
solid = solid_base(type);
|
||||||
result = mro_implementation(type);
|
|
||||||
}
|
n = PyTuple_GET_SIZE(mro);
|
||||||
else {
|
for (i = 0; i < n; i++) {
|
||||||
_Py_IDENTIFIER(mro);
|
PyTypeObject *base;
|
||||||
checkit = 1;
|
PyObject *tmp;
|
||||||
mro = lookup_method((PyObject *)type, &PyId_mro);
|
|
||||||
if (mro == NULL)
|
tmp = PyTuple_GET_ITEM(mro, i);
|
||||||
|
if (!PyType_Check(tmp)) {
|
||||||
|
PyErr_Format(
|
||||||
|
PyExc_TypeError,
|
||||||
|
"mro() returned a non-class ('%.500s')",
|
||||||
|
Py_TYPE(tmp)->tp_name);
|
||||||
return -1;
|
return -1;
|
||||||
result = PyObject_CallObject(mro, NULL);
|
}
|
||||||
Py_DECREF(mro);
|
|
||||||
}
|
|
||||||
if (result == NULL)
|
|
||||||
return -1;
|
|
||||||
tuple = PySequence_Tuple(result);
|
|
||||||
Py_DECREF(result);
|
|
||||||
if (tuple == NULL)
|
|
||||||
return -1;
|
|
||||||
if (checkit) {
|
|
||||||
Py_ssize_t i, len;
|
|
||||||
PyObject *cls;
|
|
||||||
PyTypeObject *solid;
|
|
||||||
|
|
||||||
solid = solid_base(type);
|
base = (PyTypeObject*)tmp;
|
||||||
|
if (!PyType_IsSubtype(solid, solid_base(base))) {
|
||||||
len = PyTuple_GET_SIZE(tuple);
|
PyErr_Format(
|
||||||
|
PyExc_TypeError,
|
||||||
for (i = 0; i < len; i++) {
|
"mro() returned base with unsuitable layout ('%.500s')",
|
||||||
PyTypeObject *t;
|
base->tp_name);
|
||||||
cls = PyTuple_GET_ITEM(tuple, i);
|
return -1;
|
||||||
if (!PyType_Check(cls)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"mro() returned a non-class ('%.500s')",
|
|
||||||
Py_TYPE(cls)->tp_name);
|
|
||||||
Py_DECREF(tuple);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
t = (PyTypeObject*)cls;
|
|
||||||
if (!PyType_IsSubtype(solid, solid_base(t))) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"mro() returned base with unsuitable layout ('%.500s')",
|
|
||||||
t->tp_name);
|
|
||||||
Py_DECREF(tuple);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type->tp_mro = tuple;
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lookups an mcls.mro method, invokes it and checks the result (if needed,
|
||||||
|
in case of a custom mro() implementation).
|
||||||
|
|
||||||
|
Keep in mind that during execution of this function type->tp_mro
|
||||||
|
can be replaced due to possible reentrance (for example,
|
||||||
|
through type_set_bases):
|
||||||
|
|
||||||
|
- when looking up the mcls.mro attribute (it could be
|
||||||
|
a user-provided descriptor);
|
||||||
|
|
||||||
|
- from inside a custom mro() itself;
|
||||||
|
|
||||||
|
- through a finalizer of the return value of mro().
|
||||||
|
*/
|
||||||
|
static PyObject *
|
||||||
|
mro_invoke(PyTypeObject *type)
|
||||||
|
{
|
||||||
|
PyObject *mro_result;
|
||||||
|
PyObject *new_mro;
|
||||||
|
int custom = (Py_TYPE(type) != &PyType_Type);
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
_Py_IDENTIFIER(mro);
|
||||||
|
PyObject *mro_meth = lookup_method((PyObject *)type, &PyId_mro);
|
||||||
|
if (mro_meth == NULL)
|
||||||
|
return NULL;
|
||||||
|
mro_result = PyObject_CallObject(mro_meth, NULL);
|
||||||
|
Py_DECREF(mro_meth);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mro_result = mro_implementation(type);
|
||||||
|
}
|
||||||
|
if (mro_result == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_mro = PySequence_Tuple(mro_result);
|
||||||
|
Py_DECREF(mro_result);
|
||||||
|
if (new_mro == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (custom && mro_check(type, new_mro) < 0) {
|
||||||
|
Py_DECREF(new_mro);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_mro;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculates and assigns a new MRO to type->tp_mro.
|
||||||
|
Return values and invariants:
|
||||||
|
|
||||||
|
- Returns 1 if a new MRO value has been set to type->tp_mro due to
|
||||||
|
this call of mro_internal (no tricky reentrancy and no errors).
|
||||||
|
|
||||||
|
In case if p_old_mro argument is not NULL, a previous value
|
||||||
|
of type->tp_mro is put there, and the ownership of this
|
||||||
|
reference is transferred to a caller.
|
||||||
|
Otherwise, the previous value (if any) is decref'ed.
|
||||||
|
|
||||||
|
- Returns 0 in case when type->tp_mro gets changed because of
|
||||||
|
reentering here through a custom mro() (see a comment to mro_invoke).
|
||||||
|
|
||||||
|
In this case, a refcount of an old type->tp_mro is adjusted
|
||||||
|
somewhere deeper in the call stack (by the innermost mro_internal
|
||||||
|
or its caller) and may become zero upon returning from here.
|
||||||
|
This also implies that the whole hierarchy of subclasses of the type
|
||||||
|
has seen the new value and updated their MRO accordingly.
|
||||||
|
|
||||||
|
- Returns -1 in case of an error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mro_internal(PyTypeObject *type, PyObject **p_old_mro)
|
||||||
|
{
|
||||||
|
PyObject *new_mro, *old_mro;
|
||||||
|
int reent;
|
||||||
|
|
||||||
|
/* Keep a reference to be able to do a reentrancy check below.
|
||||||
|
Don't let old_mro be GC'ed and its address be reused for
|
||||||
|
another object, like (suddenly!) a new tp_mro. */
|
||||||
|
old_mro = type->tp_mro;
|
||||||
|
Py_XINCREF(old_mro);
|
||||||
|
new_mro = mro_invoke(type); /* might cause reentrance */
|
||||||
|
reent = (type->tp_mro != old_mro);
|
||||||
|
Py_XDECREF(old_mro);
|
||||||
|
if (new_mro == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (reent) {
|
||||||
|
Py_DECREF(new_mro);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type->tp_mro = new_mro;
|
||||||
|
|
||||||
type_mro_modified(type, type->tp_mro);
|
type_mro_modified(type, type->tp_mro);
|
||||||
/* corner case: the super class might have been hidden
|
/* corner case: the super class might have been hidden
|
||||||
|
@ -1787,7 +1900,12 @@ mro_internal(PyTypeObject *type)
|
||||||
|
|
||||||
PyType_Modified(type);
|
PyType_Modified(type);
|
||||||
|
|
||||||
return 0;
|
if (p_old_mro != NULL)
|
||||||
|
*p_old_mro = old_mro; /* transfer the ownership */
|
||||||
|
else
|
||||||
|
Py_XDECREF(old_mro);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4661,9 +4779,8 @@ PyType_Ready(PyTypeObject *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate method resolution order */
|
/* Calculate method resolution order */
|
||||||
if (mro_internal(type) < 0) {
|
if (mro_internal(type, NULL) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
|
|
||||||
/* Inherit special flags from dominant base */
|
/* Inherit special flags from dominant base */
|
||||||
if (type->tp_base != NULL)
|
if (type->tp_base != NULL)
|
||||||
|
@ -4812,6 +4929,24 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (bases) {
|
||||||
|
Py_ssize_t i;
|
||||||
|
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||||
|
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||||
|
if (PyType_Check(base) &&
|
||||||
|
add_subclass((PyTypeObject*)base, type) < 0)
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
{
|
{
|
||||||
|
@ -6765,70 +6900,74 @@ static PyObject *
|
||||||
super_getattro(PyObject *self, PyObject *name)
|
super_getattro(PyObject *self, PyObject *name)
|
||||||
{
|
{
|
||||||
superobject *su = (superobject *)self;
|
superobject *su = (superobject *)self;
|
||||||
int skip = su->obj_type == NULL;
|
PyTypeObject *starttype;
|
||||||
|
PyObject *mro;
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
if (!skip) {
|
starttype = su->obj_type;
|
||||||
/* We want __class__ to return the class of the super object
|
if (starttype == NULL)
|
||||||
(i.e. super, or a subclass), not the class of su->obj. */
|
goto skip;
|
||||||
skip = (PyUnicode_Check(name) &&
|
|
||||||
PyUnicode_GET_LENGTH(name) == 9 &&
|
/* We want __class__ to return the class of the super object
|
||||||
_PyUnicode_CompareWithId(name, &PyId___class__) == 0);
|
(i.e. super, or a subclass), not the class of su->obj. */
|
||||||
|
if (PyUnicode_Check(name) &&
|
||||||
|
PyUnicode_GET_LENGTH(name) == 9 &&
|
||||||
|
_PyUnicode_CompareWithId(name, &PyId___class__) == 0)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
mro = starttype->tp_mro;
|
||||||
|
if (mro == NULL)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
assert(PyTuple_Check(mro));
|
||||||
|
n = PyTuple_GET_SIZE(mro);
|
||||||
|
|
||||||
|
/* No need to check the last one: it's gonna be skipped anyway. */
|
||||||
|
for (i = 0; i+1 < n; i++) {
|
||||||
|
if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
i++; /* skip su->type (if any) */
|
||||||
|
if (i >= n)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
if (!skip) {
|
/* keep a strong reference to mro because starttype->tp_mro can be
|
||||||
PyObject *mro, *res, *tmp, *dict;
|
replaced during PyDict_GetItem(dict, name) */
|
||||||
PyTypeObject *starttype;
|
Py_INCREF(mro);
|
||||||
|
do {
|
||||||
|
PyObject *res, *tmp, *dict;
|
||||||
descrgetfunc f;
|
descrgetfunc f;
|
||||||
Py_ssize_t i, n;
|
|
||||||
|
|
||||||
starttype = su->obj_type;
|
tmp = PyTuple_GET_ITEM(mro, i);
|
||||||
mro = starttype->tp_mro;
|
assert(PyType_Check(tmp));
|
||||||
|
|
||||||
if (mro == NULL)
|
dict = ((PyTypeObject *)tmp)->tp_dict;
|
||||||
n = 0;
|
assert(dict != NULL && PyDict_Check(dict));
|
||||||
else {
|
|
||||||
assert(PyTuple_Check(mro));
|
res = PyDict_GetItem(dict, name);
|
||||||
n = PyTuple_GET_SIZE(mro);
|
if (res != NULL) {
|
||||||
}
|
Py_INCREF(res);
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
|
f = Py_TYPE(res)->tp_descr_get;
|
||||||
break;
|
if (f != NULL) {
|
||||||
}
|
tmp = f(res,
|
||||||
i++;
|
/* Only pass 'obj' param if this is instance-mode super
|
||||||
res = NULL;
|
(See SF ID #743627) */
|
||||||
/* keep a strong reference to mro because starttype->tp_mro can be
|
(su->obj == (PyObject *)starttype) ? NULL : su->obj,
|
||||||
replaced during PyDict_GetItem(dict, name) */
|
(PyObject *)starttype);
|
||||||
Py_INCREF(mro);
|
Py_DECREF(res);
|
||||||
for (; i < n; i++) {
|
res = tmp;
|
||||||
tmp = PyTuple_GET_ITEM(mro, i);
|
|
||||||
if (PyType_Check(tmp))
|
|
||||||
dict = ((PyTypeObject *)tmp)->tp_dict;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
res = PyDict_GetItem(dict, name);
|
|
||||||
if (res != NULL) {
|
|
||||||
Py_INCREF(res);
|
|
||||||
f = Py_TYPE(res)->tp_descr_get;
|
|
||||||
if (f != NULL) {
|
|
||||||
tmp = f(res,
|
|
||||||
/* Only pass 'obj' param if
|
|
||||||
this is instance-mode super
|
|
||||||
(See SF ID #743627)
|
|
||||||
*/
|
|
||||||
(su->obj == (PyObject *)
|
|
||||||
su->obj_type
|
|
||||||
? (PyObject *)NULL
|
|
||||||
: su->obj),
|
|
||||||
(PyObject *)starttype);
|
|
||||||
Py_DECREF(res);
|
|
||||||
res = tmp;
|
|
||||||
}
|
|
||||||
Py_DECREF(mro);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_DECREF(mro);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
Py_DECREF(mro);
|
|
||||||
}
|
i++;
|
||||||
|
} while (i < n);
|
||||||
|
Py_DECREF(mro);
|
||||||
|
|
||||||
|
skip:
|
||||||
return PyObject_GenericGetAttr(self, name);
|
return PyObject_GenericGetAttr(self, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue