diff -r 283d7aa763ee Lib/test/test_descr.py --- a/Lib/test/test_descr.py Fri Aug 14 08:03:30 2015 -0700 +++ b/Lib/test/test_descr.py Mon Aug 31 15:42:46 2015 -0700 @@ -1036,6 +1036,51 @@ self.assertTrue(m.__class__ is types.ModuleType) self.assertFalse(hasattr(m, "a")) + # Make sure that builtin immutable objects don't support __class__ + # assignment, because the object instances may be interned. + # We set __slots__ = () to ensure that the subclasses are + # memory-layout compatible, and thus otherwise reasonable candidates + # for __class__ assignment. + + # The following types have immutable instances, but are not + # subclassable and thus don't need to be checked: + # NoneType, bool + + class MyInt(int): + __slots__ = () + with self.assertRaises(TypeError): + (1).__class__ = MyInt + + class MyFloat(float): + __slots__ = () + with self.assertRaises(TypeError): + (1.0).__class__ = MyFloat + + class MyComplex(complex): + __slots__ = () + with self.assertRaises(TypeError): + (1 + 2j).__class__ = MyComplex + + class MyStr(str): + __slots__ = () + with self.assertRaises(TypeError): + "a".__class__ = MyStr + + class MyBytes(bytes): + __slots__ = () + with self.assertRaises(TypeError): + b"a".__class__ = MyBytes + + class MyTuple(tuple): + __slots__ = () + with self.assertRaises(TypeError): + ().__class__ = MyTuple + + class MyFrozenSet(frozenset): + __slots__ = () + with self.assertRaises(TypeError): + frozenset().__class__ = MyFrozenSet + def test_slots(self): # Testing __slots__... class C0(object): diff -r 283d7aa763ee Objects/typeobject.c --- a/Objects/typeobject.c Fri Aug 14 08:03:30 2015 -0700 +++ b/Objects/typeobject.c Mon Aug 31 15:42:46 2015 -0700 @@ -3666,6 +3666,64 @@ return -1; } newto = (PyTypeObject *)value; + /* In versions of CPython prior to 3.5, the code in + compatible_for_assignment was not set up to correctly check for memory + layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just + disallowed __class__ assignment in any case that wasn't HEAPTYPE -> + HEAPTYPE. + + During the 3.5 development cycle, we fixed the code in + compatible_for_assignment to correctly check compatibility between + arbitrary types, and started allowing __class__ assignment in all cases + where the old and new types did in fact have compatible slots and + memory layout (regardless of whether they were implemented as HEAPTYPEs + or not). + + Just before 3.5 was released, though, we discovered that this led to + problems with immutable types like int, where the interpreter assumes + they are immutable and interns some values. Formerly this wasn't a + problem, because they really were immutable -- in particular, all the + types where the interpreter applied this interning trick happened to + also be statically allocated, so the old HEAPTYPE rules were + "accidentally" stopping them from allowing __class__ assignment. But + with the changes to __class__ assignment, we started allowing code like + + class MyInt(int): + ... + # Modifies the type of *all* instances of 1 in the whole program, + # including future instances (!), because the 1 object is interned. + (1).__class__ = MyInt + + (see https://bugs.python.org/issue24912). + + In theory the proper fix would be to identify which classes rely on + this invariant and somehow disallow __class__ assignment only for them, + perhaps via some mechanism like a new Py_TPFLAGS_IMMUTABLE flag (a + "blacklisting" approach). But in practice, since this problem wasn't + noticed late in the 3.5 RC cycle, we're taking the conservative + approach and reinstating the same HEAPTYPE->HEAPTYPE check that we used + to have, plus a "whitelist". For now, the whitelist consists only of + ModuleType subclasses, since those are the cases that motivated the + patch in the first place -- see https://bugs.python.org/issue22986 -- + and since module objects are mutable we can be sure that they are + definitely not being interned. + + So far as we know, all the code beyond the following 'if' statement + will correctly handle non-HEAPTYPE classes, and the HEAPTYPE check is + needed only to protect that subset of non-HEAPTYPE classes for which + the interpreter has code that assumes that all instances are truly + immutable. + */ + if (!PyType_IsSubtype(newto, &PyModule_Type) && + !PyType_IsSubtype(oldto, &PyModule_Type) && + (!(newto->tp_flags & Py_TPFLAGS_HEAPTYPE) || + !(oldto->tp_flags & Py_TPFLAGS_HEAPTYPE))) { + PyErr_Format(PyExc_TypeError, + "__class__ assignment only supported for heap types " + "or ModuleType subclasses"); + return -1; + } + if (compatible_for_assignment(oldto, newto, "__class__")) { if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) Py_INCREF(newto);