microsoft / microsoft/pyright

Different narrowing with `isinstance` vs match-case against Callback-Protocol

Open
#10,714 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
15.6k
Forks
1.8k
Avg merge
12h 13m
Merged PRs (30d)
52

Description

Code sample in [pyright playground](https://pyright-play.net/?reportUnreachable=true&code=GYJw9gtgBALgngBwJYDsDmUkQWEMoDCAhgDYlEBGJApgDRQAK4MYAxmCfSAK4oxbUA%2BqwAW1VgGtKNegEEUcLtQBu1UoPgJqAKG0ABHnwHCxk6TtbkAzlajy4xMuYAUTMC3YkAlAC5tUAKgAE2pgKEFhUhII5ytqEmB6ACoiEDQrHzsFZKSJAHdU9Mz7LygAWgA%2BLLhMgDp63RCwmGorGEEAZmdgFEzHcipqAG0h1BgAXXox8dLKqAA5MBRqP0DMMKQrVDaiFFZqbpQ5BX7zX381wJAVNWjNA57SqABiTD5yqrHG0NhW9oA2Q59KLmEbTKZ8GYfBZLFYXAIQIgwURQHqrS4BVhEOLVU6DZylbGo3rwjFXG7qe6HJ6vXjXIiiczaIA)

```python
from typing import Callable, Protocol, runtime_checkable, Any, reveal_type

@runtime_checkable
class AnyCallable(Protocol):
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...

def test_3(fn: Callable[[int], int]) -> None:
if isinstance(fn, AnyCallable):
reveal_type(fn) # int -> int

def test_6(fn: Callable[[int], int]) -> None:
match fn:
case AnyCallable() as fn:
reveal_type(fn) # unreachable

```

I would have expected the same `reveal_type` result in both cases.

Contributor guide

Open the contributing guide

Research direction

Start with the linked Pyright playground reproduction and compare the narrowing behavior in test_3 and test_6, focusing on the isinstance and match-case entry points. Done means the match-case branch is handled consistently with the isinstance branch and the reveal_type result is no longer considered unreachable.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.