[Python-checkins] r70931 - in python/trunk: Lib/test/test_functools.py Misc/ACKS Misc/NEWS Modules/_functoolsmodule.c

jack.diederich python-checkins at python.org
Wed Apr 1 01:46:48 CEST 2009


Author: jack.diederich
Date: Wed Apr  1 01:46:48 2009
New Revision: 70931

Log:
#5228: add pickle support to functools.partial

Modified:
   python/trunk/Lib/test/test_functools.py
   python/trunk/Misc/ACKS
   python/trunk/Misc/NEWS
   python/trunk/Modules/_functoolsmodule.c

Modified: python/trunk/Lib/test/test_functools.py
==============================================================================
--- python/trunk/Lib/test/test_functools.py	(original)
+++ python/trunk/Lib/test/test_functools.py	Wed Apr  1 01:46:48 2009
@@ -2,6 +2,7 @@
 import unittest
 from test import test_support
 from weakref import proxy
+import pickle
 
 @staticmethod
 def PythonPartial(func, *args, **keywords):
@@ -19,6 +20,10 @@
     """capture all positional and keyword arguments"""
     return args, kw
 
+def signature(part):
+    """ return the signature of a partial object """
+    return (part.func, part.args, part.keywords, part.__dict__)
+
 class TestPartial(unittest.TestCase):
 
     thetype = functools.partial
@@ -140,6 +145,12 @@
         join = self.thetype(''.join)
         self.assertEqual(join(data), '0123456789')
 
+    def test_pickle(self):
+        f = self.thetype(signature, 'asdf', bar=True)
+        f.add_something_to__dict__ = True
+        f_copy = pickle.loads(pickle.dumps(f))
+        self.assertEqual(signature(f), signature(f_copy))
+
 class PartialSubclass(functools.partial):
     pass
 
@@ -147,11 +158,13 @@
 
     thetype = PartialSubclass
 
-
 class TestPythonPartial(TestPartial):
 
     thetype = PythonPartial
 
+    # the python version isn't picklable
+    def test_pickle(self): pass
+
 class TestUpdateWrapper(unittest.TestCase):
 
     def check_wrapper(self, wrapper, wrapped,

Modified: python/trunk/Misc/ACKS
==============================================================================
--- python/trunk/Misc/ACKS	(original)
+++ python/trunk/Misc/ACKS	Wed Apr  1 01:46:48 2009
@@ -168,6 +168,7 @@
 Raghuram Devarakonda
 Toby Dickenson
 Mark Dickinson
+Jack Diederich
 Yves Dionne
 Daniel Dittmar
 Jaromir Dolecek

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Wed Apr  1 01:46:48 2009
@@ -710,6 +710,8 @@
 
 - Issue #4396: The parser module now correctly validates the with statement.
 
+- Issue #5228: Make functools.partial objects can now be pickled.
+
 Tests
 -----
 

Modified: python/trunk/Modules/_functoolsmodule.c
==============================================================================
--- python/trunk/Modules/_functoolsmodule.c	(original)
+++ python/trunk/Modules/_functoolsmodule.c	Wed Apr  1 01:46:48 2009
@@ -274,6 +274,53 @@
 	{NULL} /* Sentinel */
 };
 
+/* Pickle strategy:
+   __reduce__ by itself doesn't support getting kwargs in the unpickle
+   operation so we define a __setstate__ that replaces all the information
+   about the partial.  If we only replaced part of it someone would use
+   it as a hook to do stange things.
+ */
+
+PyObject *
+partial_reduce(partialobject *pto, PyObject *unused)
+{
+	return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn, 
+			     pto->args, pto->kw,
+			     pto->dict ? pto->dict : Py_None);
+}
+
+PyObject *
+partial_setstate(partialobject *pto, PyObject *args)
+{
+	PyObject *fn, *fnargs, *kw, *dict;
+	if (!PyArg_ParseTuple(args, "(OOOO):__setstate__", 
+			      &fn, &fnargs, &kw, &dict))
+		return NULL;
+	Py_XDECREF(pto->fn);
+	Py_XDECREF(pto->args);
+	Py_XDECREF(pto->kw);
+	Py_XDECREF(pto->dict);
+	pto->fn = fn;
+	pto->args = fnargs;
+	pto->kw = kw;
+	if (dict != Py_None) {
+	  pto->dict = dict;
+	  Py_INCREF(dict);
+	} else {
+	  pto->dict = NULL;
+	}
+	Py_INCREF(fn);
+	Py_INCREF(fnargs);
+	Py_INCREF(kw);
+	Py_RETURN_NONE;
+}
+
+static PyMethodDef partial_methods[] = {
+	{"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS},
+	{"__setstate__", (PyCFunction)partial_setstate, METH_VARARGS},
+	{NULL,		NULL}		/* sentinel */
+};
+
 static PyTypeObject partial_type = {
 	PyVarObject_HEAD_INIT(NULL, 0)
 	"functools.partial",		/* tp_name */
@@ -304,7 +351,7 @@
 	offsetof(partialobject, weakreflist),	/* tp_weaklistoffset */
 	0,				/* tp_iter */
 	0,				/* tp_iternext */
-	0,				/* tp_methods */
+	partial_methods,		/* tp_methods */
 	partial_memberlist,		/* tp_members */
 	partial_getsetlist,		/* tp_getset */
 	0,				/* tp_base */


More information about the Python-checkins mailing list