Skip to content

MappingProxyType cannot hash a hashable underlying mapping #87995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
andy-maier mannequin opened this issue Apr 13, 2021 · 7 comments
Closed

MappingProxyType cannot hash a hashable underlying mapping #87995

andy-maier mannequin opened this issue Apr 13, 2021 · 7 comments
Labels
3.10 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@andy-maier
Copy link
Mannequin

andy-maier mannequin commented Apr 13, 2021

BPO 43829
Nosy @rhettinger, @serhiy-storchaka, @andy-maier

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2021-04-13.08:25:32.893>
labels = ['type-bug', 'library', '3.10']
title = 'MappingProxyType cannot hash a hashable underlying mapping'
updated_at = <Date 2021-04-14.07:46:45.086>
user = 'https://github.com/andy-maier'

bugs.python.org fields:

activity = <Date 2021-04-14.07:46:45.086>
actor = 'serhiy.storchaka'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2021-04-13.08:25:32.893>
creator = 'andymaier'
dependencies = []
files = []
hgrepos = []
issue_num = 43829
keywords = []
message_count = 4.0
messages = ['390936', '391028', '391038', '391041']
nosy_count = 3.0
nosy_names = ['rhettinger', 'serhiy.storchaka', 'andymaier']
pr_nums = []
priority = 'low'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue43829'
versions = ['Python 3.10']

@andy-maier
Copy link
Mannequin Author

andy-maier mannequin commented Apr 13, 2021

Objects of MappingProxyType do expose a __hash__() method, but if the underlying mapping is hashable, it still does not support hashing it.

Example:

Content of mp_hash.py:

------
#!/usr/bin/env python

from nocasedict import NocaseDict, HashableMixin
from types import MappingProxyType

class HashableDict(HashableMixin, NocaseDict):
    """A hashable dictionary"""
    pass

hd = HashableDict({'a': 1, 'b': 2})
print("hash(hd): {}".format(hash(hd)))

mp = MappingProxyType(hd)
print("hash(mp): {}".format(hash(mp)))

Running the mp_hash.py script:

hash(hd): 3709951335832776636
Traceback (most recent call last):
  File "/Users/maiera/Projects/Python/cpython/issues/mappingproxy/./mp_hash.py", line 14, in <module>
    print("hash(mp): {}".format(hash(mp)))
TypeError: unhashable type: 'mappingproxy'

There are use cases where a function wants to return an immutable view on an internal dictionary, and the caller of the function should be able to use the returned object like a dictionary, except that it is read-only.

Note there is https://bugs.python.org/issue31209 on the inability to pickle MappingProxyType objects which was closed without adding the capability. That would fall under the same argument.

@andy-maier andy-maier mannequin added 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Apr 13, 2021
@rhettinger
Copy link
Contributor

Serhiy, what do you think? Would it make sense to pass through the underlying hash? I don't think there is much use for this but don't see any particular reason to block it.

@rhettinger rhettinger added 3.10 only security fixes and removed 3.9 only security fixes labels Apr 14, 2021
@serhiy-storchaka
Copy link
Member

Perhaps MappingProxyType is unhashable by accident. It implements __eq__, and it makes it unhashable by default. And nobody made request for this feature before. I think that implementing __hash__ would not make anything wrong.

@serhiy-storchaka
Copy link
Member

But there is an issue with comparison implementation in MappingProxyType (see bpo-43838). Resolving that issue can affect the decision about hashability.

There should be always true the following predicate: if x == y then hash(x) == hash(y).

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@vbrozik
Copy link

vbrozik commented Jun 21, 2022

The issue #88004 (bpo-43838) was already closed with reasoning:

... the reason we proxy in the first place is not absolute safety but to make sure
people don’t accidentally update the dict when they intend to update the
class ...

In general, it is hard to completely wall-off instances against deliberate efforts to pry them open

I do not se a reason why not allow the hashing. Recently I needed to collect a set of small unique dict objects. It is a pity that I could not utilize MappingProxyType directly.

@ambv
Copy link
Contributor

ambv commented Jun 28, 2022

This is now fixed. Thanks, Serhiy! ✨ 🍰 ✨

@ambv ambv closed this as completed Jun 28, 2022
gvanrossum pushed a commit to gvanrossum/cpython that referenced this issue Jun 30, 2022
@matanox
Copy link

matanox commented Dec 6, 2023

Does this mean that this should no longer issue an "unhashable type" TypeError?

hash(MappingProxyType({3:2, 4:3}))

Or is this feature indeed coming as of python 3.12?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.10 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

5 participants