Issue #22653: Fix an assertion failure in debug mode when doing a reentrant dict insertion in debug mode.
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index a388959..98d8a3b 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -906,6 +906,35 @@
         f.a = 'a'
         self.assertEqual(f.__dict__, {1:1, 'a':'a'})
 
+    def check_reentrant_insertion(self, mutate):
+        # This object will trigger mutation of the dict when replaced
+        # by another value.  Note this relies on refcounting: the test
+        # won't achieve its purpose on fully-GCed Python implementations.
+        class Mutating:
+            def __del__(self):
+                mutate(d)
+
+        d = {k: Mutating() for k in 'abcdefghijklmnopqr'}
+        for k in list(d):
+            d[k] = k
+
+    def test_reentrant_insertion(self):
+        # Reentrant insertion shouldn't crash (see issue #22653)
+        def mutate(d):
+            d['b'] = 5
+        self.check_reentrant_insertion(mutate)
+
+        def mutate(d):
+            d.update(self.__dict__)
+            d.clear()
+        self.check_reentrant_insertion(mutate)
+
+        def mutate(d):
+            while d:
+                d.popitem()
+        self.check_reentrant_insertion(mutate)
+
+
 from test import mapping_tests
 
 class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
diff --git a/Misc/NEWS b/Misc/NEWS
index 80721b0..2f02651 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -11,6 +11,9 @@
 Core and Builtins
 -----------------
 
+- Issue #22653: Fix an assertion failure in debug mode when doing a reentrant
+  dict insertion in debug mode.
+
 - Issue #22643: Fix integer overflow in Unicode case operations (upper, lower,
   title, swapcase, casefold).
 
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 1ccea6e..bab6242 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -814,13 +814,14 @@
     if (ep == NULL) {
         return -1;
     }
+    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
     Py_INCREF(value);
     MAINTAIN_TRACKING(mp, key, value);
     old_value = *value_addr;
     if (old_value != NULL) {
         assert(ep->me_key != NULL && ep->me_key != dummy);
         *value_addr = value;
-        Py_DECREF(old_value); /* which **CAN** re-enter */
+        Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
     }
     else {
         if (ep->me_key == NULL) {
@@ -851,9 +852,8 @@
         }
         mp->ma_used++;
         *value_addr = value;
+        assert(ep->me_key != NULL && ep->me_key != dummy);
     }
-    assert(ep->me_key != NULL && ep->me_key != dummy);
-    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
     return 0;
 }