Skip to content

parameter specification variance#2215

Open
KotlinIsland wants to merge 1 commit intopython:mainfrom
KotlinIsland:paramspec-variance
Open

parameter specification variance#2215
KotlinIsland wants to merge 1 commit intopython:mainfrom
KotlinIsland:paramspec-variance

Conversation

@KotlinIsland
Copy link
Copy Markdown
Contributor

@KotlinIsland KotlinIsland force-pushed the paramspec-variance branch 3 times, most recently from 5f8e0cc to d9d82c9 Compare March 9, 2026 03:13
@srittau srittau added the topic: typing spec For improving the typing spec label Mar 9, 2026
Copy link
Copy Markdown
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like adding this if we can get it specified nicely, but this PR is not ready; the proposed test is incorrect.

Also, https://typing.python.org/en/latest/spec/generics.html#paramspec-variables still says variance on ParamSpec is unsupported; this should be updated.

I'd also like to see an implementation in at least one type checker, even if only as a draft PR, so we can be confident this is something that can be feasibly implemented.

def in_f(self, *args: OutP.args, **kwargs: OutP.kwargs): ... # E
def out_f(self) -> Callable[OutP, None]: ... # OK

out_int_old: ContravariantParamSpecOld[int] = ContravariantParamSpecOld()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should presumably be using CovariantParamSpecOld, not ContravariantParamSpecOld.

in_obj = in_int # E


class CovariantParamSpec[**OutP]:
Copy link
Copy Markdown
Member

@JelleZijlstra JelleZijlstra Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class should not actually be covariant. Callable's argument is a contravariant position, so the variance flips.

You can see that in current type checkers with a simpler example that just uses a TypeVar:

from typing import Callable

class X[T]:
    def meth(self) -> Callable[[T], None]:
        raise NotImplementedError

def take_xint(x: X[int]): ...
def take_xobj(x: X[object]): ...

def f(xobj: X[object], xint: X[int]):
    take_xint(xobj)  # ok
    take_xobj(xint)  # error

X here is contravariant.

A correct covariant class would look like this:

from typing import Callable

class UseCallback[T]:
    def meth(self, callback: Callable[[T], None]):
        raise NotImplementedError

def take_xint(x: UseCallback[int]): ...
def take_xobj(x: UseCallback[object]): ...

def f(xobj: UseCallback[object], xint: UseCallback[int]):
    take_xint(xobj)  # error
    take_xobj(xint)  # ok

@davidhalter
Copy link
Copy Markdown
Contributor

I personally would also like to see tests added for param spec variance inference, since that would probably need to be supported as well.

@JelleZijlstra
Copy link
Copy Markdown
Member

The test cases do have variance inference, though as I noted some of the cases are wrong. But it would probably be useful to have a few more cases, and I'd recommend putting the tests for paramspec variance in their own file so we can track type checker support more precisely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic: typing spec For improving the typing spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants