if not callable(func):
raise TypeError("the first argument must be callable")
- if hasattr(func, "func"):
+ if isinstance(func, partial):
args = func.args + args
keywords = {**func.keywords, **keywords}
func = func.func
flat = partial(signature, 'asdf', bar=True)
self.assertEqual(signature(nested), signature(flat))
+ def test_nested_optimization_bug(self):
+ partial = self.partial
+ class Builder:
+ def __call__(self, tag, *children, **attrib):
+ return (tag, children, attrib)
+
+ def __getattr__(self, tag):
+ return partial(self, tag)
+
+ B = Builder()
+ m = B.m
+ assert m(1, 2, a=2) == ('m', (1, 2), dict(a=2))
+
def test_nested_partial_with_attribute(self):
# see issue 25137
partial = self.partial
--- /dev/null
+Bring pure Python implementation ``functools.partial.__new__`` more in line
+with the C-implementation by not just always checking for the presence of
+the attribute ``'func'`` on the first argument of ``partial``. Instead, both
+the Python version and the C version perform an ``isinstance(func, partial)``
+check on the first argument of ``partial``.
return NULL;
}
+ _functools_state *state = get_functools_state_by_type(type);
+ if (state == NULL) {
+ return NULL;
+ }
+
pargs = pkw = NULL;
func = PyTuple_GET_ITEM(args, 0);
- if (Py_TYPE(func)->tp_call == (ternaryfunc)partial_call) {
- // The type of "func" might not be exactly the same type object
- // as "type", but if it is called using partial_call, it must have the
- // same memory layout (fn, args and kw members).
+
+ int res = PyObject_TypeCheck(func, state->partial_type);
+ if (res == -1) {
+ return NULL;
+ }
+ if (res == 1) {
// We can use its underlying function directly and merge the arguments.
partialobject *part = (partialobject *)func;
if (part->dict == NULL) {