Skip to content

Commit 8e909e4

Browse files
Fix crash on TypeGuard plus "and" (#10496)
In python/typeshed#5473, I tried to switch a number of `inspect` functions to use the new `TypeGuard` functionality. Unfortunately, mypy-primer found a number of crashes in third-party libraries in places where a TypeGuard function was ANDed together with some other check. Examples: - https://github.com/sphinx-doc/sphinx/blob/4.x/sphinx/util/inspect.py#L252 - https://github.com/sphinx-doc/sphinx/blob/4.x/sphinx/ext/coverage.py#L212 - https://github.com/streamlit/streamlit/blob/develop/lib/streamlit/elements/doc_string.py#L105 The problems trace back to the decision in #9865 to make TypeGuardType not inherit from ProperType: in various conditions that are more complicated than a simple `if` check, mypy wants everything to become a ProperType. Therefore, to fix the crashes I had to make TypeGuardType a ProperType and support it in various visitors.
1 parent de6fd6a commit 8e909e4

File tree

17 files changed

+113
-21
lines changed

17 files changed

+113
-21
lines changed

mypy/constraints.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
CallableType, Type, TypeVisitor, UnboundType, AnyType, NoneType, TypeVarType, Instance,
88
TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType,
99
UninhabitedType, TypeType, TypeVarId, TypeQuery, is_named_instance, TypeOfAny, LiteralType,
10-
ProperType, get_proper_type, TypeAliasType
10+
ProperType, get_proper_type, TypeAliasType, TypeGuardType
1111
)
1212
from mypy.maptype import map_instance_to_supertype
1313
import mypy.subtypes
@@ -534,6 +534,9 @@ def visit_union_type(self, template: UnionType) -> List[Constraint]:
534534
def visit_type_alias_type(self, template: TypeAliasType) -> List[Constraint]:
535535
assert False, "This should be never called, got {}".format(template)
536536

537+
def visit_type_guard_type(self, template: TypeGuardType) -> List[Constraint]:
538+
assert False, "This should be never called, got {}".format(template)
539+
537540
def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> List[Constraint]:
538541
res = [] # type: List[Constraint]
539542
for t in types:

mypy/erasetype.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Type, TypeVisitor, UnboundType, AnyType, NoneType, TypeVarId, Instance, TypeVarType,
55
CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType,
66
DeletedType, TypeTranslator, UninhabitedType, TypeType, TypeOfAny, LiteralType, ProperType,
7-
get_proper_type, TypeAliasType
7+
get_proper_type, TypeAliasType, TypeGuardType
88
)
99
from mypy.nodes import ARG_STAR, ARG_STAR2
1010

@@ -90,6 +90,9 @@ def visit_union_type(self, t: UnionType) -> ProperType:
9090
from mypy.typeops import make_simplified_union
9191
return make_simplified_union(erased_items)
9292

93+
def visit_type_guard_type(self, t: TypeGuardType) -> ProperType:
94+
return TypeGuardType(t.type_guard.accept(self))
95+
9396
def visit_type_type(self, t: TypeType) -> ProperType:
9497
return TypeType.make_normalized(t.item.accept(self), line=t.line)
9598

mypy/expandtype.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Dict, Iterable, List, TypeVar, Mapping, cast
22

33
from mypy.types import (
4-
Type, Instance, CallableType, TypeVisitor, UnboundType, AnyType,
4+
Type, Instance, CallableType, TypeGuardType, TypeVisitor, UnboundType, AnyType,
55
NoneType, TypeVarType, Overloaded, TupleType, TypedDictType, UnionType,
66
ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, TypeVarId,
77
FunctionLike, TypeVarDef, LiteralType, get_proper_type, ProperType,
@@ -126,6 +126,9 @@ def visit_union_type(self, t: UnionType) -> Type:
126126
from mypy.typeops import make_simplified_union # asdf
127127
return make_simplified_union(self.expand_types(t.items), t.line, t.column)
128128

129+
def visit_type_guard_type(self, t: TypeGuardType) -> ProperType:
130+
return TypeGuardType(t.type_guard.accept(self))
131+
129132
def visit_partial_type(self, t: PartialType) -> Type:
130133
return t
131134

mypy/fixup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
TypeVarExpr, ClassDef, Block, TypeAlias,
1010
)
1111
from mypy.types import (
12-
CallableType, Instance, Overloaded, TupleType, TypedDictType,
12+
CallableType, Instance, Overloaded, TupleType, TypeGuardType, TypedDictType,
1313
TypeVarType, UnboundType, UnionType, TypeVisitor, LiteralType,
1414
TypeType, NOT_READY, TypeAliasType, AnyType, TypeOfAny, TypeVarDef
1515
)
@@ -254,6 +254,9 @@ def visit_union_type(self, ut: UnionType) -> None:
254254
for it in ut.items:
255255
it.accept(self)
256256

257+
def visit_type_guard_type(self, t: TypeGuardType) -> None:
258+
t.type_guard.accept(self)
259+
257260
def visit_void(self, o: Any) -> None:
258261
pass # Nothing to descend into.
259262

mypy/indirection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def visit_literal_type(self, t: types.LiteralType) -> Set[str]:
9797
def visit_union_type(self, t: types.UnionType) -> Set[str]:
9898
return self._visit(t.items)
9999

100+
def visit_type_guard_type(self, t: types.TypeGuardType) -> Set[str]:
101+
return self._visit(t.type_guard)
102+
100103
def visit_partial_type(self, t: types.PartialType) -> Set[str]:
101104
return set()
102105

mypy/join.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Type, AnyType, NoneType, TypeVisitor, Instance, UnboundType, TypeVarType, CallableType,
88
TupleType, TypedDictType, ErasedType, UnionType, FunctionLike, Overloaded, LiteralType,
99
PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, get_proper_type,
10-
ProperType, get_proper_types, TypeAliasType, PlaceholderType
10+
ProperType, get_proper_types, TypeAliasType, PlaceholderType, TypeGuardType
1111
)
1212
from mypy.maptype import map_instance_to_supertype
1313
from mypy.subtypes import (
@@ -340,6 +340,9 @@ def visit_type_type(self, t: TypeType) -> ProperType:
340340
def visit_type_alias_type(self, t: TypeAliasType) -> ProperType:
341341
assert False, "This should be never called, got {}".format(t)
342342

343+
def visit_type_guard_type(self, t: TypeGuardType) -> ProperType:
344+
assert False, "This should be never called, got {}".format(t)
345+
343346
def join(self, s: Type, t: Type) -> ProperType:
344347
return join_types(s, t)
345348

mypy/meet.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Type, AnyType, TypeVisitor, UnboundType, NoneType, TypeVarType, Instance, CallableType,
99
TupleType, TypedDictType, ErasedType, UnionType, PartialType, DeletedType,
1010
UninhabitedType, TypeType, TypeOfAny, Overloaded, FunctionLike, LiteralType,
11-
ProperType, get_proper_type, get_proper_types, TypeAliasType
11+
ProperType, get_proper_type, get_proper_types, TypeAliasType, TypeGuardType
1212
)
1313
from mypy.subtypes import is_equivalent, is_subtype, is_callable_compatible, is_proper_subtype
1414
from mypy.erasetype import erase_type
@@ -648,6 +648,9 @@ def visit_type_type(self, t: TypeType) -> ProperType:
648648
def visit_type_alias_type(self, t: TypeAliasType) -> ProperType:
649649
assert False, "This should be never called, got {}".format(t)
650650

651+
def visit_type_guard_type(self, t: TypeGuardType) -> ProperType:
652+
assert False, "This should be never called, got {}".format(t)
653+
651654
def meet(self, s: Type, t: Type) -> ProperType:
652655
return meet_types(s, t)
653656

mypy/sametypes.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Sequence
22

33
from mypy.types import (
4-
Type, UnboundType, AnyType, NoneType, TupleType, TypedDictType,
4+
Type, TypeGuardType, UnboundType, AnyType, NoneType, TupleType, TypedDictType,
55
UnionType, CallableType, TypeVarType, Instance, TypeVisitor, ErasedType,
66
Overloaded, PartialType, DeletedType, UninhabitedType, TypeType, LiteralType,
77
ProperType, get_proper_type, TypeAliasType)
@@ -10,6 +10,7 @@
1010

1111
def is_same_type(left: Type, right: Type) -> bool:
1212
"""Is 'left' the same type as 'right'?"""
13+
1314
left = get_proper_type(left)
1415
right = get_proper_type(right)
1516

@@ -150,6 +151,12 @@ def visit_union_type(self, left: UnionType) -> bool:
150151
else:
151152
return False
152153

154+
def visit_type_guard_type(self, left: TypeGuardType) -> bool:
155+
if isinstance(self.right, TypeGuardType):
156+
return is_same_type(left.type_guard, self.right.type_guard)
157+
else:
158+
return False
159+
153160
def visit_overloaded(self, left: Overloaded) -> bool:
154161
if isinstance(self.right, Overloaded):
155162
return is_same_types(left.items(), self.right.items())

mypy/server/astdiff.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method'
5757
FuncBase, OverloadedFuncDef, FuncItem, MypyFile, UNBOUND_IMPORTED
5858
)
5959
from mypy.types import (
60-
Type, TypeVisitor, UnboundType, AnyType, NoneType, UninhabitedType,
60+
Type, TypeGuardType, TypeVisitor, UnboundType, AnyType, NoneType, UninhabitedType,
6161
ErasedType, DeletedType, Instance, TypeVarType, CallableType, TupleType, TypedDictType,
6262
UnionType, Overloaded, PartialType, TypeType, LiteralType, TypeAliasType
6363
)
@@ -335,6 +335,9 @@ def visit_union_type(self, typ: UnionType) -> SnapshotItem:
335335
normalized = tuple(sorted(items))
336336
return ('UnionType', normalized)
337337

338+
def visit_type_guard_type(self, typ: TypeGuardType) -> SnapshotItem:
339+
return ('TypeGuardType', snapshot_type(typ.type_guard))
340+
338341
def visit_overloaded(self, typ: Overloaded) -> SnapshotItem:
339342
return ('Overloaded', snapshot_types(typ.items()))
340343

mypy/server/astmerge.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
Type, SyntheticTypeVisitor, Instance, AnyType, NoneType, CallableType, ErasedType, DeletedType,
6060
TupleType, TypeType, TypeVarType, TypedDictType, UnboundType, UninhabitedType, UnionType,
6161
Overloaded, TypeVarDef, TypeList, CallableArgument, EllipsisType, StarType, LiteralType,
62-
RawExpressionType, PartialType, PlaceholderType, TypeAliasType
62+
RawExpressionType, PartialType, PlaceholderType, TypeAliasType, TypeGuardType
6363
)
6464
from mypy.util import get_prefix, replace_object_state
6565
from mypy.typestate import TypeState
@@ -389,6 +389,9 @@ def visit_erased_type(self, t: ErasedType) -> None:
389389
def visit_deleted_type(self, typ: DeletedType) -> None:
390390
pass
391391

392+
def visit_type_guard_type(self, typ: TypeGuardType) -> None:
393+
raise RuntimeError
394+
392395
def visit_partial_type(self, typ: PartialType) -> None:
393396
raise RuntimeError
394397

0 commit comments

Comments
 (0)