Skip to content

Mark TypeVar/ParamSpec/TypeVarTuple 'name' as positional-only#15805

Closed
adamtheturtle wants to merge 3 commits into
python:mainfrom
adamtheturtle:special-form-name-positional-only
Closed

Mark TypeVar/ParamSpec/TypeVarTuple 'name' as positional-only#15805
adamtheturtle wants to merge 3 commits into
python:mainfrom
adamtheturtle:special-form-name-positional-only

Conversation

@adamtheturtle
Copy link
Copy Markdown
Contributor

@adamtheturtle adamtheturtle commented May 17, 2026

Implements #15804.

TypeVar, ParamSpec, and TypeVarTuple declare name as positional-or-keyword, but every type checker (mypy, pyright, ty, pyre) requires it positionally. This is not a per-checker quirk: the typing spec (PEP 484 / 612 / 646) requires the first argument to be a string literal so the checker can bind it to the assigned variable statically, so any conformant checker must reject name=.... mypy core, no plugins:

from typing import TypeVar
T = TypeVar(name="T")  # error: TypeVar() expects a string literal as first argument  [misc]

This marks name positional-only on every constructor overload of the three classes so the stub matches that contract. It is a no-op for type checkers (they special-case these and ignore the signature); the point is third-party tools that read typeshed as ground truth and currently suggest the type-checker-breaking name= form.

Scope: NewType and the typing_extensions <3.13 fallback classes are left out (introspectable runtime signatures, so they would need a stubtest-allowlist change); the >=3.13 typing_extensions path re-exports from typing and is covered transitively. On 3.10/3.11 these objects are pure-Python and introspectable, so allowlist entries are included for those versions.

adamtheturtle and others added 3 commits May 17, 2026 12:37
These special-form constructors require `name` to be a positional
string literal (PEP 484 / 612 / 646); type checkers reject the keyword
form, e.g. mypy:

    error: TypeVar() expects a string literal as first argument  [misc]

The stubs advertised `name` as positional-or-keyword, which misleads
tools that consume typeshed as ground truth. No type-checker behaviour
changes (these are special-cased); the runtime objects are C-level with
no introspectable signature on 3.12+, so stubtest is unaffected there.

Refs python#15804

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On 3.10/3.11 these special forms are pure-Python with introspectable
signatures whose `name` is positional-or-keyword at runtime; the stub
now marks it positional-only (the type-system contract). stubtest only
checks positional-only accuracy on 3.10 (+ TypeVarTuple.__init__ on
3.11), so allowlist exactly those entries on those versions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@adamtheturtle
Copy link
Copy Markdown
Contributor Author

CI note: the failing pyright: Run test cases jobs are unrelated to this change. They fail only in stubs/click-web/ with Expected type arguments for generic class "ParamType" (reportMissingTypeArgument), caused by a recent click release making click.ParamType generic. This breaks every open PR against main right now and is fully fixed by #15803.

All checks affected by this PR pass: stubtest is green on every OS/version (including 3.10/3.11, where the new positional-only name needed allowlist entries), and all mypy jobs are green. This PR should go green once #15803 lands and this branch is rebased.

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.

2 participants