python / python/mypy

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

Abierto
#16,709 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

bug
Lenguaje dominante
Python
Estrellas
20.6k
Forks
3.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con el sample aislado de mypy-play y las rutas de procesamiento de decoradores y coincidencia de atributos de Protocol que este ejercita. Reproduce el diagnóstico de solo lectura y, después, sigue cómo se tipan los métodos descriptor del método decorado; el objetivo se alcanza cuando un descriptor de datos coincide con Protocol como asignable y el acceso de clase conserva el tipo de retorno esperado de get.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python, sqlalchemy
Área
compilers, devtools
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.