-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Fix lambda contextual typing with keyword-only callables #11609
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15264,6 +15264,7 @@ export function createTypeEvaluator( | |
| // more sophisticated in the future, but it becomes very complex to handle | ||
| // all of the permutations. | ||
| let sawParamMismatch = false; | ||
| let sawLambdaArgsParam = false; | ||
|
|
||
| node.d.params.forEach((param, index) => { | ||
| let paramType: Type | undefined; | ||
|
|
@@ -15277,7 +15278,8 @@ export function createTypeEvaluator( | |
| // from the expected parameter. | ||
| if ( | ||
| expectedParam.param.category === param.d.category && | ||
| !param.d.name === !expectedParam.param.name | ||
| !param.d.name === !expectedParam.param.name && | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This guard also prevents contextual typing for a compatible single callable such as [verified] |
||
| (expectedParam.kind !== ParamKind.Keyword || sawLambdaArgsParam) | ||
| ) { | ||
| paramType = expectedParam.type; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Once |
||
| } else { | ||
|
|
@@ -15348,6 +15350,10 @@ export function createTypeEvaluator( | |
| ); | ||
|
|
||
| FunctionType.addParam(functionType, functionParam); | ||
|
|
||
| if (param.d.category === ParamCategory.ArgsList) { | ||
| sawLambdaArgsParam = true; | ||
| } | ||
| }); | ||
|
|
||
| if (paramsArePositionOnly && functionType.shared.parameters.length > 0) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| # This sample tests the case where a lambda is assigned to | ||
| # a union type that contains multiple callables. | ||
|
|
||
| from typing import Callable, Protocol, TypeVar | ||
| from typing import Callable, Generic, Protocol, Self, TypeVar | ||
|
|
||
|
|
||
| U1 = Callable[[int, str], bool] | Callable[[str], bool] | ||
|
|
@@ -76,3 +76,38 @@ def accepts_u2(cb: U2) -> U2: | |
| def accepts_u3(u: U3): | ||
| # This should generate an error. | ||
| u(lambda v: v.lower()) | ||
|
|
||
|
|
||
| class KeywordOnlyCallable: | ||
| def __call__(self, *, kwarg: int) -> Self: ... | ||
|
|
||
|
|
||
| keyword_only_union: Callable[[KeywordOnlyCallable], KeywordOnlyCallable] | KeywordOnlyCallable = lambda x: x | ||
|
|
||
|
|
||
| class GenericKeywordOnlyCallable(Generic[T]): | ||
| def __call__(self, *, kwarg: T) -> Self: ... | ||
|
|
||
|
|
||
| generic_keyword_only_union: ( | ||
| Callable[[GenericKeywordOnlyCallable[int]], GenericKeywordOnlyCallable[int]] | GenericKeywordOnlyCallable[int] | ||
| ) = lambda x: x | ||
|
|
||
|
|
||
| class KeywordOnlyCallback(Protocol): | ||
| def __call__(self, *, value: int) -> Self: ... | ||
|
|
||
|
|
||
| protocol_keyword_only_union: Callable[[KeywordOnlyCallback], KeywordOnlyCallback] | KeywordOnlyCallback = lambda x: x | ||
|
|
||
| ordinary_callable_union: Callable[[int], int] | Callable[[str], str] = lambda x: x | ||
|
|
||
|
|
||
| class PositionalCallable: | ||
| def __call__(self, value: int) -> Self: ... | ||
|
|
||
|
|
||
| positional_callable_union: Callable[[PositionalCallable], PositionalCallable] | PositionalCallable = lambda x: x | ||
|
|
||
| # This should generate an error. | ||
| keyword_only_callback: KeywordOnlyCallback = lambda x: x | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Add a positive regression case for the enabled branch: a lambda with [verified] |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bare
*appears to consume an index even though contextual parameter details omit separators, potentially leavinglambda *, value: ...without the contextual type forvalue. Add that protocol scenario withassert_type(value, int)and, if confirmed, track lambda and contextual indexes separately.