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

Class in list inferred as mixin type when using generic typevar #18712

Open
NathanFarmer opened this issue Feb 19, 2025 · 4 comments
Open

Class in list inferred as mixin type when using generic typevar #18712

NathanFarmer opened this issue Feb 19, 2025 · 4 comments
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions

Comments

@NathanFarmer
Copy link

NathanFarmer commented Feb 19, 2025

Bug Report

Subclasses of 2 classes:

  1. First class with generic typevar
  2. Second mixin class

are inferred as the mixin class when added to a list

To Reproduce

This works:

class SuperDuperClass: ...


class MixinClass: ...


class ConcreteWorkerClass(SuperDuperClass, MixinClass):
    ...


class ConcreteWorkerClass2(SuperDuperClass, MixinClass):
    ...


def do_stuff_with_workers(workers: list[SuperDuperClass]):
    ...


concrete_workers = [ConcreteWorkerClass(), ConcreteWorkerClass2()]
do_stuff_with_workers(concrete_workers)

On the last line, this raises the error error: Argument 1 to "do_stuff_with_workers" has incompatible type "list[MixinClass]"; expected "list[SuperDuperClass[Any]]" [arg-type]:

from typing import Generic, TypeVar

T = TypeVar('T')


class SuperDuperClass(Generic[T]): ...


class MixinClass: ...


class ConcreteWorkerClass(SuperDuperClass[int], MixinClass):
    ...


class ConcreteWorkerClass2(SuperDuperClass[float], MixinClass):
    ...


def do_stuff_with_workers(workers: list[SuperDuperClass]):
    ...


concrete_workers = [ConcreteWorkerClass(), ConcreteWorkerClass2()]
do_stuff_with_workers(concrete_workers)

Expected Behavior

No type error

Actual Behavior

Type error

Your Environment

  • Mypy version used: 1.13.0
  • Mypy command-line flags: --disallow-untyped-decorators
  • Python version used: 3.11
@NathanFarmer NathanFarmer added the bug mypy got something wrong label Feb 19, 2025
@A5rocks
Copy link
Collaborator

A5rocks commented Feb 20, 2025

I think this is just join-v-union. I recommend explicitly telling mypy what type to use via a type hint like concrete_workers: ... = ....

(join-v-union just being "should mypy try to come up with a short description that all the classes match? or should mypy just be lazy and say it's just the union of the types")

@A5rocks A5rocks added the topic-join-v-union Using join vs. using unions label Feb 20, 2025
@NathanFarmer
Copy link
Author

Thanks for the quick response! concrete_workers: list[SuperDuperClass] = [ConcreteWorkerClass(), ConcreteWorkerClass2()] does suppress the mypy error. I think I understand why it happens based on your explanation.

This is for a library API, so we would be asking users of our package to do that. We can help the situation with API documentation, but at the end of the day I'm still hoping there is something that can be done on the mypy side.

@A5rocks
Copy link
Collaborator

A5rocks commented Feb 20, 2025

You could add an intermediate class (ie class IntermediateClass(SuperDuperClass[T], MixinClass)) that both workers inherit from. I think mypy should choose that (if not then I don't know another way). Then do_stuff_with_workers would need to take a Sequence due to variance.

Otherwise mypy's behavior is generally switching to prefer unions so you could wait for this issue to be solved (but it may take a while).

@NathanFarmer
Copy link
Author

Adding the intermediate class doesn't really work for my use case given that the generic typevar can be almost anything. It's being used to narrow a type from Any. We would be asking implementers to add that intermediate class for most classes they write. I think we will document it and hope this issue is solved eventually.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions
Projects
None yet
Development

No branches or pull requests

2 participants