From 4c05d3bc561dcdd70dd0d268fa769197334349a8 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Sun, 24 Nov 2013 02:41:05 -0800 Subject: [PATCH] Make built-in methods picklable through the reduce protocol. --- Lib/pickle.py | 9 +-------- Modules/_pickle.c | 32 -------------------------------- Objects/methodobject.c | 22 +++++++++++++++++++++- 3 files changed, 22 insertions(+), 41 deletions(-) diff --git a/Lib/pickle.py b/Lib/pickle.py index 8449340e7f4..d4aa13bd0c6 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -23,7 +23,7 @@ Misc variables: """ -from types import FunctionType, BuiltinFunctionType, ModuleType +from types import FunctionType, ModuleType from copyreg import dispatch_table from copyreg import _extension_registry, _inverted_registry, _extension_cache from itertools import islice @@ -962,14 +962,7 @@ class _Pickler: self.memoize(obj) - def save_method(self, obj): - if obj.__self__ is None or type(obj.__self__) is ModuleType: - self.save_global(obj) - else: - self.save_reduce(getattr, (obj.__self__, obj.__name__), obj=obj) - dispatch[FunctionType] = save_global - dispatch[BuiltinFunctionType] = save_method dispatch[type] = save_global diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 741cb8a7a2e..f0c918f87e0 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3513,34 +3513,6 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) return 0; } -static int -save_method(PicklerObject *self, PyObject *obj) -{ - PyObject *method_self = PyCFunction_GET_SELF(obj); - - if (method_self == NULL || PyModule_Check(method_self)) { - return save_global(self, obj, NULL); - } - else { - PyObject *builtins; - PyObject *getattr; - PyObject *reduce_value; - int status = -1; - _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - reduce_value = \ - Py_BuildValue("O(Os)", getattr, method_self, - ((PyCFunctionObject *)obj)->m_ml->ml_name); - if (reduce_value != NULL) { - status = save_reduce(self, reduce_value, obj); - Py_DECREF(reduce_value); - } - return status; - } -} - static int save(PicklerObject *self, PyObject *obj, int pers_save) { @@ -3652,10 +3624,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save) goto done; } } - else if (type == &PyCFunction_Type) { - status = save_method(self, obj); - goto done; - } /* XXX: This part needs some unit tests. */ diff --git a/Objects/methodobject.c b/Objects/methodobject.c index ca21a68e2e0..55a7d6a35ea 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -159,6 +159,26 @@ meth_dealloc(PyCFunctionObject *m) } } +static PyObject * +meth_reduce(PyCFunctionObject *m) +{ + PyObject *builtins; + PyObject *getattr; + _Py_IDENTIFIER(getattr); + + if (m->m_self == NULL || PyModule_Check(m->m_self)) + return PyUnicode_FromString(m->m_ml->ml_name); + + builtins = PyEval_GetBuiltins(); + getattr = _PyDict_GetItemId(builtins, &PyId_getattr); + return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name); +} + +static PyMethodDef meth_methods[] = { + {"__reduce__", (PyCFunction)meth_reduce, METH_NOARGS, NULL}, + {NULL, NULL} +}; + /* * finds the docstring's introspection signature. * if present, returns a pointer pointing to the first '('. @@ -394,7 +414,7 @@ PyTypeObject PyCFunction_Type = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + meth_methods, /* tp_methods */ meth_members, /* tp_members */ meth_getsets, /* tp_getset */ 0, /* tp_base */