mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
GH-100242: bring functools.py partial implementation more in line with C code (GH-100244)
in partial.__new__, before checking for the existence of the attribute 'func', first check whether the argument is an instance of partial.
This commit is contained in:
parent
8e36cb7bb2
commit
5a0209fc23
4 changed files with 30 additions and 5 deletions
|
|
@ -285,7 +285,7 @@ class partial:
|
||||||
if not callable(func):
|
if not callable(func):
|
||||||
raise TypeError("the first argument must be callable")
|
raise TypeError("the first argument must be callable")
|
||||||
|
|
||||||
if hasattr(func, "func"):
|
if isinstance(func, partial):
|
||||||
args = func.args + args
|
args = func.args + args
|
||||||
keywords = {**func.keywords, **keywords}
|
keywords = {**func.keywords, **keywords}
|
||||||
func = func.func
|
func = func.func
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,19 @@ class TestPartial:
|
||||||
flat = partial(signature, 'asdf', bar=True)
|
flat = partial(signature, 'asdf', bar=True)
|
||||||
self.assertEqual(signature(nested), signature(flat))
|
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):
|
def test_nested_partial_with_attribute(self):
|
||||||
# see issue 25137
|
# see issue 25137
|
||||||
partial = self.partial
|
partial = self.partial
|
||||||
|
|
|
||||||
|
|
@ -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``.
|
||||||
|
|
@ -79,12 +79,19 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_functools_state *state = get_functools_state_by_type(type);
|
||||||
|
if (state == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
pargs = pkw = NULL;
|
pargs = pkw = NULL;
|
||||||
func = PyTuple_GET_ITEM(args, 0);
|
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
|
int res = PyObject_TypeCheck(func, state->partial_type);
|
||||||
// as "type", but if it is called using partial_call, it must have the
|
if (res == -1) {
|
||||||
// same memory layout (fn, args and kw members).
|
return NULL;
|
||||||
|
}
|
||||||
|
if (res == 1) {
|
||||||
// We can use its underlying function directly and merge the arguments.
|
// We can use its underlying function directly and merge the arguments.
|
||||||
partialobject *part = (partialobject *)func;
|
partialobject *part = (partialobject *)func;
|
||||||
if (part->dict == NULL) {
|
if (part->dict == NULL) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue