typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

Is there a good reason why `Mapping.__contains__` expects `object`, but `Mapping.get` expects a strict type?

Open pradeep90 opened this issue 2 years ago • 2 comments

I ran into a case where a user intended to check whether an enum value x was present in a dictionary Dict[int, int]: x.value in d.

But instead they wrote x in d, which always fails, since the Enum itself is never a key in the dictionary. The type checker didn't complain since the typeshed stub for Mapping.__contains__ accepts any type compatible with object:

    def __contains__(self, __o: object) -> bool: ...

However, if the user had used d.get(x), the type checker would have complained, because the typeshed stub for Mapping.get is stricter:

    def get(self, __key: _KT) -> _VT_co | None: ...

Simple repro:

d: dict[int, int] = {1: 2}

# no type error
'hello' in d

# type error
d.get('hello')

It looks like the stub for __contains__ has expected object ever since typing.pyi was added to typeshed (in 2015). If the idea is that users may want to check for existence of keys of arbitrary types, a similar argument would hold for d.get(x).

Is there a good reason why the stub complains about d.get(x) but not x in d? Otherwise, it'd be good to make __contains__ stricter.

pradeep90 avatar Jun 30 '22 19:06 pradeep90

I definitely see the logic in that, but I also expect that a change like this would break quite a lot. An exploratory PR to judge the impact would be interesting, though.

srittau avatar Jun 30 '22 20:06 srittau

This is a similar issue to #6597, though it comes at it from the opposite direction

AlexWaygood avatar Jun 30 '22 20:06 AlexWaygood