python / python/mypy

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

Aperta
#16,709 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

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.

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con il sample isolato di mypy-play e con i percorsi di elaborazione dei decorator e di corrispondenza degli attributi di Protocol che esercita. Riproduci la diagnostica di sola lettura, quindi traccia il modo in cui vengono tipizzati i metodi descriptor del metodo decorato; il risultato è raggiunto quando un descriptor di dati corrisponde a Protocol come assegnabile e l’accesso alla classe conserva il tipo restituito atteso di get.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python, sqlalchemy
Ambito
compilers, 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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.