regression with Generic/ParamSpec between 1.4.1 and 1.5.0
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Bug Report
With 1.5.0, some of our software has started to surface a var-annotated ("Need type annotation for") error that it did not surface with 1.4.1; I could not find anything in the 1.5.0 release notes that suggested that this was intentional.
To Reproduce
Our actual software is a bit domain-specific, but I've tried to extract the details; the concept involves a data descriptor that attaches a callable to a class and generates a new callable that inverts how the callable's constructor and runtime arguments are supplied. The solution depends on ParamSpec to preserve the argument signatures and uses Generic so that the same internals can be used for different functions.
#!/usr/bin/env python3
from dataclasses import dataclass
from typing import Generic, ParamSpec, Protocol, Self, TypeVar, overload
P = ParamSpec("P")
T = TypeVar("T")
class Transform(Protocol[P, T]):
"""Generic protocol to transform a value using inputs."""
def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None:
raise NotImplementedError
def __call__(self, value: T) -> T:
raise NotImplementedError
class BoundTransform(Generic[P, T]):
"""Wrapper around a transform function that performs late-binding."""
def __init__(
self,
value: T,
transform_cls: type[Transform[P, T]],
) -> None:
self.value: T = value
self.transform_cls: type[Transform[P, T]] = transform_cls
def __call__(
self,
*args: P.args,
**kwargs: P.kwargs,
) -> T:
return self.transform_cls(*args, **kwargs)(self.value)
class HasValue(Protocol[T]):
value: T
class Transformer(Generic[P, T]):
"""Data descriptor that applies the bound transformer to something with a value."""
def __init__(
self,
transform_cls: type[Transform[P, T]],
) -> None:
self.transform_cls: type[Transform[P, T]] = transform_cls
@overload
def __get__(self, obj: None, objtype: type[HasValue[T]]) -> Self:
...
@overload
def __get__(self, obj: HasValue[T], objtype: type[HasValue[T]]) -> BoundTransform[P, T]:
...
def __get__(self, obj: HasValue[T] | None, objtype: type[HasValue[T]]) -> Self | BoundTransform[P, T]:
if obj is None:
return self
return BoundTransform(obj.value, self.transform_cls)
@dataclass(frozen=True)
class Add:
"""Transform implementation that adds two integers."""
value: int
def __call__(self, value: int) -> int:
return value + self.value
@dataclass(frozen=True)
class HasNumber(HasValue[int]):
"""HasValue implementation that holds an integer."""
value: int
add = Transformer(Add)
if __name__ == "__main__":
# Usage example
foo = HasNumber(42)
bar = foo.add(-42)
assert bar == 0
Expected Behavior
No errors from mypy. This is true for 1.4.1.
Actual Behavior
Errors. This is true for 1.5.0
main.py:81: error: Need type annotation for "add" [var-annotated]
Found 1 error in 1 file (checked 1 source file)
Your Environment
- Mypy version used:
1.4.1and1.5.0 - Mypy command-line flags: n/a
- Mypy configuration options from
mypy.ini(and other config files): n/a - Python version used: 3.11.4
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Verwende die bereitgestellte main.py-Reproduktion und führe sie mit mypy 1.4.1 und 1.5.0 aus. Konzentriere dich dabei auf die add-Zuweisung in Zeile 81 und deren var-annotated-Diagnose. Vergleiche das Verhalten des Generic/ParamSpec-Deskriptors zwischen den Versionen. Die Aufgabe ist erledigt, wenn der unerwartete Fehler behoben und die Regression durch einen geeigneten Test abgedeckt ist.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- tooling
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100