-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Generic callback protocols break with kwargs #7311
Comments
So first, in any case, the error messages around this are very unhelpful. It seems to be that the situation is that if there are kwargs, then a positional-only argument (with There is an argument for the correctness of that, which is that something make a call like There are a lot of tradeoffs in function subtyping, though, so it might be worth doing it differently? |
If juts making the first argument of |
Are you referring to the upcoming python 3.8 feature to make an argument positional-only ( |
No, I am referring to |
Oh, wow, I did not realize that you could use that notation in a callable implementation. The docs only describe this in the context of a protocol, and only for the purpose of relaxing name requirements. To wrap up, I confirmed that this works: def callback_with_kwargs(__x: str, **kwargs: Any) -> int:
pass And I assume that in python 3.8, this would work: def callback_with_kwargs(x: str, /, **kwargs: Any) -> int:
pass The name prefix is a bit ugly, and I'm not sure if the maintainers of the project that I'm annotating will agree that it's an acceptable trade-off, especially considering it exposes this ugliness to end users who would be providing the callbacks. My counter argument to this is that static type checking is opt-in, and early adopters are already aware that it takes a bit of adjustment to normal patterns to fully utilize it, plus there's a proper solution coming in 3.8. Given that there's a viable solution I agree it's not worth your time to make any adjustments, unless you think it would be very easy. Documentation would be the main area of improvement here. I'll leave this open until we figure out what to do about docs. Thanks again! |
Sorry, one more followup question. How do you make a callback protocol that accepts any number of keyword args? I thought that this would work, but it doesn't: class CallbackWithKwargs(Protocol[T_contra, T_co]):
def __call__(self, __element: T_contra, **kwargs: Any) -> T_co:
pass
def applyit_with_kwargs(func: CallbackWithKwargs[T1, T2], y: T1) -> T2:
return func(y)
def callback_with_kwargs2(__x: str, arg1=None, arg2=None) -> int:
pass
# Error : Argument 1 to "applyit_with_kwargs" has incompatible type "Callable[[str, Any], int]"; expected "CallbackWithKwargs[str, <nothing>]"
applyit_with_kwargs(callback_with_kwargs, 'foo') Is this one of those cases where I have to make a protocol for each number of args? class CallbackWithKwargs1(Protocol[T_contra, T_co]):
def __call__(self, __element: T_contra, __arg1=...) -> T_co:
pass
class CallbackWithKwargs2(Protocol[T_contra, T_co]):
def __call__(self, __element: T_contra, __arg1=..., __arg2=...) -> T_co:
pass
...
CallbackWithKwargs=Union[
CallbackWithKwargs1, CallbackWithKwargs2, ...] |
Technically yes, otherwise the override is not type-safe. We have an issue to special case |
Since the behavior is technically correct (though arguably confusing) and there is a work-around, I will close this. This can be revisited if somebody has a concrete proposal about what should be changed. |
Note that the workaround in my case is to make dozens of overloads per function with this property, so I don't think the maintainers of the project I am working with will accept it as a valid solution. I've delayed even suggesting it since we're still in the early days of adoption and I don't want them to lose confidence in type annotation based on this one wart. |
Okay, reopening since this this seems to have a significant impact. It still isn't clear what's the best way to fix this. |
I'm getting an unexpected error when using callback protocols with kwargs
Normal callback with no args or kwargs works fine:
callback with args works fine:
But the same thing with kwargs produces an error:
Perhaps this is expected behavior and I'm just overlooking some subtle interplay between the args in the last case, but it seems like a bug.
mypy 0.720
The text was updated successfully, but these errors were encountered: