Skip to content

Commit 73fc6b1

Browse files
committed
Merge pull request #896 from jhance/if-expr-isinstance
Isinstance for if expressions
2 parents f7c8c40 + a4447fc commit 73fc6b1

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

mypy/checker.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,10 +2097,23 @@ def find_isinstance_check(node: Node,
20972097
if kind == ISINSTANCE_ALWAYS_TRUE:
20982098
kind = ISINSTANCE_OVERLAPPING
20992099
return (var, type, AnyType(), kind)
2100+
elif isinstance(node, UnaryExpr) and node.op == 'not':
2101+
(var, type, elsetype, kind) = find_isinstance_check(node.expr, type_map, weak)
2102+
return (var, elsetype, type, invert_isinstance_kind(kind))
2103+
21002104
# Not a supported isinstance check
21012105
return None, AnyType(), AnyType(), -1
21022106

21032107

2108+
def invert_isinstance_kind(kind: int) -> int:
2109+
if kind == ISINSTANCE_ALWAYS_TRUE:
2110+
return ISINSTANCE_ALWAYS_FALSE
2111+
elif kind == ISINSTANCE_ALWAYS_FALSE:
2112+
return ISINSTANCE_ALWAYS_TRUE
2113+
else:
2114+
return kind
2115+
2116+
21042117
def get_isinstance_type(node: Node, type_map: Dict[Node, Type]) -> Type:
21052118
type = type_map[node]
21062119
if isinstance(type, FunctionLike):

mypy/checkexpr.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,8 +1275,25 @@ def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> No
12751275
def visit_conditional_expr(self, e: ConditionalExpr) -> Type:
12761276
cond_type = self.accept(e.cond)
12771277
self.check_not_void(cond_type, e)
1278-
if_type = self.accept(e.if_expr)
1279-
else_type = self.accept(e.else_expr, context=if_type)
1278+
1279+
# Gain type information from isinstance if it is there
1280+
# but only for the current expression
1281+
variable, inst_if_type, inst_else_type, kind = mypy.checker.find_isinstance_check(
1282+
e.cond,
1283+
self.chk.type_map,
1284+
self.chk.typing_mode_weak())
1285+
if variable:
1286+
self.chk.binder.push_frame()
1287+
self.chk.binder.push(variable, inst_if_type)
1288+
if_type = self.accept(e.if_expr)
1289+
self.chk.binder.pop_frame()
1290+
self.chk.binder.push_frame()
1291+
self.chk.binder.push(variable, inst_else_type)
1292+
else_type = self.accept(e.else_expr, context=if_type)
1293+
self.chk.binder.pop_frame()
1294+
else:
1295+
if_type = self.accept(e.if_expr)
1296+
else_type = self.accept(e.else_expr, context=if_type)
12801297
return join.join_types(if_type, else_type)
12811298

12821299
#

mypy/test/data/check-isinstance.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,3 +708,16 @@ x = B() # type: A
708708
if isinstance(x, B) and x.flag:
709709
pass
710710
[builtins fixtures/isinstancelist.py]
711+
[case testIsinstanceExpression]
712+
class A:
713+
pass
714+
715+
class B(A):
716+
flag = 1
717+
718+
x = B() # type: A
719+
720+
x.flag if isinstance(x, B) else 0
721+
0 if not isinstance(x, B) else x.flag
722+
0 if isinstance(x, B) else x.flag # E: "A" has no attribute "flag"
723+
[builtins fixtures/isinstancelist.py]

0 commit comments

Comments
 (0)