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.
This commit is contained in:
Žiga Seilnacht 2007-03-16 11:59:38 +00:00
parent 6ab8452036
commit 6f2d09c949
5 changed files with 84 additions and 4 deletions

View 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}

View 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..."

View File

@ -276,6 +276,7 @@ Chris Herborth
Ivan Herman
Jürgen Hermann
Gary Herron
Thomas Herve
Bernhard Herzog
Magnus L. Hetland
Raymond Hettinger

View File

@ -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.

View 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;
}