Skip to content

Commit fb6fb57

Browse files
vstinnerGlyphack
authored andcommitted
pythongh-111545: Test PyHash_GetFuncDef() function (python#112098)
Add Modules/_testcapi/hash.c and Lib/test/test_capi/test_hash.py.
1 parent a6f4795 commit fb6fb57

File tree

9 files changed

+147
-1
lines changed

9 files changed

+147
-1
lines changed

Doc/c-api/hash.rst

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.. highlight:: c
2+
3+
PyHash API
4+
----------
5+
6+
See also the :c:member:`PyTypeObject.tp_hash` member.
7+
8+
.. c:type:: Py_hash_t
9+
10+
Hash value type: signed integer.
11+
12+
.. versionadded:: 3.2
13+
14+
.. c:type:: Py_uhash_t
15+
16+
Hash value type: unsigned integer.
17+
18+
.. versionadded:: 3.2
19+
20+
21+
.. c:type:: PyHash_FuncDef
22+
23+
Hash function definition used by :c:func:`PyHash_GetFuncDef`.
24+
25+
.. c::member:: Py_hash_t (*const hash)(const void *, Py_ssize_t)
26+
27+
Hash function.
28+
29+
.. c:member:: const char *name
30+
31+
Hash function name (UTF-8 encoded string).
32+
33+
.. c:member:: const int hash_bits
34+
35+
Internal size of the hash value in bits.
36+
37+
.. c:member:: const int seed_bits
38+
39+
Size of seed input in bits.
40+
41+
.. versionadded:: 3.4
42+
43+
44+
.. c:function:: PyHash_FuncDef* PyHash_GetFuncDef(void)
45+
46+
Get the hash function definition.
47+
48+
.. versionadded:: 3.4

Doc/c-api/utilities.rst

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and parsing function arguments and constructing Python values from C values.
1717
marshal.rst
1818
arg.rst
1919
conversion.rst
20+
hash.rst
2021
reflection.rst
2122
codec.rst
2223
perfmaps.rst

Lib/test/test_capi/test_hash.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sys
2+
import unittest
3+
from test.support import import_helper
4+
_testcapi = import_helper.import_module('_testcapi')
5+
6+
7+
SIZEOF_PY_HASH_T = _testcapi.SIZEOF_VOID_P
8+
9+
10+
class CAPITest(unittest.TestCase):
11+
def test_hash_getfuncdef(self):
12+
# Test PyHash_GetFuncDef()
13+
hash_getfuncdef = _testcapi.hash_getfuncdef
14+
func_def = hash_getfuncdef()
15+
16+
match func_def.name:
17+
case "fnv":
18+
self.assertEqual(func_def.hash_bits, 8 * SIZEOF_PY_HASH_T)
19+
self.assertEqual(func_def.seed_bits, 16 * SIZEOF_PY_HASH_T)
20+
case "siphash13":
21+
self.assertEqual(func_def.hash_bits, 64)
22+
self.assertEqual(func_def.seed_bits, 128)
23+
case "siphash24":
24+
self.assertEqual(func_def.hash_bits, 64)
25+
self.assertEqual(func_def.seed_bits, 128)
26+
case _:
27+
self.fail(f"unknown function name: {func_def.name!r}")
28+
29+
# compare with sys.hash_info
30+
hash_info = sys.hash_info
31+
self.assertEqual(func_def.name, hash_info.algorithm)
32+
self.assertEqual(func_def.hash_bits, hash_info.hash_bits)
33+
self.assertEqual(func_def.seed_bits, hash_info.seed_bits)

Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
160160
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
161161
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
162-
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c
162+
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c
163163
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
164164
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
165165

Modules/_testcapi/hash.c

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "parts.h"
2+
#include "util.h"
3+
4+
static PyObject *
5+
hash_getfuncdef(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
6+
{
7+
// bind PyHash_GetFuncDef()
8+
PyHash_FuncDef *def = PyHash_GetFuncDef();
9+
10+
PyObject *types = PyImport_ImportModule("types");
11+
if (types == NULL) {
12+
return NULL;
13+
}
14+
15+
PyObject *result = PyObject_CallMethod(types, "SimpleNamespace", NULL);
16+
Py_DECREF(types);
17+
if (result == NULL) {
18+
return NULL;
19+
}
20+
21+
// ignore PyHash_FuncDef.hash
22+
23+
PyObject *value = PyUnicode_FromString(def->name);
24+
int res = PyObject_SetAttrString(result, "name", value);
25+
Py_DECREF(value);
26+
if (res < 0) {
27+
return NULL;
28+
}
29+
30+
value = PyLong_FromLong(def->hash_bits);
31+
res = PyObject_SetAttrString(result, "hash_bits", value);
32+
Py_DECREF(value);
33+
if (res < 0) {
34+
return NULL;
35+
}
36+
37+
value = PyLong_FromLong(def->seed_bits);
38+
res = PyObject_SetAttrString(result, "seed_bits", value);
39+
Py_DECREF(value);
40+
if (res < 0) {
41+
return NULL;
42+
}
43+
44+
return result;
45+
}
46+
47+
static PyMethodDef test_methods[] = {
48+
{"hash_getfuncdef", hash_getfuncdef, METH_NOARGS},
49+
{NULL},
50+
};
51+
52+
int
53+
_PyTestCapi_Init_Hash(PyObject *m)
54+
{
55+
return PyModule_AddFunctions(m, test_methods);
56+
}

Modules/_testcapi/parts.h

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ int _PyTestCapi_Init_Codec(PyObject *module);
5858
int _PyTestCapi_Init_Immortal(PyObject *module);
5959
int _PyTestCapi_Init_GC(PyObject *module);
6060
int _PyTestCapi_Init_Sys(PyObject *module);
61+
int _PyTestCapi_Init_Hash(PyObject *module);
6162

6263
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
6364
int _PyTestCapi_Init_HeaptypeRelative(PyObject *module);

Modules/_testcapimodule.c

+3
Original file line numberDiff line numberDiff line change
@@ -3995,6 +3995,9 @@ PyInit__testcapi(void)
39953995
if (_PyTestCapi_Init_HeaptypeRelative(m) < 0) {
39963996
return NULL;
39973997
}
3998+
if (_PyTestCapi_Init_Hash(m) < 0) {
3999+
return NULL;
4000+
}
39984001

39994002
PyState_AddModule(m, &_testcapimodule);
40004003
return m;

PCbuild/_testcapi.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<ClCompile Include="..\Modules\_testcapi\file.c" />
125125
<ClCompile Include="..\Modules\_testcapi\codec.c" />
126126
<ClCompile Include="..\Modules\_testcapi\sys.c" />
127+
<ClCompile Include="..\Modules\_testcapi\hash.c" />
127128
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
128129
<ClCompile Include="..\Modules\_testcapi\gc.c" />
129130
</ItemGroup>

PCbuild/_testcapi.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@
102102
<ClCompile Include="..\Modules\_testcapi\sys.c">
103103
<Filter>Source Files</Filter>
104104
</ClCompile>
105+
<ClCompile Include="..\Modules\_testcapi\hash.c">
106+
<Filter>Source Files</Filter>
107+
</ClCompile>
105108
<ClCompile Include="..\Modules\_testcapi\gc.c">
106109
<Filter>Source Files</Filter>
107110
</ClCompile>

0 commit comments

Comments
 (0)