Skip to content

parameter specification/type variable tuple variance - #2215

Open
KotlinIsland wants to merge 2 commits into
python:mainfrom
KotlinIsland:paramspec-variance
Open

parameter specification/type variable tuple variance#2215
KotlinIsland wants to merge 2 commits into
python:mainfrom
KotlinIsland:paramspec-variance

Conversation

@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
@KotlinIsland
KotlinIsland force-pushed the paramspec-variance branch 2 times, most recently from 81ba400 to 54ad5e1 Compare April 1, 2026 07:59

@JelleZijlstra JelleZijlstra left a comment

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.

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.

Comment thread conformance/tests/generics_paramspec_semantics.py Outdated
Comment thread conformance/tests/generics_paramspec_semantics.py Outdated
@davidhalter

Copy link
Copy Markdown
Collaborator

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.

@KotlinIsland

KotlinIsland commented Apr 3, 2026

Copy link
Copy Markdown
Contributor Author

I'd also like to see an implementation in at least one type checker

I have wip support in PyCharm, but I could also add it to basedpyright

it was very straightforward to implement

@davidhalter

davidhalter commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

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.

Oh right, sorry I didn't see them, because I thought they would be in a different file. I'm also very much +1 on putting those tests into a different file (for example generics_paramspec_variance.py).

@KotlinIsland
KotlinIsland force-pushed the paramspec-variance branch 2 times, most recently from 8f30004 to 117964e Compare April 7, 2026 01:29
@KotlinIsland KotlinIsland changed the title parameter specification variance parameter specification/type variable tuple variance Apr 7, 2026
@KotlinIsland

Copy link
Copy Markdown
Contributor Author

support has landed in pycharm and cpython

@JelleZijlstra

Copy link
Copy Markdown
Member

https://typing.python.org/en/latest/spec/generics.html#paramspec-variables stills says that we don't support variance in ParamSpec, that should be fixed in this PR.

@JelleZijlstra

Copy link
Copy Markdown
Member

Also can you open an issue on python/typing-council asking for a formal pronouncement?

@KotlinIsland

Copy link
Copy Markdown
Contributor Author

python/typing-council#59

@JelleZijlstra

Copy link
Copy Markdown
Member

Noticed some more issues:

  • generics_typevartuple_basic.py still has a line Ts1 = TypeVarTuple("Ts1", covariant=True) # E. But of course that's legal now. I'd delete the line and leave all variance testing to the new file generics_typevartuple_variance.py.
  • The description of the variance inference algorithm says "In the upper specialized class, specialize the target type parameter with an object instance". But that doesn't make sense for ParamSpec and TypeVarTuple. I think TypeVarTuple should instead be specialized with *tuple[object, ...]. For ParamSpec perhaps Callable[..., object] is right but not 100% sure.
  • The way the tests are written produces some incidental errors, like from ty generics_paramspec_variance.py:28:23: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `(...) -> Unknown` and mypy generics_typevartuple_variance.py:18: error: Missing return statement [empty-body]. Should add something like raise NotImplementedError.

@KotlinIsland

KotlinIsland commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

Callable[..., object] is right but not 100% sure.

not Callable, and not ..., as this is the gradual form. but (*object, **object), which is a non-denotable psuedo representation of the widest value for a parameter specification

@JelleZijlstra

Copy link
Copy Markdown
Member

I don't think that's right. The form we want here should be a supertype of every other possible value, and something like a one-parameter callable is not a subtype of (*: object, **: object).

@AlexWaygood

Copy link
Copy Markdown
Member

In ty we have a type that we spell as Top[Callable[..., object]] in ty's type display (a fully static type describing the infinite union of all possible Callable types). It's not representable using standard Python notation currently.

thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
## Summary

This PR adds `covariant`, `contravariant`, and `infer_variance` support
to `ParamSpec`.

See: python/typing#2215.

See:
astral-sh#24479 (review).

