python / python/mypy

`d or {}` where `d` is something other than builtin `dict` gets errors about `Never` for indexing and splatting

Aperta
#17,790 0 commenti 0 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

When using d or {} where d is a subclass of dict or something that implements the SupportsKeysAndGetItem protocol can lead to errors/different behaviour in downstream code than when it is a dict directly. There errors are things indicating the Never key type of {} is causing problem:

Invalid index type "str" for "DictSubclass | dict[Never, Never]"; expected type "Never"  [index]
Unpacked dict entry 0 has incompatible type "SKAGI | dict[Never, Never]"; expected "SupportsKeysAndGetItem[str, int]"  [dict-item]

This is potentially the same as #17684, but felt different.

(As always, thanks for Mypy!)

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.12&gist=a7075c4918e6e95f4f9429bd261aa4c0

from typing import Iterable

class DictSubclass(dict[str, int]): pass

class SKAGI: # SupportsKeysAndGetItem
    def keys(self) -> Iterable[str]:
        return ['foo']

    def __getitem__(self, __key: str) -> int:
        return 1

def dict_subclass(d: None | DictSubclass):
    if d is not None: # proving the basic operations work:
        d["x"] # no error
        {**d} # no error

    index = (d or {})["x"] # error: Invalid index type "str" for "DictSubclass | dict[Never, Never]"; expected type "Never"  [index]
    get = (d or {}).get("x") # error: No overload variant of "get" of "dict" matches argument type "str"  [call-overload]
    splat = {**(d or {})} # error: Unpacked dict entry 0 has incompatible type "DictSubclass | dict[Never, Never]"; expected "SupportsKeysAndGetItem[str, int]"  [dict-item]

def skagi(d: None | SKAGI):
    if d is not None: # proving the basic operations work:
        d["x"] # no error
        {**d} # no error
        
    index = (d or {})["x"] # error: Invalid index type "str" for "SKAGI | dict[Never, Never]"; expected type "Never"  [index]
    splat = {**(d or {})} # error: Unpacked dict entry 0 has incompatible type "SKAGI | dict[Never, Never]"; expected "SupportsKeysAndGetItem[str, int]"  [dict-item]

# using the normal dict for comparison:
def dict_builtin(d: None | dict[str, int]):
    index = (d or {})["x"] # no error
    get = (d or {}).get("x") # no error
    splat = {**(d or {})} # no error

Expected Behavior

I'd expect the subclass and SupportsKeysAndGetItem-implementing type to behave the same as the normal dict instance, where the Never key arg is unified and/or ignored appropriately.

Actual Behavior

main.py:17: error: Invalid index type "str" for "DictSubclass | dict[Never, Never]"; expected type "Never"  [index]
main.py:18: error: No overload variant of "get" of "dict" matches argument type "str"  [call-overload]
main.py:18: note: Possible overload variants:
main.py:18: note:     def get(self, Never, /) -> None
main.py:18: note:     def get(self, Never, Never, /) -> Never
main.py:18: note:     def [_T] get(self, Never, _T, /) -> _T
main.py:19: error: Unpacked dict entry 0 has incompatible type "DictSubclass | dict[Never, Never]"; expected "SupportsKeysAndGetItem[str, int]"  [dict-item]
main.py:26: error: Invalid index type "str" for "SKAGI | dict[Never, Never]"; expected type "Never"  [index]
main.py:27: error: Unpacked dict entry 0 has incompatible type "SKAGI | dict[Never, Never]"; expected "SupportsKeysAndGetItem[str, int]"  [dict-item]
Found 5 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.11.2
  • Mypy command-line flags: N/A
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.12

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 con la riproduzione collegata in mypy-play e confronta il comportamento di d or {} per i dict integrati, le sottoclassi di dict e le implementazioni di SupportsKeysAndGetItem. Traccia l’inferenza dei tipi e la gestione delle union rilevanti; il lavoro è completato quando gli esempi riprodotti di indicizzazione, get e dictionary-splat non segnalano più gli errori relativi a Never, mentre il comportamento dei dict integrati rimane corretto.

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

Valutazione

Stack tecnologico
python
Ambito
compilers, devtools
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.