Wrapped methods are not properly resolved until after the class definition
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 20.6k
- Forks
- 3.3k
- Métriques de merge des PR
- Métriques de PR en attente
Description
Context & setup
Consider the following simple protocol:
class Proto(Protocol):
@property
def f(self) -> int:
...
One can implement this protocol wrongly as follows:
T = TypeVar("T")
P = ParamSpec("P")
def wrapper(f: Callable[P, T]) -> Callable[P, str]:
def wrapped(*args: P.args, **kwargs: P.kwargs) -> str:
return 0
return wrapped
class Impl:
@wrapper # converts return type from `bool` to `str`, while the protocol expects `int`
def f(self) -> bool:
return False
# error: Incompatible return value type (got "Impl", expected "Proto") [return-value]
# note: Following member(s) of "Impl" have conflicts:
# note: Expected:
# note: def f(self) -> int
# note: Got:
# note: def f(self) -> str
def b() -> Proto:
return Impl()
The bug (false negative)
When this same function b is placed before class Impl, mypy does not report an error. Adding reveal_type(Impl.f) before and after the class definition hints at what is going on:
main.py:21: error: Expression has type "Any" [misc]
main.py:21: error: Cannot determine type of "f" [has-type]
main.py:21: note: Revealed type is "Any"
main.py:21: error: Name "Impl" is used before definition [used-before-def]
main.py:28: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
If f is not decorated but explicitly returns str, one gets
main.py:21: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
main.py:21: error: Name "Impl" is used before definition [used-before-def]
main.py:27: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
as expected. This indicates that the wrapped decorator is only properly resolved for code after the class definition.
False positive variant
If f is decorated with @property, this can also produce false positives. This is how I encountered the issue in the first place. Playground.
Additional info
Python: 3.11
Mypy: 371219347a6d17e16924bbabf3e693c6874e7138
Flags: --strict --disallow-any-*
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Reproduisez le faux négatif et le faux positif dans main.py à l’aide des exemples mypy-play liés, en comparant l’emplacement de la fonction, @wrapper et @property. Suivez la manière dont le décorateur et le type de la méthode encapsulée sont résolus avant et après class Impl ; le travail est terminé lorsque les deux variantes signalent systématiquement les diagnostics attendus de compatibilité avec le protocole.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- tooling
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100