diff -r a757bdfce6b3 Lib/functools.py --- a/Lib/functools.py Fri Dec 13 12:30:29 2013 -0500 +++ b/Lib/functools.py Wed Oct 08 10:51:09 2014 -0400 @@ -228,6 +228,14 @@ """New function with partial application of the given arguments and keywords. """ + if hasattr(func, 'func'): + args = func.args + args + tmpkw = func.keywords.copy() + tmpkw.update(keywords) + keywords = tmpkw + del tmpkw + func = func.func + def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) diff -r a757bdfce6b3 Lib/test/test_functools.py --- a/Lib/test/test_functools.py Fri Dec 13 12:30:29 2013 -0500 +++ b/Lib/test/test_functools.py Wed Oct 08 10:51:09 2014 -0400 @@ -131,6 +131,16 @@ join = self.partial(''.join) self.assertEqual(join(data), '0123456789') + def test_nested_optimization(self): + partial = self.partial + # Only "true" partial is optimized + if partial.__name__ != 'partial': + return + inner = partial(signature, 'asdf') + nested = partial(inner, bar=True) + flat = partial(signature, 'asdf', bar=True) + self.assertEqual(signature(nested), signature(flat)) + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): diff -r a757bdfce6b3 Modules/_functoolsmodule.c --- a/Modules/_functoolsmodule.c Fri Dec 13 12:30:29 2013 -0500 +++ b/Modules/_functoolsmodule.c Wed Oct 08 10:51:09 2014 -0400 @@ -25,7 +25,7 @@ static PyObject * partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - PyObject *func; + PyObject *func, *pargs, *nargs, *pkw; partialobject *pto; if (PyTuple_GET_SIZE(args) < 1) { @@ -34,7 +34,16 @@ return NULL; } + pargs = pkw = Py_None; func = PyTuple_GET_ITEM(args, 0); + if (Py_TYPE(func) == &partial_type && type == &partial_type) { + partialobject *part = (partialobject *)func; + if (part->dict == NULL) { + pargs = part->args; + pkw = part->kw; + func = part->fn; + } + } if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "the first argument must be callable"); @@ -48,21 +57,53 @@ pto->fn = func; Py_INCREF(func); - pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); - if (pto->args == NULL) { + + nargs = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); + if (nargs == NULL) { + pto->args = NULL; pto->kw = NULL; Py_DECREF(pto); return NULL; } + if (pargs == Py_None || PyTuple_GET_SIZE(pargs) == 0) { + pto->args = nargs; + Py_INCREF(nargs); + } + else if (PyTuple_GET_SIZE(nargs) == 0) { + pto->args = pargs; + Py_INCREF(pargs); + } + else { + pto->args = PySequence_Concat(pargs, nargs); + if (pto->args == NULL) { + pto->kw = NULL; + Py_DECREF(pto); + return NULL; + } + } + Py_DECREF(nargs); + if (kw != NULL) { - pto->kw = PyDict_Copy(kw); + if (pkw == Py_None) { + pto->kw = PyDict_Copy(kw); + } + else { + pto->kw = PyDict_Copy(pkw); + if (pto->kw != NULL) { + if (PyDict_Merge(pto->kw, kw, 1) != 0) { + Py_DECREF(pto); + return NULL; + } + } + } if (pto->kw == NULL) { Py_DECREF(pto); return NULL; } - } else { - pto->kw = Py_None; - Py_INCREF(Py_None); + } + else { + pto->kw = pkw; + Py_INCREF(pkw); } pto->weakreflist = NULL;