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 jab
Recipients jab, rhettinger
Date 2021-10-29.16:36:50
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1635525410.44.0.846161043783.issue45670@roundup.psfhosted.org>
In-reply-to
Content
As of bpo-40890 (released in Python 3.10), dict views now provide a public .mapping attribute, intended to allow users to recover a mappingproxy pointing to the original mapping.

However, this new attribute can actually point to the wrong mapping for some existing uses of dict views. And since the .mapping attribute is read-only, these existing uses have no way to set it to the correct value.

My bidict library (see https://github.com/jab/bidict) provides an example of this.

A bidict implements a bidirectional mapping by building on top of two dicts (i.e. regular old one-directional mappings) internally -- one for the forward direction, and one for the inverse. When you call e.g. keys() or values() on a bidict, you get back a dict_keys view from one of the backing dicts, because this is a much more optimized implementation of these views than collections.abc.KeysView would be:

>>> import bidict
>>> b = bidict.bidict(one=1, two=2)
>>> b
bidict({'one': 1, 'two': 2})
>>> b.keys()
dict_keys(['one', 'two'])
>>> b.values()
dict_keys([1, 2])


However, now that these built-in dict_keys objects provide a .mapping attribute in Python 3.10, it points to one of the internal, backing dicts in this case, which is an implementation detail, rather than to the bidict instance:

>>> b.keys().mapping  # wrong
mappingproxy({'one': 1, 'two': 2})
>>> b.values().mapping  # wrong
mappingproxy({1: 'one', 2: 'two'})


Instead of the above, you should get:

>>> b.keys().mapping  # corrected:
mappingproxy(bidict({'one': 1, 'two': 2}))
>>> b.values().mapping  # corrected:
mappingproxy(bidict({'one': 1, 'two': 2}))


Since the .mapping attribute is read-only, there's no way for bidict to both keep exposing the optimized dict_keys implementations, which up till now have been perfectly correct, while now exposing a correct .mapping attribute for users of Python 3.10+.

(Other bidict types demonstrate this problem more by exposing even more obviously-unintended implementation details via this new .mapping attribute:

>>> f = bidict.FrozenOrderedBidict(b)
>>> f
FrozenOrderedBidict([('one', 1), ('two', 2)])
>>> f.keys().mapping  # ouch
mappingproxy({'one': _Node(prv=..., self=..., nxt=...), 'two': _Node(prv=..., self=..., nxt=...)})

Those internal _Node objects were never meant to be exposed to consumers, they're an implementation detail.)


It looks like cases like this were not considered when discussing bpo-40890, and if they had been, I wonder if the implementation would have been accepted as-is.

Here are some potential directions for how to improve things for the future:

1. Expose a way for dict view users like bidict to continue to use optimized dict view implementations while opting out of the new .mapping attribute

2. Make the .mapping attribute no longer read-only, so libraries like bidict can set it correctly before exposing it to users

3. Merely update the documentation in https://docs.python.org/3/library/stdtypes.html#:~:text=dictview.mapping,in%20version%203.10. to mention that, because the .mapping attribute is read-only, it may not point to the original, intended mapping, but rather some internal mapping that the user was not intended to be exposed to.


Looking forward to hearing your thoughts, and thanks for your consideration.
History
Date User Action Args
2021-10-29 16:36:50jabsetrecipients: + jab, rhettinger
2021-10-29 16:36:50jabsetmessageid: <1635525410.44.0.846161043783.issue45670@roundup.psfhosted.org>
2021-10-29 16:36:50jablinkissue45670 messages
2021-10-29 16:36:50jabcreate