This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author AlexWaygood
Recipients AlexWaygood, Dennis Sweeney, JelleZijlstra, gvanrossum, kj, rhettinger, serhiy.storchaka, sobolevn
Date 2022-01-16.13:55:18
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1642341319.06.0.115818889997.issue46399@roundup.psfhosted.org>
In-reply-to
Content
Issue40890 added a new `.mapping` attribute to dict_keys, dict_values and dict_items in 3.10. This addition is great for introspection. However, it has inadvertently caused some unexpected problems for type-checkers.

Prior to Issue40890, the typeshed stub for the three `dict` methods was something like this:

```
from typing import MutableMapping, KeysView, ItemsView, ValuesView, TypeVar

_KT = TypeVar("_KT")
_VT = TypeVar("_VT")

class dict(MutableMapping[_KT, _VT]):
    def keys(self) -> KeysView[_KT]: ...
    def values(self) -> ValuesView[_VT]: ...
    def items(self) -> ItemsView[_KT, _VT]: ...
```

In other words, typeshed did not acknowledge the existence of a "dict_keys" class at all. Instead, it viewed the precise class as an implementation detail, and merely stated in the stub that `dict.keys()` would return some class that implemented the `KeysView` interface.

After Issue40890, however, it was clear that this approach would no longer suffice, as mypy (and other type-checkers) would yield false-positive errors for the following code:

```
m = dict().keys().mapping
```

Following several PRs, the typeshed stub for these `dict` methods now looks something like this:

```
# _collections_abc.pyi

import sys
from types import MappingProxyType
from typing import Generic, KeysView, ValuesView, ItemsView, TypeVar, final

_KT_co = TypeVar("_KT_co", covariant=True)
_VT_co = TypeVar("_VT_co", covariant=True)

@final
class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]):
    if sys.version_info >= (3, 10):
        mapping: MappingProxyType[_KT_co, _VT_co]

@final
class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]):
    if sys.version_info >= (3, 10):
        mapping: MappingProxyType[_KT_co, _VT_co]

@final
class dict_items(ItemsView[_KT_co, _VT_co], Generic[_KT_co, _VT_co]):    
    if sys.version_info >= (3, 10):
        mapping: MappingProxyType[_KT_co, _VT_co]

# builtins.pyi

from _collections_abc import dict_keys, dict_views, dict_items
from typing import MutableMapping, TypeVar

_KT = TypeVar("_KT")
_VT = TypeVar("_VT")

class dict(MutableMapping[_KT, _VT]):
    def keys(self) -> dict_keys[KT, VT]: ...
    def values(self) -> dict_values[_KT, _VT]: ...
    def items(self) -> dict_items[_KT, _VT]: ...
```

The alteration to the typeshed stub means that mypy will no longer give false-positive errors for code in which a user attempts to access the `mapping` attribute. However, it has serious downsides. Users wanting to create typed subclasses of `dict` have found that they are no longer able to annotate `.keys()`, `.values()` and `.items()` as returning `KeysView`, `ValuesView` and `ItemsView`, as mypy now flags this as an incompatible override. Instead, they now have to import `dict_keys`, `dict_values` and `dict_items` from `_collections_abc`, a private module. Moreover, they are unable to parameterise these classes at runtime.

In other words, you used to be able to do this:

```
from typing import KeysView, TypeVar

K = TypeVar("K")
V = TypeVar("V")

class DictSubclass(dict[K, V]):
    def keys(self) -> KeysView[K]:
        return super().keys()
```

But now, you have to do this:

```
from _collections_abc import dict_keys
from typing import TypeVar

K = TypeVar("K")
V = TypeVar("V")

class DictSubclass(dict[K, V]):
    def keys(self) -> "dict_keys[K, V]":
        return super().keys()
```

References:
* PR where `.mapping` attribute was added to the typeshed stubs: https://github.com/python/typeshed/pull/6039
* typeshed issue where this was recently raised: https://github.com/python/typeshed/issues/6837
* typeshed PR where this was further discussed: https://github.com/python/typeshed/pull/6888
History
Date User Action Args
2022-01-16 13:55:19AlexWaygoodsetrecipients: + AlexWaygood, gvanrossum, rhettinger, serhiy.storchaka, JelleZijlstra, Dennis Sweeney, sobolevn, kj
2022-01-16 13:55:19AlexWaygoodsetmessageid: <1642341319.06.0.115818889997.issue46399@roundup.psfhosted.org>
2022-01-16 13:55:19AlexWaygoodlinkissue46399 messages
2022-01-16 13:55:18AlexWaygoodcreate