From 4a9cb57b3ef8be504eae153b000b7a3c2fd0bb67 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 1 Jul 2026 19:22:03 +0300 Subject: [PATCH] gh-89360: Fix ValueError in IDLE MultiCall event_delete (GH-152738) Deleting a key binding for a sequence not bound to the virtual event no longer raises a ValueError; the discrepancy is now ignored. (cherry picked from commit ea7619faeacf310ba1b2ef41ad07966ce2aae11f) Co-authored-by: Serhiy Storchaka --- Lib/idlelib/idle_test/test_multicall.py | 16 ++++++++++++++++ Lib/idlelib/multicall.py | 3 ++- ...2026-07-01-14-00-00.gh-issue-89360.mUlTiC.rst | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/IDLE/2026-07-01-14-00-00.gh-issue-89360.mUlTiC.rst diff --git a/Lib/idlelib/idle_test/test_multicall.py b/Lib/idlelib/idle_test/test_multicall.py index 7d73761cfdfee82..fcce2030579f4be 100644 --- a/Lib/idlelib/idle_test/test_multicall.py +++ b/Lib/idlelib/idle_test/test_multicall.py @@ -43,6 +43,22 @@ def test_yview(self): mctext = self.mc(self.root) self.assertIs(mctext.yview.__func__, Text.yview) + def test_event_delete_unbound_sequence(self): + # gh-89360: deleting a sequence that was not added to a virtual + # event is ignored instead of raising ValueError. + mctext = self.mc(self.root) + mctext.event_add('<>', '') + info = mctext.event_info('<>') + self.assertEqual(len(info), 1) + + # A different sequence, never added: a no-op, not an error. + mctext.event_delete('<>', '') + self.assertEqual(mctext.event_info('<>'), info) + + # The added sequence can still be deleted normally. + mctext.event_delete('<>', '') + self.assertNotIn(info[0], mctext.event_info('<>')) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 41f818131130621..95f4fee7fbe740a 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -386,10 +386,11 @@ def event_delete(self, virtual, *sequences): if triplet is None: #print("Tkinter event_delete: %s" % seq, file=sys.__stderr__) widget.event_delete(self, virtual, seq) - else: + elif triplet in triplets: if func is not None: self.__binders[triplet[1]].unbind(triplet, func) triplets.remove(triplet) + # Else the sequence is not bound; ignore it (gh-89360). def event_info(self, virtual=None): if virtual is None or virtual not in self.__eventinfo: diff --git a/Misc/NEWS.d/next/IDLE/2026-07-01-14-00-00.gh-issue-89360.mUlTiC.rst b/Misc/NEWS.d/next/IDLE/2026-07-01-14-00-00.gh-issue-89360.mUlTiC.rst new file mode 100644 index 000000000000000..9e3023f8228233f --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2026-07-01-14-00-00.gh-issue-89360.mUlTiC.rst @@ -0,0 +1,3 @@ +Fix a rare crash in the IDLE editor when the completion window is closed: +deleting a key binding for a sequence that is not bound to the virtual +event is now ignored instead of raising a ``ValueError``.