]> granicus.if.org Git - python/commitdiff
Merged revisions 70931 via svnmerge from
authorJack Diederich <jackdied@gmail.com>
Wed, 1 Apr 2009 04:27:09 +0000 (04:27 +0000)
committerJack Diederich <jackdied@gmail.com>
Wed, 1 Apr 2009 04:27:09 +0000 (04:27 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r70931 | jack.diederich | 2009-03-31 19:46:48 -0400 (Tue, 31 Mar 2009) | 1 line

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

Lib/test/test_functools.py
Misc/ACKS
Misc/NEWS
Modules/_functoolsmodule.c

index 84e974a8db0a02bc02e39cdb4cf439bd25c1d3b0..dedfb1ebe28e82e84d2771a85001fca888590fe8 100644 (file)
@@ -2,6 +2,7 @@ import functools
 import unittest
 from test import support
 from weakref import proxy
+import pickle
 
 @staticmethod
 def PythonPartial(func, *args, **keywords):
@@ -19,6 +20,9 @@ def capture(*args, **kw):
     """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):
 
@@ -141,6 +145,12 @@ class TestPartial(unittest.TestCase):
         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
 
@@ -148,11 +158,13 @@ class TestPartialSubclass(TestPartial):
 
     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,
index acde81522348793b3e05dab2d8b0dc289944456c..ccc32b18dc99ad1f3943ff27f7d6245d0dc824ee 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -167,6 +167,7 @@ Roger Dev
 Raghuram Devarakonda
 Toby Dickenson
 Mark Dickinson
+Jack Diederich
 Humberto Diogenes
 Yves Dionne
 Daniel Dittmar
index 7f0808d3514595673729aa63fc816867b89c192a..ce0db2ab87c3a09fa308d29be9855bcd41fa9af2 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -726,6 +726,8 @@ Extension Modules
   buffer.
 
 
+- Issue #5228: Make functools.partial objects can now be pickled.
+
 Tests
 -----
 
index 369eb5cc5a44b190fa23c4678eac7042db239e30..5f389a9589ab009426eff2dbf2434babe5f16c0d 100644 (file)
@@ -196,6 +196,53 @@ static PyGetSetDef partial_getsetlist[] = {
        {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 */
@@ -226,7 +273,7 @@ static PyTypeObject partial_type = {
        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 */