Skip to content

Commit 9e71de0

Browse files
authored
Improve several stdlib setdefault methods (#9612)
1 parent 064f7af commit 9e71de0

File tree

5 files changed

+37
-6
lines changed

5 files changed

+37
-6
lines changed

stdlib/collections/__init__.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]):
354354
def fromkeys(cls, iterable: Iterable[_T], value: _S) -> OrderedDict[_T, _S]: ...
355355
# Keep OrderedDict.setdefault in line with MutableMapping.setdefault, modulo positional-only differences.
356356
@overload
357-
def setdefault(self: OrderedDict[_KT, _T | None], key: _KT) -> _T | None: ...
357+
def setdefault(self: OrderedDict[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ...
358358
@overload
359359
def setdefault(self, key: _KT, default: _VT) -> _VT: ...
360360

@@ -404,7 +404,11 @@ class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]):
404404
def __contains__(self, key: object) -> bool: ...
405405
def __missing__(self, key: _KT) -> _VT: ... # undocumented
406406
def __bool__(self) -> bool: ...
407-
def setdefault(self, key: _KT, default: _VT = ...) -> _VT: ...
407+
# Keep ChainMap.setdefault in line with MutableMapping.setdefault, modulo positional-only differences.
408+
@overload
409+
def setdefault(self: ChainMap[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ...
410+
@overload
411+
def setdefault(self, key: _KT, default: _VT) -> _VT: ...
408412
@overload
409413
def pop(self, key: _KT) -> _VT: ...
410414
@overload

stdlib/typing.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,13 @@ class MutableMapping(Mapping[_KT, _VT], Generic[_KT, _VT]):
602602
def pop(self, __key: _KT, default: _VT | _T) -> _VT | _T: ...
603603
def popitem(self) -> tuple[_KT, _VT]: ...
604604
# This overload should be allowed only if the value type is compatible with None.
605-
# Keep OrderedDict.setdefault in line with MutableMapping.setdefault, modulo positional-only differences.
605+
#
606+
# Keep the following methods in line with MutableMapping.setdefault, modulo positional-only differences:
607+
# -- collections.OrderedDict.setdefault
608+
# -- collections.ChainMap.setdefault
609+
# -- weakref.WeakKeyDictionary.setdefault
606610
@overload
607-
def setdefault(self: MutableMapping[_KT, _T | None], __key: _KT) -> _T | None: ...
611+
def setdefault(self: MutableMapping[_KT, _T | None], __key: _KT, __default: None = None) -> _T | None: ...
608612
@overload
609613
def setdefault(self, __key: _KT, __default: _VT) -> _VT: ...
610614
# 'update' used to take a Union, but using overloading is better.

stdlib/weakref.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]):
7070
def items(self) -> Iterator[tuple[_KT, _VT]]: ... # type: ignore[override]
7171
def itervaluerefs(self) -> Iterator[KeyedRef[_KT, _VT]]: ...
7272
def valuerefs(self) -> list[KeyedRef[_KT, _VT]]: ...
73-
def setdefault(self, key: _KT, default: _VT = ...) -> _VT: ...
73+
def setdefault(self, key: _KT, default: _VT) -> _VT: ... # type: ignore[override]
7474
@overload
7575
def pop(self, key: _KT) -> _VT: ...
7676
@overload
@@ -109,7 +109,11 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]):
109109
def values(self) -> Iterator[_VT]: ... # type: ignore[override]
110110
def items(self) -> Iterator[tuple[_KT, _VT]]: ... # type: ignore[override]
111111
def keyrefs(self) -> list[ref[_KT]]: ...
112-
def setdefault(self, key: _KT, default: _VT = ...) -> _VT: ...
112+
# Keep WeakKeyDictionary.setdefault in line with MutableMapping.setdefault, modulo positional-only differences
113+
@overload
114+
def setdefault(self: WeakKeyDictionary[_KT, _VT | None], key: _KT, default: None = None) -> _VT: ...
115+
@overload
116+
def setdefault(self, key: _KT, default: _VT) -> _VT: ...
113117
@overload
114118
def pop(self, key: _KT) -> _VT: ...
115119
@overload
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, Union
4+
from typing_extensions import assert_type
5+
6+
7+
def check_setdefault_method() -> None:
8+
d: dict[int, str] = {}
9+
d2: dict[int, str | None] = {}
10+
d3: dict[int, Any] = {}
11+
12+
d.setdefault(1) # type: ignore
13+
assert_type(d.setdefault(1, "x"), str)
14+
assert_type(d2.setdefault(1), Union[str, None])
15+
assert_type(d2.setdefault(1, None), Union[str, None])
16+
assert_type(d2.setdefault(1, "x"), Union[str, None])
17+
assert_type(d3.setdefault(1), Union[Any, None])
18+
assert_type(d3.setdefault(1, "x"), Any)

tests/stubtest_allowlists/py3_common.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ weakref.ReferenceType.* # Alias for _weakref.ReferenceType, problems should be
214214
weakref.WeakKeyDictionary.get
215215
weakref.WeakKeyDictionary.update
216216
weakref.WeakValueDictionary.get
217+
weakref.WeakValueDictionary.setdefault # has a default value for the "default" argument, but always errors out if no value is supplied for the parameter by the user
217218
weakref.ref.* # Alias for _weakref.ReferenceType, problems should be fixed there
218219
webbrowser.UnixBrowser.remote_action # always overridden in inheriting class
219220
webbrowser.UnixBrowser.remote_action_newtab # always overridden in inheriting class

0 commit comments

Comments
 (0)