Decorator confused by keyword-only parameter with Union, erroneously expects `<nothing>`, but only in some cases?
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 20.6k
- Fork
- 3.3k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
I am trying to write a decorator function that takes optional keyword arguments. I.e., it can be called like …
@decr
def func(…):
…
… or …
@decr(param=val)
def func(…):
…
I have gotten myself into a pickle with a variation of the second.
# test_case.py
from __future__ import annotations
from functools import wraps
from typing import Callable, Optional, Protocol, TypeVar, Union, overload
_ReturnsIntT = Callable[..., int]
_SubjectT = TypeVar("_SubjectT", bound=_ReturnsIntT)
class _DecoratedT(Protocol):
def __call__(self, *args: int, join_str="", **kw: int) -> str:
...
@overload
def strintify(
*,
addtl: Union[int, _SubjectT] = 0,
) -> Callable[[_SubjectT], _DecoratedT]:
...
@overload
def strintify(
f: _SubjectT,
) -> _DecoratedT:
...
def strintify(
f: Optional[_SubjectT] = None,
*,
addtl: Union[int, _SubjectT] = 0,
) -> Union[Callable[[_SubjectT], _DecoratedT], _DecoratedT]:
assert callable(f) or f is None
def _decorator(f):
@wraps(f)
def _f(*args: int, join_str="", **kw: int) -> str:
res = [f(*args, **kw)]
if isinstance(addtl, int):
res[0] += addtl
else:
res.append(addtl(*args, **kw))
return join_str.join(str(i) for i in res)
return _f
return _decorator(f) if callable(f) else _decorator
@strintify # <-- this is fine
def func1(a: int, b: int, c: int) -> int:
return a**3 + b**2 + c
print(func1(3, 2, 1)) # prints 32
print(func1(3, 2, 1, join_str="|")) # prints 32
@strintify(addtl=1) # <-- #@#@#@#@# ERRORS HERE #@#@#@#@#
def func2(a: int, b: int, c: int) -> int:
return a**3 + b**2 + c
print(func2(3, 2, 1)) # prints 33
print(func2(3, 2, 1, join_str="|")) # prints 33
@strintify(addtl=lambda a, b, c: c**3 + b**2 + a) # <-- this is fine
def func3(a: int, b: int, c: int) -> int:
return a**3 + b**2 + c
print(func3(3, 2, 1)) # prints 328
print(func3(3, 2, 1, join_str="|")) # prints 32|8
Type-checking that gives:
% mypy --config=/dev/null test_case.py
/dev/null: No [mypy] section in config file
test_case.py:53: error: Argument 1 has incompatible type "Callable[[int, int, int], int]"; expected <nothing>
Found 1 error in 1 file (checked 1 source file)
% mypy --version
mypy 0.931
% python --version
Python 3.9.10
Apologies for the long use case, I haven't been able to figure out how to reduce it yet.
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia con l'esempio autonomo di test_case.py ed esegui mypy 0.931 usando il comando mostrato per riprodurre l'errore nel caso @strintify(addtl=1). Traccia il modo in cui viene analizzato l'overload Union riservato alle keyword, quindi aggiungi un test di regressione che mostri che func2 viene accettata, mentre i casi esistenti func1 e func3 rimangono validi.
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
- 35/100