Skip to content

Allow empty type applications for ParamSpec - #20572

Merged
hauntsaninja merged 4 commits into
python:masterfrom
A5rocks:empty-tuple-paramspec
Jan 13, 2026
Merged

Allow empty type applications for ParamSpec#20572
hauntsaninja merged 4 commits into
python:masterfrom
A5rocks:empty-tuple-paramspec

Conversation

@A5rocks

@A5rocks A5rocks commented Jan 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #20571. @JelleZijlstra I somehow didn't realize this was something until I saw microsoft/pyright#11225, so I didn't put this as a test case last time...

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@A5rocks

A5rocks commented Jan 13, 2026

Copy link
Copy Markdown
Collaborator Author

I just realized in the pyright issue there, @erictraut thinks this should error (i.e. current behavior) rather than be accepted. (I just skimmed and realized that my last PR didn't address this case)

I think generally we should be forgiving and that this is justifiable by the PEP, but I would be OK if we want to discuss this more instead. The weak justification being just this:

By the rules defined above, spelling a concrete instance of a class generic with respect to only a single ParamSpec would require unsightly double brackets. For aesthetic purposes we allow these to be omitted.

And there being no reason that shouldn't apply for an empty Parameters.

@hauntsaninja hauntsaninja 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 thing that is clearly a bug in pyright is the inconsistency between the class case and the type alias case.

I think the behaviour of this PR makes sense. This is the test case I used when I reviewed it (maybe worth adding?):

# flags: --strict
from typing import Callable

class Cls[**P]:
    def __call__(self, *a: P.args, **k: P.kwargs) -> None: ...
type TA[**P] = Callable[P, None]

def f(cls: Cls[()], vt: TA[()], vt2: TA[(int,)]) -> None:
    reveal_type(cls)
    reveal_type(vt)
    reveal_type(vt2)

    cls()
    cls(1)

    vt()
    vt(1)
    
    vt2()
    vt2(1)

@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@hauntsaninja
hauntsaninja merged commit 1f0ab28 into python:master Jan 13, 2026
23 checks passed
@A5rocks
A5rocks deleted the empty-tuple-paramspec branch January 13, 2026 06:09
michaelm-openai pushed a commit to michaelm-openai/mypy that referenced this pull request Jan 16, 2026
Fixes python#20571.
I somehow didn't realize this was something until I saw
microsoft/pyright#11225, so I didn't put this
as a test case last time...

---------

Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>
michaelm-openai pushed a commit to michaelm-openai/mypy that referenced this pull request Jan 16, 2026
Fixes python#20571.
I somehow didn't realize this was something until I saw
microsoft/pyright#11225, so I didn't put this
as a test case last time...

---------

Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ParamSpec should accept empty tuple

2 participants