Skip to content

Commit fbd3b25

Browse files
authored
gh-141510: Change dict_unhashable_type() error message for frozendict (#145085)
1 parent 25fc04d commit fbd3b25

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

Lib/test/test_dict.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,35 @@ def test_pickle_iter(self):
18711871
self.assertEqual(list(unpickled), expected)
18721872
self.assertEqual(list(it), expected)
18731873

1874+
def test_unhashable_key(self):
1875+
d = frozendict(a=1)
1876+
key = [1, 2, 3]
1877+
1878+
def check_unhashable_key():
1879+
msg = "cannot use 'list' as a frozendict key (unhashable type: 'list')"
1880+
return self.assertRaisesRegex(TypeError, re.escape(msg))
1881+
1882+
with check_unhashable_key():
1883+
key in d
1884+
with check_unhashable_key():
1885+
d[key]
1886+
with check_unhashable_key():
1887+
d.get(key)
1888+
1889+
# Only TypeError exception is overridden,
1890+
# other exceptions are left unchanged.
1891+
class HashError:
1892+
def __hash__(self):
1893+
raise KeyError('error')
1894+
1895+
key2 = HashError()
1896+
with self.assertRaises(KeyError):
1897+
key2 in d
1898+
with self.assertRaises(KeyError):
1899+
d[key2]
1900+
with self.assertRaises(KeyError):
1901+
d.get(key2)
1902+
18741903

18751904
if __name__ == "__main__":
18761905
unittest.main()

Objects/dictobject.c

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,7 +2377,7 @@ PyDict_GetItem(PyObject *op, PyObject *key)
23772377
}
23782378

23792379
static void
2380-
dict_unhashable_type(PyObject *key)
2380+
dict_unhashable_type(PyObject *op, PyObject *key)
23812381
{
23822382
PyObject *exc = PyErr_GetRaisedException();
23832383
assert(exc != NULL);
@@ -2386,9 +2386,14 @@ dict_unhashable_type(PyObject *key)
23862386
return;
23872387
}
23882388

2389-
PyErr_Format(PyExc_TypeError,
2390-
"cannot use '%T' as a dict key (%S)",
2391-
key, exc);
2389+
const char *errmsg;
2390+
if (PyObject_IsInstance(op, (PyObject*)&PyFrozenDict_Type)) {
2391+
errmsg = "cannot use '%T' as a frozendict key (%S)";
2392+
}
2393+
else {
2394+
errmsg = "cannot use '%T' as a dict key (%S)";
2395+
}
2396+
PyErr_Format(PyExc_TypeError, errmsg, key, exc);
23922397
Py_DECREF(exc);
23932398
}
23942399

@@ -2401,7 +2406,7 @@ _PyDict_LookupIndexAndValue(PyDictObject *mp, PyObject *key, PyObject **value)
24012406

24022407
Py_hash_t hash = _PyObject_HashFast(key);
24032408
if (hash == -1) {
2404-
dict_unhashable_type(key);
2409+
dict_unhashable_type((PyObject*)mp, key);
24052410
return -1;
24062411
}
24072412

@@ -2505,7 +2510,7 @@ PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result)
25052510

25062511
Py_hash_t hash = _PyObject_HashFast(key);
25072512
if (hash == -1) {
2508-
dict_unhashable_type(key);
2513+
dict_unhashable_type(op, key);
25092514
*result = NULL;
25102515
return -1;
25112516
}
@@ -2521,7 +2526,7 @@ _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject **
25212526

25222527
Py_hash_t hash = _PyObject_HashFast(key);
25232528
if (hash == -1) {
2524-
dict_unhashable_type(key);
2529+
dict_unhashable_type((PyObject*)op, key);
25252530
*result = NULL;
25262531
return -1;
25272532
}
@@ -2559,7 +2564,7 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key)
25592564
}
25602565
hash = _PyObject_HashFast(key);
25612566
if (hash == -1) {
2562-
dict_unhashable_type(key);
2567+
dict_unhashable_type(op, key);
25632568
return NULL;
25642569
}
25652570

@@ -2705,7 +2710,7 @@ setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
27052710

27062711
Py_hash_t hash = _PyObject_HashFast(key);
27072712
if (hash == -1) {
2708-
dict_unhashable_type(key);
2713+
dict_unhashable_type((PyObject*)mp, key);
27092714
Py_DECREF(key);
27102715
Py_DECREF(value);
27112716
return -1;
@@ -2864,7 +2869,7 @@ PyDict_DelItem(PyObject *op, PyObject *key)
28642869
assert(key);
28652870
Py_hash_t hash = _PyObject_HashFast(key);
28662871
if (hash == -1) {
2867-
dict_unhashable_type(key);
2872+
dict_unhashable_type(op, key);
28682873
return -1;
28692874
}
28702875

@@ -3183,7 +3188,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result)
31833188

31843189
Py_hash_t hash = _PyObject_HashFast(key);
31853190
if (hash == -1) {
3186-
dict_unhashable_type(key);
3191+
dict_unhashable_type(op, key);
31873192
if (result) {
31883193
*result = NULL;
31893194
}
@@ -3596,7 +3601,7 @@ dict_subscript(PyObject *self, PyObject *key)
35963601

35973602
hash = _PyObject_HashFast(key);
35983603
if (hash == -1) {
3599-
dict_unhashable_type(key);
3604+
dict_unhashable_type(self, key);
36003605
return NULL;
36013606
}
36023607
ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
@@ -4515,7 +4520,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
45154520

45164521
hash = _PyObject_HashFast(key);
45174522
if (hash == -1) {
4518-
dict_unhashable_type(key);
4523+
dict_unhashable_type((PyObject*)self, key);
45194524
return NULL;
45204525
}
45214526
ix = _Py_dict_lookup_threadsafe(self, key, hash, &val);
@@ -4547,7 +4552,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
45474552

45484553
hash = _PyObject_HashFast(key);
45494554
if (hash == -1) {
4550-
dict_unhashable_type(key);
4555+
dict_unhashable_type(d, key);
45514556
if (result) {
45524557
*result = NULL;
45534558
}
@@ -4990,7 +4995,7 @@ dict_contains(PyObject *op, PyObject *key)
49904995
{
49914996
Py_hash_t hash = _PyObject_HashFast(key);
49924997
if (hash == -1) {
4993-
dict_unhashable_type(key);
4998+
dict_unhashable_type(op, key);
49944999
return -1;
49955000
}
49965001

@@ -7066,7 +7071,7 @@ _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value)
70667071
if (value == NULL) {
70677072
Py_hash_t hash = _PyObject_HashFast(name);
70687073
if (hash == -1) {
7069-
dict_unhashable_type(name);
7074+
dict_unhashable_type((PyObject*)dict, name);
70707075
return -1;
70717076
}
70727077
return _PyDict_DelItem_KnownHash_LockHeld((PyObject *)dict, name, hash);

0 commit comments

Comments
 (0)