Skip to content

Commit

Permalink
pythongh-113024: C API: Add PyObject_GenericHash() function
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka committed Dec 12, 2023
1 parent cde1417 commit 9999130
Show file tree
Hide file tree
Showing 14 changed files with 50 additions and 7 deletions.
10 changes: 10 additions & 0 deletions Doc/c-api/hash.rst
Expand Up @@ -59,3 +59,13 @@ See also the :c:member:`PyTypeObject.tp_hash` member.
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.
It's result only depends on the object's identitity.
In CPython it is equivalent to :c:func:`Py_HashPointer`.
.. versionadded:: 3.13
4 changes: 4 additions & 0 deletions Doc/c-api/typeobj.rst
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Expand Up @@ -1274,6 +1274,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`.)


Porting to Python 3.13
----------------------
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pyhash.h
Expand Up @@ -37,3 +37,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 *);
6 changes: 6 additions & 0 deletions Lib/test/test_capi/test_abstract.py
Expand Up @@ -1000,6 +1000,12 @@ def test_number_check(self):
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()
@@ -0,0 +1 @@
Add :c:func:`PyObject_GenericHash` function.
2 changes: 1 addition & 1 deletion Modules/_decimal/_decimal.c
Expand Up @@ -4842,7 +4842,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));
Expand Down
11 changes: 11 additions & 0 deletions Modules/_testcapi/hash.c
Expand Up @@ -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},
};

Expand Down
2 changes: 1 addition & 1 deletion Objects/classobject.c
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Objects/descrobject.c
Expand Up @@ -1311,7 +1311,7 @@ static Py_hash_t
wrapper_hash(wrapperobject *wp)
{
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)
Expand Down
2 changes: 1 addition & 1 deletion Objects/methodobject.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Objects/typeobject.c
Expand Up @@ -6575,7 +6575,7 @@ PyTypeObject PyBaseObject_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)_Py_HashPointer, /* tp_hash */
PyObject_GenericHash, /* tp_hash */
0, /* tp_call */
object_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
Expand Down
2 changes: 1 addition & 1 deletion PC/winreg.c
Expand Up @@ -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);
}


Expand Down
8 changes: 7 additions & 1 deletion Python/pyhash.c
Expand Up @@ -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);
Expand Down Expand Up @@ -139,6 +139,12 @@ Py_HashPointer(const void *ptr)
return hash;
}

Py_hash_t
PyObject_GenericHash(PyObject *obj)
{
return Py_HashPointer(obj);
}

Py_hash_t
_Py_HashBytes(const void *src, Py_ssize_t len)
{
Expand Down

0 comments on commit 9999130

Please sign in to comment.