python / python/mypy

Incorrectly narrows `type` from signature annotation to `Never`

Open
#20,467 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug pending
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

When taking the parameter type from the signature, mypy incorrectly thinks that the annotation of type <class 'type'> is the actual type of the variable.

To Reproduce

In the following example, process_signature extracts the type of input from the signature of a function and then tries to match it to int or float. However the type of input_type is <class 'type'> not int or float. Therefore, when the code is run assert_never is reached.

from typing import Callable, assert_never, reveal_type
from inspect import signature


def takes_int(x: int) -> None:
    print(f"{x} is a int")

def process_signature(
        func: Callable[[int], None] | Callable[[float], None]
) -> None:
    input_type = signature(func).parameters['x'].annotation
    reveal_type(input_type)  # Runtime type is 'type' => should reach `case _:`
    match input_type:
        case int():
            print("This is an int")
        case float():
            print("This is a float")
        case _:
            assert_never(input_type)  # this is reached during runtime


process_signature(takes_int)

Expected Behavior

mypy to complain that input to assert_never is <class 'type'> but expected Never.

Actual Behavior

mypy does not complain.

Your Environment

  • Mypy version used: 1.19.1
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.13.7

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 by running the provided reproducer with mypy 1.19.1 and inspect the revealed type for input_type. Trace how the match cases narrow the annotation from inspect.signature through assert_never. Done means mypy reports that the remaining input_type is type rather than treating the wildcard case as Never.

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
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.