Skip to content

Fix inference logic for isinstance #2997

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 8 commits into from
Mar 19, 2017
Merged

Conversation

pkch
Copy link
Contributor

@pkch pkch commented Mar 14, 2017

Fixes #2993

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

Can you add unit tests?

mypy/checker.py Outdated
@@ -2711,9 +2711,14 @@ def get_isinstance_type(expr: Expression, type_map: Dict[Expression, Type]) -> T

types.append(type)

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 there are too many blank lines here. IMO the only blank line should be outside the for-loop, before the assert on line 2720 below.

@pkch pkch force-pushed the fix-isinstance-inference branch from a61bf32 to 79a8085 Compare March 14, 2017 20:30
@ilevkivskyi
Copy link
Member

It looks like all commits from this PR and from #2995 are also included in #3005. I have made a review on the latter that also concern this PR and #2995.

For the future, I would propose to have non-overlapping PRs, it will be much easier to understand them.

mypy/semanal.py Outdated
@@ -2640,8 +2640,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
# This branch handles the case foo.bar where foo is a module.
# In this case base.node is the module's MypyFile and we look up
# bar in its namespace. This must be done for all types of bar.
file = base.node
assert isinstance(file, (MypyFile, type(None)))
file = cast(Optional[MypyFile], base.node)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add comment describing why we don't use assert isinstance here.

mypy/semanal.py Outdated
@@ -2658,7 +2657,8 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
# one type checker run. If we reported errors here,
# the build would terminate after semantic analysis
# and we wouldn't be able to report any type errors.
full_name = '%s.%s' % (file.fullname() if file is not None else None, expr.name)
full_name = '%s.%s' % (file.fullname() if file is not None
Copy link
Collaborator

Choose a reason for hiding this comment

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

This change seems unnecessary now?

from typing import *
def f(x: Union[int, str], typ: type) -> None:
if isinstance(x, (typ, int)):
x + 1 # E: Unsupported operand types for + (likely involving Union)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add also an else: block and do a reveal_type(x) in that block. Add another reveal_type(x) just after the if statement.

if isinstance(x, (typ, int)):
x + 1 # E: Unsupported operand types for + (likely involving Union)
[builtins fixtures/isinstancelist.pyi]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Add test for the type object that is Type[A] or similar. Example:

class A: pass

def f(x: Union[int, A], a: Type[A]) -> None:
    if isinstance(x, a):
        reveal_type(x)
    elif isinstance(x, int):
        reveal_type(x)
    else:
        reveal_type(x)
    reveal_type(x)

@pkch pkch force-pushed the fix-isinstance-inference branch from bca997d to 6ea4f4d Compare March 18, 2017 21:30
@pkch pkch force-pushed the fix-isinstance-inference branch from 2fa26d1 to be30faa Compare March 19, 2017 01:58
mypy/checker.py Outdated
return UnionType(types)
types.append(TypeRange(type, is_upper_bound=False))
elif isinstance(type, TypeType):
types.append(TypeRange(type.item, is_upper_bound=True))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add comment here describing why is_upper_bound=True.

mypy/checker.py Outdated
types.append(TypeRange(type.item, is_upper_bound=True))
else: # we didn't see an actual type, but rather a variable whose value is unknown to us
return None
assert len(types) != 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if we should return None if the length if 0? For example, what happens if we have isinstance(x, ())?

x + 1 # E: Unsupported operand types for + (likely involving Union)
reveal_type(x) # E: Revealed type is 'Union[builtins.int, builtins.str]'
else:
reveal_type(x) # E: Revealed type is 'Union[builtins.int, builtins.str]'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here we could actually infer the type to be builtins.str, since we know that it can't be builtins.int due to the isinstance check.

reveal_type(x) # E: Revealed type is '__main__.A'
reveal_type(x) # E: Revealed type is 'Union[builtins.int, __main__.A]'

[builtins fixtures/isinstancelist.pyi]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not directly related, but can you add a test case that does something like isinstance(x, ())?

mypy/checker.py Outdated
if current_type:
if is_proper_subtype(current_type, proposed_type):
# Expression is always of type proposed_type
if not any(type_range.is_upper_bound for type_range in proposed_type_ranges) \
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use if (...)" to avoid the use of \.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants