Skip to content

Using Self with __init_subclass__ uses the base class as Self rather than the class being defined #15807

Open
@chrisbouchard

Description

@chrisbouchard

Bug Report

I wanted to define two type hierarchies: model types, and handlers that are contravariant over models, where models can specify their handlers as class keyword arguments. I've included a simplified example in the To Reproduce section below.

The problem seems to be that, when __init_subclass__ runs for Foo, it's treating Self as if it were Model rather than Foo, which fails because T is contravariant and FooHandler is not a subtype of Handler[Model].

To Reproduce

Playground Link

from __future__ import annotations
from typing import Any, ClassVar, Self, TypeVar, Generic


T = TypeVar("T", bound="Model", contravariant=True)

class Handler(Generic[T]):
    pass

class Model:
    handler: ClassVar[type[Handler[Self]]]
    
    def __init_subclass__(cls, *, handler: type[Handler[Self]]) -> None:
        super().__init_subclass__()
        cls.handler = handler


class FooHandler(Handler[Foo]):
    pass

class Foo(Model, handler=FooHandler):
    pass

Expected Behavior

I expected mypy to treat Self as Foo when running Model.__init_subclass__(Foo, ...).

Actual Behavior

main.py:15: error: Incompatible types in assignment (expression has type "type[Handler[Self]]", variable has type "type[Handler[Model]]")  [assignment]
main.py:21: error: Argument "handler" to "__init_subclass__" of "Model" has incompatible type "type[FooHandler]"; expected "type[Handler[Model]]"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.4.1 and master branch (both from playground)
  • Python version used: 3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-self-typesTypes for self

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions