mirror of https://github.com/python/cpython
allow changing __class__ between a heaptype and non-heaptype in some cases (closes #22986)
Patch by Nathaniel Smith.
This commit is contained in:
parent
91496a08d4
commit
9d4cbcc86b
|
@ -1026,6 +1026,22 @@ order (MRO) for bases """
|
||||||
self.assertEqual(x.foo, 1)
|
self.assertEqual(x.foo, 1)
|
||||||
self.assertEqual(x.__dict__, {'foo': 1})
|
self.assertEqual(x.__dict__, {'foo': 1})
|
||||||
|
|
||||||
|
def test_object_class_assignment_between_heaptypes_and_nonheaptypes(self):
|
||||||
|
class SubType(types.ModuleType):
|
||||||
|
a = 1
|
||||||
|
|
||||||
|
m = types.ModuleType("m")
|
||||||
|
self.assertTrue(m.__class__ is types.ModuleType)
|
||||||
|
self.assertFalse(hasattr(m, "a"))
|
||||||
|
|
||||||
|
m.__class__ = SubType
|
||||||
|
self.assertTrue(m.__class__ is SubType)
|
||||||
|
self.assertTrue(hasattr(m, "a"))
|
||||||
|
|
||||||
|
m.__class__ = types.ModuleType
|
||||||
|
self.assertTrue(m.__class__ is types.ModuleType)
|
||||||
|
self.assertFalse(hasattr(m, "a"))
|
||||||
|
|
||||||
def test_slots(self):
|
def test_slots(self):
|
||||||
# Testing __slots__...
|
# Testing __slots__...
|
||||||
class C0(object):
|
class C0(object):
|
||||||
|
|
|
@ -10,6 +10,9 @@ Release date: TBA
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #22986: Allow changing an object's __class__ between a dynamic type and
|
||||||
|
static type in some cases.
|
||||||
|
|
||||||
- Issue #15859: PyUnicode_EncodeFSDefault(), PyUnicode_EncodeMBCS() and
|
- Issue #15859: PyUnicode_EncodeFSDefault(), PyUnicode_EncodeMBCS() and
|
||||||
PyUnicode_EncodeCodePage() now raise an exception if the object is not an
|
PyUnicode_EncodeCodePage() now raise an exception if the object is not an
|
||||||
Unicode object. For PyUnicode_EncodeFSDefault(), it was already the case on
|
Unicode object. For PyUnicode_EncodeFSDefault(), it was already the case on
|
||||||
|
|
|
@ -1196,8 +1196,11 @@ subtype_dealloc(PyObject *self)
|
||||||
assert(basedealloc);
|
assert(basedealloc);
|
||||||
basedealloc(self);
|
basedealloc(self);
|
||||||
|
|
||||||
/* Can't reference self beyond this point */
|
/* Can't reference self beyond this point. It's possible tp_del switched
|
||||||
Py_DECREF(type);
|
our type from a HEAPTYPE to a non-HEAPTYPE, so be careful about
|
||||||
|
reference counting. */
|
||||||
|
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
|
||||||
|
Py_DECREF(type);
|
||||||
|
|
||||||
endlabel:
|
endlabel:
|
||||||
++_PyTrash_delete_nesting;
|
++_PyTrash_delete_nesting;
|
||||||
|
@ -3425,17 +3428,18 @@ object_get_class(PyObject *self, void *closure)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
equiv_structs(PyTypeObject *a, PyTypeObject *b)
|
compatible_with_tp_base(PyTypeObject *child)
|
||||||
{
|
{
|
||||||
return a == b ||
|
PyTypeObject *parent = child->tp_base;
|
||||||
(a != NULL &&
|
return (parent != NULL &&
|
||||||
b != NULL &&
|
child->tp_basicsize == parent->tp_basicsize &&
|
||||||
a->tp_basicsize == b->tp_basicsize &&
|
child->tp_itemsize == parent->tp_itemsize &&
|
||||||
a->tp_itemsize == b->tp_itemsize &&
|
child->tp_dictoffset == parent->tp_dictoffset &&
|
||||||
a->tp_dictoffset == b->tp_dictoffset &&
|
child->tp_weaklistoffset == parent->tp_weaklistoffset &&
|
||||||
a->tp_weaklistoffset == b->tp_weaklistoffset &&
|
((child->tp_flags & Py_TPFLAGS_HAVE_GC) ==
|
||||||
((a->tp_flags & Py_TPFLAGS_HAVE_GC) ==
|
(parent->tp_flags & Py_TPFLAGS_HAVE_GC)) &&
|
||||||
(b->tp_flags & Py_TPFLAGS_HAVE_GC)));
|
(child->tp_dealloc == subtype_dealloc ||
|
||||||
|
child->tp_dealloc == parent->tp_dealloc));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -3453,6 +3457,10 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b)
|
||||||
size += sizeof(PyObject *);
|
size += sizeof(PyObject *);
|
||||||
|
|
||||||
/* Check slots compliance */
|
/* Check slots compliance */
|
||||||
|
if (!(a->tp_flags & Py_TPFLAGS_HEAPTYPE) ||
|
||||||
|
!(b->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
slots_a = ((PyHeapTypeObject *)a)->ht_slots;
|
slots_a = ((PyHeapTypeObject *)a)->ht_slots;
|
||||||
slots_b = ((PyHeapTypeObject *)b)->ht_slots;
|
slots_b = ((PyHeapTypeObject *)b)->ht_slots;
|
||||||
if (slots_a && slots_b) {
|
if (slots_a && slots_b) {
|
||||||
|
@ -3468,9 +3476,7 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, char* attr)
|
||||||
{
|
{
|
||||||
PyTypeObject *newbase, *oldbase;
|
PyTypeObject *newbase, *oldbase;
|
||||||
|
|
||||||
if (newto->tp_dealloc != oldto->tp_dealloc ||
|
if (newto->tp_free != oldto->tp_free) {
|
||||||
newto->tp_free != oldto->tp_free)
|
|
||||||
{
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"%s assignment: "
|
"%s assignment: "
|
||||||
"'%s' deallocator differs from '%s'",
|
"'%s' deallocator differs from '%s'",
|
||||||
|
@ -3479,11 +3485,21 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, char* attr)
|
||||||
oldto->tp_name);
|
oldto->tp_name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
It's tricky to tell if two arbitrary types are sufficiently compatible as
|
||||||
|
to be interchangeable; e.g., even if they have the same tp_basicsize, they
|
||||||
|
might have totally different struct fields. It's much easier to tell if a
|
||||||
|
type and its supertype are compatible; e.g., if they have the same
|
||||||
|
tp_basicsize, then that means they have identical fields. So to check
|
||||||
|
whether two arbitrary types are compatible, we first find the highest
|
||||||
|
supertype that each is compatible with, and then if those supertypes are
|
||||||
|
compatible then the original types must also be compatible.
|
||||||
|
*/
|
||||||
newbase = newto;
|
newbase = newto;
|
||||||
oldbase = oldto;
|
oldbase = oldto;
|
||||||
while (equiv_structs(newbase, newbase->tp_base))
|
while (compatible_with_tp_base(newbase))
|
||||||
newbase = newbase->tp_base;
|
newbase = newbase->tp_base;
|
||||||
while (equiv_structs(oldbase, oldbase->tp_base))
|
while (compatible_with_tp_base(oldbase))
|
||||||
oldbase = oldbase->tp_base;
|
oldbase = oldbase->tp_base;
|
||||||
if (newbase != oldbase &&
|
if (newbase != oldbase &&
|
||||||
(newbase->tp_base != oldbase->tp_base ||
|
(newbase->tp_base != oldbase->tp_base ||
|
||||||
|
@ -3518,17 +3534,12 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
newto = (PyTypeObject *)value;
|
newto = (PyTypeObject *)value;
|
||||||
if (!(newto->tp_flags & Py_TPFLAGS_HEAPTYPE) ||
|
|
||||||
!(oldto->tp_flags & Py_TPFLAGS_HEAPTYPE))
|
|
||||||
{
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"__class__ assignment: only for heap types");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (compatible_for_assignment(oldto, newto, "__class__")) {
|
if (compatible_for_assignment(oldto, newto, "__class__")) {
|
||||||
Py_INCREF(newto);
|
if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE)
|
||||||
|
Py_INCREF(newto);
|
||||||
Py_TYPE(self) = newto;
|
Py_TYPE(self) = newto;
|
||||||
Py_DECREF(oldto);
|
if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE)
|
||||||
|
Py_DECREF(oldto);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in New Issue