Skip to content

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #289

Expanded key‑existence narrowing to dict‑like types so "key" in mapping now makes mapping.get("key") return the value type (not None)

Test Plan

Added a regression test for the dict case. This matches the expected behavior described in issue 289.

Copilot AI review requested due to automatic review settings February 1, 2026 13:15
@meta-cla meta-cla bot added the cla signed label Feb 1, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes issue #289 where pyrefly didn't track mapping keys after explicit in checks. Previously, narrowing only worked for TypedDict types. After this change, if "key" in mapping: now properly narrows the type so that mapping.get("key") returns the value type instead of value_type | None.

Changes:

  • Extended key-existence narrowing from TypedDict-only to all dict-like types (dict and its subtypes)
  • Added regression test for dict narrowing with in checks

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
pyrefly/lib/alt/narrow.rs Replaced TypedDict-specific check with is_dict_like() method call for both HasKey and NotHasKey narrowing operations
pyrefly/lib/test/subscript_narrow.rs Added test case verifying that in checks properly narrow dict types for both .get() and subscript access

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link

github-actions bot commented Feb 1, 2026

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

urllib3 (https://github.com/urllib3/urllib3)
+ ERROR src/urllib3/poolmanager.py:483:27-45: Object of class `Mapping` has no attribute `copy` [missing-attribute]
+ ::error file=src/urllib3/poolmanager.py,line=483,col=27,endLine=483,endColumn=45,title=Pyrefly missing-attribute::Object of class `Mapping` has no attribute `copy`

cloud-init (https://github.com/canonical/cloud-init)
+ ERROR cloudinit/config/cc_growpart.py:563:35-39: Argument `bool | list[str] | str | Unknown` is not assignable to parameter `mode` with type `str` in function `resizer_factory` [bad-argument-type]
+ ERROR cloudinit/sources/DataSourceVMware.py:358:9-23: Class member `DataSourceVMware.network_config` overrides parent class `DataSource` in an inconsistent manner [bad-override]
+ ::error file=cloudinit/config/cc_growpart.py,line=563,col=35,endLine=563,endColumn=39,title=Pyrefly bad-argument-type::Argument `bool | list[str] | str | Unknown` is not assignable to parameter `mode` with type `str` in function `resizer_factory`
+ ::error file=cloudinit/sources/DataSourceVMware.py,line=358,col=9,endLine=358,endColumn=23,title=Pyrefly bad-override::Class member `DataSourceVMware.network_config` overrides parent class `DataSource` in an inconsistent manner%0A  Property getter for `DataSourceVMware.network_config` has type `BoundMethod[DataSourceVMware, (self: DataSourceVMware) -> dict[Unknown, Unknown] | Unknown | None]`, which is not assignable to `BoundMethod[DataSourceVMware, (self: DataSourceVMware) -> None]`, the property getter for `DataSource.network_config`

pwndbg (https://github.com/pwndbg/pwndbg)
- ERROR pwndbg/gdblib/vmmap.py:433:39-73: `None` is not subscriptable [unsupported-operation]
- ::error file=pwndbg/gdblib/vmmap.py,line=433,col=39,endLine=433,endColumn=73,title=Pyrefly unsupported-operation::`None` is not subscriptable

pyjwt (https://github.com/jpadilla/pyjwt)
- ERROR jwt/algorithms.py:749:34-46: Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode` [bad-argument-type]
- ERROR jwt/algorithms.py:750:34-46: Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode` [bad-argument-type]
- ERROR jwt/algorithms.py:795:34-46: Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode` [bad-argument-type]
- ERROR jwt/algorithms.py:986:34-46: Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode` [bad-argument-type]
- ERROR jwt/algorithms.py:993:38-50: Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode` [bad-argument-type]
- ::error file=jwt/algorithms.py,line=749,col=34,endLine=749,endColumn=46,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode`
- ::error file=jwt/algorithms.py,line=750,col=34,endLine=750,endColumn=46,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode`
- ::error file=jwt/algorithms.py,line=795,col=34,endLine=795,endColumn=46,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode`
- ::error file=jwt/algorithms.py,line=986,col=34,endLine=986,endColumn=46,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode`
- ::error file=jwt/algorithms.py,line=993,col=38,endLine=993,endColumn=50,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `input` with type `bytes | str` in function `jwt.utils.base64url_decode`

apprise (https://github.com/caronc/apprise)
- ERROR apprise/config/base.py:146:29-51: `object | None` is not assignable to attribute `encoding` with type `str` [bad-assignment]
+ ERROR apprise/config/base.py:146:29-51: `object` is not assignable to attribute `encoding` with type `str` [bad-assignment]
- ERROR apprise/plugins/bluesky.py:258:21-52: Cannot set item in `None` [unsupported-operation]
- ERROR apprise/plugins/bluesky.py:261:21-47: Cannot set item in `None` [unsupported-operation]
- ERROR apprise/plugins/bluesky.py:263:17-44: Cannot set item in `None` [unsupported-operation]
- ERROR apprise/plugins/bluesky.py:327:12-26: Object of class `NoneType` has no attribute `startswith` [missing-attribute]
- ERROR apprise/plugins/bluesky.py:354:14-28: Object of class `NoneType` has no attribute `startswith` [missing-attribute]
- ERROR apprise/plugins/bluesky.py:356:22-29: `None` is not subscriptable [unsupported-operation]
- ERROR apprise/url.py:252:50-67: Argument `Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__` [bad-argument-type]
- ERROR apprise/url.py:261:53-70: Argument `Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__` [bad-argument-type]
- ::error file=apprise/config/base.py,line=146,col=29,endLine=146,endColumn=51,title=Pyrefly bad-assignment::`object | None` is not assignable to attribute `encoding` with type `str`
+ ::error file=apprise/config/base.py,line=146,col=29,endLine=146,endColumn=51,title=Pyrefly bad-assignment::`object` is not assignable to attribute `encoding` with type `str`
- ::error file=apprise/plugins/bluesky.py,line=258,col=21,endLine=258,endColumn=52,title=Pyrefly unsupported-operation::Cannot set item in `None`%0A  Object of class `NoneType` has no attribute `__setitem__`
- ::error file=apprise/plugins/bluesky.py,line=261,col=21,endLine=261,endColumn=47,title=Pyrefly unsupported-operation::Cannot set item in `None`%0A  Object of class `NoneType` has no attribute `__setitem__`
- ::error file=apprise/plugins/bluesky.py,line=263,col=17,endLine=263,endColumn=44,title=Pyrefly unsupported-operation::Cannot set item in `None`%0A  Object of class `NoneType` has no attribute `__setitem__`
- ::error file=apprise/plugins/bluesky.py,line=327,col=12,endLine=327,endColumn=26,title=Pyrefly missing-attribute::Object of class `NoneType` has no attribute `startswith`
- ::error file=apprise/plugins/bluesky.py,line=354,col=14,endLine=354,endColumn=28,title=Pyrefly missing-attribute::Object of class `NoneType` has no attribute `startswith`
- ::error file=apprise/plugins/bluesky.py,line=356,col=22,endLine=356,endColumn=29,title=Pyrefly unsupported-operation::`None` is not subscriptable
- ::error file=apprise/url.py,line=252,col=50,endLine=252,endColumn=67,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__`
- ::error file=apprise/url.py,line=261,col=53,endLine=261,endColumn=70,title=Pyrefly bad-argument-type::Argument `Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsFloat | SupportsIndex | str` in function `float.__new__`

sphinx (https://github.com/sphinx-doc/sphinx)
+ ERROR sphinx/cmd/quickstart.py:248:19-28: `bool | str | Any` is not assignable to `str` [bad-assignment]
+ ERROR sphinx/cmd/quickstart.py:319:57-69: Argument `bool | str | Any` is not assignable to parameter `default` with type `str | None` in function `do_prompt` [bad-argument-type]
+ ERROR sphinx/cmd/quickstart.py:361:14-23: `bool | str | Any` is not assignable to variable `d_path` with type `str` [bad-assignment]
+ ERROR sphinx/cmd/quickstart.py:362:21-32: `bool | str | Any` is not assignable to `str` [bad-assignment]
+ ERROR sphinx/cmd/quickstart.py:363:21-32: `bool | str | Any` is not assignable to `str` [bad-assignment]
+ ERROR sphinx/cmd/quickstart.py:385:13-24: Argument `bool | str | Any` is not assignable to parameter `default` with type `str | None` in function `do_prompt` [bad-argument-type]
+ ::error file=sphinx/cmd/quickstart.py,line=248,col=19,endLine=248,endColumn=28,title=Pyrefly bad-assignment::`bool | str | Any` is not assignable to `str`
+ ::error file=sphinx/cmd/quickstart.py,line=319,col=57,endLine=319,endColumn=69,title=Pyrefly bad-argument-type::Argument `bool | str | Any` is not assignable to parameter `default` with type `str | None` in function `do_prompt`
+ ::error file=sphinx/cmd/quickstart.py,line=361,col=14,endLine=361,endColumn=23,title=Pyrefly bad-assignment::`bool | str | Any` is not assignable to variable `d_path` with type `str`
+ ::error file=sphinx/cmd/quickstart.py,line=362,col=21,endLine=362,endColumn=32,title=Pyrefly bad-assignment::`bool | str | Any` is not assignable to `str`
+ ::error file=sphinx/cmd/quickstart.py,line=363,col=21,endLine=363,endColumn=32,title=Pyrefly bad-assignment::`bool | str | Any` is not assignable to `str`
+ ::error file=sphinx/cmd/quickstart.py,line=385,col=13,endLine=385,endColumn=24,title=Pyrefly bad-argument-type::Argument `bool | str | Any` is not assignable to parameter `default` with type `str | None` in function `do_prompt`

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.

pyrefly does not track mapping keys after explicit check

1 participant