python / python/mypy

`mypy` doesn't understand that `*args: P.args` implies that `args` is a `tuple`

Open
#19,663 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-calls topic-paramspec
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

from typing import Callable

def as_tuple[*Ts](*args: *Ts) -> tuple[*Ts]: return args
def tuple_identity[*Ts](t: tuple[*Ts]) -> tuple[*Ts]: return t
def tuple_identity2[T: tuple](t: T) -> T: return t


def test_paramspec[**P](
    dummy: Callable[P, None],  # ensure P is bound
    /,
    *args: P.args,
    **kwargs: P.kwargs,
) -> None:
    reveal_type(args)                   # N: "P.args`-1" 
    reveal_type( (*args,) )             # N: "builtins.tuple[P.args`-1, ...]"
    reveal_type( tuple(args) )          # N: "builtins.tuple[builtins.object, ...]"
    reveal_type(as_tuple(*args))        # N: "builtins.tuple[P.args`-1, ...]"
    reveal_type(tuple_identity(args))   # N: "builtins.tuple[Never, ...]"
                                        # E: [arg-type]
    reveal_type(tuple_identity2(args))  # N: "P.args`-1" ✅

    if isinstance(args, tuple):
        pass
    else:
        reveal_type(args)  # false negative [warn-unreachable]

https://mypy-play.net/?mypy=latest&python=3.12&flags=warn-unreachable&gist=3eabd8f4ee599e272934c94080f02bd8

Ideally, all the reveal_types should show the same result (or raise errors if considered misuse of ParamSpec[^1]), and the else-branch should trigger an unreachable warning.

[^1]: For instance, pyright says as_tuple(*args) is illegal. Code sample in pyright playground

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

Start with the supplied test_params reproducer and run it with warn-unreachable enabled, comparing mypy’s reveal_type results with the expected behavior and the linked playgrounds. Trace the handling of ParamSpec args and tuple narrowing in mypy; done means the reveal types are consistent or correctly rejected and the impossible else branch reports unreachable.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.