Skip to content

Commit bee7c4b

Browse files
allow arbitrary types in dict.pop
1 parent 5e59deb commit bee7c4b

File tree

8 files changed

+38
-54
lines changed

8 files changed

+38
-54
lines changed

stdlib/@tests/test_cases/builtins/check_dict.py

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -74,49 +74,42 @@ def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str]
7474
assert_type(d_any.get("key"), Union[Any, None])
7575
assert_type(d_any.get("key", None), Union[Any, None])
7676
assert_type(d_any.get("key", any_value), Any)
77-
assert_type(d_any.get("key", str_value), Union[Any, str])
78-
assert_type(d_any.get("key", int_value), Union[Any, int])
77+
assert_type(d_any.get("key", str_value), Any)
78+
assert_type(d_any.get("key", int_value), Any)
7979

8080
assert_type(d_str["key"], str)
8181
assert_type(d_str.get("key"), Union[str, None])
8282
assert_type(d_str.get("key", None), Union[str, None])
8383
# Pyright has str instead of Any here
84-
assert_type(d_str.get("key", any_value), Union[str, Any])
84+
assert_type(d_str.get("key", any_value), Any) # pyright: ignore[reportAssertTypeFailure]
8585
assert_type(d_str.get("key", str_value), str)
8686
assert_type(d_str.get("key", int_value), Union[str, int])
8787

8888
# Now with context!
8989
result: str
9090
result = d_any["key"]
91-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[assignment] here
92-
result = d_any.get("key") # type: ignore
93-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[assignment] here
94-
result = d_any.get("key", None) # type: ignore
91+
result = d_any.get("key") # type: ignore[assignment]
92+
result = d_any.get("key", None) # type: ignore[assignment]
9593
result = d_any.get("key", any_value)
9694
result = d_any.get("key", str_value)
97-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[assignment] here
98-
result = d_any.get("key", int_value) # type: ignore
95+
result = d_any.get("key", int_value)
9996

10097
result = d_str["key"]
101-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[assignment] here
102-
result = d_str.get("key") # type: ignore
103-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[assignment] here
104-
result = d_str.get("key", None) # type: ignore
105-
result = d_str.get("key", any_value)
98+
result = d_str.get("key") # type: ignore[assignment]
99+
result = d_str.get("key", None) # type: ignore[assignment]
100+
# Pyright has str | None here, see https://github.com/microsoft/pyright/discussions/9570
101+
result = d_str.get("key", any_value) # pyright: ignore[reportAssignmentType]
106102
result = d_str.get("key", str_value)
107103
result = d_str.get("key", int_value) # type: ignore[arg-type]
108104

109105

110-
def test_get_literal(d: dict[Literal["foo", "bar"], int], dynamic_key: str) -> None:
106+
def test_pop_literal(d: dict[Literal["foo", "bar"], int], key: str) -> None:
111107
# Note: annotations also allow using keys of a disjoint type (e.g., int),
112108
# linters / type checkers are free to issue warnings in such cases.
113109
# statically, a .get(arg) is superfluous if the intersection of the
114110
# dict key type and the argument type is empty.
115111
# So we only test a case with non-empty intersection here.
116-
117-
# check that dict wth Literal keys can get/pop a string key.
118-
d.get(dynamic_key)
119-
d.pop(dynamic_key)
112+
d.pop(key)
120113

121114

122115
# Return values also make things weird
@@ -128,8 +121,7 @@ def test_get_literal(d: dict[Literal["foo", "bar"], int], dynamic_key: str) -> N
128121

129122

130123
def test2() -> str:
131-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[return-value] here
132-
return d_any.get("key") # type: ignore
124+
return d_any.get("key") # type: ignore[return-value]
133125

134126

135127
# def test3() -> str:
@@ -153,18 +145,15 @@ def test7() -> str:
153145

154146

155147
def test8() -> str:
156-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[return-value] here
157-
return d_str.get("key") # type: ignore
148+
return d_str.get("key") # type: ignore[return-value]
158149

159150

160151
def test9() -> str:
161-
# FIXME: https://github.com/python/mypy/issues/20576 prevents using ignore[return-value] here
162-
return d_str.get("key", None) # type: ignore
152+
return d_str.get("key", None) # type: ignore[return-value]
163153

164154

165155
def test10() -> str:
166-
# OK, return is Union[str, Any]
167-
return d_str.get("key", any_value)
156+
return d_str.get("key", any_value) # type: ignore[no-any-return]
168157

169158

170159
def test11() -> str:

stdlib/builtins.pyi

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ if sys.version_info >= (3, 14):
7979
from _typeshed import AnnotateFunc
8080

8181
_T = TypeVar("_T")
82-
_N = TypeVar("_N", default=None)
8382
_I = TypeVar("_I", default=int)
8483
_T_co = TypeVar("_T_co", covariant=True)
8584
_T_contra = TypeVar("_T_contra", contravariant=True)
@@ -1219,8 +1218,17 @@ class dict(MutableMapping[_KT, _VT]):
12191218
@classmethod
12201219
@overload
12211220
def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ...
1222-
def get(self, key: object, default: _N = None, /) -> _VT | _N: ...
1223-
def pop(self, key: object, default: _N = None, /) -> _VT | _N: ...
1221+
# Positional-only in dict, but not in MutableMapping
1222+
@overload # type: ignore[override]
1223+
def get(self, key: _KT, default: None = None, /) -> _VT | None: ...
1224+
@overload
1225+
def get(self, key: _KT, default: _VT, /) -> _VT: ...
1226+
@overload
1227+
def get(self, key: _KT, default: _T, /) -> _VT | _T: ...
1228+
@overload
1229+
def pop(self, key: object, /) -> _VT: ...
1230+
@overload
1231+
def pop(self, key: object, default: _T, /) -> _VT | _T: ...
12241232
def __len__(self) -> int: ...
12251233
def __getitem__(self, key: _KT, /) -> _VT: ...
12261234
def __setitem__(self, key: _KT, value: _VT, /) -> None: ...

stdlib/collections/__init__.pyi

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,12 @@ class UserDict(MutableMapping[_KT, _VT]):
105105
@overload
106106
def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
107107
if sys.version_info >= (3, 12):
108-
# UserDict allows key and default as keyword arguments
109108
@overload
110-
def get(self, key: object, default: _T) -> _VT | _T: ...
109+
def get(self, key: _KT, default: None = None) -> _VT | None: ...
111110
@overload
112-
def get(self, key: object, default: None = None) -> _VT | None: ...
111+
def get(self, key: _KT, default: _VT) -> _VT: ...
113112
@overload
114-
def pop(self, key: object, default: _T) -> _VT | _T: ...
115-
@overload # type: ignore[override]
116-
def pop(self, key: object, default: None = None) -> _VT | None: ...
113+
def get(self, key: _KT, default: _T) -> _VT | _T: ...
117114

118115
class UserList(MutableSequence[_T]):
119116
data: list[_T]
@@ -385,9 +382,9 @@ class OrderedDict(dict[_KT, _VT]):
385382
def setdefault(self, key: _KT, default: _VT) -> _VT: ...
386383
# Same as dict.pop, but accepts keyword arguments
387384
@overload
385+
def pop(self, key: object) -> _VT: ...
386+
@overload
388387
def pop(self, key: object, default: _T) -> _VT | _T: ...
389-
@overload # type: ignore[override]
390-
def pop(self, key: object, default: None = None) -> _VT | None: ...
391388
def __eq__(self, value: object, /) -> bool: ...
392389
@overload
393390
def __or__(self, value: dict[_KT, _VT], /) -> Self: ...
@@ -474,8 +471,6 @@ class ChainMap(MutableMapping[_KT, _VT]):
474471
@overload
475472
def pop(self, key: _KT) -> _VT: ...
476473
@overload
477-
def pop(self, key: _KT, default: _VT) -> _VT: ...
478-
@overload
479474
def pop(self, key: _KT, default: _T) -> _VT | _T: ...
480475
def copy(self) -> Self: ...
481476
__copy__ = copy

stdlib/importlib/metadata/__init__.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12):
165165
class Deprecated(Generic[_KT, _VT]):
166166
def __getitem__(self, name: _KT) -> _VT: ...
167167
@overload
168-
def get(self, key: object, default: _T) -> _VT | _T: ...
168+
def get(self, name: _KT, default: None = None) -> _VT | None: ...
169169
@overload
170-
def get(self, key: object, default: None = None) -> _VT | None: ...
170+
def get(self, name: _KT, default: _VT) -> _VT: ...
171+
@overload
172+
def get(self, name: _KT, default: _T) -> _VT | _T: ...
171173
def __iter__(self) -> Iterator[_KT]: ...
172174
def __contains__(self, *args: object) -> bool: ...
173175
def keys(self) -> dict_keys[_KT, _VT]: ...

stdlib/multiprocessing/managers.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ if sys.version_info >= (3, 13):
8888
@overload
8989
def pop(self, key: _KT, /) -> _VT: ...
9090
@overload
91-
def pop(self, key: _KT, default: _VT, /) -> _VT: ...
92-
@overload
9391
def pop(self, key: _KT, default: _T, /) -> _VT | _T: ...
9492
def keys(self) -> list[_KT]: ... # type: ignore[override]
9593
def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override]

stdlib/os/__init__.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,6 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]):
719719
@overload
720720
def pop(self, key: AnyStr) -> AnyStr: ...
721721
@overload
722-
def pop(self, key: AnyStr, default: AnyStr) -> AnyStr: ...
723-
@overload
724722
def pop(self, key: AnyStr, default: _T) -> AnyStr | _T: ...
725723
def setdefault(self, key: AnyStr, value: AnyStr) -> AnyStr: ...
726724
def copy(self) -> dict[AnyStr, AnyStr]: ...

stdlib/typing.pyi

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -792,11 +792,9 @@ class MutableMapping(Mapping[_KT, _VT]):
792792
def __delitem__(self, key: _KT, /) -> None: ...
793793
def clear(self) -> None: ...
794794
@overload
795-
def pop(self, key: _KT, /) -> _VT: ...
795+
def pop(self, key: Any, /) -> _VT: ...
796796
@overload
797-
def pop(self, key: _KT, default: _VT, /) -> _VT: ...
798-
@overload
799-
def pop(self, key: _KT, default: _T, /) -> _VT | _T: ...
797+
def pop(self, key: Any, default: _T, /) -> _VT | _T: ...
800798
def popitem(self) -> tuple[_KT, _VT]: ...
801799
# This overload should be allowed only if the value type is compatible with None.
802800
#

stdlib/weakref.pyi

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,6 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]):
114114
@overload
115115
def pop(self, key: _KT) -> _VT: ...
116116
@overload
117-
def pop(self, key: _KT, default: _VT) -> _VT: ...
118-
@overload
119117
def pop(self, key: _KT, default: _T) -> _VT | _T: ...
120118
@overload
121119
def update(self, other: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ...
@@ -170,8 +168,6 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]):
170168
@overload
171169
def pop(self, key: _KT) -> _VT: ...
172170
@overload
173-
def pop(self, key: _KT, default: _VT) -> _VT: ...
174-
@overload
175171
def pop(self, key: _KT, default: _T) -> _VT | _T: ...
176172
@overload
177173
def update(self, dict: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ...

0 commit comments

Comments
 (0)