Skip to content

Commit fe27fda

Browse files
committed
Infer type from issubclass
1 parent a4db99e commit fe27fda

File tree

1 file changed

+55
-1
lines changed

1 file changed

+55
-1
lines changed

mypy/checker.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from contextlib import contextmanager
66

77
from typing import (
8-
Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator
8+
Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator, MutableMapping
99
)
1010

1111
from mypy.errors import Errors, report_internal_error
@@ -2588,6 +2588,20 @@ def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap:
25882588
return result
25892589

25902590

2591+
def convert_to_types(m: MutableMapping[Expression, Type]) -> None:
2592+
for k in m:
2593+
x = m[k]
2594+
if isinstance(x, UnionType):
2595+
m[k] = UnionType([TypeType(t) for t in x.items])
2596+
elif isinstance(x, Instance):
2597+
m[k] = TypeType(m[k])
2598+
else:
2599+
# TODO: verify this is ok
2600+
# unknown object, don't know how to convert
2601+
m.clear()
2602+
return
2603+
2604+
25912605
def find_isinstance_check(node: Expression,
25922606
type_map: Dict[Expression, Type],
25932607
) -> Tuple[TypeMap, TypeMap]:
@@ -2613,6 +2627,22 @@ def find_isinstance_check(node: Expression,
26132627
vartype = type_map[expr]
26142628
type = get_isinstance_type(node.args[1], type_map)
26152629
return conditional_type_map(expr, vartype, type)
2630+
elif refers_to_fullname(node.callee, 'builtins.issubclass'):
2631+
expr = node.args[0]
2632+
if expr.literal == LITERAL_TYPE:
2633+
vartype = type_map[expr]
2634+
type = get_issubclass_type(node.args[1], type_map)
2635+
if isinstance(vartype, UnionType):
2636+
vartype = UnionType([t.item for t in vartype.items]) # type: ignore
2637+
elif isinstance(vartype, TypeType):
2638+
vartype = vartype.item
2639+
else:
2640+
# TODO: verify this is ok
2641+
return {}, {} # unknown type
2642+
yes_map, no_map = conditional_type_map(expr, vartype, type)
2643+
convert_to_types(yes_map)
2644+
convert_to_types(no_map)
2645+
return yes_map, no_map
26162646
elif refers_to_fullname(node.callee, 'builtins.callable'):
26172647
expr = node.args[0]
26182648
if expr.literal == LITERAL_TYPE:
@@ -2716,6 +2746,30 @@ def get_isinstance_type(expr: Expression, type_map: Dict[Expression, Type]) -> T
27162746
return UnionType(types)
27172747

27182748

2749+
def get_issubclass_type(expr: Expression, type_map: Dict[Expression, Type]) -> Type:
2750+
all_types = [type_map[e] for e in flatten(expr)]
2751+
2752+
types = [] # type: List[Type]
2753+
2754+
for type in all_types:
2755+
if isinstance(type, FunctionLike):
2756+
if type.is_type_obj():
2757+
# Type variables may be present -- erase them, which is the best
2758+
# we can do (outside disallowing them here).
2759+
type = erase_typevars(type.items()[0].ret_type)
2760+
types.append(type)
2761+
elif isinstance(type, TypeType):
2762+
types.append(type.item)
2763+
else: # we didn't see an actual type, but rather a variable whose value is unknown to us
2764+
return None
2765+
2766+
assert len(types) != 0
2767+
if len(types) == 1:
2768+
return types[0]
2769+
else:
2770+
return UnionType(types)
2771+
2772+
27192773
def expand_func(defn: FuncItem, map: Dict[TypeVarId, Type]) -> FuncItem:
27202774
visitor = TypeTransformVisitor(map)
27212775
ret = defn.accept(visitor)

0 commit comments

Comments
 (0)