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}. ...@@ -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 Mappings may also be used; however, in the future, special meaning may
be assigned to the values corresponding to each key. 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} \end{itemize}
...@@ -2223,3 +2228,6 @@ exception; this is the caller's responsibility. ...@@ -2223,3 +2228,6 @@ exception; this is the caller's responsibility.
Python \keyword{with} statement.} Python \keyword{with} statement.}
\end{seealso} \end{seealso}
...@@ -2853,6 +2853,51 @@ def setclass(): ...@@ -2853,6 +2853,51 @@ def setclass():
cant(o, type(1)) cant(o, type(1))
cant(o, type(None)) cant(o, type(None))
del o 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(): def setdict():
if verbose: print "Testing __dict__ assignment..." if verbose: print "Testing __dict__ assignment..."
......
...@@ -276,6 +276,7 @@ Chris Herborth ...@@ -276,6 +276,7 @@ Chris Herborth
Ivan Herman Ivan Herman
Jrgen Hermann Jrgen Hermann
Gary Herron Gary Herron
Thomas Herve
Bernhard Herzog Bernhard Herzog
Magnus L. Hetland Magnus L. Hetland
Raymond Hettinger Raymond Hettinger
......
...@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1? ...@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
Core and builtins 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 - Patch #1642547: Fix an error/crash when encountering syntax errors in
complex if statements. complex if statements.
......
...@@ -1844,8 +1844,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) ...@@ -1844,8 +1844,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
} }
} }
/* Copy slots into yet another tuple, demangling names */ /* Copy slots into a list, mangle names and sort them.
newslots = PyTuple_New(nslots - add_dict - add_weak); 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) if (newslots == NULL)
goto bad_slots; goto bad_slots;
for (i = j = 0; i < nslots; i++) { for (i = j = 0; i < nslots; i++) {
...@@ -1858,13 +1861,23 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) ...@@ -1858,13 +1861,23 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
tmp =_Py_Mangle(name, tmp); tmp =_Py_Mangle(name, tmp);
if (!tmp) if (!tmp)
goto bad_slots; goto bad_slots;
PyTuple_SET_ITEM(newslots, j, tmp); PyList_SET_ITEM(newslots, j, tmp);
j++; j++;
} }
assert(j == nslots - add_dict - add_weak); assert(j == nslots - add_dict - add_weak);
nslots = j; nslots = j;
Py_DECREF(slots); 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 */ /* Secondary bases may provide weakrefs or dict */
if (nbases > 1 && if (nbases > 1 &&
...@@ -2481,6 +2494,7 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) ...@@ -2481,6 +2494,7 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b)
{ {
PyTypeObject *base = a->tp_base; PyTypeObject *base = a->tp_base;
Py_ssize_t size; Py_ssize_t size;
PyObject *slots_a, *slots_b;
if (base != b->tp_base) if (base != b->tp_base)
return 0; return 0;
...@@ -2491,6 +2505,15 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) ...@@ -2491,6 +2505,15 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b)
size += sizeof(PyObject *); size += sizeof(PyObject *);
if (a->tp_weaklistoffset == size && b->tp_weaklistoffset == size) if (a->tp_weaklistoffset == size && b->tp_weaklistoffset == size)
size += sizeof(PyObject *); 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; 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