Allow arbitrary types in dict.pop (3 overloads)#15297
Allow arbitrary types in dict.pop (3 overloads)#15297randolf-scholz wants to merge 11 commits intopython:mainfrom
dict.pop (3 overloads)#15297Conversation
dict.pop (3 overloads)
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Although the primer looks nicer as in #15296, I think this is mostly due to false negatives (see #15296 (comment) and #15296 (comment)) |
|
Reopened due to suggestion of splitting #15296 into 2 steps: (1) convert key to object, (2) investigate the removal of middle overload. |
|
Diff from mypy_primer, showing the effect of this PR on open source code: mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ bson/son.py:126: error: Signature of "pop" incompatible with supertype "builtins.dict" [override]
+ bson/son.py:126: note: Superclass:
+ bson/son.py:126: note: @overload
+ bson/son.py:126: note: def pop(self, object, /) -> _Value
+ bson/son.py:126: note: @overload
+ bson/son.py:126: note: def pop(self, object, _Value, /) -> _Value
+ bson/son.py:126: note: @overload
+ bson/son.py:126: note: def [_T] pop(self, object, _T, /) -> _Value | _T
+ bson/son.py:126: note: Subclass:
+ bson/son.py:126: note: def [_T] pop(self, key: _Key, *args: _Value | _T) -> _Value | _T
pylox (https://github.com/sco1/pylox)
+ pylox/containers/array.py:83: note: def pop(self, object, /) -> Any
- pylox/containers/array.py:83: note: def pop(self, Any, /) -> Any
+ pylox/containers/array.py:83: note: def pop(self, object, Any, /) -> Any
- pylox/containers/array.py:83: note: def pop(self, Any, Any, /) -> Any
- pylox/containers/array.py:83: note: def [_T] pop(self, Any, _T, /) -> Any | _T
+ pylox/containers/array.py:83: note: def [_T] pop(self, object, _T, /) -> Any | _T
rotki (https://github.com/rotki/rotki)
+ rotkehlchen/chain/aggregator.py:727: error: Unused "type: ignore" comment [unused-ignore]
django-stubs (https://github.com/typeddjango/django-stubs)
+ django-stubs/http/request.pyi:202: error: Signature of "pop" incompatible with supertype "builtins.dict" [override]
+ django-stubs/http/request.pyi:202: note: Superclass:
+ django-stubs/http/request.pyi:202: note: @overload
+ django-stubs/http/request.pyi:202: note: def pop(self, object, /) -> str
+ django-stubs/http/request.pyi:202: note: @overload
+ django-stubs/http/request.pyi:202: note: def pop(self, object, str, /) -> str
+ django-stubs/http/request.pyi:202: note: @overload
+ django-stubs/http/request.pyi:202: note: def [_T] pop(self, object, _T, /) -> str | _T
+ django-stubs/http/request.pyi:202: note: Subclass:
+ django-stubs/http/request.pyi:202: note: @overload
+ django-stubs/http/request.pyi:202: note: def pop(self, str | bytes, /) -> Never
+ django-stubs/http/request.pyi:202: note: @overload
+ django-stubs/http/request.pyi:202: note: def [_Z] pop(self, str | bytes, str | _Z = ..., /) -> Never
pylint (https://github.com/pycqa/pylint)
+ pylint/config/arguments_provider.py:54: error: Incompatible types in "yield" (actual type "tuple[None, list[tuple[str, dict[str, str | bool | int | Pattern[str] | Iterable[str | int | Pattern[str]] | type[_CallbackAction] | Callable[[Any], Any] | Callable[[Any, Any, Any, Any], Any] | None], Any]]]", expected type "tuple[str, list[tuple[str, dict[str, str | bool | int | Pattern[str] | Iterable[str | int | Pattern[str]] | type[_CallbackAction] | Callable[[Any], Any] | Callable[[Any, Any, Any, Any], Any] | None], Any]]] | tuple[None, dict[str, list[tuple[str, dict[str, str | bool | int | Pattern[str] | Iterable[str | int | Pattern[str]] | type[_CallbackAction] | Callable[[Any], Any] | Callable[[Any, Any, Any, Any], Any] | None], Any]]]]") [misc]
+ pylint/config/arguments_provider.py:54: note: Error code "misc" not covered by "type: ignore" comment
+ pylint/config/arguments_provider.py:54: error: Unused "type: ignore" comment [unused-ignore]
scrapy (https://github.com/scrapy/scrapy)
+ scrapy/utils/datatypes.py:98: error: Signature of "pop" incompatible with supertype "builtins.dict" [override]
+ scrapy/utils/datatypes.py:98: note: Superclass:
+ scrapy/utils/datatypes.py:98: note: @overload
+ scrapy/utils/datatypes.py:98: note: def pop(self, object, /) -> Any
+ scrapy/utils/datatypes.py:98: note: @overload
+ scrapy/utils/datatypes.py:98: note: def pop(self, object, Any, /) -> Any
+ scrapy/utils/datatypes.py:98: note: @overload
+ scrapy/utils/datatypes.py:98: note: def [_T] pop(self, object, _T, /) -> Any | _T
+ scrapy/utils/datatypes.py:98: note: Subclass:
+ scrapy/utils/datatypes.py:98: note: def [AnyStr: (str, bytes)] pop(self, key: AnyStr, *args: Any) -> Any
discord.py (https://github.com/Rapptz/discord.py)
+ discord/ui/view.py:932: error: Unused "type: ignore" comment [unused-ignore]
steam.py (https://github.com/Gobot1234/steam.py)
- steam/ext/commands/utils.py:52: note: def pop(self, str, /) -> _VT
+ steam/ext/commands/utils.py:52: note: def pop(self, object, /) -> _VT
- steam/ext/commands/utils.py:52: note: def pop(self, str, _VT, /) -> _VT
+ steam/ext/commands/utils.py:52: note: def pop(self, object, _VT, /) -> _VT
- steam/ext/commands/utils.py:52: note: def [_T] pop(self, str, _T, /) -> _VT | _T
+ steam/ext/commands/utils.py:52: note: def [_T] pop(self, object, _T, /) -> _VT | _T
- steam/ext/commands/utils.py:52: note: def pop(self, str, /) -> _VT
+ steam/ext/commands/utils.py:52: note: def pop(self, Any, /) -> _VT
- steam/ext/commands/utils.py:52: note: def pop(self, str, _VT, /) -> _VT
+ steam/ext/commands/utils.py:52: note: def pop(self, Any, _VT, /) -> _VT
- steam/ext/commands/utils.py:52: note: def [_T] pop(self, str, _T, /) -> _VT | _T
+ steam/ext/commands/utils.py:52: note: def [_T] pop(self, Any, _T, /) -> _VT | _T
operator (https://github.com/canonical/operator)
- ops/_private/harness.py:2645: error: No overload variant of "pop" of "dict" matches argument types "str", "None" [call-overload]
- ops/_private/harness.py:2645: note: Possible overload variants:
- ops/_private/harness.py:2645: note: def pop(self, int, /) -> dict[str, Any]
- ops/_private/harness.py:2645: note: def pop(self, int, dict[str, Any], /) -> dict[str, Any]
- ops/_private/harness.py:2645: note: def [_T] pop(self, int, _T, /) -> dict[str, Any] | _T
|
|
Why are you sometimes using The primer output is not very convincing. Lots of churn, and only one weird case – which could arguably point to a real problem or at least weird typing – and one case that could be solved by using I remain unconvinced that this change is actually beneficial to our users, although I won't argue if other maintainers feel differently. |
|
I am using Let me do a test of only removing the second overload without changing the annotation. I have a suspicion that these two things may be entangled, so disentangling them into 2 separate PRs may not be appropriate after all. |
|
I don't have a strong opinion on
This change would be very beneficial to ty users. ty attempts to do type narrowing in a stricter, more principled way than other type checkers, but this causes false positives when encountering methods that are annotated like this in typeshed, so I would be in favour of landing something along these lines. For example, mypy's behaviour here is as follows: def f(x: object):
if isinstance(x, dict):
reveal_type(x) # revealed: dict[Any, Any]
x.pop("foo") # no errorwhereas ty's behaviour is: def f(x: object):
if isinstance(x, dict):
reveal_type(x) # revealed: Top[dict[Unknown, Unknown]]
x.pop("foo") # Argument to bound method `pop` is incorrect: Expected `Never`, found `Literal["foo"]`
Our reasoning for using |
|
Cf. python/typing#2154 for a potential future "best of both worlds" solution. If this would be beneficial for ty, this has my blessing (for now). |
|
Actually, I think we should use |
|
That was my rationale for using This is the default implementation: https://github.com/python/cpython/blob/3c9c3d33cbdef257526871cbc12e93635026f5d6/Lib/_collections_abc.py#L929-L941 def pop(self, key, default=__marker):
'''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
'''
try:
value = self[key]
except KeyError:
if default is self.__marker:
raise
return default
else:
del self[key]
return valueIt will catch But I am easily convinced that there are |
Fixes #15271
new overloads: