Skip to content

Commit b739bd1

Browse files
authored
[3.13] gh-143189: fix insertdict() for non-Unicode key (GH-143285) (#143772)
1 parent 92d80c6 commit b739bd1

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

Lib/test/test_dict.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@ def make_pairs():
16551655
self.assertGreaterEqual(eq_count, 1)
16561656

16571657
def test_clear_at_lookup(self):
1658+
# gh-140551 dict crash if clear is called at lookup stage
16581659
class X:
16591660
def __hash__(self):
16601661
return 1
@@ -1675,13 +1676,31 @@ def __eq__(self, other):
16751676
self.assertEqual(len(d), 1)
16761677

16771678
def test_split_table_update_with_str_subclass(self):
1679+
# gh-142218: inserting into a split table dictionary with a non str
1680+
# key that matches an existing key.
16781681
class MyStr(str): pass
16791682
class MyClass: pass
16801683
obj = MyClass()
16811684
obj.attr = 1
16821685
obj.__dict__[MyStr('attr')] = 2
16831686
self.assertEqual(obj.attr, 2)
16841687

1688+
def test_split_table_insert_with_str_subclass(self):
1689+
# gh-143189: inserting into split table dictionary with a non str
1690+
# key that matches an existing key in the shared table but not in
1691+
# the dict yet.
1692+
1693+
class MyStr(str): pass
1694+
class MyClass: pass
1695+
1696+
obj = MyClass()
1697+
obj.attr1 = 1
1698+
1699+
obj2 = MyClass()
1700+
d = obj2.__dict__
1701+
d[MyStr("attr1")] = 2
1702+
self.assertIsInstance(list(d)[0], MyStr)
1703+
16851704

16861705
class CAPITest(unittest.TestCase):
16871706

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix crash when inserting a non-:class:`str` key into a split table
2+
dictionary when the key matches an existing key in the split table
3+
but has no corresponding value in the dict.

Objects/dictobject.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,7 @@ static int
18321832
insertdict(PyInterpreterState *interp, PyDictObject *mp,
18331833
PyObject *key, Py_hash_t hash, PyObject *value)
18341834
{
1835-
PyObject *old_value;
1835+
PyObject *old_value = NULL;
18361836
Py_ssize_t ix;
18371837

18381838
ASSERT_DICT_LOCKED(mp);
@@ -1856,11 +1856,14 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
18561856

18571857
}
18581858

1859-
if (ix == DKIX_EMPTY) {
1859+
if (old_value == NULL) {
18601860
// insert_combined_dict() will convert from non DICT_KEYS_GENERAL table
18611861
// into DICT_KEYS_GENERAL table if key is not Unicode.
18621862
// We don't convert it before _Py_dict_lookup because non-Unicode key
18631863
// may change generic table into Unicode table.
1864+
//
1865+
// NOTE: ix may not be DKIX_EMPTY because split table may have key
1866+
// without value.
18641867
if (insert_combined_dict(interp, mp, hash, key, value) < 0) {
18651868
goto Fail;
18661869
}

0 commit comments

Comments
 (0)