Disallow class assignment completely unless both old and new are heap
types. This prevents nonsense like 2.__class__ = bool or
True.__class__ = int.
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 51fa0d9..1ce08b7 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -2478,6 +2478,11 @@
cant(C(), object)
cant(object(), list)
cant(list(), object)
+ class Int(int): __slots__ = []
+ cant(2, Int)
+ cant(Int(), int)
+ cant(True, int)
+ cant(2, bool)
def setdict():
if verbose: print "Testing __dict__ assignment..."
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 020cbf2..fddde51 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -1745,6 +1745,13 @@
return -1;
}
new = (PyTypeObject *)value;
+ if (!(new->tp_flags & Py_TPFLAGS_HEAPTYPE) ||
+ !(old->tp_flags & Py_TPFLAGS_HEAPTYPE))
+ {
+ PyErr_Format(PyExc_TypeError,
+ "__class__ assignment: only for heap types");
+ return -1;
+ }
if (new->tp_dealloc != old->tp_dealloc ||
new->tp_free != old->tp_free)
{
@@ -1771,13 +1778,9 @@
old->tp_name);
return -1;
}
- if (new->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- Py_INCREF(new);
- }
+ Py_INCREF(new);
self->ob_type = new;
- if (old->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- Py_DECREF(old);
- }
+ Py_DECREF(old);
return 0;
}