Skip to content

Commit

Permalink
pythonGH-100242: bring functools.py partial implementation more in li…
Browse files Browse the repository at this point in the history
…ne with C code (pythonGH-100244)

in partial.__new__, before checking for the existence of the attribute
'func', first check whether the argument is an instance of partial.
  • Loading branch information
cfbolz authored Apr 17, 2024
1 parent 8e36cb7 commit 5a0209f
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def __new__(cls, func, /, *args, **keywords):
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
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ def test_nested_optimization(self):
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
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``.
15 changes: 11 additions & 4 deletions Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,19 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
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) {
Expand Down

0 comments on commit 5a0209f

Please sign in to comment.