@davidhalter davidhalter left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like the general approach. I think it would also be useful to have at least one type checker with a proper implementation of this new feature.

Comment thread docs/spec/generics.rst Outdated
Comment thread conformance/tests/generics_typevartuple_basic.py Outdated
@KotlinIsland

KotlinIsland commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

proper implementation of this new feature.

see the mr description, it's implemented in pycharm and basedpyright

@KotlinIsland
KotlinIsland requested a review from davidhalter June 22, 2026 14:00
@KotlinIsland
KotlinIsland force-pushed the paramspec-variance branch 3 times, most recently from 94b1653 to 7a4c793 Compare June 22, 2026 14:10
Comment thread conformance/tests/generics_typevartuple_variance.py Outdated
@KotlinIsland
KotlinIsland requested a review from rchen152 June 23, 2026 02:00
@KotlinIsland
KotlinIsland force-pushed the paramspec-variance branch 3 times, most recently from 7c7dc6a to 34c9c0d Compare June 23, 2026 03:52
Comment thread conformance/results/ty/generics_paramspec_variance.toml Outdated
@KotlinIsland
KotlinIsland force-pushed the paramspec-variance branch 3 times, most recently from 1d4ba59 to 424faf1 Compare July 14, 2026 05:56

@jorenham jorenham left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The lack of co/contra variance for TypeVarTuples has caused problems for me on several occasions in the past. So thanks for patching this hole in the spec!

The changes to the variance algorithm also look like the way to go. It's too bad we don't have a general notion of the "top signature" like we do for the general top type (object) and the top product type (tuple[object, ...]). But I expect that the way you informally describe it here will be understood just fine by anyone that needs to know.

@KotlinIsland

Copy link
Copy Markdown
Contributor Author

@carljm good to review now

@KotlinIsland
KotlinIsland requested a review from carljm August 6, 2026 22:25

@carljm carljm left a comment

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.

The direction here remains correct IMO, but I think there are some updates needed.

Comment thread conformance/results/basilisk/generics_paramspec_variance.toml Outdated
Comment thread conformance/results/pycroscope/generics_typevartuple_basic.toml Outdated
Comment thread conformance/tests/generics_typevartuple_variance.py Outdated
Comment thread conformance/tests/generics_typevartuple_variance.py Outdated
Comment thread conformance/tests/generics_typevartuple_variance.py
Comment thread docs/spec/generics.rst
Comment thread conformance/tests/generics_paramspec_variance.py Outdated
def f(self, *args: InP.args, **kwargs: InP.kwargs): ...

in_obj: ContravariantParamSpec[object] = ContravariantParamSpec[int]() # E
in_int: ContravariantParamSpec[int] = ContravariantParamSpec[object]() # OK

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.

All of the ParamSpec specializations here have one required positional parameter. Could we add cases involving keyword-only or positional-only parameters, parameter names, defaults, and differing callable arities, perhaps also Concatenate? Otherwise an implementation that compares only a tuple of parameter types could pass without implementing actual signature assignability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

without the ability to denote any of these forms (apart from arity), i'm having trouble find a way to test them

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

okay, i think i managed to get some of these cases using an invariant box

Comment thread docs/spec/generics.rst
constructor call. No further inference is needed.

3. Create two specialized versions of the class. We'll refer to these as
2. Create two specialized versions of the class. We'll refer to these as

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.

What should the "dummy type instance" be when one of the other parameters is a TypeVarTuple or ParamSpec? An ordinary dummy class isn't a valid specialization for either kind. Could we spell out kind-compatible substitutions and cover a mixed example such as:

class Mixed[T, *Ts, **P]:
    def f(self, x: T, /, *args: P.args, **kwargs: P.kwargs) -> tuple[*Ts]: ...

I would expect T and P to be contravariant and Ts to be covariant, independently.

Comment thread conformance/tests/generics_typevartuple_variance.py
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.

9 participants