diff --git a/Doc/library/types.rst b/Doc/library/types.rst index e0e77dfbfe7..cce0ad960ed 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -417,6 +417,12 @@ Standard names are defined for the following types: .. versionadded:: 3.9 + .. describe:: hash(proxy) + + Return a hash of the underlying mapping. + + .. versionadded:: 3.12 + Additional Utility Classes and Functions ---------------------------------------- diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0a4d49841ef..11e8b87f028 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -79,6 +79,9 @@ New Features Other Language Changes ====================== +* :class:`types.MappingProxyType` instances are now hashable if the underlying + mapping is hashable. + (Contributed by Serhiy Storchaka in :gh:`87995`.) New Modules diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 8556ca35ca0..f00da0a758d 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1203,6 +1203,16 @@ class MappingProxyTests(unittest.TestCase): self.assertDictEqual(mapping, {'a': 0, 'b': 1, 'c': 2}) self.assertDictEqual(other, {'c': 3, 'p': 0}) + def test_hash(self): + class HashableDict(dict): + def __hash__(self): + return 3844817361 + view = self.mappingproxy({'a': 1, 'b': 2}) + self.assertRaises(TypeError, hash, view) + mapping = HashableDict({'a': 1, 'b': 2}) + view = self.mappingproxy(mapping) + self.assertEqual(hash(view), hash(mapping)) + class ClassCreationTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-25-10-19-43.gh-issue-87995.aMDHnp.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-25-10-19-43.gh-issue-87995.aMDHnp.rst new file mode 100644 index 00000000000..4154ebce234 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-25-10-19-43.gh-issue-87995.aMDHnp.rst @@ -0,0 +1,2 @@ +:class:`types.MappingProxyType` instances are now hashable if the underlying +mapping is hashable. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 8ef6a82d7c6..82570e08514 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1178,6 +1178,12 @@ mappingproxy_getiter(mappingproxyobject *pp) return PyObject_GetIter(pp->mapping); } +static Py_hash_t +mappingproxy_hash(mappingproxyobject *pp) +{ + return PyObject_Hash(pp->mapping); +} + static PyObject * mappingproxy_str(mappingproxyobject *pp) { @@ -1901,7 +1907,7 @@ PyTypeObject PyDictProxy_Type = { &mappingproxy_as_number, /* tp_as_number */ &mappingproxy_as_sequence, /* tp_as_sequence */ &mappingproxy_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)mappingproxy_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)mappingproxy_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */