python / python/mypy

Unexpected Errors When Using Invariant, Covariant, and Contravariant Type Variables in Protocols for `__class_getitem__`

Open
#17,733 2 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

Bug Report

I encountered an issue while defining a protocol with type variables that are invariant, covariant, and contravariant for the __class_getitem__ method. My goal was to create a protocol that supports different variance for the key parameter. I expected that at least one of the variance options (invariant, covariant, or contravariant) would work without errors. However, all three approaches result in type errors:

  1. Invariant Type Variable: Using an invariant type variable T results in an error because the protocol expects a covariant type variable in this context.
  2. Covariant Type Variable: When using a covariant type variable T_co, I cannot use it as a parameter for the __class_getitem__ method, which expects invariance or contravariance.
  3. Contravariant Type Variable: Using a contravariant type variable T_contra also leads to an error since it is expected to be covariant in this context.

I was expecting that at least one of these cases would not raise an error.

To Reproduce

from abc import abstractmethod
from typing import Protocol, Self, TypeVar

T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)
R = TypeVar("R")
R_co = TypeVar("R_co", covariant=True)
R_contra = TypeVar("R_contra", contravariant=True)

class SupportsClassGetItemInvariant(Protocol[T, R_co]):
    @classmethod
    @abstractmethod
    def __class_getitem__(cls: type[Self], key: T, /) -> R_co: ...

class SupportsClassGetItemCovariant(Protocol[T_co, R_co]):
    @classmethod
    @abstractmethod
    def __class_getitem__(cls: type[Self], key: T_co, /) -> R_co: ...

class SupportsClassGetItemContravariant(Protocol[T_contra, R_co]):
    @classmethod
    @abstractmethod
    def __class_getitem__(cls: type[Self], key: T_contra, /) -> R_co: ...

Expected Behavior

I expected that at least one of these variations (invariant, covariant, or contravariant) would work without causing a type error in Mypy, allowing me to use the type variable as the key parameter in the __class_getitem__ method.

Actual Behavior

Mypy raises errors for all three cases, indicating that none of the variance annotations are accepted for the key parameter in the __class_getitem__ method within the protocol.

Your Environment

  • Mypy version used: mypy 1.11.2
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini: None
  • Python version used: Python 3.12.2

I would appreciate any insights or suggestions on how to approach this issue, as I was expecting at least one of these scenarios to work.

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 three protocol definitions from the issue with Python 3.12.2 and mypy 1.11.2, then compare the reported variance diagnostics. Review mypy's protocol variance handling and existing tests for class_getitem; done means establishing the intended valid variance behavior and adding regression coverage for these cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.