Skip to content

Incorrect type inference in Derived class that was explicitly typed in Base #12268

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

Closed
Riolku opened this issue Mar 2, 2022 · 7 comments
Closed
Labels
bug mypy got something wrong topic-inheritance Inheritance and incompatible overrides

Comments

@Riolku
Copy link

Riolku commented Mar 2, 2022

I think this might be best explained with an example.

from typing import Union, List

class A: pass
class B: pass

class Base:
    variable1 : List[Union[A, B]] = []
    variable2 : List[Union[A, B]] = []

class Derived(Base):
    variable1 = [A()]
    variable2 = variable1

Here is the output of mypy test.py:

test.py:12: error: Incompatible types in assignment (expression has type "List[A]", base class "Base" defined the type as "List[Union[A, B]]")
test.py:12: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
test.py:12: note: Consider using "Sequence" instead, which is covariant
Found 1 error in 1 file (checked 1 source file)

The error is on the last line. The complaint is that variable1 is of type List[A], and so it cannot be assigned to variable2. At first, this seems reasonable, but notice that the type of variable1 is explicitly defined to be List[Union[A, B]] in the superclass. More interesting still is that the previous line, line 11, is accepted just fine.

I believe Derived types should inherit their superclass types if possible before resorting to a type inference.

Debug information:

MyPy Version: 0.931
Python Version: 3.9.2
Operating System: Debian 11
Linux Version: 5.14

And also a real world example of this.

@Riolku Riolku added the bug mypy got something wrong label Mar 2, 2022
@Riolku
Copy link
Author

Riolku commented Mar 2, 2022

I'm trying to file a PR for this but am struggling to navigate the codebase. My understanding of the problem is that although variable1 is type-checked in the base, and MyPy correctly type checks that assignment, it still infers the type of variable1. The problem is, I don't know in what function this should be fixed.

Basically, on line 2256 of checker.py, the inferred value is generated. Even after checking the type with the supertype on line 2280, the inferred type is still used. However, I don't have access to the base type in this class or anything. Maybe I should be looking elsewhere?

Riolku added a commit to Riolku/mypy that referenced this issue Mar 2, 2022
Fixes python#12268. If the type of an lvalue is not given at definition time, 
attempt to infer it from the type of a base member.
Riolku added a commit to Riolku/mypy that referenced this issue Mar 2, 2022
Fixes python#12268. If the type of an lvalue is not given at definition time, 
attempt to infer it from the type of a base member.
@Riolku
Copy link
Author

Riolku commented Mar 3, 2022

Closed PR because this seems like an anti-feature. I think the real issue we want to solve has something more subtle to do with variance. I'll file in a separate PR.

@Riolku Riolku closed this as completed Mar 3, 2022
Riolku added a commit to Riolku/mypy that referenced this issue Mar 3, 2022
Would help with python#12268. Modified a test because the two behaviours 
conflict right now.
@erictraut
Copy link

For what it's worth, pyright implements the behavior you're describing here. I don't consider it an anti-feature :).

It seems logical to me that a type declaration should always take precedence, and type inference should be used only in cases where a declaration is missing. Mypy applies this rule for local variables and for instance/class variables within a class, but it doesn't do it for inherited instance/class variables.

Is this behavior intended, or is it a bug / oversight? I'd be curious to hear from core mypy developers.

class Base:
    # Base class contains type declaration for "var1"
    var1: float | None

class Derived(Base):
    # Child class contains assignment but no declaration
    var1 = 0

reveal_type(Derived().var1)  # Pyright: float | None, Mypy: int

@Riolku
Copy link
Author

Riolku commented Mar 3, 2022

I don't know. Have you looked at the open source snippets I mentioned? I want the code there to still work.

@Riolku
Copy link
Author

Riolku commented Mar 4, 2022

Sorry, said snippet is in the old closed PR.

@Riolku Riolku reopened this Mar 4, 2022
@Riolku
Copy link
Author

Riolku commented Mar 4, 2022

I re-opened because I think that this could be fixed by #12284

Riolku added a commit to Riolku/mypy that referenced this issue Mar 4, 2022
Riolku added a commit to Riolku/mypy that referenced this issue Mar 4, 2022
Riolku added a commit to Riolku/mypy that referenced this issue Mar 4, 2022
@JelleZijlstra JelleZijlstra added the topic-inheritance Inheritance and incompatible overrides label Mar 19, 2022
@erictraut
Copy link

This appears to have been fixed in mypy 0.9.0. I think it can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-inheritance Inheritance and incompatible overrides
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants