From 152fee56ae355c7bc42fd9dea33b43fbd2ea76aa Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 28 Feb 2026 17:57:26 +0100 Subject: [PATCH 1/4] allow object type in dict.get and dict.pop --- stdlib/@tests/test_cases/builtins/check_dict.py | 12 +++++++++++- stdlib/builtins.pyi | 16 ++++++++++------ stdlib/collections/__init__.pyi | 6 +++--- stdlib/mailbox.pyi | 2 +- stubs/boltons/boltons/cacheutils.pyi | 4 ++-- stubs/boltons/boltons/dictutils.pyi | 2 +- stubs/yt-dlp/yt_dlp/utils/networking.pyi | 6 +++--- 7 files changed, 31 insertions(+), 17 deletions(-) diff --git a/stdlib/@tests/test_cases/builtins/check_dict.py b/stdlib/@tests/test_cases/builtins/check_dict.py index fe74ad49408e..c47da721c331 100644 --- a/stdlib/@tests/test_cases/builtins/check_dict.py +++ b/stdlib/@tests/test_cases/builtins/check_dict.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from typing import Any, Dict, Generic, Iterable, Mapping, TypeVar, Union +from typing import Any, Dict, Generic, Iterable, Literal, Mapping, TypeVar, Union from typing_extensions import Self, assert_type ################################################################### @@ -103,6 +103,16 @@ def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str] result = d_str.get("key", int_value) # type: ignore[arg-type] +def test_dict_signature_overlapping_type(d: dict[Literal["foo", "bar"], int], key: str) -> None: + # Note: annotations also allow using keys of a disjoint type (e.g., int), + # linters / type checkers are free to issue warnings in such cases. + # statically, a .get(arg) is superfluous if the intersection of the + # dict key type and the argument type is empty. + # So we only test a case with non-empty intersection here. + d.get(key) + d.pop(key) + + # Return values also make things weird # Pyright doesn't have a version of no-any-return, diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 903b13aa460a..a829c069ae95 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1336,19 +1336,23 @@ class dict(MutableMapping[_KT, _VT]): def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ... # Positional-only in dict, but not in MutableMapping + # dict.get allows arbitrary types, which matches runtime semantics. + # linters may choose to warn if the given type does not overlap with the key type @overload # type: ignore[override] - def get(self, key: _KT, default: None = None, /) -> _VT | None: ... + def get(self, key: object, default: None = None, /) -> _VT | None: ... @overload - def get(self, key: _KT, default: _VT, /) -> _VT: ... + def get(self, key: object, default: _VT, /) -> _VT: ... @overload - def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + def get(self, key: object, default: _T, /) -> _VT | _T: ... + # dict.pop allows arbitrary types, which matches runtime semantics. + # linters may choose to warn if the given type does not overlap with the key type @overload - def pop(self, key: _KT, /) -> _VT: ... + def pop(self, key: object, /) -> _VT: ... @overload - def pop(self, key: _KT, default: _VT, /) -> _VT: ... + def pop(self, key: object, default: _VT, /) -> _VT: ... @overload - def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... + def pop(self, key: object, default: _T, /) -> _VT | _T: ... def __len__(self) -> int: ... def __getitem__(self, key: _KT, /) -> _VT: ... diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 3e2e838eaa26..8d590ab881a8 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -406,11 +406,11 @@ class OrderedDict(dict[_KT, _VT]): # Same as dict.pop, but accepts keyword arguments @overload - def pop(self, key: _KT) -> _VT: ... + def pop(self, key: object) -> _VT: ... @overload - def pop(self, key: _KT, default: _VT) -> _VT: ... + def pop(self, key: object, default: _VT) -> _VT: ... @overload - def pop(self, key: _KT, default: _T) -> _VT | _T: ... + def pop(self, key: object, default: _T) -> _VT | _T: ... def __eq__(self, value: object, /) -> bool: ... diff --git a/stdlib/mailbox.pyi b/stdlib/mailbox.pyi index 4215120a5753..7255927e1441 100644 --- a/stdlib/mailbox.pyi +++ b/stdlib/mailbox.pyi @@ -113,7 +113,7 @@ class Mailbox(Generic[_MessageT_co]): def clear(self) -> None: ... @overload - def pop(self, key: str, default: None = None) -> _MessageT_co | None: ... + def pop(self, key: str) -> _MessageT_co | None: ... @overload def pop(self, key: str, default: _T) -> _MessageT_co | _T: ... diff --git a/stubs/boltons/boltons/cacheutils.pyi b/stubs/boltons/boltons/cacheutils.pyi index 978da569d563..a7ae3dbe5ead 100644 --- a/stubs/boltons/boltons/cacheutils.pyi +++ b/stubs/boltons/boltons/cacheutils.pyi @@ -34,9 +34,9 @@ class LRI(dict[_KT, _VT]): def __delitem__(self, key: _KT) -> None: ... @overload - def pop(self, key: _KT) -> _VT: ... + def pop(self, key: object) -> _VT: ... @overload - def pop(self, key: _KT, default: _T) -> _T | _VT: ... + def pop(self, key: object, default: _T) -> _T | _VT: ... def popitem(self) -> tuple[_KT, _VT]: ... def clear(self) -> None: ... diff --git a/stubs/boltons/boltons/dictutils.pyi b/stubs/boltons/boltons/dictutils.pyi index e2a8277fca00..9192872ac500 100644 --- a/stubs/boltons/boltons/dictutils.pyi +++ b/stubs/boltons/boltons/dictutils.pyi @@ -69,7 +69,7 @@ class OneToOne(dict[_KT, _VT]): inv: OneToOne[_VT, _KT] def clear(self) -> None: ... def copy(self) -> Self: ... - def pop(self, key: _KT, default: _VT | _T = ...) -> _VT | _T: ... + def pop(self, key: object, default: _VT | _T = ...) -> _VT | _T: ... def popitem(self) -> tuple[_KT, _VT]: ... @overload diff --git a/stubs/yt-dlp/yt_dlp/utils/networking.pyi b/stubs/yt-dlp/yt_dlp/utils/networking.pyi index a773bb7b4fe8..d6f0088bbb58 100644 --- a/stubs/yt-dlp/yt_dlp/utils/networking.pyi +++ b/stubs/yt-dlp/yt_dlp/utils/networking.pyi @@ -23,11 +23,11 @@ class HTTPHeaderDict(dict[str, str]): def get(self, key: str, /, default: type[NO_DEFAULT] | _T = ...) -> str | _T | type[NO_DEFAULT]: ... @overload - def pop(self, key: str, /) -> str: ... + def pop(self, key: object, /) -> str: ... @overload - def pop(self, key: str, /, default: _T) -> str | _T: ... + def pop(self, key: object, /, default: _T) -> str | _T: ... @overload - def pop(self, key: str, /, default: type[NO_DEFAULT] | _T | str = ...) -> str | _T | type[NO_DEFAULT]: ... + def pop(self, key: object, /, default: type[NO_DEFAULT] | _T | str = ...) -> str | _T | type[NO_DEFAULT]: ... @overload def setdefault(self, key: str, /) -> str: ... From e538d7db48418412189026f0ad2d3e8f308f8beb Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 28 Feb 2026 18:02:07 +0100 Subject: [PATCH 2/4] mixed metadata stubtest failure --- stdlib/importlib/metadata/__init__.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/importlib/metadata/__init__.pyi b/stdlib/importlib/metadata/__init__.pyi index 866fd969e2fe..af397e6bbb94 100644 --- a/stdlib/importlib/metadata/__init__.pyi +++ b/stdlib/importlib/metadata/__init__.pyi @@ -164,11 +164,11 @@ if sys.version_info < (3, 12): def __getitem__(self, name: _KT) -> _VT: ... @overload - def get(self, name: _KT, default: None = None) -> _VT | None: ... + def get(self, name: object, default: None = None) -> _VT | None: ... @overload - def get(self, name: _KT, default: _VT) -> _VT: ... + def get(self, name: object, default: _VT) -> _VT: ... @overload - def get(self, name: _KT, default: _T) -> _VT | _T: ... + def get(self, name: object, default: _T) -> _VT | _T: ... def __iter__(self) -> Iterator[_KT]: ... def __contains__(self, *args: object) -> bool: ... From dced49468685046c9f713cbefd71d27907020a6f Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 28 Feb 2026 18:03:59 +0100 Subject: [PATCH 3/4] fixed boltons stubtest failure --- stubs/boltons/boltons/cacheutils.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stubs/boltons/boltons/cacheutils.pyi b/stubs/boltons/boltons/cacheutils.pyi index a7ae3dbe5ead..f4842579892b 100644 --- a/stubs/boltons/boltons/cacheutils.pyi +++ b/stubs/boltons/boltons/cacheutils.pyi @@ -25,11 +25,11 @@ class LRI(dict[_KT, _VT]): def __getitem__(self, key: _KT) -> _VT: ... @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... + def get(self, key: object, default: None = None) -> _VT | None: ... @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: object, default: _VT) -> _VT: ... @overload - def get(self, key: _KT, default: _T) -> _T | _VT: ... + def get(self, key: object, default: _T) -> _T | _VT: ... def __delitem__(self, key: _KT) -> None: ... From d796036eb16ec40fee448cc5317a00a96e0feb59 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 28 Feb 2026 18:22:03 +0100 Subject: [PATCH 4/4] fixes stubtest failures --- stubs/oauthlib/oauthlib/common.pyi | 4 ++-- stubs/requests/requests/structures.pyi | 4 ++-- stubs/www-authenticate/www_authenticate.pyi | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stubs/oauthlib/oauthlib/common.pyi b/stubs/oauthlib/oauthlib/common.pyi index 281303f83078..ab2d8e17720f 100644 --- a/stubs/oauthlib/oauthlib/common.pyi +++ b/stubs/oauthlib/oauthlib/common.pyi @@ -56,9 +56,9 @@ class CaseInsensitiveDict(dict[str, Incomplete]): def __getitem__(self, k: str): ... @overload - def get(self, k: str, default: None = None) -> Incomplete | None: ... + def get(self, k: object, default: None = None) -> Incomplete | None: ... @overload - def get(self, k: str, default): ... + def get(self, k: object, default): ... def __setitem__(self, k: str, v) -> None: ... def update(self, *args, **kwargs) -> None: ... diff --git a/stubs/requests/requests/structures.pyi b/stubs/requests/requests/structures.pyi index fbfffefce04b..71e9aff04a62 100644 --- a/stubs/requests/requests/structures.pyi +++ b/stubs/requests/requests/structures.pyi @@ -21,6 +21,6 @@ class LookupDict(dict[str, _VT]): def __setattr__(self, attr: str, value: _VT, /) -> None: ... @overload - def get(self, key: str, default: None = None) -> _VT | None: ... + def get(self, key: object, default: None = None) -> _VT | None: ... @overload - def get(self, key: str, default: _D | _VT) -> _D | _VT: ... + def get(self, key: object, default: _D | _VT) -> _D | _VT: ... diff --git a/stubs/www-authenticate/www_authenticate.pyi b/stubs/www-authenticate/www_authenticate.pyi index 392bee206f6d..9fb92c55759e 100644 --- a/stubs/www-authenticate/www_authenticate.pyi +++ b/stubs/www-authenticate/www_authenticate.pyi @@ -19,7 +19,7 @@ class CaseFoldedOrderedDict(OrderedDict[str, _VT]): def __contains__(self, key: _SupportsCasefold | _SupportsLower) -> bool: ... # type: ignore[override] # See overloads for dict class: - @overload + @overload # type: ignore[override] def get(self, key: _SupportsCasefold | _SupportsLower, default: None = None) -> _VT | None: ... @overload def get(self, key: _SupportsCasefold | _SupportsLower, default: _VT) -> _VT: ...