8000 Callable protocol does not infer generic parameters · Issue #15827 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Callable protocol does not infer generic parameters #15827

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

Closed
devsnek opened this issue Aug 8, 2023 · 5 comments
Closed

Callable protocol does not infer generic parameters #15827

devsnek opened this issue Aug 8, 2023 · 5 comments
Labels
bug mypy got something wrong

Comments

@devsnek
Copy link
devsnek commented Aug 8, 2023

Bug Report

Callable protocols don't seem to be working quite right with generics?

To Reproduce

import functools
from typing import Callable, TypeVar, Protocol, ParamSpec

T = TypeVar('T', covariant=True)
P = ParamSpec('P')

class CallableWithFoo(Protocol[P, T]):
    def __call__(self, *args: P.args, foo: int, **kwargs: P.kwargs) -> T: ...

def decorator(func: CallableWithFoo[P, T]) -> Callable[P, T]:
    @functools.wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        return func(*args, foo=1, **kwargs)
    return wrapper

@decorator
def bar(foo: int) -> None:
    return None
    
reveal_type(bar)

Expected Behavior

I feel like this should work in theory?

Actual Behavior

main.py:15: error: Argument 1 to "decorator" has incompatible type "Callable[[int], None]"; expected "CallableWithFoo[<nothing>, <nothing>]"  [arg-type]
main.py:19: note: Revealed type is "def (*<nothing>, **<nothing>) -> <nothing>"
Found 1 error in 1 file (checked 1 source file)

Your Environment

https://mypy-play.net/?mypy=latest&python=3.11

  • Mypy version used: 1.4.1
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.11
@devsnek devsnek added the bug mypy got something wrong label Aug 8, 2023
@erictraut
Copy link

PEP 612 (which introduced ParamSpecs) says that it is not legal to include a keyword argument between *args: P.args and **kwargs: P.kwargs as you're doing above. Mypy should emit an error here but does not. This bug has been reported here.

If you're interested in the reason why this limitation was required in PEP 612, please refer to Khttps://peps.python.org/pep-0612/#id2. Search for the text "Note that this also why we have to reject signatures of the form".

If you remove the illegal foo: int parameter from the CallableWithFoo.__call__ method, the other error goes away, but you will see another error correctly reported within the implementation of the decorator function.

@devsnek
Copy link
Author
devsnek commented Aug 8, 2023

Ah i see, thanks for clarifying. Is there any current way to correctly type this code in that case?

@erictraut
Copy link

There's unfortunately no way to correctly type this code if you want foo to be a keyword-only parameter.

If you're willing to make foo a positional + keyword parameter, you can place it before the *args: P.args parameter in the callback protocol definition. This should type check without errors (and does in pyright), but mypy emits different errors in this case because of various other bugs in its handling of ParamSpec.

@christianbundy
Copy link
Contributor

This should type check without errors (and does in pyright), but mypy emits different errors in this case because of various other bugs in its handling of ParamSpec.

@erictraut Is there an issue for this bug?

@erictraut
Copy link

I don't know if this is covered by an existing bug. You could lo 5ED9 ok through the open bugs with the "topic-paramspec" tag to see if any of them look like they cover the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants
0