Commit 6f2d09c9 authored by Žiga Seilnacht's avatar Žiga Seilnacht

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.
parent 6ab84520
......@@ -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}
......@@ -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..."
......
......@@ -276,6 +276,7 @@ Chris Herborth
Ivan Herman
Jrgen Hermann
Gary Herron
Thomas Herve
Bernhard Herzog
Magnus L. Hetland
Raymond Hettinger
......
......@@ -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.
......
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment