python / python/typeshed

functools.update_wrapper() will not accept descriptor (decorator) as wrapper

Abierto
#9,846 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

priority: regression
Lenguaje dominante
Python
Estrellas
5.1k
Forks
2.1k
Merge medio
1 d 19 h
PR fusionados (30 d)
82

Descripción

Description:

The type stub for functools.update_wrapper() does not permit a descriptor for the wrapper (first) argument, a legitimate case arising when defining a descriptor-type decorator (like @property is).
https://github.com/python/typeshed/blob/main/stdlib/functools.pyi#L83

@erictraut While perhaps not exactly a regression, this used to work up until this commit of yours here (last week): https://github.com/python/typeshed/commit/9c4bfd5d11b47677e128452baa8ac7eeb1903a8e#diff-02c51ed45b741b6765c365fb5d6578c586510bbbd57b10a1dba10aeba37ae157L70-R84. I say not exactly a regression because the prior constraint introduced here was perhaps overly loose: https://github.com/python/typeshed/pull/4627/files

Example
As an example, the following (pyright) typing error is detected in the lazyproperty descriptor-type decorator defined here: https://gist.github.com/scanny/255f16339b74b2b59e896d67bd459d6b

lazyproperty.py|65 col 34-38 error| Argument of type "Self@lazyproperty[T@lazyproperty]"
  cannot be assigned to parameter "wrapper"
  of type "(**_PWrapper@update_wrapper) -> _RWapper@update_wrapper"
  in function "update_wrapper"
 Type "Self@lazyproperty[T@lazyproperty]" cannot be assigned to
 type "(**_PWrapper@update_wrapper) -> _RWapper@update_wrapper"

To Reproduce
The gist referenced above has no dependencies other than the stdlib so can be type-checked directly (using pyright v1.1.296):

$ pyright lazyproperty.py

Analysis

The type definition at work constrains wrapper to be a Callable[_PWrapper, _RWapper]. I believe it also needs to permit Descriptor, although I'm not completely sure what that type definition is. My best guess is something like this:

class HasDunderGet(Protocol):
    def __get__(self, obj: Any, type: type = None) -> Any: ...
class HasDunderSet(Protocol):
    def __set__(self, obj: Any, value: Any) -> None: ...
class HasDunderDelete(Protocol):
    def __delete__(self, obj: Any) -> None: ...

Descriptor = Union[HasDunderGet, HasDunderSet, HasDunderDelete]

Expected behavior
As far as I can tell, this is a legitimate usage of functools.update_wrapper() and decorators that produce a descriptor (like @property does) are also legitimate Python. It has certainly worked for a long time and also even typechecked with pyright previously (v1.1.273), maybe six months ago. So I would expect it to be accepted as written.

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

Empieza por stdlib/functools.pyi, alrededor de las líneas 82-87, y compara la anotación del wrapper con el uso del descriptor en el gist enlazado lazyproperty.py. Ejecuta pyright v1.1.296 sobre lazyproperty.py para reproducir el error y, después, inspecciona el commit de typeshed referenciado y el pull request anterior para obtener contexto. Se considera terminado cuando el decorador de descriptor legítimo pasa la comprobación de tipos sin debilitar la tipificación de update_wrapper en aspectos no relacionados.

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

Evaluación

Stack tecnológico
python
Área
developer-experience, tooling
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.