python / python/mypy

Overloaded `__getattr__` resolves attributes to the overloaded function

Aperta
#15,692 2 commenti 2 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Bug Report

If __getattr__ is overloaded, all attribute accesses resolves as if they were getting __getattr__ itself. This is most obvious if you expect it to return a callable - mypy will complain that the args to the callable don't match the signatures of __getattr__. This does not happen if you call __getattr__ manually, only via regular attribute access.

To Reproduce

class A:
    @overload
    def __getattr__(self, name: str) -> Callable[..., Any]: ...
    @overload
    def __getattr__(self, name: int) -> int: ...
    def __getattr__(self, name: Any) -> Any:
        pass

reveal_type(A().__getattr__)
reveal_type(A().xyz)
reveal_type(A().__getattr__("xyz"))
reveal_type(A().__getattr__(1))

A().xyz(1, 2, 3)
main.py:7: error: Invalid signature "Callable[[A, int], int]" for "__getattr__"  [misc]
main.py:11: note: Revealed type is "Overload(def (builtins.str) -> def (*Any, **Any) -> Any, def (builtins.int) -> builtins.int)"
main.py:12: note: Revealed type is "Overload(def (builtins.str) -> def (*Any, **Any) -> Any, def (builtins.int) -> builtins.int)"
main.py:13: note: Revealed type is "def (*Any, **Any) -> Any"
main.py:14: note: Revealed type is "builtins.int"
main.py:16: error: No overload variant of "__getattr__" of "A" matches argument types "int", "int", "int"  [call-overload]
main.py:16: note: Possible overload variants:
main.py:16: note:     def __getattr__(self, str, /) -> Callable[..., Any]
main.py:16: note:     def __getattr__(self, int, /) -> int

The first error is expected - __getattr__ doesn't normally take ints, I'm just using them to make types clear. In practice, subclasses of str, or literals as in #8203 might make more sense, though they currently all error the same way.

This behavior is the same as accessing, but not calling, an overloaded function elsewhere.

class B:
    @overload
    def overloaded(self, name: str) -> Callable[..., Any]: ...
    @overload
    def overloaded(self, name: int) -> int: ...
    def overloaded(self, name: Any) -> Any:
        pass

reveal_type(B().overloaded)
main.py:26: note: Revealed type is "Overload(def (name: builtins.str) -> def (*Any, **Any) -> Any, def (name: builtins.int) -> builtins.int)"

Expected Behavior

The second revealed type, of A().xyz, should worst case resolve to Callable[..., Any] | int - i.e. when overloaded, attribute access should resolve to the union of the overloads' return values, in the same way other overloads with unknown args do.

x: str | int
reveal_type(B().overloaded(x))
main.py:29: note: Revealed type is "Union[def (*Any, **Any) -> Any, builtins.int]"

Additionally, I think it should be possible to deduce that __getattr__ gets called with a string in normal attribute access, meaning the type of the result can be narrowed, down to just Callable[..., Any] in this case.

Your Environment
Python 3.11, Mypy 1.4.1, default settings

https://mypy-play.net/?mypy=1.4.1&python=3.11&gist=0e629e0ec4d8a4a4a69f8ed979bdd04d

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia eseguendo gli snippet Python forniti con mypy e confronta i tipi rivelati per un getattr sovraccaricato e per i normali metodi sovraccaricati. Traccia il percorso del controllo dei tipi per l’accesso agli attributi e la risoluzione dei sovraccarichi. Il lavoro è completato quando l’accesso ordinario restituisce il risultato del sovraccarico appropriato, idealmente ristretto per un nome di attributo di tipo string, senza l’erronea diagnostica di call-overload.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
compilers, tooling
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.