summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/hash.rst11
-rw-r--r--Doc/c-api/typeobj.rst4
-rw-r--r--Doc/whatsnew/3.13.rst4
-rw-r--r--Include/cpython/pyhash.h1
-rw-r--r--Lib/test/test_capi/test_abstract.py6
-rw-r--r--Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst1
-rw-r--r--Modules/_decimal/_decimal.c2
-rw-r--r--Modules/_testcapi/hash.c11
-rw-r--r--Objects/classobject.c2
-rw-r--r--Objects/descrobject.c2
-rw-r--r--Objects/methodobject.c2
-rw-r--r--Objects/typeobject.c8
-rw-r--r--PC/winreg.c2
-rw-r--r--Python/pyhash.c8
14 files changed, 51 insertions, 13 deletions
diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst
index 1cf094c..ddf0b3e 100644
--- a/Doc/c-api/hash.rst
+++ b/Doc/c-api/hash.rst
@@ -82,3 +82,14 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`.
The function cannot fail: it cannot return ``-1``.
.. versionadded:: 3.13
+
+.. c:function:: Py_hash_t PyObject_GenericHash(PyObject *obj)
+
+ Generic hashing function that is meant to be put into a type
+ object's ``tp_hash`` slot.
+ Its result only depends on the object's identity.
+
+ .. impl-detail::
+ In CPython, it is equivalent to :c:func:`Py_HashPointer`.
+
+ .. versionadded:: 3.13
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 8a26f23..e66ab01 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -883,6 +883,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash`, when the subtype's
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash` are both ``NULL``.
+ **Default:**
+
+ :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericHash`.
+
.. c:member:: ternaryfunc PyTypeObject.tp_call
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index bec788e..c9a93d5 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -1702,6 +1702,10 @@ New Features
* Add :c:func:`Py_HashPointer` function to hash a pointer.
(Contributed by Victor Stinner in :gh:`111545`.)
+* Add :c:func:`PyObject_GenericHash` function that implements the default
+ hashing function of a Python object.
+ (Contributed by Serhiy Storchaka in :gh:`113024`.)
+
* Add PyTime C API:
* :c:type:`PyTime_t` type.
diff --git a/Include/cpython/pyhash.h b/Include/cpython/pyhash.h
index b476c3f..2f8e12c 100644
--- a/Include/cpython/pyhash.h
+++ b/Include/cpython/pyhash.h
@@ -43,3 +43,4 @@ typedef struct {
PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void);
PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr);
+PyAPI_FUNC(Py_hash_t) PyObject_GenericHash(PyObject *);
diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py
index 7e6cc9a..bc39036 100644
--- a/Lib/test/test_capi/test_abstract.py
+++ b/Lib/test/test_capi/test_abstract.py
@@ -1001,6 +1001,12 @@ class CAPITest(unittest.TestCase):
self.assertTrue(number_check(0.5))
self.assertFalse(number_check("1 + 1j"))
+ def test_object_generichash(self):
+ # Test PyObject_GenericHash()
+ generichash = _testcapi.object_generichash
+ for obj in object(), 1, 'string', []:
+ self.assertEqual(generichash(obj), object.__hash__(obj))
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst b/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst
new file mode 100644
index 0000000..60ed6e6
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst
@@ -0,0 +1 @@
+Add :c:func:`PyObject_GenericHash` function.
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index 5b053c7..2481455 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -4780,7 +4780,7 @@ _dec_hash(PyDecObject *v)
return -1;
}
else if (mpd_isnan(MPD(v))) {
- return _Py_HashPointer(v);
+ return PyObject_GenericHash((PyObject *)v);
}
else {
return py_hash_inf * mpd_arith_sign(MPD(v));
diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c
index aee7678..809d537 100644
--- a/Modules/_testcapi/hash.c
+++ b/Modules/_testcapi/hash.c
@@ -59,9 +59,20 @@ hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg)
}
+static PyObject *
+object_generichash(PyObject *Py_UNUSED(module), PyObject *arg)
+{
+ NULLABLE(arg);
+ Py_hash_t hash = PyObject_GenericHash(arg);
+ Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash));
+ return PyLong_FromLongLong(hash);
+}
+
+
static PyMethodDef test_methods[] = {
{"hash_getfuncdef", hash_getfuncdef, METH_NOARGS},
{"hash_pointer", hash_pointer, METH_O},
+ {"object_generichash", object_generichash, METH_O},
{NULL},
};
diff --git a/Objects/classobject.c b/Objects/classobject.c
index d7e520f..9cbb944 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -301,7 +301,7 @@ static Py_hash_t
method_hash(PyMethodObject *a)
{
Py_hash_t x, y;
- x = _Py_HashPointer(a->im_self);
+ x = PyObject_GenericHash(a->im_self);
y = PyObject_Hash(a->im_func);
if (y == -1)
return -1;
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index df546a0..3423f15 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -1346,7 +1346,7 @@ wrapper_hash(PyObject *self)
{
wrapperobject *wp = (wrapperobject *)self;
Py_hash_t x, y;
- x = _Py_HashPointer(wp->self);
+ x = PyObject_GenericHash(wp->self);
y = _Py_HashPointer(wp->descr);
x = x ^ y;
if (x == -1)
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 599fb05..d6773a2 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -320,7 +320,7 @@ static Py_hash_t
meth_hash(PyCFunctionObject *a)
{
Py_hash_t x, y;
- x = _Py_HashPointer(a->m_self);
+ x = PyObject_GenericHash(a->m_self);
y = _Py_HashPointer((void*)(a->m_ml->ml_meth));
x ^= y;
if (x == -1)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 06c2fc8..8282278 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6891,12 +6891,6 @@ PyDoc_STRVAR(object_doc,
"When called, it accepts no arguments and returns a new featureless\n"
"instance that has no instance attributes and cannot be given any.\n");
-static Py_hash_t
-object_hash(PyObject *obj)
-{
- return _Py_HashPointer(obj);
-}
-
PyTypeObject PyBaseObject_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"object", /* tp_name */
@@ -6911,7 +6905,7 @@ PyTypeObject PyBaseObject_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
- object_hash, /* tp_hash */
+ PyObject_GenericHash, /* tp_hash */
0, /* tp_call */
object_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
diff --git a/PC/winreg.c b/PC/winreg.c
index 77b8021..8096d17 100644
--- a/PC/winreg.c
+++ b/PC/winreg.c
@@ -200,7 +200,7 @@ PyHKEY_hashFunc(PyObject *ob)
/* Just use the address.
XXX - should we use the handle value?
*/
- return _Py_HashPointer(ob);
+ return PyObject_GenericHash(ob);
}
diff --git a/Python/pyhash.c b/Python/pyhash.c
index 141407c..d508d78 100644
--- a/Python/pyhash.c
+++ b/Python/pyhash.c
@@ -94,7 +94,7 @@ _Py_HashDouble(PyObject *inst, double v)
if (Py_IS_INFINITY(v))
return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
else
- return _Py_HashPointer(inst);
+ return PyObject_GenericHash(inst);
}
m = frexp(v, &e);
@@ -140,6 +140,12 @@ Py_HashPointer(const void *ptr)
}
Py_hash_t
+PyObject_GenericHash(PyObject *obj)
+{
+ return Py_HashPointer(obj);
+}
+
+Py_hash_t
_Py_HashBytes(const void *src, Py_ssize_t len)
{
Py_hash_t x;