]> granicus.if.org Git - python/commitdiff
Patch #1623563: allow __class__ assignment for classes with __slots__.
authorŽiga Seilnacht <ziga.seilnacht@gmail.com>
Fri, 16 Mar 2007 11:59:38 +0000 (11:59 +0000)
committerŽiga Seilnacht <ziga.seilnacht@gmail.com>
Fri, 16 Mar 2007 11:59:38 +0000 (11:59 +0000)
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.

Doc/ref/ref3.tex
Lib/test/test_descr.py
Misc/ACKS
Misc/NEWS
Objects/typeobject.c

index c5dbfd22d815df33f2286a03a1e2ace5297af7a8..c0f3219891977a223bdc00ae1264221833ddc154 100644 (file)
@@ -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}
 
+
+
+
index 71f4ec43cd5ba8c834a5dfeecffaba49c21aea2e..5abecf4b008e23403a48c2b003b910e6d7a5d48b 100644 (file)
@@ -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..."
index 7b6002cd423ad039b250cd1efe65b9d61c1d6219..eeb266a1c8541cf1c2b0005ff9031735a317ecc1 100644 (file)
--- 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
index 8af1eb114dc6380f808aa93df36667ed6b573e31..6e336b69d3406efa35ff97c9f57e4e5cdfc0a189 100644 (file)
--- 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.
 
index 64abe5a2f9d594c9f1afdf73a0df07bae3ea0e23..64003fc4f10ae3ec799a44f5c274e5949e1e5baf 100644 (file)
@@ -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;
 }