Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3926a63

Browse files
committed
- 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.
1 parent ad39aba commit 3926a63

4 files changed

Lines changed: 142 additions & 12 deletions

File tree

Lib/copy_reg.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,32 @@ def pickle_complex(c):
3333
return complex, (c.real, c.imag)
3434

3535
pickle(type(1j), pickle_complex, complex)
36+
37+
# Support for picking new-style objects
38+
39+
_dummy_classes = {}
40+
41+
def _reconstructor(cls, base, state):
42+
dummy = _dummy_classes.get(base)
43+
if dummy is None:
44+
class dummy(base): pass
45+
_dummy_classes[base] = dummy
46+
obj = dummy(state)
47+
obj._foo = 1; del obj._foo # hack to create __dict__
48+
obj.__class__ = cls
49+
return obj
50+
_reconstructor.__safe_for_unpickling__ = 1
51+
52+
_HEAPTYPE = 1<<9
53+
54+
def _reduce(self):
55+
for base in self.__class__.__mro__:
56+
if not base.__flags__ & _HEAPTYPE:
57+
break
58+
else:
59+
base = object # not really reachable
60+
if base is object:
61+
state = None
62+
else:
63+
state = base(self)
64+
return _reconstructor, (self.__class__, base, state), self.__dict__

Lib/test/test_descr.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ def objects():
785785
class Cdict(object):
786786
pass
787787
x = Cdict()
788-
verify(x.__dict__ is None)
788+
verify(x.__dict__ == {})
789789
x.foo = 1
790790
verify(x.foo == 1)
791791
verify(x.__dict__ == {'foo': 1})
@@ -2032,6 +2032,66 @@ def cant(x, C):
20322032
cant(object(), list)
20332033
cant(list(), object)
20342034

2035+
def pickles():
2036+
if verbose: print "Testing pickling new-style classes and objects..."
2037+
import pickle, cPickle
2038+
2039+
def sorteditems(d):
2040+
L = d.items()
2041+
L.sort()
2042+
return L
2043+
2044+
global C
2045+
class C(object):
2046+
def __init__(self, a, b):
2047+
super(C, self).__init__()
2048+
self.a = a
2049+
self.b = b
2050+
def __repr__(self):
2051+
return "C(%r, %r)" % (self.a, self.b)
2052+
2053+
global C1
2054+
class C1(list):
2055+
def __new__(cls, a, b):
2056+
return super(C1, cls).__new__(cls)
2057+
def __init__(self, a, b):
2058+
self.a = a
2059+
self.b = b
2060+
def __repr__(self):
2061+
return "C1(%r, %r)<%r>" % (self.a, self.b, list(self))
2062+
2063+
global C2
2064+
class C2(int):
2065+
def __new__(cls, a, b, val=0):
2066+
return super(C2, cls).__new__(cls, val)
2067+
def __init__(self, a, b, val=0):
2068+
self.a = a
2069+
self.b = b
2070+
def __repr__(self):
2071+
return "C2(%r, %r)<%r>" % (self.a, self.b, int(self))
2072+
2073+
for p in pickle, cPickle:
2074+
for bin in 0, 1:
2075+
2076+
for cls in C, C1, C2:
2077+
s = p.dumps(cls, bin)
2078+
cls2 = p.loads(s)
2079+
verify(cls2 is cls)
2080+
2081+
a = C1(1, 2); a.append(42); a.append(24)
2082+
b = C2("hello", "world", 42)
2083+
s = p.dumps((a, b), bin)
2084+
x, y = p.loads(s)
2085+
assert x.__class__ == a.__class__
2086+
assert sorteditems(x.__dict__) == sorteditems(a.__dict__)
2087+
assert y.__class__ == b.__class__
2088+
assert sorteditems(y.__dict__) == sorteditems(b.__dict__)
2089+
assert `x` == `a`
2090+
assert `y` == `b`
2091+
if verbose:
2092+
print "a = x =", a
2093+
print "b = y =", b
2094+
20352095

20362096
def test_main():
20372097
lists()
@@ -2075,6 +2135,7 @@ def test_main():
20752135
coercions()
20762136
descrdoc()
20772137
setclass()
2138+
pickles()
20782139
if verbose: print "All OK"
20792140

20802141
if __name__ == "__main__":

Lib/test/test_descrtut.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ def merge(self, other):
206206
'__mul__',
207207
'__ne__',
208208
'__new__',
209+
'__reduce__',
209210
'__repr__',
210211
'__rmul__',
211212
'__setattr__',

Objects/typeobject.c

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,23 @@ type_module(PyTypeObject *type, void *context)
5353
return NULL;
5454
}
5555

56+
static int
57+
type_set_module(PyTypeObject *type, PyObject *value, void *context)
58+
{
59+
if (!(type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) ||
60+
strrchr(type->tp_name, '.')) {
61+
PyErr_Format(PyExc_TypeError,
62+
"can't set %s.__module__", type->tp_name);
63+
return -1;
64+
}
65+
if (!value) {
66+
PyErr_Format(PyExc_TypeError,
67+
"can't delete %s.__module__", type->tp_name);
68+
return -1;
69+
}
70+
return PyDict_SetItemString(type->tp_dict, "__module__", value);
71+
}
72+
5673
static PyObject *
5774
type_dict(PyTypeObject *type, void *context)
5875
{
@@ -93,7 +110,7 @@ type_dynamic(PyTypeObject *type, void *context)
93110

94111
PyGetSetDef type_getsets[] = {
95112
{"__name__", (getter)type_name, NULL, NULL},
96-
{"__module__", (getter)type_module, NULL, NULL},
113+
{"__module__", (getter)type_module, (setter)type_set_module, NULL},
97114
{"__dict__", (getter)type_dict, NULL, NULL},
98115
{"__defined__", (getter)type_defined, NULL, NULL},
99116
{"__dynamic__", (getter)type_dynamic, NULL, NULL},
@@ -656,14 +673,10 @@ subtype_dict(PyObject *obj, void *context)
656673
return NULL;
657674
}
658675
dict = *dictptr;
659-
if (dict == NULL) {
660-
Py_INCREF(Py_None);
661-
return Py_None;
662-
}
663-
else {
664-
Py_INCREF(dict);
665-
return dict;
666-
}
676+
if (dict == NULL)
677+
*dictptr = dict = PyDict_New();
678+
Py_XINCREF(dict);
679+
return dict;
667680
}
668681

669682
PyGetSetDef subtype_getsets[] = {
@@ -1283,6 +1296,31 @@ static PyGetSetDef object_getsets[] = {
12831296
{0}
12841297
};
12851298

1299+
static PyObject *
1300+
object_reduce(PyObject *self, PyObject *args)
1301+
{
1302+
/* Call copy_reg._reduce(self) */
1303+
static PyObject *copy_reg_str;
1304+
PyObject *copy_reg, *res;
1305+
1306+
if (!copy_reg_str) {
1307+
copy_reg_str = PyString_InternFromString("copy_reg");
1308+
if (copy_reg_str == NULL)
1309+
return NULL;
1310+
}
1311+
copy_reg = PyImport_Import(copy_reg_str);
1312+
if (!copy_reg)
1313+
return NULL;
1314+
res = PyEval_CallMethod(copy_reg, "_reduce", "(O)", self);
1315+
Py_DECREF(copy_reg);
1316+
return res;
1317+
}
1318+
1319+
static PyMethodDef object_methods[] = {
1320+
{"__reduce__", object_reduce, METH_NOARGS, "helper for pickle"},
1321+
{0}
1322+
};
1323+
12861324
PyTypeObject PyBaseObject_Type = {
12871325
PyObject_HEAD_INIT(&PyType_Type)
12881326
0, /* ob_size */
@@ -1312,7 +1350,7 @@ PyTypeObject PyBaseObject_Type = {
13121350
0, /* tp_weaklistoffset */
13131351
0, /* tp_iter */
13141352
0, /* tp_iternext */
1315-
0, /* tp_methods */
1353+
object_methods, /* tp_methods */
13161354
0, /* tp_members */
13171355
object_getsets, /* tp_getset */
13181356
0, /* tp_base */
@@ -3009,7 +3047,8 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
30093047
res = PyObject_GenericGetAttr(self, name);
30103048
else
30113049
res = PyObject_CallFunction(getattribute, "OO", self, name);
3012-
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
3050+
if (getattr != NULL &&
3051+
res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
30133052
PyErr_Clear();
30143053
res = PyObject_CallFunction(getattr, "OO", self, name);
30153054
}

0 commit comments

Comments
 (0)