A decorator implementing the descriptor protocol is incorrectly regarded read-only
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Bug Report
A class-based decorator that implements the data descriptor protocol is incorrectly regarded as read-only, and does not match against itself in a Protocol. This bug was discovered when trying to implement protocols that match against SQLAlchemy's declared_attr decorator.
To Reproduce
Pattern in typical use:
from sqlalchemy.orm import Mapped, declared_attr
class MyModel:
@declared_attr
@classmethod
def foo(cls) -> Mapped[int]: ...
reveal_type(foo) # declared_attr[int]
reveal_type(foo.__set__) # exists, so this is a data descriptor
reveal_type(foo.__get__) # Typed as `-> int | InstrumentedAttribute[int]`, unlike `property.__get__`
class MyProtocol:
foo: declared_attr[int]
o: MyProtocol = MyModel() # Error: MyProtocol.foo expected settable variable, got read-only attribute
Here is an isolated code sample without external dependencies. This bug does not appear when the method decorator is replaced with a function call after method definition, and appears to be a side-effect of how Mypy processes decorators.
https://mypy-play.net/?gist=5eb1e9262cd880a77fd3b9fb5bb7dca0
Marking the attribute as read-only in the Protocol using @property works when the attribute is accessed via the instance, but not when accessed via the class (as o.__class__.foo), because it gets typed as property.fget (Mypy) or as an error (Pyright), instead of the expected declared_attr.__get__ return type.
This problem exists in all versions of Mypy available at mypy-play, including the latest release 1.8.0. Pyright 1.1.338 does not have this problem.
Expected Behavior
A decorated method that is turned into a descriptor should not be regarded as read-only when matching against a protocol.
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
Beginne mit dem isolierten mypy-play-Sample und den Pfaden zur Decorator-Verarbeitung und zur Attributübereinstimmung von Protocol, die es durchläuft. Reproduziere die Diagnose für den schreibgeschützten Zugriff und verfolge dann, wie die Descriptor-Methoden der dekorierten Methode typisiert werden; die Aufgabe ist abgeschlossen, wenn ein Daten-Descriptor zu Protocol als setzbar passt und der Klassen-Zugriff den erwarteten get-Rückgabetyp beibehält.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python, sqlalchemy
- Bereich
- compilers, devtools
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100