python / python/mypy

Making a type alias of a Callable that describes a decorator discards all type information

Aperta
#18,842 14 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug topic-type-alias
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Bug Report

Attempting to make a type alias for a decorator results in MyPy losing type information.

To Reproduce

from typing import *

type Fn[**P, R] = Callable[P, R]
type Decorator[**P, R] = Callable[[Fn[P, R]], Fn[P, R]]


def do_the_thing[**P, R]() -> Decorator[P, R]:
    def decorator(fn: Fn[P, R]) -> Fn[P, R]:
        return fn
    return decorator


@do_the_thing()
def blahblah() -> None:
    pass

Seems this is not an artifact of how variance is being calculated with the new type alias syntax as using the old style syntax has the same issue when declaring the type hints...

from typing import *

P = ParamSpec("P")
R_co = TypeVar("R_co", covariant=True)
Fn: TypeAlias = Callable[P, R_co]
Decorator: TypeAlias = Callable[[Fn[P, R_co]], Fn[P, R_co]]


def do_the_thing[**P, R_co]() -> Decorator[P, R_co]:
    def decorator(fn: Fn[P, R_co]) -> Fn[P, R_co]:
        return fn
    return decorator


@do_the_thing()
def blahblah() -> None:
    pass

...and the same issue if I just discard the type variables entirely in the function definition:

from typing import *

P = ParamSpec("P")
R_co = TypeVar("R_co", covariant=True)
Fn: TypeAlias = Callable[P, R_co]
Decorator: TypeAlias = Callable[[Fn[P, R_co]], Fn[P, R_co]]

def do_the_thing() -> Decorator[P, R_co]:
    def decorator(fn: Fn[P, R_co]) -> Fn[P, R_co]:
        return fn
    return decorator

@do_the_thing()
def blahblah() -> None:
    pass

Using a protocol doesn't work either:

from typing import *

type Fn[**P, R] = Callable[P, R]

class Decorator[**P, R](Protocol):
    def __call__(self, fn: Fn[P, R], /) -> Fn[P, R]: ...

def do_the_thing[**P, R]() -> Decorator[P, R]:
    def decorator(fn: Fn[P, R]) -> Fn[P, R]:
        return fn
    return decorator

@do_the_thing()
def blahblah() -> None:
    pass

...so it seems the type information is just totally discarded.

Strangely, if I don't type-alias the decorator, then it works fine (which is a workaround, but very annoying and verbose for more complicated logic).

# this is fine.
def do_the_thing[**P, R]() -> Callable[[Fn[P, R]], Fn[P, R]]:
    def decorator(fn: Fn[P, R]) -> Fn[P, R]:
        return fn
    return decorator

The problem is that I want to be able to alias Callable[[Fn[P, R]], Fn[P, R]] to a clean single identifier that I can reuse in the current type context, but I cannot find a nice way of doing that at the moment.

Expected Behavior

This should be valid.

Actual Behavior

main.py:14: error: Argument 1 has incompatible type "Callable[[], None]"; expected "Callable[[VarArg(Never), KwArg(Never)], Never]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Related Issues:

I came across GH-16512, but that was closed as fixed. Not sure if it is the same problem or not!

Your Environment

  • Mypy version used: 1.14.1, 1.15.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.12, 3.13
  • Reproducible in https://mypy-play.net/?mypy=1.15.0&python=3.13

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con il riproduttore minimo dell’alias di tipo del decoratore nell’issue e confrontalo con la versione inline di Callable, usando l’esempio mypy-play collegato o le versioni di mypy indicate. Traccia il punto in cui il Callable con alias perde le informazioni su ParamSpec e sul tipo restituito, quindi aggiungi un test di regressione che mostri che la funzione decorata mantiene la propria firma e passa senza errori.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
devtools
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
38/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.