Skip to content

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

Open
@huonw

Description

@huonw

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions