8000 Infer types from issubclass() calls by pkch · Pull Request #3005 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Infer types from issubclass() calls #3005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Apr 21, 2017
Merged
Prev Previous commit
Next Next commit
Infer type from issubclass
  • Loading branch information
pkch committed Mar 20, 2017
commit 1776e908aa2b5ba84550fb373173aa6ac159b53c
60 changes: 57 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys

from typing import (
Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator
Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator, MutableMapping
)

from mypy.errors import Errors, report_internal_error
Expand Down Expand Up @@ -2616,6 +2616,20 @@ def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap:
return result


def convert_to_types(m: MutableMapping[Expression, Type]) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name of this function does not reflect its behaviour. Something like "convert_to_typetype" 8000 would be better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you just reuse the TypeMap alias defined above instead of MutableMapping[Expression, Type]?
Or maybe just use Dict?

for k in m:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use longer names for variables:

  • m -> type_map
  • k -> expr
  • x -> typ

Copy link
Contributor Author
@pkch pkch Mar 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done for this and other CR changes

x = m[k]
if isinstance(x, UnionType):
m[k] = UnionType([TypeType(t) for t in x.items])
elif isinstance(x, Instance):
m[k] = TypeType(m[k])
else:
# TODO: verify this is ok
# unknown object, don't know how to convert
m.clear()
return


def find_isinstance_check(node: Expression,
type_map: Dict[Expression, Type],
) -> Tuple[TypeMap, TypeMap]:
Expand All @@ -2639,8 +2653,24 @@ def find_isinstance_check(node: Expression,
expr = node.args[0]
if expr.literal == LITERAL_TYPE:
vartype = type_map[expr]
types = get_isinstance_type(node.args[1], type_map)
return conditional_type_map(expr, vartype, types)
type = get_isinstance_type(node.args[1], type_map)
return conditional_type_map(expr, vartype, type)
elif refers_to_fullname(node.callee, 'builtins.issubclass'):
expr = node.args[0]
if expr.literal == LITERAL_TYPE:
vartype = type_map[expr]
type = get_issubclass_type(node.args[1], type_map)
if isinstance(vartype, UnionType):
vartype = UnionType([t.item for t in vartype.items]) # type: ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need # type: ignore here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed using isinstance

elif isinstance(vartype, TypeType):
vartype = vartype.item
else:
# TODO: verify this is ok
return {}, {} # unknown type
yes_map, no_map = conditional_type_map(expr, vartype, type)
convert_to_types(yes_map)
convert_to_types(no_map)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to make in-place changes in convert_to_types instead of just returning a new map? I think the latter is safer.

return yes_map, no_map
elif refers_to_fullname(node.callee, 'builtins.callable'):
expr = node.args[0]
if expr.literal == LITERAL_TYPE:
Expand Down Expand Up @@ -2747,6 +2777,30 @@ def get_isinstance_type(expr: Expression, type_map: Dict[Expression, Type]) -> L
return types


def get_issubclass_type(expr: Expression, type_map: Dict[Expression, Type]) -> Type:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function exactly duplicates the function get_isinstance_type above. Why do you need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I thought it might be similar, but then I actually managed to make it work without changing anything at all - and didn't notice. I removed it.

all_types = [type_map[e] for e in flatten(expr)]

types = [] # type: List[Type]

for type in all_types:
if isinstance(type, FunctionLike):
if type.is_type_obj():
# Type variables may be present -- erase them, which is the best
# we can do (outside disallowing them here).
type = erase_typevars(type.items()[0].ret_type)
types.append(type)
elif isinstance(type, TypeType):
types.append(type.item)
else: # we didn't see an actual type, but rather a variable whose value is unknown to us
return None

assert len(types) != 0
if len(types) == 1:
return types[0]
else:
return UnionType(types)


def expand_func(defn: FuncItem, map: Dict[TypeVarId, Type]) -> FuncItem:
visitor = TypeTransformVisitor(map)
ret = defn.accept(visitor)
Expand Down
0