Skip to content

Commit e2e0b4b

Browse files
gh-113024: C API: Add PyObject_GenericHash() function (GH-113025)
1 parent 567ab3b commit e2e0b4b

File tree

14 files changed

+51
-13
lines changed

14 files changed

+51
-13
lines changed

Doc/c-api/hash.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,14 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`.
8282
The function cannot fail: it cannot return ``-1``.
8383
8484
.. versionadded:: 3.13
85+
86+
.. c:function:: Py_hash_t PyObject_GenericHash(PyObject *obj)
87+
88+
Generic hashing function that is meant to be put into a type
89+
object's ``tp_hash`` slot.
90+
Its result only depends on the object's identity.
91+
92+
.. impl-detail::
93+
In CPython, it is equivalent to :c:func:`Py_HashPointer`.
94+
95+
.. versionadded:: 3.13

Doc/c-api/typeobj.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
883883
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash`, when the subtype's
884884
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash` are both ``NULL``.
885885

886+
**Default:**
887+
888+
:c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericHash`.
889+
886890

887891
.. c:member:: ternaryfunc PyTypeObject.tp_call
888892

Doc/whatsnew/3.13.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,10 @@ New Features
17021702
* Add :c:func:`Py_HashPointer` function to hash a pointer.
17031703
(Contributed by Victor Stinner in :gh:`111545`.)
17041704

1705+
* Add :c:func:`PyObject_GenericHash` function that implements the default
1706+
hashing function of a Python object.
1707+
(Contributed by Serhiy Storchaka in :gh:`113024`.)
1708+
17051709
* Add PyTime C API:
17061710

17071711
* :c:type:`PyTime_t` type.

Include/cpython/pyhash.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ typedef struct {
4343
PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void);
4444

4545
PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr);
46+
PyAPI_FUNC(Py_hash_t) PyObject_GenericHash(PyObject *);

Lib/test/test_capi/test_abstract.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,12 @@ def test_number_check(self):
10011001
self.assertTrue(number_check(0.5))
10021002
self.assertFalse(number_check("1 + 1j"))
10031003

1004+
def test_object_generichash(self):
1005+
# Test PyObject_GenericHash()
1006+
generichash = _testcapi.object_generichash
1007+
for obj in object(), 1, 'string', []:
1008+
self.assertEqual(generichash(obj), object.__hash__(obj))
1009+
10041010

10051011
if __name__ == "__main__":
10061012
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :c:func:`PyObject_GenericHash` function.

Modules/_decimal/_decimal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4780,7 +4780,7 @@ _dec_hash(PyDecObject *v)
47804780
return -1;
47814781
}
47824782
else if (mpd_isnan(MPD(v))) {
4783-
return _Py_HashPointer(v);
4783+
return PyObject_GenericHash((PyObject *)v);
47844784
}
47854785
else {
47864786
return py_hash_inf * mpd_arith_sign(MPD(v));

Modules/_testcapi/hash.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,20 @@ hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg)
5959
}
6060

6161

62+
static PyObject *
63+
object_generichash(PyObject *Py_UNUSED(module), PyObject *arg)
64+
{
65+
NULLABLE(arg);
66+
Py_hash_t hash = PyObject_GenericHash(arg);
67+
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash));
68+
return PyLong_FromLongLong(hash);
69+
}
70+
71+
6272
static PyMethodDef test_methods[] = {
6373
{"hash_getfuncdef", hash_getfuncdef, METH_NOARGS},
6474
{"hash_pointer", hash_pointer, METH_O},
75+
{"object_generichash", object_generichash, METH_O},
6576
{NULL},
6677
};
6778

Objects/classobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ static Py_hash_t
301301
method_hash(PyMethodObject *a)
302302
{
303303
Py_hash_t x, y;
304-
x = _Py_HashPointer(a->im_self);
304+
x = PyObject_GenericHash(a->im_self);
305305
y = PyObject_Hash(a->im_func);
306306
if (y == -1)
307307
return -1;

Objects/descrobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ wrapper_hash(PyObject *self)
13461346
{
13471347
wrapperobject *wp = (wrapperobject *)self;
13481348
Py_hash_t x, y;
1349-
x = _Py_HashPointer(wp->self);
1349+
x = PyObject_GenericHash(wp->self);
13501350
y = _Py_HashPointer(wp->descr);
13511351
x = x ^ y;
13521352
if (x == -1)

Objects/methodobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ static Py_hash_t
320320
meth_hash(PyCFunctionObject *a)
321321
{
322322
Py_hash_t x, y;
323-
x = _Py_HashPointer(a->m_self);
323+
x = PyObject_GenericHash(a->m_self);
324324
y = _Py_HashPointer((void*)(a->m_ml->ml_meth));
325325
x ^= y;
326326
if (x == -1)

Objects/typeobject.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6891,12 +6891,6 @@ PyDoc_STRVAR(object_doc,
68916891
"When called, it accepts no arguments and returns a new featureless\n"
68926892
"instance that has no instance attributes and cannot be given any.\n");
68936893

6894-
static Py_hash_t
6895-
object_hash(PyObject *obj)
6896-
{
6897-
return _Py_HashPointer(obj);
6898-
}
6899-
69006894
PyTypeObject PyBaseObject_Type = {
69016895
PyVarObject_HEAD_INIT(&PyType_Type, 0)
69026896
"object", /* tp_name */
@@ -6911,7 +6905,7 @@ PyTypeObject PyBaseObject_Type = {
69116905
0, /* tp_as_number */
69126906
0, /* tp_as_sequence */
69136907
0, /* tp_as_mapping */
6914-
object_hash, /* tp_hash */
6908+
PyObject_GenericHash, /* tp_hash */
69156909
0, /* tp_call */
69166910
object_str, /* tp_str */
69176911
PyObject_GenericGetAttr, /* tp_getattro */

PC/winreg.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ PyHKEY_hashFunc(PyObject *ob)
200200
/* Just use the address.
201201
XXX - should we use the handle value?
202202
*/
203-
return _Py_HashPointer(ob);
203+
return PyObject_GenericHash(ob);
204204
}
205205

206206

Python/pyhash.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ _Py_HashDouble(PyObject *inst, double v)
9494
if (Py_IS_INFINITY(v))
9595
return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
9696
else
97-
return _Py_HashPointer(inst);
97+
return PyObject_GenericHash(inst);
9898
}
9999

100100
m = frexp(v, &e);
@@ -139,6 +139,12 @@ Py_HashPointer(const void *ptr)
139139
return hash;
140140
}
141141

142+
Py_hash_t
143+
PyObject_GenericHash(PyObject *obj)
144+
{
145+
return Py_HashPointer(obj);
146+
}
147+
142148
Py_hash_t
143149
_Py_HashBytes(const void *src, Py_ssize_t len)
144150
{

0 commit comments

Comments
 (0)