Skip to content

Commit c6fd9a6

Browse files
committed
Fix #2978.
1 parent 4ad5215 commit c6fd9a6

4 files changed

Lines changed: 36 additions & 8 deletions

File tree

‎mypy/checkexpr.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2225,7 +2225,8 @@ def narrow_type_from_binder(self, expr: Expression, known_type: Type) -> Type:
22252225
if expr.literal >= LITERAL_TYPE:
22262226
restriction = self.chk.binder.get(expr)
22272227
if restriction:
2228-
return narrow_declared_type(known_type, restriction)
2228+
ans = narrow_declared_type(known_type, restriction)
2229+
return ans
22292230
return known_type
22302231

22312232

‎mypy/join.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
UninhabitedType, TypeType, true_or_false
1111
)
1212
from mypy.maptype import map_instance_to_supertype
13-
from mypy.subtypes import is_subtype, is_equivalent, is_subtype_ignoring_tvars
13+
from mypy.subtypes import is_subtype, is_equivalent, is_subtype_ignoring_tvars, is_proper_subtype
1414

1515
from mypy import experiments
1616

@@ -29,10 +29,10 @@ def join_simple(declaration: Type, s: Type, t: Type) -> Type:
2929
if isinstance(s, ErasedType):
3030
return t
3131

32-
if is_subtype(s, t):
32+
if is_proper_subtype(s, t):
3333
return t
3434

35-
if is_subtype(t, s):
35+
if is_proper_subtype(t, s):
3636
return s
3737

3838
if isinstance(declaration, UnionType):

‎mypy/subtypes.py‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
)
88
import mypy.applytype
99
import mypy.constraints
10+
from mypy.erasetype import erase_type
1011
# Circular import; done in the function instead.
1112
# import mypy.solve
1213
from mypy import messages, sametypes
@@ -512,13 +513,22 @@ def unify_generic_callable(type: CallableType, target: CallableType,
512513

513514

514515
def restrict_subtype_away(t: Type, s: Type) -> Type:
515-
"""Return a supertype of (t intersect not s)
516+
"""Return t minus s.
516517
517-
Currently just remove elements of a union type.
518+
If we can't determine a precise result, return a supertype of the
519+
ideal result (just t is a valid result).
520+
521+
This is used for type inference of runtime type checks such as
522+
isinstance.
523+
524+
Currently this just removes elements of a union type.
518525
"""
519526
if isinstance(t, UnionType):
520-
new_items = [item for item in t.items if (not is_subtype(item, s)
521-
or isinstance(item, AnyType))]
527+
# Since runtime type checks will ignore type arguments, erase the types.
528+
erased_s = erase_type(s)
529+
new_items = [item for item in t.items
530+
if (not is_proper_subtype(erase_type(item), erased_s)
531+
or isinstance(item, AnyType))]
522532
return UnionType.make_union(new_items)
523533
else:
524534
return t
@@ -640,6 +650,9 @@ def visit_tuple_type(self, left: TupleType) -> bool:
640650
if not right.args:
641651
return False
642652
iter_type = right.args[0]
653+
if is_named_instance(right, 'builtins.tuple') and isinstance(iter_type, AnyType):
654+
# Special case plain 'tuple' which is needed for isinstance(x, tuple).
655+
return True
643656
return all(is_proper_subtype(li, iter_type) for li in left.items)
644657
return is_proper_subtype(left.fallback, right)
645658
elif isinstance(right, TupleType):

‎test-data/unit/check-optional.test‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,17 @@ reveal_type(u(a, None)) # E: Revealed type is 'Union[builtins.None, Any]'
575575
reveal_type(u(None, a)) # E: Revealed type is 'Union[Any, builtins.None]'
576576
reveal_type(u(1, None)) # E: Revealed type is 'Union[builtins.None, builtins.int*]'
577577
reveal_type(u(None, 1)) # E: Revealed type is 'Union[builtins.int*, builtins.None]'
578+
579+
[case testIsinstanceAndOptionalAndAnyBase]
580+
from typing import Any, Optional
581+
582+
class A(Any): pass
583+
584+
def f(a: Optional[A]):
585+
reveal_type(a) # E: Revealed type is 'Union[__main__.A, builtins.None]'
586+
if a is not None:
587+
reveal_type(a) # E: Revealed type is '__main__.A'
588+
else:
589+
reveal_type(a) # E: Revealed type is 'builtins.None'
590+
reveal_type(a) # E: Revealed type is 'Union[__main__.A, builtins.None]'
591+
[builtins fixtures/isinstance.pyi]

0 commit comments

Comments
 (0)