Skip to content
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

Class variable type is reset in inheritance chain #10506

Open
54sledgehammer45 opened this issue May 19, 2021 · 3 comments
Open

Class variable type is reset in inheritance chain #10506

54sledgehammer45 opened this issue May 19, 2021 · 3 comments
Labels
bug mypy got something wrong

Comments

@54sledgehammer45
Copy link

Bug Report

When a List class variable is declared in a base class, subclasses that assign new values to the class variable will limit their children to the type of that assignment.

To Reproduce

from typing import ClassVar, List


class Seat(object):
    ...


class LeatherSeat(Seat):
    ...


class PlasticSeat(Seat):
    ...


class SomeBucket(Seat):
    ...


##############
# Failing Case 1
class TransportMedium(object):
    seats: ClassVar[List[Seat]] = []


class Car(TransportMedium):
    ...


class ElectricCar(Car):
    seats = [LeatherSeat()]


class ManufacturerCar(ElectricCar):
    # List item 0 has incompatible type "PlasticSeat"; expected "LeatherSeat"
    seats = [PlasticSeat()]


############
# Failing Case 2
class Train(TransportMedium):
    # Need type annotation for 'seats' (hint: "seats: List[<type>] = ...")
    seats = []


############
# Probably successful case
class Plane(TransportMedium):
    #  List item 0 has incompatible type "None"; expected "Seat"
    seats = [None]


################################
# Simple class var type checking works
class Base(object):
    class_var: ClassVar[str] = "this is a class var"


class Sub(Base):
    ...


class SubSub(Sub):
    # Incompatible types in assignment (expression has type "None", base class "Base" defined the type as "str")
    class_var = None


class Last(Sub):
    class_var = "should be OK"

Expected Behavior

ManufacturerCar.seats should not have been marked as an error since the list can contain Seats. The class isn't a generic and (to my knowledge) there isn't a way to say List[? inherits Seat] like in Java with wildcards.

If that's what the problem is, then warning should come already at ElectricCar.seats = [LeatherSeat()] and the type shouldn't be erased or overwritten to then fail in the next class.

Actual Behavior

.seats type is overwritten (erased?) in one subclass and the children of that are then limited to that type.

Your Environment

  • Mypy version used: 0.812
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files):
[mypy]
ignore_missing_imports = True
warn_return_any = False
  • Python version used: 3.8.7
  • Operating system and version: Using docker image python:3.8.7-slim
@54sledgehammer45 54sledgehammer45 added the bug mypy got something wrong label May 19, 2021
@mathieui
Copy link

mathieui commented Jul 5, 2021

It seems to be that way for any complex types, I can see the same issue here for Dict ClassVars:

from typing import Dict, Tuple, ClassVar


class A:
    a: ClassVar[int] = 1
    b: ClassVar[Dict[str, Tuple[int, str]]] = {}


class B:
    a = 2
    b = {} # fails with error: Need type annotation for 'b' (hint: "b: Dict[<type>, <type>] = ...")

wrwrwr added a commit to wrwrwr/mypy that referenced this issue Jan 21, 2023
Related python#10375 and python#10506.

For an unhinted assignment to a class variable defined in a base class,
this allows subderived classes to only match the type in the base class,
rather than the one inferred from the assignment value.
wrwrwr added a commit to wrwrwr/mypy that referenced this issue Jan 21, 2023
Related python#10375 and python#10506.

For an unhinted assignment to a class variable defined in a base class,
this allows subderived classes to only match the type in the base class,
rather than the one inferred from the assignment value.
wrwrwr added a commit to wrwrwr/mypy that referenced this issue Jan 21, 2023
Related python#10375 and python#10506.

For an unhinted assignment to a class variable defined in a base class,
this allows subderived classes to only match the type in the base class,
rather than the one inferred from the assignment value.
wrwrwr added a commit to wrwrwr/mypy that referenced this issue Jan 21, 2023
Related python#10375 and python#10506.

For an unhinted assignment to a class variable defined in a base class,
this allows subderived classes to only match the type in the base class,
rather than the one inferred from the assignment value.
wrwrwr added a commit to wrwrwr/mypy that referenced this issue Jan 21, 2023
Related python#10375 and python#10506.

For an unhinted assignment to a class variable defined in a base class,
this allows subderived classes to only match the type in the base class,
rather than the one inferred from the assignment value.
@beauxq
Copy link

beauxq commented Sep 14, 2023

Here's a simplified example where I ran into this:

class P:
    x: ClassVar[Union[int, Literal["X"]]] = 0


class Q(P):
    x = 0


class R(Q):
    x = 1

https://mypy-play.net/?mypy=latest&python=3.11&gist=1f83185d54fbb490447484a5a2fd7677&flags=strict

@beauxq
Copy link

beauxq commented Jan 15, 2024

I found that this bug doesn't require any complex types. The bug happens with any type that isn't what would be inferred.

from typing import *


class P:
    x: ClassVar[Any] = 0


class Q(P):
    x = "random"


class R(Q):
    x = 3.14

error: Incompatible types in assignment (expression has type "float", base class "Q" defined the type as "str")

https://mypy-play.net/?mypy=latest&python=3.11&gist=ef5fea9cb8e11b10cb1568ce5dc12fdc&flags=strict

Because we explicitly annotated the type of x as Any, it should be ok to put anything in x


I think it would be good to remove List from the title of this issue, since it has nothing to do with lists.

"Class variable type is reset in inheritance chain"

@JelleZijlstra JelleZijlstra changed the title Class variable List type is reset in inheritance chain Class variable type is reset in inheritance chain Jan 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants