Skip to content

Adds attribute type inference from super-types for partial types #10871

New issue

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

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

Already on GitHub? # to your account

Closed
wants to merge 5 commits into from
Closed

Adds attribute type inference from super-types for partial types #10871

wants to merge 5 commits into from

Conversation

sobolevn
Copy link
Member

This PR implements the suggested general case from #10870 (comment)

What we do? When we are stuck with the partial type inside a class definition, we try to fetch types from parent definitions.
If this definition exist, we reuse the type.

This way, we won't have a warning here:

from typing import List

class P:
   x: List[int]

class C(P):
   x = []  # used to be an error here: "Need type annotation for "x" (hint: "x: List[<type>] = ...")"

reveal_type(C.x)  # revealed type is: "List[int]"

Closes #10870

Test Plan

I am going to add more tests after someone from the core team approves this approach 🙂

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

sobolevn commented Jul 26, 2021

Ok, here's the problematic part:

from typing import List
class P:
    x: List[int]
class M:
    x: List[str]
class C(P, M):
    x = []
reveal_type(C.x)  # N: Revealed type is "builtins.list[builtins.int]"

This code does not raise error: Definition of "x" in base class "P" is incompatible with definition in base class "M" because we forcefully insert x type after the type-checking is already finished.

I have tried to defer this node to the second pass, but we don't have the AssignmentStatement in the enter_partial_types context. It didn't work.

Now, I will try to do something similar in semanal.

The last option is: if we have more than one different type definition in mro, just fallback to old behavior.

@sobolevn
Copy link
Member Author

Solved it using new checker method similar to try_infer_partial_generic_type_from_assignment

@github-actions

This comment has been minimized.

@sobolevn
Copy link
Member Author

sobolevn commented Jul 26, 2021

Oh, this one is hard 😕

Solution with try_infer_partial_generic_type_from_super is too unreliable right now as shown in #10871 (comment)

It is also related to #3208

One more failing case:

from typing import List

class P:
    @property
    def x(self) -> List[int]:
        ...

class C(P):
    x = []

C.x.append(1)  # error: "Callable[[P], List[int]]" has no attribute "append"

It works with explicit x: List[int] = []

@sobolevn
Copy link
Member Author

sobolevn commented Jul 26, 2021

To be fair, current master also does not find subtyping issue here:

from typing import List

class Parent:
    a: List[str]

class A(Parent):
    a = []
    a.append(1)

reveal_type(A.a)  # note: Revealed type is "builtins.list[builtins.int]"

It should give the same result as:

from typing import List

class Parent:
    a: List[str]

class A(Parent):
    a: List[int]  # error: Incompatible types in assignment (expression has type "List[int]", base class "Parent" defined the type as "List[str]")
reveal_type(A.a)  # note: Revealed type is "builtins.list[builtins.int]"

But, I think it is a different task.

@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

sympy (https://github.com/sympy/sympy.git)
- sympy/core/numbers.py:2514: error: Need type annotation for "free_symbols" (hint: "free_symbols: Set[<type>] = ...")

@sobolevn sobolevn closed this Aug 9, 2021
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mypy complains when [] is used for __slots__ definition
1 participant