python / python/typeshed

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

Offen
#9,846 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

priority: regression
Vorherrschende Sprache
Python
Sterne
5.1k
Forks
2.1k
Ø Merge
1 T. 19 Std.
Gemergte PRs (30 T.)
82

Beschreibung

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.

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 stdlib/functools.pyi ungefähr in den Zeilen 82–87 und vergleiche die Wrapper-Annotation mit der Deskriptorverwendung im verlinkten lazyproperty.py-Gist. Führe pyright v1.1.296 für lazyproperty.py aus, um den Fehler zu reproduzieren, und untersuche anschließend den referenzierten typeshed-Commit und den früheren Pull Request als Kontext. Als erledigt gilt die Aufgabe, wenn der legitime Deskriptor-Decorator die Typprüfung besteht, ohne die Typisierung von update_wrapper in nicht verwandten Bereichen abzuschwächen.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
developer-experience, tooling
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.