python / python/mypy

`ClassVar` cannot contain `Self` type in generic classes

Open
#20,331 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

This rule seems correct when Self appears in a covariant position, like

class MyGeneric[T]:
    # ill-defined, what's the type of MySequence.example?
    example: ClassVar[Self]  # error: ClassVar cannot contain Self type in generic classes

However, if Self appears in a contravariant position, I do not see why this should be an error.

class MyGeneric[T]:
    pre_method_hooks: ClassVar[list[ Callable[[Self], None] ]] = []

Full example: https://mypy-play.net/?mypy=master&python=3.12&gist=951d5b3a168378cd33d9fc4aebd4572e

from typing import Callable, ClassVar, Self, Any
from abc import abstractmethod

class Transform[X, Y]:
    pre_transform_hooks: ClassVar[list[Callable[[Self], None]]] = []  # ❌️

    def __init_subclass__(cls) -> None:
        super().__init_subclass__()
        original_transform = cls.transform

        def wrapped_transform(self: Self, x: X, /, *args: Any, **kwargs: Any) -> Y:
            for hook in cls.pre_transform_hooks:
                hook(self)
            return original_transform(self, x, *args, **kwargs)
        cls.transform = wrapped_transform  # type: ignore[method-assign]

    @abstractmethod
    def transform(self, x: X, /) -> Y: ...

class Demo(Transform[int, int]):
    def transform(self, x: int, /) -> int: return 0

    def hello_world(self) -> None:
        print(f"Hello, world! from {self}")

    pre_transform_hooks: ClassVar[list[Callable]] = [hello_world]

Demo().transform(0)  # prints "Hello, world! from <__main__.Demo object at ...>"
main.py:5: error: ClassVar cannot contain Self type in generic classes  [misc]
Found 1 error in 1 file (checked 1 source file)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the diagnostic using the linked mypy-play example or the shown main.py snippet, then trace the ClassVar and Self validation that rejects the generic class member. Compare the covariant and contravariant cases and check existing tests for this rule. Done means the intended contravariant case is handled consistently while the invalid covariant case remains diagnosed.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.