Implement appropriate __getnewargs__ for all immutable subclassable builtin
types. The special handling for these can now be removed from save_newobj(). Add some testing for this. Also add support for setting the 'fast' flag on the Python Pickler class, which suppresses use of the memo.
This commit is contained in:
parent
d3590f937f
commit
5d9113d8be
|
@ -191,6 +191,7 @@ class Pickler:
|
||||||
self.memo = {}
|
self.memo = {}
|
||||||
self.proto = int(proto)
|
self.proto = int(proto)
|
||||||
self.bin = proto >= 1
|
self.bin = proto >= 1
|
||||||
|
self.fast = 0
|
||||||
|
|
||||||
def clear_memo(self):
|
def clear_memo(self):
|
||||||
"""Clears the pickler's "memo".
|
"""Clears the pickler's "memo".
|
||||||
|
@ -230,6 +231,8 @@ class Pickler:
|
||||||
# But there appears no advantage to any other scheme, and this
|
# But there appears no advantage to any other scheme, and this
|
||||||
# scheme allows the Unpickler memo to be implemented as a plain (but
|
# scheme allows the Unpickler memo to be implemented as a plain (but
|
||||||
# growable) array, indexed by memo key.
|
# growable) array, indexed by memo key.
|
||||||
|
if self.fast:
|
||||||
|
return
|
||||||
memo_len = len(self.memo)
|
memo_len = len(self.memo)
|
||||||
self.write(self.put(memo_len))
|
self.write(self.put(memo_len))
|
||||||
self.memo[id(obj)] = memo_len, obj
|
self.memo[id(obj)] = memo_len, obj
|
||||||
|
@ -378,14 +381,7 @@ class Pickler:
|
||||||
if getnewargs:
|
if getnewargs:
|
||||||
args = getnewargs() # This bette not reference obj
|
args = getnewargs() # This bette not reference obj
|
||||||
else:
|
else:
|
||||||
# XXX These types should each grow a __getnewargs__
|
args = ()
|
||||||
# implementation so this special-casing is unnecessary.
|
|
||||||
for cls in int, long, float, complex, str, UnicodeType, tuple:
|
|
||||||
if cls and isinstance(obj, cls):
|
|
||||||
args = (cls(obj),)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
args = ()
|
|
||||||
|
|
||||||
save = self.save
|
save = self.save
|
||||||
write = self.write
|
write = self.write
|
||||||
|
|
|
@ -324,6 +324,21 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
## print
|
## print
|
||||||
## pickletools.dis(s)
|
## pickletools.dis(s)
|
||||||
|
|
||||||
|
def test_newobj_generic(self):
|
||||||
|
for proto in [0, 1, 2]:
|
||||||
|
for C in myclasses:
|
||||||
|
B = C.__base__
|
||||||
|
x = C(C.sample)
|
||||||
|
x.foo = 42
|
||||||
|
s = self.dumps(x, proto)
|
||||||
|
## import pickletools
|
||||||
|
## print
|
||||||
|
## pickletools.dis(s)
|
||||||
|
y = self.loads(s)
|
||||||
|
detail = (proto, C, B, x, y, type(y))
|
||||||
|
self.assertEqual(B(x), B(y), detail)
|
||||||
|
self.assertEqual(x.__dict__, y.__dict__, detail)
|
||||||
|
|
||||||
# XXX Temporary hack, so long as the C implementation of pickle protocol
|
# XXX Temporary hack, so long as the C implementation of pickle protocol
|
||||||
# XXX 2 isn't ready. When it is, move the methods in TempAbstractPickleTests
|
# XXX 2 isn't ready. When it is, move the methods in TempAbstractPickleTests
|
||||||
# XXX into AbstractPickleTests above, and get rid of TempAbstractPickleTests
|
# XXX into AbstractPickleTests above, and get rid of TempAbstractPickleTests
|
||||||
|
@ -405,11 +420,38 @@ class TempAbstractPickleTests(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
copy_reg.remove_extension(__name__, "MyList", 0xfffff0)
|
copy_reg.remove_extension(__name__, "MyList", 0xfffff0)
|
||||||
|
|
||||||
|
class MyInt(int):
|
||||||
|
sample = 1
|
||||||
|
|
||||||
|
class MyLong(long):
|
||||||
|
sample = 1L
|
||||||
|
|
||||||
|
class MyFloat(float):
|
||||||
|
sample = 1.0
|
||||||
|
|
||||||
|
class MyComplex(complex):
|
||||||
|
sample = 1.0 + 0.0j
|
||||||
|
|
||||||
|
class MyStr(str):
|
||||||
|
sample = "hello"
|
||||||
|
|
||||||
|
class MyUnicode(unicode):
|
||||||
|
sample = u"hello \u1234"
|
||||||
|
|
||||||
class MyTuple(tuple):
|
class MyTuple(tuple):
|
||||||
pass
|
sample = (1, 2, 3)
|
||||||
|
|
||||||
class MyList(list):
|
class MyList(list):
|
||||||
pass
|
sample = [1, 2, 3]
|
||||||
|
|
||||||
|
class MyDict(dict):
|
||||||
|
sample = {"a": 1, "b": 2}
|
||||||
|
|
||||||
|
myclasses = [MyInt, MyLong, MyFloat,
|
||||||
|
# MyComplex, # XXX complex somehow doesn't work here :-(
|
||||||
|
MyStr, MyUnicode,
|
||||||
|
MyTuple, MyList, MyDict]
|
||||||
|
|
||||||
|
|
||||||
class SlotList(MyList):
|
class SlotList(MyList):
|
||||||
__slots__ = ["foo"]
|
__slots__ = ["foo"]
|
||||||
|
|
|
@ -11,9 +11,13 @@ from test.pickletester import AbstractPersistentPicklerTests
|
||||||
|
|
||||||
class PickleTests(AbstractPickleTests, AbstractPickleModuleTests, XXXTemp):
|
class PickleTests(AbstractPickleTests, AbstractPickleModuleTests, XXXTemp):
|
||||||
|
|
||||||
def setUp(self):
|
def dumps(self, arg, proto=0, fast=0):
|
||||||
self.dumps = pickle.dumps
|
# Ignore fast
|
||||||
self.loads = pickle.loads
|
return pickle.dumps(arg, proto)
|
||||||
|
|
||||||
|
def loads(self, buf):
|
||||||
|
# Ignore fast
|
||||||
|
return pickle.loads(buf)
|
||||||
|
|
||||||
module = pickle
|
module = pickle
|
||||||
error = KeyError
|
error = KeyError
|
||||||
|
@ -22,9 +26,11 @@ class PicklerTests(AbstractPickleTests):
|
||||||
|
|
||||||
error = KeyError
|
error = KeyError
|
||||||
|
|
||||||
def dumps(self, arg, proto=0):
|
def dumps(self, arg, proto=0, fast=0):
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
p = pickle.Pickler(f, proto)
|
p = pickle.Pickler(f, proto)
|
||||||
|
if fast:
|
||||||
|
p.fast = fast
|
||||||
p.dump(arg)
|
p.dump(arg)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
return f.read()
|
return f.read()
|
||||||
|
@ -36,12 +42,14 @@ class PicklerTests(AbstractPickleTests):
|
||||||
|
|
||||||
class PersPicklerTests(AbstractPersistentPicklerTests):
|
class PersPicklerTests(AbstractPersistentPicklerTests):
|
||||||
|
|
||||||
def dumps(self, arg, proto=0):
|
def dumps(self, arg, proto=0, fast=0):
|
||||||
class PersPickler(pickle.Pickler):
|
class PersPickler(pickle.Pickler):
|
||||||
def persistent_id(subself, obj):
|
def persistent_id(subself, obj):
|
||||||
return self.persistent_id(obj)
|
return self.persistent_id(obj)
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
p = PersPickler(f, proto)
|
p = PersPickler(f, proto)
|
||||||
|
if fast:
|
||||||
|
p.fast = fast
|
||||||
p.dump(arg)
|
p.dump(arg)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
|
@ -639,8 +639,15 @@ complex_conjugate(PyObject *self)
|
||||||
return PyComplex_FromCComplex(c);
|
return PyComplex_FromCComplex(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
complex_getnewargs(PyComplexObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(D)", v->cval);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef complex_methods[] = {
|
static PyMethodDef complex_methods[] = {
|
||||||
{"conjugate", (PyCFunction)complex_conjugate, METH_NOARGS},
|
{"conjugate", (PyCFunction)complex_conjugate, METH_NOARGS},
|
||||||
|
{"__getnewargs__", (PyCFunction)complex_getnewargs, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -726,6 +726,17 @@ float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
float_getnewargs(PyFloatObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(d)", v->ob_fval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef float_methods[] = {
|
||||||
|
{"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
PyDoc_STRVAR(float_doc,
|
PyDoc_STRVAR(float_doc,
|
||||||
"float(x) -> floating point number\n\
|
"float(x) -> floating point number\n\
|
||||||
\n\
|
\n\
|
||||||
|
@ -803,7 +814,7 @@ PyTypeObject PyFloat_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
float_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|
|
@ -850,6 +850,17 @@ int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
int_getnewargs(PyIntObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(l)", v->ob_ival);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef int_methods[] = {
|
||||||
|
{"__getnewargs__", (PyCFunction)int_getnewargs, METH_NOARGS},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
PyDoc_STRVAR(int_doc,
|
PyDoc_STRVAR(int_doc,
|
||||||
"int(x[, base]) -> integer\n\
|
"int(x[, base]) -> integer\n\
|
||||||
\n\
|
\n\
|
||||||
|
@ -931,7 +942,7 @@ PyTypeObject PyInt_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
int_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|
|
@ -2646,6 +2646,17 @@ long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
return (PyObject *)new;
|
return (PyObject *)new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
long_getnewargs(PyLongObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(N)", _PyLong_Copy(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef long_methods[] = {
|
||||||
|
{"__getnewargs__", (PyCFunction)long_getnewargs, METH_NOARGS},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
PyDoc_STRVAR(long_doc,
|
PyDoc_STRVAR(long_doc,
|
||||||
"long(x[, base]) -> integer\n\
|
"long(x[, base]) -> integer\n\
|
||||||
\n\
|
\n\
|
||||||
|
@ -2726,7 +2737,7 @@ PyTypeObject PyLong_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
long_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|
|
@ -3045,6 +3045,12 @@ string_splitlines(PyStringObject *self, PyObject *args)
|
||||||
|
|
||||||
#undef SPLIT_APPEND
|
#undef SPLIT_APPEND
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
string_getnewargs(PyStringObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(s#)", v->ob_sval, v->ob_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef
|
static PyMethodDef
|
||||||
string_methods[] = {
|
string_methods[] = {
|
||||||
|
@ -3091,6 +3097,7 @@ string_methods[] = {
|
||||||
expandtabs__doc__},
|
expandtabs__doc__},
|
||||||
{"splitlines", (PyCFunction)string_splitlines, METH_VARARGS,
|
{"splitlines", (PyCFunction)string_splitlines, METH_VARARGS,
|
||||||
splitlines__doc__},
|
splitlines__doc__},
|
||||||
|
{"__getnewargs__", (PyCFunction)string_getnewargs, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -587,6 +587,18 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
tuple_getnewargs(PyTupleObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(N)", tupleslice(v, 0, v->ob_size));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef tuple_methods[] = {
|
||||||
|
{"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
static PyMappingMethods tuple_as_mapping = {
|
static PyMappingMethods tuple_as_mapping = {
|
||||||
(inquiry)tuplelength,
|
(inquiry)tuplelength,
|
||||||
(binaryfunc)tuplesubscript,
|
(binaryfunc)tuplesubscript,
|
||||||
|
@ -625,7 +637,7 @@ PyTypeObject PyTuple_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
tuple_iter, /* tp_iter */
|
tuple_iter, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
tuple_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|
|
@ -5741,6 +5741,14 @@ unicode_endswith(PyUnicodeObject *self,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
unicode_getnewargs(PyUnicodeObject *v)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("(u#)", v->str, v->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef unicode_methods[] = {
|
static PyMethodDef unicode_methods[] = {
|
||||||
|
|
||||||
/* Order is according to common usage: often used methods should
|
/* Order is according to common usage: often used methods should
|
||||||
|
@ -5791,6 +5799,7 @@ static PyMethodDef unicode_methods[] = {
|
||||||
{"freelistsize", (PyCFunction) unicode_freelistsize, METH_NOARGS},
|
{"freelistsize", (PyCFunction) unicode_freelistsize, METH_NOARGS},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
{"__getnewargs__", (PyCFunction)unicode_getnewargs, METH_NOARGS},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue