From 810091b9711312b294a55813e3ff063e926cac34 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Tue, 10 Mar 2026 18:55:06 +0100 Subject: [PATCH 1/6] fixed covariant mapping value type used in overload --- stdlib/collections/__init__.pyi | 8 ++------ stdlib/os/__init__.pyi | 4 +--- stdlib/types.pyi | 2 -- stdlib/typing.pyi | 2 -- stdlib/weakref.pyi | 8 ++------ 5 files changed, 5 insertions(+), 19 deletions(-) diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 39b8a3c9ab6a..fbfd55bb1dab 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -111,9 +111,7 @@ class UserDict(MutableMapping[_KT, _VT]): if sys.version_info >= (3, 12): @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... @@ -504,9 +502,7 @@ class ChainMap(MutableMapping[_KT, _VT]): def __contains__(self, key: object) -> bool: ... @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... diff --git a/stdlib/os/__init__.pyi b/stdlib/os/__init__.pyi index 00c155956fb0..f6298761d165 100644 --- a/stdlib/os/__init__.pyi +++ b/stdlib/os/__init__.pyi @@ -770,9 +770,7 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]): ) -> None: ... @overload - def get(self, key: AnyStr, default: None = None) -> AnyStr | None: ... - @overload - def get(self, key: AnyStr, default: AnyStr) -> AnyStr: ... + def get(self, key: AnyStr) -> AnyStr | None: ... @overload def get(self, key: AnyStr, default: _T) -> AnyStr | _T: ... diff --git a/stdlib/types.pyi b/stdlib/types.pyi index 328bb7552538..0009e55e3033 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -283,8 +283,6 @@ class MappingProxyType(Mapping[_KT_co, _VT_co]): # type: ignore[type-var] # py @overload def get(self, key: _KT_co, /) -> _VT_co | None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter @overload - def get(self, key: _KT_co, default: _VT_co, /) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter - @overload def get(self, key: _KT_co, default: _T2, /) -> _VT_co | _T2: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 8c1fb9d28d7c..ce049222ecda 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -814,8 +814,6 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]): @overload def get(self, key: _KT, /) -> _VT_co | None: ... @overload - def get(self, key: _KT, default: _VT_co, /) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter - @overload def get(self, key: _KT, default: _T, /) -> _VT_co | _T: ... def items(self) -> ItemsView[_KT, _VT_co]: ... diff --git a/stdlib/weakref.pyi b/stdlib/weakref.pyi index 3308ae42e1cf..78cbb0a43129 100644 --- a/stdlib/weakref.pyi +++ b/stdlib/weakref.pyi @@ -101,9 +101,7 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def __deepcopy__(self, memo: Any) -> Self: ... @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... @@ -161,9 +159,7 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def __deepcopy__(self, memo: Any) -> Self: ... @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... From 0839954a9076ff8d22f8895686d146150bfc692b Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Tue, 10 Mar 2026 19:08:10 +0100 Subject: [PATCH 2/6] updated 3rd party stubs with Mapping.get changes --- stubs/WebOb/webob/cookies.pyi | 4 +--- stubs/WebOb/webob/multidict.pyi | 4 +--- stubs/boltons/boltons/cacheutils.pyi | 7 ++----- stubs/grpcio/grpc/aio/__init__.pyi | 4 +--- stubs/inifile/inifile.pyi | 4 +--- stubs/protobuf/google/protobuf/internal/containers.pyi | 8 ++------ 6 files changed, 8 insertions(+), 23 deletions(-) diff --git a/stubs/WebOb/webob/cookies.pyi b/stubs/WebOb/webob/cookies.pyi index 3bda178fc43f..6947eb73ba5b 100644 --- a/stubs/WebOb/webob/cookies.pyi +++ b/stubs/WebOb/webob/cookies.pyi @@ -36,9 +36,7 @@ class RequestCookies(MutableMapping[str, str]): def __getitem__(self, name: str) -> str: ... @overload - def get(self, name: str, default: None = None) -> str | None: ... - @overload - def get(self, name: str, default: str) -> str: ... + def get(self, name: str) -> str | None: ... @overload def get(self, name: str, default: _T) -> str | _T: ... diff --git a/stubs/WebOb/webob/multidict.pyi b/stubs/WebOb/webob/multidict.pyi index 4272d2197564..9302d464ede3 100644 --- a/stubs/WebOb/webob/multidict.pyi +++ b/stubs/WebOb/webob/multidict.pyi @@ -51,9 +51,7 @@ class MultiDict(MutableMapping[_KT, _VT]): def add(self, key: _KT, value: _VT) -> None: ... @overload - def get(self, key: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT) -> _VT: ... + def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... diff --git a/stubs/boltons/boltons/cacheutils.pyi b/stubs/boltons/boltons/cacheutils.pyi index 978da569d563..691939dcf7da 100644 --- a/stubs/boltons/boltons/cacheutils.pyi +++ b/stubs/boltons/boltons/cacheutils.pyi @@ -25,12 +25,9 @@ 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: _KT) -> _VT | None: ... @overload - def get(self, key: _KT, default: _VT) -> _VT: ... - @overload - def get(self, key: _KT, default: _T) -> _T | _VT: ... - + def get(self, key: _KT, default: _T) -> _VT | _T: ... def __delitem__(self, key: _KT) -> None: ... @overload diff --git a/stubs/grpcio/grpc/aio/__init__.pyi b/stubs/grpcio/grpc/aio/__init__.pyi index cb3c27ebaa58..f7a6b886c6b2 100644 --- a/stubs/grpcio/grpc/aio/__init__.pyi +++ b/stubs/grpcio/grpc/aio/__init__.pyi @@ -520,9 +520,7 @@ class Metadata(Collection[_MetadatumType]): def __iter__(self) -> Iterator[_MetadatumType]: ... @overload - def get(self, key: _MetadataKey, default: None = None) -> _MetadataValue | None: ... - @overload - def get(self, key: _MetadataKey, default: _MetadataValue) -> _MetadataValue: ... + def get(self, key: _MetadataKey) -> _MetadataValue | None: ... @overload def get(self, key: _MetadataKey, default: _T) -> _MetadataValue | _T: ... diff --git a/stubs/inifile/inifile.pyi b/stubs/inifile/inifile.pyi index db23bc4e06ce..21aba3017390 100644 --- a/stubs/inifile/inifile.pyi +++ b/stubs/inifile/inifile.pyi @@ -69,9 +69,7 @@ class IniData(MutableMapping[str, str]): def __len__(self) -> int: ... @overload - def get(self, name: str, default: None = None) -> str | None: ... - @overload - def get(self, name: str, default: str) -> str: ... + def get(self, name: str) -> str | None: ... @overload def get(self, name: str, default: _T) -> str | _T: ... diff --git a/stubs/protobuf/google/protobuf/internal/containers.pyi b/stubs/protobuf/google/protobuf/internal/containers.pyi index bb48c5bb6ddd..4f4d96186a01 100644 --- a/stubs/protobuf/google/protobuf/internal/containers.pyi +++ b/stubs/protobuf/google/protobuf/internal/containers.pyi @@ -85,9 +85,7 @@ class ScalarMap(MutableMapping[_K, _ScalarV]): def __getitem__(self, key: _K) -> _ScalarV: ... @overload - def get(self, key: _K, default: None = None) -> _ScalarV | None: ... - @overload - def get(self, key: _K, default: _ScalarV) -> _ScalarV: ... + def get(self, key: _K) -> _ScalarV | None: ... @overload def get(self, key: _K, default: _T) -> _ScalarV | _T: ... @@ -115,9 +113,7 @@ class MessageMap(MutableMapping[_K, _MessageV]): def get_or_create(self, key: _K) -> _MessageV: ... @overload - def get(self, key: _K, default: None = None) -> _MessageV | None: ... - @overload - def get(self, key: _K, default: _MessageV) -> _MessageV: ... + def get(self, key: _K) -> _MessageV | None: ... @overload def get(self, key: _K, default: _T) -> _MessageV | _T: ... From 122e315db5fb5b4f0290f6610c4a12d18e2d78cb Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Tue, 10 Mar 2026 19:18:32 +0100 Subject: [PATCH 3/6] synchronized Mapping.get and dict.get; updated tests --- .../@tests/test_cases/builtins/check_dict.py | 37 +++++++++++-------- stdlib/builtins.pyi | 4 +- stdlib/importlib/metadata/__init__.pyi | 4 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/stdlib/@tests/test_cases/builtins/check_dict.py b/stdlib/@tests/test_cases/builtins/check_dict.py index fe74ad49408e..2ac86597cef7 100644 --- a/stdlib/@tests/test_cases/builtins/check_dict.py +++ b/stdlib/@tests/test_cases/builtins/check_dict.py @@ -71,34 +71,35 @@ def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str] int_value = 1 assert_type(d_any["key"], Any) -assert_type(d_any.get("key"), Union[Any, None]) -assert_type(d_any.get("key", None), Union[Any, None]) +assert_type(d_any.get("key"), Any | None) +assert_type(d_any.get("key", None), Any | None) assert_type(d_any.get("key", any_value), Any) -assert_type(d_any.get("key", str_value), Any) -assert_type(d_any.get("key", int_value), Any) +assert_type(d_any.get("key", str_value), Any | str) +assert_type(d_any.get("key", int_value), Any | int) assert_type(d_str["key"], str) -assert_type(d_str.get("key"), Union[str, None]) -assert_type(d_str.get("key", None), Union[str, None]) +assert_type(d_str.get("key"), str | None) +assert_type(d_str.get("key", None), str | None) # Pyright has str instead of Any here -assert_type(d_str.get("key", any_value), Any) # pyright: ignore[reportAssertTypeFailure] +assert_type(d_str.get("key", any_value), str | Any) assert_type(d_str.get("key", str_value), str) -assert_type(d_str.get("key", int_value), Union[str, int]) +assert_type(d_str.get("key", int_value), str | int) # Now with context! +# Note: One would expect 'assignment' rather than 'arg-type' error code, +# but this is a bug in mypy (https://github.com/python/mypy/issues/20576) result: str result = d_any["key"] result = d_any.get("key") # type: ignore[assignment] -result = d_any.get("key", None) # type: ignore[assignment] +result = d_any.get("key", None) # type: ignore[arg-type] result = d_any.get("key", any_value) result = d_any.get("key", str_value) -result = d_any.get("key", int_value) +result = d_any.get("key", int_value) # type: ignore[arg-type] result = d_str["key"] result = d_str.get("key") # type: ignore[assignment] -result = d_str.get("key", None) # type: ignore[assignment] -# Pyright has str | None here, see https://github.com/microsoft/pyright/discussions/9570 -result = d_str.get("key", any_value) # pyright: ignore[reportAssignmentType] +result = d_str.get("key", None) # type: ignore[arg-type] +result = d_str.get("key", any_value) result = d_str.get("key", str_value) result = d_str.get("key", int_value) # type: ignore[arg-type] @@ -140,11 +141,15 @@ def test8() -> str: def test9() -> str: - return d_str.get("key", None) # type: ignore[return-value] + # Note: One would expect 'return-value' rather than 'arg-type' error code, + # but this is a bug in mypy (https://github.com/python/mypy/issues/20576) + return d_str.get("key", None) # type: ignore[arg-type] -def test10() -> str: - return d_str.get("key", any_value) # type: ignore[no-any-return] +# Pyright doesn't have a version of no-any-return, +# and mypy doesn't have a type: ignore that pyright will ignore. +# def test10() -> str: +# return d_str.get("key", any_value) # mypy: ignore[no-any-return] def test11() -> str: diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 096d440ac38c..1e69ca45bc92 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1330,9 +1330,7 @@ class dict(MutableMapping[_KT, _VT]): # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] - def get(self, key: _KT, default: None = None, /) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT, /) -> _VT: ... + def get(self, key: _KT, /) -> _VT | None: ... @overload def get(self, key: _KT, default: _T, /) -> _VT | _T: ... diff --git a/stdlib/importlib/metadata/__init__.pyi b/stdlib/importlib/metadata/__init__.pyi index 866fd969e2fe..08d7df8766c3 100644 --- a/stdlib/importlib/metadata/__init__.pyi +++ b/stdlib/importlib/metadata/__init__.pyi @@ -164,9 +164,7 @@ if sys.version_info < (3, 12): def __getitem__(self, name: _KT) -> _VT: ... @overload - def get(self, name: _KT, default: None = None) -> _VT | None: ... - @overload - def get(self, name: _KT, default: _VT) -> _VT: ... + def get(self, name: _KT) -> _VT | None: ... @overload def get(self, name: _KT, default: _T) -> _VT | _T: ... From 5e5306e7ec48a70e7f9ee8378bbb3c134a93f8df Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Tue, 10 Mar 2026 19:20:48 +0100 Subject: [PATCH 4/6] updated allowlist (removed MappingProxyType after get-method change) --- stdlib/@tests/stubtest_allowlists/py314.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py314.txt b/stdlib/@tests/stubtest_allowlists/py314.txt index b76fa29cd7d2..10308758eb34 100644 --- a/stdlib/@tests/stubtest_allowlists/py314.txt +++ b/stdlib/@tests/stubtest_allowlists/py314.txt @@ -149,9 +149,6 @@ multiprocessing.managers._BaseDictProxy.keys multiprocessing.managers._BaseDictProxy.popitem multiprocessing.managers._BaseDictProxy.values -# To match `dict`, we lie about the runtime, but use overloads to match the correct behavior -types.MappingProxyType.get - typing_extensions.Protocol # Super-special typing primitive From 3f94c97a758d92332ac581d8f62d925cfd21d453 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Tue, 10 Mar 2026 19:32:50 +0100 Subject: [PATCH 5/6] fixed setuptools _DictLike.get --- stubs/setuptools/setuptools/__init__.pyi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stubs/setuptools/setuptools/__init__.pyi b/stubs/setuptools/setuptools/__init__.pyi index e89fce7ebdc6..19d00e4077df 100644 --- a/stubs/setuptools/setuptools/__init__.pyi +++ b/stubs/setuptools/setuptools/__init__.pyi @@ -38,6 +38,7 @@ _CommandT = TypeVar("_CommandT", bound=_Command) _DistributionT = TypeVar("_DistributionT", bound=_Distribution, default=Distribution) _KT = TypeVar("_KT") _VT_co = TypeVar("_VT_co", covariant=True) +_T = TypeVar("_T") __all__ = [ "setup", @@ -58,7 +59,11 @@ __version__: str @type_check_only class _DictLike(Protocol[_KT, _VT_co]): # See note about using _VT_co instead of Any - def get(self, key: _KT, default: Any | None = None, /) -> _VT_co | None: ... + @overload + def get(self, key: _KT, /) -> _VT_co | None: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT_co | _T: ... + def items(self) -> ItemsView[_KT, _VT_co]: ... def keys(self) -> Iterable[_KT]: ... def __getitem__(self, key: _KT, /) -> _VT_co: ... From bfd12d8f930a4bc2bb221786519186bdb3b67d3e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:15:53 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/boltons/boltons/cacheutils.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stubs/boltons/boltons/cacheutils.pyi b/stubs/boltons/boltons/cacheutils.pyi index 691939dcf7da..66f4130c8bd1 100644 --- a/stubs/boltons/boltons/cacheutils.pyi +++ b/stubs/boltons/boltons/cacheutils.pyi @@ -28,6 +28,7 @@ class LRI(dict[_KT, _VT]): def get(self, key: _KT) -> _VT | None: ... @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... + def __delitem__(self, key: _KT) -> None: ... @overload