python / python/mypy

A decorator implementing the descriptor protocol is incorrectly regarded read-only

Offen
#16,709 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug
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

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Ö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

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.