- Provisional support for pickling new-style objects. (*)

- Made cls.__module__ writable.

- Ensure that obj.__dict__ is returned as {}, not None, even upon first
  reference; it simply springs into life when you ask for it.

(*) The pickling support is provisional for the following reasons:

- It doesn't support classes with __slots__.

- It relies on additional support in copy_reg.py: the C method
  __reduce__, defined in the object class, really calls calling
  copy_reg._reduce(obj).  Eventually the Python code in copy_reg.py
  needs to be migrated to C, but I'd like to experiment with the
  Python implementation first.  The _reduce() code also relies on an
  additional helper function, _reconstructor(), defined in
  copy_reg.py; this should also be reimplemented in C.
This commit is contained in:
Guido van Rossum 2001-09-25 16:25:58 +00:00
parent ad39aba2f6
commit 3926a63d05
4 changed files with 142 additions and 12 deletions

View File

@ -33,3 +33,32 @@ def pickle_complex(c):
return complex, (c.real, c.imag) return complex, (c.real, c.imag)
pickle(type(1j), pickle_complex, complex) pickle(type(1j), pickle_complex, complex)
# Support for picking new-style objects
_dummy_classes = {}
def _reconstructor(cls, base, state):
dummy = _dummy_classes.get(base)
if dummy is None:
class dummy(base): pass
_dummy_classes[base] = dummy
obj = dummy(state)
obj._foo = 1; del obj._foo # hack to create __dict__
obj.__class__ = cls
return obj
_reconstructor.__safe_for_unpickling__ = 1
_HEAPTYPE = 1<<9
def _reduce(self):
for base in self.__class__.__mro__:
if not base.__flags__ & _HEAPTYPE:
break
else:
base = object # not really reachable
if base is object:
state = None
else:
state = base(self)
return _reconstructor, (self.__class__, base, state), self.__dict__

View File

@ -785,7 +785,7 @@ def objects():
class Cdict(object): class Cdict(object):
pass pass
x = Cdict() x = Cdict()
verify(x.__dict__ is None) verify(x.__dict__ == {})
x.foo = 1 x.foo = 1
verify(x.foo == 1) verify(x.foo == 1)
verify(x.__dict__ == {'foo': 1}) verify(x.__dict__ == {'foo': 1})
@ -2032,6 +2032,66 @@ def setclass():
cant(object(), list) cant(object(), list)
cant(list(), object) cant(list(), object)
def pickles():
if verbose: print "Testing pickling new-style classes and objects..."
import pickle, cPickle
def sorteditems(d):
L = d.items()
L.sort()
return L
global C
class C(object):
def __init__(self, a, b):
super(C, self).__init__()
self.a = a
self.b = b
def __repr__(self):
return "C(%r, %r)" % (self.a, self.b)
global C1
class C1(list):
def __new__(cls, a, b):
return super(C1, cls).__new__(cls)
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return "C1(%r, %r)<%r>" % (self.a, self.b, list(self))
global C2
class C2(int):
def __new__(cls, a, b, val=0):
return super(C2, cls).__new__(cls, val)
def __init__(self, a, b, val=0):
self.a = a
self.b = b
def __repr__(self):
return "C2(%r, %r)<%r>" % (self.a, self.b, int(self))
for p in pickle, cPickle:
for bin in 0, 1:
for cls in C, C1, C2:
s = p.dumps(cls, bin)
cls2 = p.loads(s)
verify(cls2 is cls)
a = C1(1, 2); a.append(42); a.append(24)
b = C2("hello", "world", 42)
s = p.dumps((a, b), bin)
x, y = p.loads(s)
assert x.__class__ == a.__class__
assert sorteditems(x.__dict__) == sorteditems(a.__dict__)
assert y.__class__ == b.__class__
assert sorteditems(y.__dict__) == sorteditems(b.__dict__)
assert `x` == `a`
assert `y` == `b`
if verbose:
print "a = x =", a
print "b = y =", b
def test_main(): def test_main():
lists() lists()
@ -2075,6 +2135,7 @@ def test_main():
coercions() coercions()
descrdoc() descrdoc()
setclass() setclass()
pickles()
if verbose: print "All OK" if verbose: print "All OK"
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -206,6 +206,7 @@ Instead, you can get the same information from the list type:
'__mul__', '__mul__',
'__ne__', '__ne__',
'__new__', '__new__',
'__reduce__',
'__repr__', '__repr__',
'__rmul__', '__rmul__',
'__setattr__', '__setattr__',

