diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index f97e787b0..3f2271d46 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1021,9 +1021,13 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def __init__(self: dict[str, _VT], **kwargs: _VT) -> None: ... @overload - def __init__(self, __map: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ... + def __init__(self, __map: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... @overload - def __init__(self, __iterable: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ... + def __init__(self: dict[str, _VT], __map: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> None: ... + @overload + def __init__(self, __iterable: Iterable[tuple[_KT, _VT]]) -> None: ... + @overload + def __init__(self: dict[str, _VT], __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> None: ... # Next overload is for dict(string.split(sep) for string in iterable) # Cannot be Iterable[Sequence[_T]] or otherwise dict(["foo", "bar", "baz"]) is not an error @overload diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index b546c45ab..40cf999df 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -41,9 +41,13 @@ class UserDict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def __init__(self: UserDict[str, _VT], __dict: None = ..., **kwargs: _VT) -> None: ... @overload - def __init__(self, __dict: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ... + def __init__(self, __dict: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... @overload - def __init__(self, __iterable: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ... + def __init__(self: UserDict[str, _VT], __dict: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> None: ... + @overload + def __init__(self, __iterable: Iterable[tuple[_KT, _VT]]) -> None: ... + @overload + def __init__(self: UserDict[str, _VT], __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> None: ... @overload def __init__(self: UserDict[str, str], __iterable: Iterable[list[str]]) -> None: ... def __len__(self) -> int: ... diff --git a/stdlib/multiprocessing/managers.pyi b/stdlib/multiprocessing/managers.pyi index dfbcb395e..d953785d8 100644 --- a/stdlib/multiprocessing/managers.pyi +++ b/stdlib/multiprocessing/managers.pyi @@ -183,9 +183,13 @@ class SyncManager(BaseManager): @overload def dict(self, **kwargs: _VT) -> DictProxy[str, _VT]: ... @overload - def dict(self, __map: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> DictProxy[_KT, _VT]: ... + def dict(self, __map: SupportsKeysAndGetItem[_KT, _VT]) -> DictProxy[_KT, _VT]: ... @overload - def dict(self, __iterable: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> DictProxy[_KT, _VT]: ... + def dict(self, __map: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> DictProxy[str, _VT]: ... + @overload + def dict(self, __iterable: Iterable[tuple[_KT, _VT]]) -> DictProxy[_KT, _VT]: ... + @overload + def dict(self, __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> DictProxy[str, _VT]: ... @overload def dict(self, __iterable: Iterable[list[str]]) -> DictProxy[str, str]: ... @overload diff --git a/test_cases/stdlib/builtins/test_dict.py b/test_cases/stdlib/builtins/test_dict.py new file mode 100644 index 000000000..08f763333 --- /dev/null +++ b/test_cases/stdlib/builtins/test_dict.py @@ -0,0 +1,49 @@ +# pyright: reportUnnecessaryTypeIgnoreComment=true + +from typing import Dict, Generic, Iterable, Tuple, TypeVar +from typing_extensions import assert_type + +# These do follow `__init__` overloads order: +# mypy and pyright have different opinions about this one: +# mypy raises: 'Need type annotation for "bad"' +# pyright is fine with it. +# bad = dict() +good: Dict[str, str] = dict() +assert_type(good, Dict[str, str]) + +assert_type(dict(arg=1), Dict[str, int]) + +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + + +class KeysAndGetItem(Generic[_KT, _VT]): + def keys(self) -> Iterable[_KT]: + ... + + def __getitem__(self, __k: _KT) -> _VT: + ... + + +kt1: KeysAndGetItem[int, str] = KeysAndGetItem() +assert_type(dict(kt1), Dict[int, str]) +dict(kt1, arg="a") # type: ignore + +kt2: KeysAndGetItem[str, int] = KeysAndGetItem() +assert_type(dict(kt2, arg=1), Dict[str, int]) + + +def test_iterable_tuple_overload(x: Iterable[Tuple[int, str]]) -> Dict[int, str]: + return dict(x) + + +i1: Iterable[Tuple[int, str]] = [(1, "a"), (2, "b")] +test_iterable_tuple_overload(i1) +dict(i1, arg="a") # type: ignore + +i2: Iterable[Tuple[str, int]] = [("a", 1), ("b", 2)] +assert_type(dict(i2, arg=1), Dict[str, int]) + +i3: Iterable[str] = ["a.b"] +assert_type(dict(string.split(".") for string in i3), Dict[str, str]) +dict(["foo", "bar", "baz"]) # type: ignore