diff -r 6bcedf96d25f Lib/test/test_dict.py --- a/Lib/test/test_dict.py Tue Sep 13 20:48:25 2016 +0200 +++ b/Lib/test/test_dict.py Wed Sep 14 16:57:54 2016 +0800 @@ -916,6 +916,34 @@ self.assertEqual(list(a), ['x', 'y']) self.assertEqual(list(b), ['x', 'y', 'z']) + @support.cpython_only + def test_capi_getitem_knownhash(self): + from _testcapi import dict_getitem_knownhash, error + + d = {'x': 1, 'y': 2, 'z': 3} + self.assertEqual(dict_getitem_knownhash(d, 'x', hash('x')), 1) + self.assertEqual(dict_getitem_knownhash(d, 'y', hash('y')), 2) + self.assertEqual(dict_getitem_knownhash(d, 'z', hash('z')), 3) + + # dict_getitem_knownhash needs 3 arguments + self.assertRaises(TypeError, dict_getitem_knownhash) + # not a dict + self.assertRaises(error, dict_getitem_knownhash, [], 1, hash(1)) + # key does not exist + self.assertRaises(error, dict_getitem_knownhash, {}, 1, hash(1)) + + class Exc(Exception): pass + class BadEq: + def __eq__(self, other): + raise Exc + def __hash__(self): + return 7 + + k1, k2 = BadEq(), BadEq() + d = {k1: 1} + self.assertEqual(dict_getitem_knownhash(d, k1, hash(k1)), 1) + self.assertRaises(error, dict_getitem_knownhash, d, k2, hash(k2)) + def test_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): data = {1:"a", 2:"b", 3:"c"} diff -r 6bcedf96d25f Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Tue Sep 13 20:48:25 2016 +0200 +++ b/Modules/_testcapimodule.c Wed Sep 14 16:57:54 2016 +0800 @@ -238,6 +238,28 @@ return Py_None; } +static PyObject* +dict_getitem_knownhash(PyObject *self, PyObject *args) +{ + PyObject *mp, *key, *result; + Py_hash_t hash; + + if (!PyArg_ParseTuple(args, "OOn:dict_get_item_knownhash", + &mp, &key, &hash)) { + return NULL; + } + + PyErr_Clear(); + result = _PyDict_GetItem_KnownHash(mp, key, hash); // borrowed reference + if (result == NULL && !PyErr_Occurred()) { + PyErr_SetString(TestError, "dict_getitem_knownhash: the key " + "does not exist or an unexcepted error happened"); + } + + Py_XINCREF(result); + return result; +} + /* Issue #4701: Check that PyObject_Hash implicitly calls * PyType_Ready if it hasn't already been called @@ -3940,6 +3962,7 @@ {"test_datetime_capi", test_datetime_capi, METH_NOARGS}, {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, + {"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS}, {"test_lazy_hash_inheritance", (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS}, {"test_long_api", (PyCFunction)test_long_api, METH_NOARGS}, {"test_xincref_doesnt_leak",(PyCFunction)test_xincref_doesnt_leak, METH_NOARGS}, diff -r 6bcedf96d25f Objects/dictobject.c --- a/Objects/dictobject.c Tue Sep 13 20:48:25 2016 +0200 +++ b/Objects/dictobject.c Wed Sep 14 16:57:54 2016 +0800 @@ -649,8 +649,10 @@ Py_INCREF(startkey); cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); - if (cmp < 0) + if (cmp < 0) { + *value_addr = NULL; return DKIX_ERROR; + } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = &ep->me_value; @@ -1366,12 +1368,12 @@ ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); - if (ix == DKIX_EMPTY) + if (ix < 0) return NULL; } else { ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); - if (ix == DKIX_EMPTY) { + if (ix < 0) { PyErr_Clear(); return NULL; }