A decorator implementing the descriptor protocol is incorrectly regarded read-only
Nadie ha tomado este issue todavía.
- 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
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- 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