Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e49af34

Browse files
committed
Issue #7830: Flatten nested functools.partial.
1 parent e2e178e commit e49af34

4 files changed

Lines changed: 68 additions & 7 deletions

File tree

Lib/functools.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ def partial(func, *args, **keywords):
241241
"""New function with partial application of the given arguments
242242
and keywords.
243243
"""
244+
if hasattr(func, 'func'):
245+
args = func.args + args
246+
tmpkw = func.keywords.copy()
247+
tmpkw.update(keywords)
248+
keywords = tmpkw
249+
del tmpkw
250+
func = func.func
251+
244252
def newfunc(*fargs, **fkeywords):
245253
newkeywords = keywords.copy()
246254
newkeywords.update(fkeywords)

Lib/test/test_functools.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ def test_with_bound_and_unbound_methods(self):
131131
join = self.partial(''.join)
132132
self.assertEqual(join(data), '0123456789')
133133

134+
def test_nested_optimization(self):
135+
partial = self.partial
136+
# Only "true" partial is optimized
137+
if partial.__name__ != 'partial':
138+
return
139+
inner = partial(signature, 'asdf')
140+
nested = partial(inner, bar=True)
141+
flat = partial(signature, 'asdf', bar=True)
142+
self.assertEqual(signature(nested), signature(flat))
143+
134144

135145
@unittest.skipUnless(c_functools, 'requires the C _functools module')
136146
class TestPartialC(TestPartial, unittest.TestCase):

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue #7830: Flatten nested functools.partial.
17+
1618
- Issue #20204: Added the __module__ attribute to _tkinter classes.
1719

1820
- Issue #19980: Improved help() for non-recognized strings. help('') now

Modules/_functoolsmodule.c

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static PyTypeObject partial_type;
2525
static PyObject *
2626
partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
2727
{
28-
PyObject *func;
28+
PyObject *func, *pargs, *nargs, *pkw;
2929
partialobject *pto;
3030

3131
if (PyTuple_GET_SIZE(args) < 1) {
@@ -34,7 +34,16 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
3434
return NULL;
3535
}
3636

37+
pargs = pkw = Py_None;
3738
func = PyTuple_GET_ITEM(args, 0);
39+
if (Py_TYPE(func) == &partial_type && type == &partial_type) {
40+
partialobject *part = (partialobject *)func;
41+
if (part->dict == NULL) {
42+
pargs = part->args;
43+
pkw = part->kw;
44+
func = part->fn;
45+
}
46+
}
3847
if (!PyCallable_Check(func)) {
3948
PyErr_SetString(PyExc_TypeError,
4049
"the first argument must be callable");
@@ -48,21 +57,53 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
4857

4958
pto->fn = func;
5059
Py_INCREF(func);
51-
pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX);
52-
if (pto->args == NULL) {
60+
61+
nargs = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX);
62+
if (nargs == NULL) {
63+
pto->args = NULL;
5364
pto->kw = NULL;
5465
Py_DECREF(pto);
5566
return NULL;
5667
}
68+
if (pargs == Py_None || PyTuple_GET_SIZE(pargs) == 0) {
69+
pto->args = nargs;
70+
Py_INCREF(nargs);
71+
}
72+
else if (PyTuple_GET_SIZE(nargs) == 0) {
73+
pto->args = pargs;
74+
Py_INCREF(pargs);
75+
}
76+
else {
77+
pto->args = PySequence_Concat(pargs, nargs);
78+
if (pto->args == NULL) {
79+
pto->kw = NULL;
80+
Py_DECREF(pto);
81+
return NULL;
82+
}
83+
}
84+
Py_DECREF(nargs);
85+
5786
if (kw != NULL) {
58-
pto->kw = PyDict_Copy(kw);
87+
if (pkw == Py_None) {
88+
pto->kw = PyDict_Copy(kw);
89+
}
90+
else {
91+
pto->kw = PyDict_Copy(pkw);
92+
if (pto->kw != NULL) {
93+
if (PyDict_Merge(pto->kw, kw, 1) != 0) {
94+
Py_DECREF(pto);
95+
return NULL;
96+
}
97+
}
98+
}
5999
if (pto->kw == NULL) {
60100
Py_DECREF(pto);
61101
return NULL;
62102
}
63-
} else {
64-
pto->kw = Py_None;
65-
Py_INCREF(Py_None);
103+
}
104+
else {
105+
pto->kw = pkw;
106+
Py_INCREF(pkw);
66107
}
67108

68109
pto->weakreflist = NULL;

0 commit comments

Comments
 (0)