Index: Lib/test/test_functools.py =================================================================== --- Lib/test/test_functools.py (revision 77804) +++ Lib/test/test_functools.py (working copy) @@ -7,6 +7,14 @@ @staticmethod def PythonPartial(func, *args, **keywords): 'Pure Python approximation of partial()' + 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) @@ -123,7 +131,7 @@ self.assertRaises(ZeroDivisionError, self.thetype(f, y=0), 1) def test_attributes(self): - p = self.thetype(hex) + p = self.thetype(hex, 1) try: del p.__dict__ except TypeError: @@ -151,6 +159,12 @@ f_copy = pickle.loads(pickle.dumps(f)) self.assertEqual(signature(f), signature(f_copy)) + def test_nested(self): + partial = self.thetype + f = partial(partial(signature, 'asdf'), bar=True) + g = partial(signature, 'asdf', bar=True) + self.assertEqual(signature(f), signature(g)) + class PartialSubclass(functools.partial): pass @@ -158,6 +172,12 @@ thetype = PartialSubclass + def test_nested(self): + partial = self.thetype + f = partial(signature, 'asdf') + g = partial(f, bar=True) + self.assertEqual(signature(g), (f, (), {'bar': True}, {})) + class TestPythonPartial(TestPartial): thetype = PythonPartial Index: Modules/_functoolsmodule.c =================================================================== --- Modules/_functoolsmodule.c (revision 77804) +++ Modules/_functoolsmodule.c (working copy) @@ -103,7 +103,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) { @@ -113,6 +113,15 @@ } func = PyTuple_GET_ITEM(args, 0); + if (Py_TYPE(func) == &partial_type && type == &partial_type) { + pargs = ((partialobject *)func)->args; + pkw = ((partialobject *)func)->kw; + func = ((partialobject *)func)->fn; + } else { + pargs = Py_None; + pkw = Py_None; + + } if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "the first argument must be callable"); @@ -126,21 +135,49 @@ 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); + pto->kw = pkw; + Py_INCREF(pkw); } pto->weakreflist = NULL;