From: Žiga Seilnacht Date: Fri, 16 Mar 2007 11:59:38 +0000 (+0000) Subject: Patch #1623563: allow __class__ assignment for classes with __slots__. X-Git-Tag: v2.6a1~1982 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6f2d09c949ac0c21c56c97e7f772a2726e34d74f;p=python Patch #1623563: allow __class__ assignment for classes with __slots__. The old and the new class are still required to have the same slot names, but the order in which they are specified is not relevant. --- diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index c5dbfd22d8..c0f3219891 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -1593,6 +1593,11 @@ built-in types such as \class{long}, \class{str} and \class{tuple}. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key. +\item \var{__class__} assignment works only if both classes have the +same \var{__slots__}. +\versionchanged[Previously, \var{__class__} assignment raised an error +if either new or old class had \var{__slots__}]{2.6} + \end{itemize} @@ -2223,3 +2228,6 @@ exception; this is the caller's responsibility. Python \keyword{with} statement.} \end{seealso} + + + diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 71f4ec43cd..5abecf4b00 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2853,6 +2853,51 @@ def setclass(): cant(o, type(1)) cant(o, type(None)) del o + class G(object): + __slots__ = ["a", "b"] + class H(object): + __slots__ = ["b", "a"] + try: + unicode + except NameError: + class I(object): + __slots__ = ["a", "b"] + else: + class I(object): + __slots__ = [unicode("a"), unicode("b")] + class J(object): + __slots__ = ["c", "b"] + class K(object): + __slots__ = ["a", "b", "d"] + class L(H): + __slots__ = ["e"] + class M(I): + __slots__ = ["e"] + class N(J): + __slots__ = ["__weakref__"] + class P(J): + __slots__ = ["__dict__"] + class Q(J): + pass + class R(J): + __slots__ = ["__dict__", "__weakref__"] + + for cls, cls2 in ((G, H), (G, I), (I, H), (Q, R), (R, Q)): + x = cls() + x.a = 1 + x.__class__ = cls2 + verify(x.__class__ is cls2, + "assigning %r as __class__ for %r silently failed" % (cls2, x)) + vereq(x.a, 1) + x.__class__ = cls + verify(x.__class__ is cls, + "assigning %r as __class__ for %r silently failed" % (cls, x)) + vereq(x.a, 1) + for cls in G, J, K, L, M, N, P, R, list, Int: + for cls2 in G, J, K, L, M, N, P, R, list, Int: + if cls is cls2: + continue + cant(cls(), cls2) def setdict(): if verbose: print "Testing __dict__ assignment..." diff --git a/Misc/ACKS b/Misc/ACKS index 7b6002cd42..eeb266a1c8 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -276,6 +276,7 @@ Chris Herborth Ivan Herman Jürgen Hermann Gary Herron +Thomas Herve Bernhard Herzog Magnus L. Hetland Raymond Hettinger diff --git a/Misc/NEWS b/Misc/NEWS index 8af1eb114d..6e336b69d3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1? Core and builtins ----------------- +- Patch #1623563: allow __class__ assignment for classes with __slots__. + The old and the new class are still required to have the same slot names. + - Patch #1642547: Fix an error/crash when encountering syntax errors in complex if statements. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 64abe5a2f9..64003fc4f1 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1844,8 +1844,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } } - /* Copy slots into yet another tuple, demangling names */ - newslots = PyTuple_New(nslots - add_dict - add_weak); + /* Copy slots into a list, mangle names and sort them. + Sorted names are needed for __class__ assignment. + Convert them back to tuple at the end. + */ + newslots = PyList_New(nslots - add_dict - add_weak); if (newslots == NULL) goto bad_slots; for (i = j = 0; i < nslots; i++) { @@ -1858,13 +1861,23 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) tmp =_Py_Mangle(name, tmp); if (!tmp) goto bad_slots; - PyTuple_SET_ITEM(newslots, j, tmp); + PyList_SET_ITEM(newslots, j, tmp); j++; } assert(j == nslots - add_dict - add_weak); nslots = j; Py_DECREF(slots); - slots = newslots; + if (PyList_Sort(newslots) == -1) { + Py_DECREF(bases); + Py_DECREF(newslots); + return NULL; + } + slots = PyList_AsTuple(newslots); + Py_DECREF(newslots); + if (slots == NULL) { + Py_DECREF(bases); + return NULL; + } /* Secondary bases may provide weakrefs or dict */ if (nbases > 1 && @@ -2481,6 +2494,7 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) { PyTypeObject *base = a->tp_base; Py_ssize_t size; + PyObject *slots_a, *slots_b; if (base != b->tp_base) return 0; @@ -2491,6 +2505,15 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) size += sizeof(PyObject *); if (a->tp_weaklistoffset == size && b->tp_weaklistoffset == size) size += sizeof(PyObject *); + + /* Check slots compliance */ + slots_a = ((PyHeapTypeObject *)a)->ht_slots; + slots_b = ((PyHeapTypeObject *)b)->ht_slots; + if (slots_a && slots_b) { + if (PyObject_Compare(slots_a, slots_b) != 0) + return 0; + size += sizeof(PyObject *) * PyTuple_GET_SIZE(slots_a); + } return size == a->tp_basicsize && size == b->tp_basicsize; }