Skip to content

Commit a5c4ebe

Browse files
improve narrowing + better tests
1 parent 5fbcbed commit a5c4ebe

File tree

4 files changed

+36
-14
lines changed

4 files changed

+36
-14
lines changed

mypy/checker.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8015,11 +8015,16 @@ def conditional_types(
80158015
# attempt to narrow anything. Instead, we broaden the expr to Any to
80168016
# avoid false positives
80178017
return proposed_type, default
8018-
elif not any(
8018+
elif not any( # handle concrete subtypes
80198019
type_range.is_upper_bound for type_range in proposed_type_ranges
80208020
) and is_proper_subtype(current_type, proposed_type, ignore_promotions=True):
80218021
# Expression is always of one of the types in proposed_type_ranges
80228022
return default, UninhabitedType()
8023+
elif ( # handle structural subtypes
8024+
isinstance(proposed_type, CallableType)
8025+
or (isinstance(proposed_type, Instance) and proposed_type.type.runtime_protocol)
8026+
) and is_subtype(current_type, proposed_type, ignore_promotions=True):
8027+
return default, UninhabitedType()
80238028
elif not is_overlapping_types(current_type, proposed_type, ignore_promotions=True):
80248029
# Expression is never of any type in proposed_type_ranges
80258030
return UninhabitedType(), default

mypy/checkpattern.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
562562
# Create a `Callable[..., Any]`
563563
fallback = self.chk.named_type("builtins.function")
564564
any_type = AnyType(TypeOfAny.unannotated)
565-
fn_type = callable_with_ellipsis(any_type, ret_type=any_type, fallback=fallback)
566-
# if typ is itself callable, use its own type, otherwise Callable[..,, Any]
567-
typ = current_type if is_subtype(current_type, fn_type) else fn_type
565+
typ = callable_with_ellipsis(any_type, ret_type=any_type, fallback=fallback)
568566
else:
569567
if isinstance(type_info, Var) and type_info.type is not None:
570568
name = type_info.type.str_with_options(self.options)

test-data/unit/check-protocols.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,11 +1506,12 @@ class C: pass
15061506
def f(x: P1) -> int: ...
15071507
@overload
15081508
def f(x: P2) -> str: ...
1509-
def f(x):
1509+
def f(x: object) -> object:
15101510
if isinstance(x, P1):
15111511
return P1.attr1
15121512
if isinstance(x, P2): # E: Only @runtime_checkable protocols can be used with instance and class checks
1513-
return P1.attr2
1513+
return P2.attr2
1514+
return None
15141515

15151516
reveal_type(f(C1())) # N: Revealed type is "builtins.int"
15161517
reveal_type(f(C2())) # N: Revealed type is "builtins.str"

test-data/unit/check-python310.test

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,18 +1072,36 @@ match m:
10721072
[case testMatchClassPatternCallable]
10731073
from typing import Callable
10741074

1075-
x: object
1075+
def test_object(x: object) -> None:
1076+
match x:
1077+
case Callable() as fn:
1078+
reveal_type(fn) # N: Revealed type is "def (*Any, **Any) -> Any"
10761079

1077-
match x:
1078-
case Callable() as fn:
1079-
reveal_type(fn) # N: Revealed type is "def (*Any, **Any) -> Any"
1080+
def test_intfun(x: Callable[[int], int]) -> None:
1081+
match x:
1082+
case Callable() as fn:
1083+
reveal_type(fn) # N: Revealed type is "def (builtins.int) -> builtins.int"
10801084

1081-
def int_fun(x: int) -> int: return x+1
10821085

1083-
match int_fun:
1084-
case Callable() as fn:
1085-
reveal_type(fn) # N: Revealed type is "def (x: builtins.int) -> builtins.int"
1086+
[case testMatchClassPatternCallbackProtocol]
1087+
from typing import Any, Callable
1088+
from typing_extensions import Protocol, runtime_checkable
10861089

1090+
@runtime_checkable
1091+
class AnyCallable(Protocol):
1092+
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
1093+
1094+
def test_object(x: object) -> None:
1095+
match x:
1096+
case AnyCallable() as fn:
1097+
reveal_type(fn) # N: Revealed type is "__main__.AnyCallable"
1098+
1099+
def test_intfun(x: Callable[[int], int]) -> None:
1100+
match x:
1101+
case AnyCallable() as fn:
1102+
reveal_type(fn) # N: Revealed type is "def (builtins.int) -> builtins.int"
1103+
1104+
[builtins fixtures/dict.pyi]
10871105

10881106
[case testMatchClassPatternNestedGenerics]
10891107
# From cpython test_patma.py

0 commit comments

Comments
 (0)