impl type guard for Ellipsis #650#2253
impl type guard for Ellipsis #650#2253asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
Conversation
|
Diff from mypy_primer, showing the effect of this PR on open source code: urllib3 (https://github.com/urllib3/urllib3)
- ERROR src/urllib3/util/timeout.py:128:16-79: Returned type `_TYPE_DEFAULT | float | None` is not assignable to declared return type `float | None` [bad-return]
- ERROR src/urllib3/util/timeout.py:150:19-24: Argument `_TYPE_DEFAULT | float` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__` [bad-argument-type]
- ERROR src/urllib3/util/timeout.py:158:16-26: `<=` is not supported between `_TYPE_DEFAULT` and `Literal[0]` [unsupported-operation]
- ERROR src/urllib3/util/timeout.py:270:24-34: Returned type `_TYPE_DEFAULT | float` is not assignable to declared return type `float | None` [bad-return]
- ERROR src/urllib3/util/timeout.py:271:30-84: No matching overload found for function `min` called with arguments: (float, _TYPE_DEFAULT | float) [no-matching-overload]
+ ERROR src/urllib3/util/timeout.py:271:23-85: No matching overload found for function `max` called with arguments: (Literal[0], float) [no-matching-overload]
- ERROR src/urllib3/util/timeout.py:271:31-71: `-` is not supported between `_TYPE_DEFAULT` and `float` [unsupported-operation]
- ERROR src/urllib3/util/timeout.py:273:27-67: `-` is not supported between `_TYPE_DEFAULT` and `float` [unsupported-operation]
- ::error file=src/urllib3/util/timeout.py,line=128,col=16,endLine=128,endColumn=79,title=Pyrefly bad-return::Returned type `_TYPE_DEFAULT | float | None` is not assignable to declared return type `float | None`
- ::error file=src/urllib3/util/timeout.py,line=150,col=19,endLine=150,endColumn=24,title=Pyrefly bad-argument-type::Argument `_TYPE_DEFAULT | float` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__`
- ::error file=src/urllib3/util/timeout.py,line=158,col=16,endLine=158,endColumn=26,title=Pyrefly unsupported-operation::`<=` is not supported between `_TYPE_DEFAULT` and `Literal[0]`%0A Argument `_TYPE_DEFAULT` is not assignable to parameter `value` with type `int` in function `int.__ge__`
- ::error file=src/urllib3/util/timeout.py,line=270,col=24,endLine=270,endColumn=34,title=Pyrefly bad-return::Returned type `_TYPE_DEFAULT | float` is not assignable to declared return type `float | None`
- ::error file=src/urllib3/util/timeout.py,line=271,col=30,endLine=271,endColumn=84,title=Pyrefly no-matching-overload::No matching overload found for function `min` called with arguments: (float, _TYPE_DEFAULT | float)%0A Possible overloads:%0A (arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, *, key: None = None) -> SupportsRichComparisonT [closest match]%0A (arg1: _T, arg2: _T, /, *_args: _T, *, key: (_T) -> SupportsRichComparison) -> _T%0A (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT%0A (iterable: Iterable[_T], /, *, key: (_T) -> SupportsRichComparison) -> _T%0A (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T%0A (iterable: Iterable[_T1], /, *, key: (_T1) -> SupportsRichComparison, default: _T2) -> _T1 | _T2
+ ::error file=src/urllib3/util/timeout.py,line=271,col=23,endLine=271,endColumn=85,title=Pyrefly no-matching-overload::No matching overload found for function `max` called with arguments: (Literal[0], float)%0A Possible overloads:%0A (arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, *, key: None = None) -> SupportsRichComparisonT [closest match]%0A (arg1: _T, arg2: _T, /, *_args: _T, *, key: (_T) -> SupportsRichComparison) -> _T%0A (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT%0A (iterable: Iterable[_T], /, *, key: (_T) -> SupportsRichComparison) -> _T%0A (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T%0A (iterable: Iterable[_T1], /, *, key: (_T1) -> SupportsRichComparison, default: _T2) -> _T1 | _T2
- ::error file=src/urllib3/util/timeout.py,line=271,col=31,endLine=271,endColumn=71,title=Pyrefly unsupported-operation::`-` is not supported between `_TYPE_DEFAULT` and `float`%0A Argument `_TYPE_DEFAULT` is not assignable to parameter `value` with type `float` in function `float.__rsub__`
- ::error file=src/urllib3/util/timeout.py,line=273,col=27,endLine=273,endColumn=67,title=Pyrefly unsupported-operation::`-` is not supported between `_TYPE_DEFAULT` and `float`%0A Argument `_TYPE_DEFAULT` is not assignable to parameter `value` with type `float` in function `float.__rsub__`
xarray (https://github.com/pydata/xarray)
- ERROR xarray/core/dataset.py:8274:23-28: No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType) [no-matching-overload]
- ERROR xarray/core/groupby.py:1107:32-35: Argument `Collection[Hashable] | EllipsisType` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__` [bad-argument-type]
- ERROR xarray/core/utils.py:1025:24-29: No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType | tuple[str]) [no-matching-overload]
- ERROR xarray/core/utils.py:1026:18-21: Argument `Collection[Hashable] | EllipsisType | tuple[str]` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__` [bad-argument-type]
- ERROR xarray/core/utils.py:1064:14-19: No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType | set[Hashable]) [no-matching-overload]
- ERROR xarray/core/utils.py:1122:76-86: `in` is not supported between `Ellipsis` and `EllipsisType` [not-iterable]
- ERROR xarray/core/utils.py:1123:53-58: No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType) [no-matching-overload]
- ERROR xarray/core/utils.py:1129:22-25: Argument `Collection[Hashable] | EllipsisType` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__` [bad-argument-type]
- ERROR xarray/namedarray/core.py:916:28-35: `object` is not assignable to variable `axis` with type `Sequence[int] | int | None` [bad-assignment]
+ ERROR xarray/namedarray/core.py:916:28-35: `int | object` is not assignable to variable `axis` with type `Sequence[int] | int | None` [bad-assignment]
- ::error file=xarray/core/dataset.py,line=8274,col=23,endLine=8274,endColumn=28,title=Pyrefly no-matching-overload::No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType)%0A Possible overloads:%0A () -> None%0A (iterable: Iterable[Hashable], /) -> None [closest match]
- ::error file=xarray/core/groupby.py,line=1107,col=32,endLine=1107,endColumn=35,title=Pyrefly bad-argument-type::Argument `Collection[Hashable] | EllipsisType` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__`%0A Protocol `Iterable` requires attribute `__iter__`
- ::error file=xarray/core/utils.py,line=1025,col=24,endLine=1025,endColumn=29,title=Pyrefly no-matching-overload::No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType | tuple[str])%0A Possible overloads:%0A () -> None%0A (iterable: Iterable[Hashable], /) -> None [closest match]
- ::error file=xarray/core/utils.py,line=1026,col=18,endLine=1026,endColumn=21,title=Pyrefly bad-argument-type::Argument `Collection[Hashable] | EllipsisType | tuple[str]` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__`%0A Protocol `Iterable` requires attribute `__iter__`
- ::error file=xarray/core/utils.py,line=1064,col=14,endLine=1064,endColumn=19,title=Pyrefly no-matching-overload::No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType | set[Hashable])%0A Possible overloads:%0A () -> None%0A (iterable: Iterable[Hashable], /) -> None [closest match]
- ::error file=xarray/core/utils.py,line=1122,col=76,endLine=1122,endColumn=86,title=Pyrefly not-iterable::`in` is not supported between `Ellipsis` and `EllipsisType`
- ::error file=xarray/core/utils.py,line=1123,col=53,endLine=1123,endColumn=58,title=Pyrefly no-matching-overload::No matching overload found for function `set.__init__` called with arguments: (Collection[Hashable] | EllipsisType)%0A Possible overloads:%0A () -> None%0A (iterable: Iterable[EllipsisType | Hashable], /) -> None [closest match]
- ::error file=xarray/core/utils.py,line=1129,col=22,endLine=1129,endColumn=25,title=Pyrefly bad-argument-type::Argument `Collection[Hashable] | EllipsisType` is not assignable to parameter `iterable` with type `Iterable[Hashable]` in function `tuple.__new__`%0A Protocol `Iterable` requires attribute `__iter__`
- ::error file=xarray/namedarray/core.py,line=916,col=28,endLine=916,endColumn=35,title=Pyrefly bad-assignment::`object` is not assignable to variable `axis` with type `Sequence[int] | int | None`
+ ::error file=xarray/namedarray/core.py,line=916,col=28,endLine=916,endColumn=35,title=Pyrefly bad-assignment::`int | object` is not assignable to variable `axis` with type `Sequence[int] | int | None`
sphinx (https://github.com/sphinx-doc/sphinx)
- ERROR sphinx/theming.py:542:65-81: Object of class `EllipsisType` has no attribute `split` [missing-attribute]
- ERROR sphinx/theming.py:550:62-75: Object of class `EllipsisType` has no attribute `split` [missing-attribute]
+ ERROR sphinx/theming.py:542:53-87: No matching overload found for function `map.__new__` called with arguments: (type[map[_S]], Overload[
+ (self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString
+ (self: str, chars: str | None = None, /) -> str
+ ], list[str] | Unknown) [no-matching-overload]
+ ERROR sphinx/theming.py:550:50-81: No matching overload found for function `map.__new__` called with arguments: (type[map[_S]], Overload[
+ (self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString
+ (self: str, chars: str | None = None, /) -> str
+ ], list[str] | Unknown) [no-matching-overload]
- ::error file=sphinx/theming.py,line=542,col=65,endLine=542,endColumn=81,title=Pyrefly missing-attribute::Object of class `EllipsisType` has no attribute `split`
- ::error file=sphinx/theming.py,line=550,col=62,endLine=550,endColumn=75,title=Pyrefly missing-attribute::Object of class `EllipsisType` has no attribute `split`
+ ::error file=sphinx/theming.py,line=542,col=53,endLine=542,endColumn=87,title=Pyrefly no-matching-overload::No matching overload found for function `map.__new__` called with arguments: (type[map[_S]], Overload[%0A (self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString%0A (self: str, chars: str | None = None, /) -> str%0A], list[str] | Unknown)%0A Possible overloads:%0A (cls: type[map[_S]], func: (_T1) -> _S, iterable: Iterable[_T1], /) -> map[_S] [closest match]%0A (cls: type[map[_S]], func: (_T1, _T2) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3, _T4) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3, _T4, _T5) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], /) -> map[_S]%0A (cls: type[map[_S]], func: (...) -> _S, iterable: Iterable[Any], iter2: Iterable[Any], iter3: Iterable[Any], iter4: Iterable[Any], iter5: Iterable[Any], iter6: Iterable[Any], /, *iterables: Iterable[Any]) -> map[_S]
+ ::error file=sphinx/theming.py,line=550,col=50,endLine=550,endColumn=81,title=Pyrefly no-matching-overload::No matching overload found for function `map.__new__` called with arguments: (type[map[_S]], Overload[%0A (self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString%0A (self: str, chars: str | None = None, /) -> str%0A], list[str] | Unknown)%0A Possible overloads:%0A (cls: type[map[_S]], func: (_T1) -> _S, iterable: Iterable[_T1], /) -> map[_S] [closest match]%0A (cls: type[map[_S]], func: (_T1, _T2) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3, _T4) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /) -> map[_S]%0A (cls: type[map[_S]], func: (_T1, _T2, _T3, _T4, _T5) -> _S, iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], /) -> map[_S]%0A (cls: type[map[_S]], func: (...) -> _S, iterable: Iterable[Any], iter2: Iterable[Any], iter3: Iterable[Any], iter4: Iterable[Any], iter5: Iterable[Any], iter6: Iterable[Any], /, *iterables: Iterable[Any]) -> map[_S]
|
There was a problem hiding this comment.
Pull request overview
This PR implements type guard narrowing for ellipsis literals (...) to support identity and equality checks as specified in issue #650. The implementation treats ... as a singleton and handles both the Ellipsis type and EllipsisType class for comprehensive narrowing support.
Changes:
- Added ellipsis narrowing logic for
is,is not,==, and!=operators - Extended narrowing to handle
EllipsisTypeclass in negative narrowing scenarios - Added test cases for ellipsis identity and equality checks
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pyrefly/lib/alt/narrow.rs | Core narrowing logic for ellipsis guards, including is_ellipsis_class_type helper and updates to all four comparison operators |
| pyrefly/lib/test/narrow.rs | Added test cases for is ... and == ... / != ... narrowing |
| crates/pyrefly_types/src/literal.rs | Added LitSentinel struct (from stacked PR #2252) |
| crates/pyrefly_types/src/display.rs | Display formatting for Lit::Sentinel |
| crates/pyrefly_types/src/type_output.rs | Output handling for Lit::Sentinel |
| Cargo.lock | Checksum update for backtrace crate (automated) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| testcase!( | ||
| test_ellipsis_is, | ||
| r#" | ||
| from types import EllipsisType | ||
| def f(x: object): | ||
| if x is ...: | ||
| y: EllipsisType = x | ||
| "#, | ||
| ); | ||
|
|
||
| testcase!( | ||
| test_ellipsis_eq, | ||
| r#" | ||
| from types import EllipsisType | ||
| def f(x: int | EllipsisType): | ||
| if x == ...: | ||
| y_ellipsis: EllipsisType = x | ||
| if x != ...: | ||
| y_int: int = x | ||
| "#, | ||
| ); |
There was a problem hiding this comment.
Test coverage is incomplete. The implementation supports is not ... narrowing (lines 560-595, 970-1008), but there's no test case validating this behavior. Consider adding a test case like:
def f(x: int | EllipsisType):
if x is not ...:
y_int: int = xto ensure the negative narrowing works correctly.
|
This pull request has been automatically marked as stale because it has not had recent activity for more than 2 weeks. If you are still working on this this pull request, please add a comment or push new commits to keep it active. Otherwise, please unassign yourself and allow someone else to take over. Thank you for your contributions! |
|
oops, I think I got the stacking for this commit mixed up with the other one. This is being merged as part of the sentinel change, while that sentinel change itself may not land... I'll explain more on the other PR. In any case, thanks for the work! |
Summary
Fixes #650
this is stack on #2252
Implemented ellipsis guards (is, is not, ==, !=) by treating ... as a singleton and also handling EllipsisType as its class counterpart for negative narrowing.
Test Plan
Added tests that validate narrowing via assignment to types.EllipsisType/int.