python / python/mypy

Wrong type inference for class with metaclass that acts as descriptor

Offen
#10,964 2 Kommentare 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug priority-2-low topic-descriptors topic-metaclasses
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug Report
Descriptor protocol does not work on metaclasses - defining __get__ method in a metaclass does not make classes using that metaclass work as descriptors.

To Reproduce

  1. Put this code in a py file:
from typing import Any, TYPE_CHECKING, overload

class _IntDescriptorMeta(type):
    def __get__(self, instance: Any, owner: Any) -> int:
        return 123

class IntDescriptorClass(metaclass=_IntDescriptorMeta):
    ...

class IntDescriptor:
    def __get__(self, instance: Any, owner: Any) -> int:
        return 123

class X:
    number_cls = IntDescriptorClass
    number = IntDescriptor()

print(X.number_cls)
print(X().number_cls)
print(X.number)
print(X().number)

if TYPE_CHECKING:
    reveal_type(X.number_cls)
    reveal_type(X().number_cls)
    reveal_type(X.number)
    reveal_type(X().number)
  1. Run it, you should see:
123
123
123
123
  1. Type check it with mypy and see the incorrect output.

Expected Behavior
I expected mypy to infer type of X.number_cls/X().number_cls correctly:

main.py:24: note: Revealed type is "builtins.int"
main.py:25: note: Revealed type is "builtins.int"
main.py:26: note: Revealed type is "builtins.int"
main.py:27: note: Revealed type is "builtins.int"

Note:
To simplify this example, __get__ returns an int no matter if instance is None or not but I assume a fix would also make it work properly with overloads as it does for instances of classes.

Actual Behavior

Mypy does not infer type of X.number_cls/X().number_cls correctly:

main.py:24: note: Revealed type is "def () -> __main__.IntDescriptorClass"
main.py:25: note: Revealed type is "def () -> __main__.IntDescriptorClass"
main.py:26: note: Revealed type is "builtins.int"
main.py:27: note: Revealed type is "builtins.int"

Your Environment

Additional notes
This works properly on pyright 1.1.161+, see the issue: https://github.com/microsoft/pyright/issues/2164

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 damit, die bereitgestellte Python-Reproduktion auszuführen und die aufgedeckten Typen mit mypy zu prüfen; verwende bei Bedarf die verknüpfte mypy-play-URL. Verfolge mypys Typinferenz für Deskriptoren, die auf Metaklassen definiert sind, und vergleiche sie mit gewöhnlichen Instanzdeskriptoren. Als abgeschlossen gilt die Aufgabe, wenn alle vier Reveals für den klassenbasierten Deskriptor builtins.int melden und das bestehende Verhalten von Deskriptoren weiterhin korrekt ist.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
devtools
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
42/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.