View File

@ -53,6 +53,23 @@ type_module(PyTypeObject *type, void *context)
return NULL; return NULL;
} }
static int
type_set_module(PyTypeObject *type, PyObject *value, void *context)
{
if (!(type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) ||
strrchr(type->tp_name, '.')) {
PyErr_Format(PyExc_TypeError,
"can't set %s.__module__", type->tp_name);
return -1;
}
if (!value) {
PyErr_Format(PyExc_TypeError,
"can't delete %s.__module__", type->tp_name);
return -1;
}
return PyDict_SetItemString(type->tp_dict, "__module__", value);
}
static PyObject * static PyObject *
type_dict(PyTypeObject *type, void *context) type_dict(PyTypeObject *type, void *context)
{ {
@ -93,7 +110,7 @@ type_dynamic(PyTypeObject *type, void *context)
PyGetSetDef type_getsets[] = { PyGetSetDef type_getsets[] = {
{"__name__", (getter)type_name, NULL, NULL}, {"__name__", (getter)type_name, NULL, NULL},
{"__module__", (getter)type_module, NULL, NULL}, {"__module__", (getter)type_module, (setter)type_set_module, NULL},
{"__dict__", (getter)type_dict, NULL, NULL}, {"__dict__", (getter)type_dict, NULL, NULL},
{"__defined__", (getter)type_defined, NULL, NULL}, {"__defined__", (getter)type_defined, NULL, NULL},
{"__dynamic__", (getter)type_dynamic, NULL, NULL}, {"__dynamic__", (getter)type_dynamic, NULL, NULL},
@ -656,14 +673,10 @@ subtype_dict(PyObject *obj, void *context)
return NULL; return NULL;
} }
dict = *dictptr; dict = *dictptr;
if (dict == NULL) { if (dict == NULL)
Py_INCREF(Py_None); *dictptr = dict = PyDict_New();
return Py_None; Py_XINCREF(dict);
} return dict;
else {
Py_INCREF(dict);
return dict;
}
} }
PyGetSetDef subtype_getsets[] = { PyGetSetDef subtype_getsets[] = {
@ -1283,6 +1296,31 @@ static PyGetSetDef object_getsets[] = {
{0} {0}
}; };
static PyObject *
object_reduce(PyObject *self, PyObject *args)
{
/* Call copy_reg._reduce(self) */
static PyObject *copy_reg_str;
PyObject *copy_reg, *res;
if (!copy_reg_str) {
copy_reg_str = PyString_InternFromString("copy_reg");
if (copy_reg_str == NULL)
return NULL;
}
copy_reg = PyImport_Import(copy_reg_str);
if (!copy_reg)
return NULL;
res = PyEval_CallMethod(copy_reg, "_reduce", "(O)", self);
Py_DECREF(copy_reg);
return res;
}
static PyMethodDef object_methods[] = {
{"__reduce__", object_reduce, METH_NOARGS, "helper for pickle"},
{0}
};
PyTypeObject PyBaseObject_Type = { PyTypeObject PyBaseObject_Type = {
PyObject_HEAD_INIT(&PyType_Type) PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */ 0, /* ob_size */
@ -1312,7 +1350,7 @@ PyTypeObject PyBaseObject_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
0, /* tp_iter */ 0, /* tp_iter */
0, /* tp_iternext */ 0, /* tp_iternext */
0, /* tp_methods */ object_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
object_getsets, /* tp_getset */ object_getsets, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
@ -3009,7 +3047,8 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
res = PyObject_GenericGetAttr(self, name); res = PyObject_GenericGetAttr(self, name);
else else
res = PyObject_CallFunction(getattribute, "OO", self, name); res = PyObject_CallFunction(getattribute, "OO", self, name);
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { if (getattr != NULL &&
res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear(); PyErr_Clear();
res = PyObject_CallFunction(getattr, "OO", self, name); res = PyObject_CallFunction(getattr, "OO", self, name);
} }