Skip to content

Conversation

@tannguyencse19
Copy link
Contributor

Summary

Update negative isinstance narrowing to drop a union member when it is a runtime instance of the checked class:

  • For Type::ClassType, use class hierarchy checks (has_superclass) on the underlying class objects.
  • For Type::TypedDict and Type::PartialTypedDict, treat them as dict at runtime.
  • Fall back to the old is_subset_eq check as an additional removal condition.

Fixes #1616

Test Plan

cargo test -p pyrefly -- test_dict_literal_key_isinstance_narrowing

@meta-cla meta-cla bot added the cla signed label Jan 3, 2026
@github-actions
Copy link

github-actions bot commented Jan 6, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

Tanjun (https://github.com/FasterSpeeding/Tanjun)
- ERROR tanjun/annotations.py:1521:33-52: No matching overload found for function `dict.__init__` called with arguments: (Mapping[str, _ChoiceT] | Sequence[_ChoiceT], **dict[str, _ChoiceT]) [no-matching-overload]
- ::error file=tanjun/annotations.py,line=1521,col=33,endLine=1521,endColumn=52,title=Pyrefly no-matching-overload::No matching overload found for function `dict.__init__` called with arguments: (Mapping[str, _ChoiceT] | Sequence[_ChoiceT], **dict[str, _ChoiceT])%0A  Possible overloads:%0A  () -> None%0A  (**kwargs: _ChoiceUnion) -> None%0A  (map: SupportsKeysAndGetItem[str, _ChoiceUnion], /) -> None [closest match]%0A  (map: SupportsKeysAndGetItem[str, _ChoiceUnion], /, **kwargs: _ChoiceUnion) -> None%0A  (iterable: Iterable[tuple[str, _ChoiceUnion]], /) -> None%0A  (iterable: Iterable[tuple[str, _ChoiceUnion]], /, **kwargs: _ChoiceUnion) -> None%0A  (iterable: Iterable[list[str]], /) -> None%0A  (iterable: Iterable[list[bytes]], /) -> None

tornado (https://github.com/tornadoweb/tornado)
- ERROR tornado/web.py:3577:42-48: Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v1` [bad-argument-type]
- ERROR tornado/web.py:3616:42-48: Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v2` [bad-argument-type]
- ERROR tornado/web.py:3674:40-46: Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_decode_signed_value_v1` [bad-argument-type]
- ERROR tornado/web.py:3761:41-47: Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v2` [bad-argument-type]
- ::error file=tornado/web.py,line=3577,col=42,endLine=3577,endColumn=48,title=Pyrefly bad-argument-type::Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v1`
- ::error file=tornado/web.py,line=3616,col=42,endLine=3616,endColumn=48,title=Pyrefly bad-argument-type::Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v2`
- ::error file=tornado/web.py,line=3674,col=40,endLine=3674,endColumn=46,title=Pyrefly bad-argument-type::Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_decode_signed_value_v1`
- ::error file=tornado/web.py,line=3761,col=41,endLine=3761,endColumn=47,title=Pyrefly bad-argument-type::Argument `bytes | dict[int, str] | str` is not assignable to parameter `secret` with type `bytes | str` in function `_create_signature_v2`

PyGithub (https://github.com/PyGithub/PyGithub)
- ERROR github/AdvisoryVulnerability.py:170:30-51: Object of class `SimpleAdvisoryVulnerability` has no attribute `package` [missing-attribute]
- ERROR github/AdvisoryVulnerability.py:171:25-46: Object of class `SimpleAdvisoryVulnerability` has no attribute `package` [missing-attribute]
- ERROR github/AdvisoryVulnerability.py:173:33-63: Object of class `SimpleAdvisoryVulnerability` has no attribute `patched_versions` [missing-attribute]
- ERROR github/AdvisoryVulnerability.py:174:37-71: Object of class `SimpleAdvisoryVulnerability` has no attribute `vulnerable_functions` [missing-attribute]
- ERROR github/AdvisoryVulnerability.py:175:41-79: Object of class `SimpleAdvisoryVulnerability` has no attribute `vulnerable_version_range` [missing-attribute]
- ::error file=github/AdvisoryVulnerability.py,line=170,col=30,endLine=170,endColumn=51,title=Pyrefly missing-attribute::Object of class `SimpleAdvisoryVulnerability` has no attribute `package`%0A  Did you mean `_package`?
- ::error file=github/AdvisoryVulnerability.py,line=171,col=25,endLine=171,endColumn=46,title=Pyrefly missing-attribute::Object of class `SimpleAdvisoryVulnerability` has no attribute `package`%0A  Did you mean `_package`?
- ::error file=github/AdvisoryVulnerability.py,line=173,col=33,endLine=173,endColumn=63,title=Pyrefly missing-attribute::Object of class `SimpleAdvisoryVulnerability` has no attribute `patched_versions`%0A  Did you mean `_patched_versions`?
- ::error file=github/AdvisoryVulnerability.py,line=174,col=37,endLine=174,endColumn=71,title=Pyrefly missing-attribute::Object of class `SimpleAdvisoryVulnerability` has no attribute `vulnerable_functions`%0A  Did you mean `_vulnerable_functions`?
- ::error file=github/AdvisoryVulnerability.py,line=175,col=41,endLine=175,endColumn=79,title=Pyrefly missing-attribute::Object of class `SimpleAdvisoryVulnerability` has no attribute `vulnerable_version_range`%0A  Did you mean `_vulnerable_version_range`?

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/server/orchestration/core_policy.py:1131:17-27: Argument `float | int | list[int]` is not assignable to parameter `average_interval` with type `float` in function `prefect.utilities.math.clamped_poisson_interval` [bad-argument-type]
- ERROR src/prefect/server/orchestration/core_policy.py:1142:72-77: Argument `float | int | list[int]` is not assignable to parameter `seconds` with type `float` in function `datetime.timedelta.__new__` [bad-argument-type]
- ::error file=src/prefect/server/orchestration/core_policy.py,line=1131,col=17,endLine=1131,endColumn=27,title=Pyrefly bad-argument-type::Argument `float | int | list[int]` is not assignable to parameter `average_interval` with type `float` in function `prefect.utilities.math.clamped_poisson_interval`
- ::error file=src/prefect/server/orchestration/core_policy.py,line=1142,col=72,endLine=1142,endColumn=77,title=Pyrefly bad-argument-type::Argument `float | int | list[int]` is not assignable to parameter `seconds` with type `float` in function `datetime.timedelta.__new__`

pydantic (https://github.com/pydantic/pydantic)
- ERROR pydantic/json_schema.py:1547:26-31: Argument `list[list[int | str]] | Any` is not assignable to parameter `arg` with type `Never` in function `typing.assert_never` [bad-argument-type]
- ::error file=pydantic/json_schema.py,line=1547,col=26,endLine=1547,endColumn=31,title=Pyrefly bad-argument-type::Argument `list[list[int | str]] | Any` is not assignable to parameter `arg` with type `Never` in function `typing.assert_never`

pandas (https://github.com/pandas-dev/pandas)
- ERROR pandas/io/excel/_base.py:739:22-34: `list[int | list[str]]` is not assignable to variable `sheets` with type `list[int] | list[str]` [bad-assignment]
- ::error file=pandas/io/excel/_base.py,line=739,col=22,endLine=739,endColumn=34,title=Pyrefly bad-assignment::`list[int | list[str]]` is not assignable to variable `sheets` with type `list[int] | list[str]`

freqtrade (https://github.com/freqtrade/freqtrade)
- ERROR freqtrade/optimize/optimize_reports/optimize_reports.py:208:9-12: `in` is not supported between `list[str]` and `Index[str]` [unsupported-operation]
- ::error file=freqtrade/optimize/optimize_reports/optimize_reports.py,line=208,col=9,endLine=208,endColumn=12,title=Pyrefly unsupported-operation::`in` is not supported between `list[str]` and `Index[str]`%0A  Argument `list[str]` is not assignable to parameter `key` with type `Hashable` in function `pandas.core.indexes.base.Index.__contains__`%0A  `list[str].__hash__` is read-only, but `Hashable.__hash__` is read-write

anyio (https://github.com/agronholm/anyio)
- ERROR src/anyio/_backends/_asyncio.py:2597:17-25: Type `PathLike[str]` is not iterable [not-iterable]
- ERROR src/anyio/_backends/_trio.py:1107:49-56: Type `PathLike[str]` is not iterable [not-iterable]
+ ERROR src/anyio/functools.py:352:31-39: Runtime checkable protocol `Iterable` has an unsafe overlap with type `Iterable[S] | Iterable[T]` [unsafe-overlap]
- ::error file=src/anyio/_backends/_asyncio.py,line=2597,col=17,endLine=2597,endColumn=25,title=Pyrefly not-iterable::Type `PathLike[str]` is not iterable
- ::error file=src/anyio/_backends/_trio.py,line=1107,col=49,endLine=1107,endColumn=56,title=Pyrefly not-iterable::Type `PathLike[str]` is not iterable
+ ::error file=src/anyio/functools.py,line=352,col=31,endLine=352,endColumn=39,title=Pyrefly unsafe-overlap::Runtime checkable protocol `Iterable` has an unsafe overlap with type `Iterable[S] | Iterable[T]`%0A  Attribute `__iter__` has incompatible types: `Iterable[S] | Iterable[T].__iter__` has type `BoundMethod[Iterable[T], (self: Iterable[T]) -> Iterator[T]]`, which is not assignable to `BoundMethod[Iterable[S] | Iterable[T], (self: Iterable[S] | Iterable[T]) -> Iterator[@_]]`, the type of `Iterable.__iter__`

antidote (https://github.com/Finistere/antidote)
- ERROR src/antidote/lib/interface_ext/_interface.py:540:36-45: Argument `(ImplementationWeight & Predicate[Weight]) | Weight` is not assignable to parameter `object` with type `Weight` in function `list.append` [bad-argument-type]
+ ERROR src/antidote/lib/interface_ext/_interface.py:538:40-60: Runtime checkable protocol `ImplementationWeight` has an unsafe overlap with type `NeutralWeight | Weight` [unsafe-overlap]
- ::error file=src/antidote/lib/interface_ext/_interface.py,line=540,col=36,endLine=540,endColumn=45,title=Pyrefly bad-argument-type::Argument `(ImplementationWeight & Predicate[Weight]) | Weight` is not assignable to parameter `object` with type `Weight` in function `list.append`
+ ::error file=src/antidote/lib/interface_ext/_interface.py,line=538,col=40,endLine=538,endColumn=60,title=Pyrefly unsafe-overlap::Runtime checkable protocol `ImplementationWeight` has an unsafe overlap with type `NeutralWeight | Weight`%0A  Attribute `__lt__` has incompatible types: `NeutralWeight | Weight.__lt__` has type `BoundMethod[NeutralWeight, (self: NeutralWeight, other: NeutralWeight) -> bool]`, which is not assignable to `BoundMethod[NeutralWeight | Weight, [SelfWeight](self: SelfWeight, other: SelfWeight) -> bool]`, the type of `ImplementationWeight.__lt__`%0A  Attribute `__add__` has incompatible types: `NeutralWeight | Weight.__add__` has type `BoundMethod[NeutralWeight, (self: NeutralWeight, other: NeutralWeight) -> NeutralWeight]`, which is not assignable to `BoundMethod[NeutralWeight | Weight, [SelfWeight](self: SelfWeight, other: SelfWeight) -> SelfWeight]`, the type of `ImplementationWeight.__add__`

meson (https://github.com/mesonbuild/meson)
- ERROR mesonbuild/interpreter/type_checking.py:255:12-17: Returned type `EnvironmentVariables | list[str]` is not assignable to declared return type `EnvironmentVariables` [bad-return]
- ::error file=mesonbuild/interpreter/type_checking.py,line=255,col=12,endLine=255,endColumn=17,title=Pyrefly bad-return::Returned type `EnvironmentVariables | list[str]` is not assignable to declared return type `EnvironmentVariables`

@kinto0 kinto0 requested a review from stroxler January 7, 2026 16:49
Copy link
Contributor

@stroxler stroxler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable to me, thanks for the PR! I'll try to get a second review and get it merged next week

@meta-codesync
Copy link

meta-codesync bot commented Jan 10, 2026

@stroxler has imported this pull request. If you are a Meta employee, you can view this in D90434164.

@tannguyencse19
Copy link
Contributor Author

Looks reasonable to me, thanks for the PR! I'll try to get a second review and get it merged next week

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type narrowing not working with dict with literal keys

3 participants