Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functools.partial support #1484

Open
berdario opened this issue May 5, 2016 · 12 comments

Comments

Projects
None yet
6 participants
@berdario
Copy link

commented May 5, 2016

I haven't been able to find another issue for this, weird that this hasn't been reported already.

from typing import *
from functools import partial

def inc(a:int) -> int:
    return a+1

def foo(f: Callable[[], int]) -> int:
    return f()

foo(partial(inc, 1))

This will fail with

error: Argument 1 to "foo" has incompatible type partial[int]; expected Callable[[], int]

a similar code by using a dyadic add instead of inc will yield:

error: Argument 1 to "foo" has incompatible type partial[int]; expected Callable[[int], int]

(so, partial apparently doesn't currently retain any type information on the non-applied inputs... I guess this'll make the fix non trivial)

@JukkaL

This comment has been minimized.

Copy link
Collaborator

commented May 5, 2016

The error message is generated because a class with a __call__ method (in this case, partial) isn't considered a subtype of Callable, even though it should be. See #797.

Also, partial only retains information about the return type, since the type system can't represent a more precise type.

@berdario

This comment has been minimized.

Copy link
Author

commented May 5, 2016

the type system can't represent a more precise type.

You mean that the partial class doesn't represent it at runtime, right?
I think mypy could special case partial, and coerce it to the appropriate Callable type transparently

@JukkaL

This comment has been minimized.

Copy link
Collaborator

commented May 5, 2016

I meant that mypy would have to special case partial, since we can't write a good enough stub using PEP 484 features only. Mypy already does some special casing, but for this I'd rather have a more general mypy plugin/extension system instead of even more ad-hoc special case logic in the type checker.

@gvanrossum

This comment has been minimized.

Copy link
Member

commented May 5, 2017

Maybe this could be supported by #3299

@JukkaL

This comment has been minimized.

Copy link
Collaborator

commented Jun 13, 2017

Yes, this looks like a good candidate for function plugins. Increasing priority since this is may be pretty easy to implement now. Just supporting positional arguments for partial should cover the majority of uses and would probably be good enough.

@ilevkivskyi

This comment has been minimized.

Copy link
Collaborator

commented Jun 13, 2017

This particular case and probably other basic use cases are already supported by protocols (see tests in PR #3132), simply because partial has __call__ and therefore is a structural subtype of Callable.

@JukkaL

This comment has been minimized.

Copy link
Collaborator

commented Jun 13, 2017

Protocols won't help with inferring the argument types of the result, though?

@ilevkivskyi

This comment has been minimized.

Copy link
Collaborator

commented Jun 13, 2017

Protocols won't help with inferring the argument types of the result, though?

Yes, I didn't do any special-casing, partial in typeshed is only generic in one type _T which is the original return type. Maybe with the new support for flexible Callable this might be improved, but probably a plugin will still be required if we want precise types.

@ilevkivskyi

This comment has been minimized.

Copy link
Collaborator

commented Aug 19, 2017

The original example is now fixed by #3132. This issue can be kept open to track better support of partial using a plugin.

@Juanlu001

This comment has been minimized.

Copy link

commented Nov 21, 2017

Not sure if this has been fixed entirely or perhaps it's a different issue:

(py34) juanlu@centauri /tmp $ cat test.py 
from functools import reduce, partial

def sum_two(a, b, c=0):
  return a + b + c

f_0 = partial(sum_two, c=0)
print(reduce(f_0, [1, 2, 3], 10))
(py34) juanlu@centauri /tmp $ mypy --version
mypy 0.550
(py34) juanlu@centauri /tmp $ mypy --ignore-missing-imports --check-untyped-defs test.py 
test.py:7: error: No overload variant of "reduce" matches argument types [functools.partial[Any], builtins.list[builtins.int*], builtins.int]

bhtucker added a commit to harrystech/arthur-redshift-etl that referenced this issue Jan 8, 2018

@nicktimko

This comment has been minimized.

Copy link

commented Apr 18, 2018

I think this crops up also when trying to use a partial for a defaultdict factory:

from collections import defaultdict
from functools import partial
from typing import cast, Callable, Mapping

# error: No overload variant of "defaultdict" matches argument types [functools.partial[builtins.int*]]
x: Mapping[str, int] = defaultdict(partial(int, 10))

# (ok, but feels like it shouldn't be needed)
y: Mapping[str, int] = defaultdict(cast(Callable, partial(int, 10)))
@JukkaL

This comment has been minimized.

Copy link
Collaborator

commented May 14, 2019

python/typeshed#2878 changed how partial works. Now mypy can often preserve the argument types, but it loses keyword argument names. It may still be worth it to special case partial using a plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.