[conformance] Handle type[T] calls where T is bounded by a class#2381
Closed
fangyi-zhou wants to merge 4 commits intofacebook:mainfrom
Closed
[conformance] Handle type[T] calls where T is bounded by a class#2381fangyi-zhou wants to merge 4 commits intofacebook:mainfrom
type[T] calls where T is bounded by a class#2381fangyi-zhou wants to merge 4 commits intofacebook:mainfrom
Conversation
When `T` is an unbounded type var, using `type[T]` as a callable should resolve to `object.__init__` (i.e. doesn't accept any parameters).
For those cases we use the class type as a call target, but we need to override the return type as the type variable. To do so, we add a new optional field on CallTarget::Class for the type variable override. If one is present, the inferred type should be the type variable, instead of the concrete class.
This comment has been minimized.
This comment has been minimized.
Contributor
Author
|
Checking the mypy_primer results, I think we're getting some false positives due spec warning and dynamic behaviours. The error in |
|
Diff from mypy_primer, showing the effect of this PR on open source code: meson (https://github.com/mesonbuild/meson)
+ ERROR mesonbuild/interpreter/interpreter.py:2304:22-25: Argument `CustomTarget | CustomTargetIndex | Executable | ExternalProgram | Jar | Program` is not assignable to parameter `exe` with type `CustomTarget | CustomTargetIndex | Executable | ExternalProgram` in function `mesonbuild.interpreter.interpreterobjects.Test.__init__` [bad-argument-type]
+ ERROR mesonbuild/interpreter/interpreter.py:2306:22-54: Argument `bool | object` is not assignable to parameter `is_parallel` with type `bool` in function `mesonbuild.interpreter.interpreterobjects.Test.__init__` [bad-argument-type]
+ ::error file=mesonbuild/interpreter/interpreter.py,line=2304,col=22,endLine=2304,endColumn=25,title=Pyrefly bad-argument-type::Argument `CustomTarget | CustomTargetIndex | Executable | ExternalProgram | Jar | Program` is not assignable to parameter `exe` with type `CustomTarget | CustomTargetIndex | Executable | ExternalProgram` in function `mesonbuild.interpreter.interpreterobjects.Test.__init__`
+ ::error file=mesonbuild/interpreter/interpreter.py,line=2306,col=22,endLine=2306,endColumn=54,title=Pyrefly bad-argument-type::Argument `bool | object` is not assignable to parameter `is_parallel` with type `bool` in function `mesonbuild.interpreter.interpreterobjects.Test.__init__`
psycopg (https://github.com/psycopg/psycopg)
+ ERROR psycopg/psycopg/rows.py:162:38-53: No matching overload found for function `zip.__new__` called with arguments: (type[zip[_T_co]], list[str] | None, Sequence[Any]) [no-matching-overload]
+ ::error file=psycopg/psycopg/rows.py,line=162,col=38,endLine=162,endColumn=53,title=Pyrefly no-matching-overload::No matching overload found for function `zip.__new__` called with arguments: (type[zip[_T_co]], list[str] | None, Sequence[Any])%0A Possible overloads:%0A (cls: type[zip[_T_co]], *, strict: bool = False) -> zip[Any]%0A (cls: type[zip[_T_co]], iter1: Iterable[_T1], /, *, strict: bool = False) -> zip[tuple[_T1]]%0A (cls: type[zip[_T_co]], iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = False) -> zip[tuple[_T1, _T2]] [closest match]%0A (cls: type[zip[_T_co]], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, strict: bool = False) -> zip[tuple[_T1, _T2, _T3]]%0A (cls: type[zip[_T_co]], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, strict: bool = False) -> zip[tuple[_T1, _T2, _T3, _T4]]%0A (cls: type[zip[_T_co]], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], /, *, strict: bool = False) -> zip[tuple[_T1, _T2, _T3, _T4, _T5]]%0A (cls: type[zip[_T_co]], iter1: Iterable[Any], iter2: Iterable[Any], iter3: Iterable[Any], iter4: Iterable[Any], iter5: Iterable[Any], iter6: Iterable[Any], /, *iterables: Iterable[Any], *, strict: bool = False) -> zip[tuple[Any, ...]]
artigraph (https://github.com/artigraph/artigraph)
+ ERROR src/arti/storage/__init__.py:221:13-27: Unpacked keyword argument `str | Any` is not assignable to parameter `_arti_fingerprint_fields_` with type `tuple[str, ...]` in function `StoragePartition.__init__` [bad-argument-type]
+ ::error file=src/arti/storage/__init__.py,line=221,col=13,endLine=221,endColumn=27,title=Pyrefly bad-argument-type::Unpacked keyword argument `str | Any` is not assignable to parameter `_arti_fingerprint_fields_` with type `tuple[str, ...]` in function `StoragePartition.__init__`
pydantic (https://github.com/pydantic/pydantic)
+ ERROR pydantic/v1/validators.py:615:30-55: No matching overload found for function `typing.NamedTuple.__init__` called with arguments: (**dict[str, Any]) [no-matching-overload]
+ ::error file=pydantic/v1/validators.py,line=615,col=30,endLine=615,endColumn=55,title=Pyrefly no-matching-overload::No matching overload found for function `typing.NamedTuple.__init__` called with arguments: (**dict[str, Any])%0A Possible overloads:%0A (typename: str, fields: Iterable[tuple[str, Any]], /) -> None%0A (typename: str, fields: None = None, /, **kwargs: Any) -> None [closest match]
static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/index_datetime.py:284:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:310:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:334:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:418:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:444:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:469:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:518:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:543:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ERROR static_frame/core/index_datetime.py:568:25-51: Expected a callable, got `Literal[SortStatus.from_range_step]` [not-callable]
+ ::error file=static_frame/core/index_datetime.py,line=284,col=25,endLine=284,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=310,col=25,endLine=310,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=334,col=25,endLine=334,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=418,col=25,endLine=418,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=444,col=25,endLine=444,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=469,col=25,endLine=469,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=518,col=25,endLine=518,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=543,col=25,endLine=543,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
+ ::error file=static_frame/core/index_datetime.py,line=568,col=25,endLine=568,endColumn=51,title=Pyrefly not-callable::Expected a callable, got `Literal[SortStatus.from_range_step]`
|
stroxler
approved these changes
Feb 11, 2026
Contributor
stroxler
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When types of
type[T]are called, we currently infer a callable that takes any arguments and returns the type variable T. This is a conformance gap, where we should treat such calls as onT. However, the spec made clear that this is not free of problems:From https://typing.python.org/en/latest/spec/constructors.html#constructor-calls-for-type-t
If
Tis unrestricted, we assume an implicit bound ofobject, and use the callable type() -> T.If
Tis bounded, we use the bound class type to infer the callable signature, but use the typeTas the return type.We cannot handle constrained type variables, since we don't have any intersection type support at the call target level, so I'm leaving the current behaviour as is.
Let's assess the impact on open source codebases, since I expect this change will bring some false positives.
#2369
Test Plan
Ran conformance test suite and fixed 4 failing tests.