Skip to content

Commit fd08181

Browse files
gh-55646: Fix the invalid key binding test on macOS
On macOS an invalid binding such as <Alt-Key-up> is not parsed into a MultiCall triplet, so it falls back to Tk's event_add(), which was left unguarded and still crashed. Guard the fallback too, and test both the parsed (bind()) and unparsed (event_add()) paths on every platform.
1 parent e7ab891 commit fd08181

2 files changed

Lines changed: 35 additions & 16 deletions

File tree

Lib/idlelib/idle_test/test_multicall.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
"Test multicall, coverage 33%."
22

33
from idlelib import multicall
4-
import io
54
import unittest
6-
from test.support import requires
5+
from test.support import requires, captured_stderr
76
from tkinter import Tk, Text
8-
from unittest import mock
97

108

119
class MultiCallTest(unittest.TestCase):
@@ -45,12 +43,29 @@ def test_yview(self):
4543
mctext = self.mc(self.root)
4644
self.assertIs(mctext.yview.__func__, Text.yview)
4745

48-
def test_invalid_binding(self):
49-
# gh-55646: an invalid key binding must not crash IDLE.
46+
def test_valid_binding(self):
47+
# A valid key binding must bind without a warning (cf. gh-55646).
5048
mctext = self.mc(self.root)
51-
# 'up' is not a keysym; it should be 'Up'.
52-
mctext.event_add('<<test-bad>>', '<Alt-Key-up>')
53-
with mock.patch('sys.stderr', new_callable=io.StringIO) as stderr:
49+
with captured_stderr() as stderr:
50+
mctext.event_add('<<test-good>>', '<Control-Key-Up>')
51+
mctext.bind('<<test-good>>', lambda e: None)
52+
self.assertEqual(stderr.getvalue(), '')
53+
54+
def test_invalid_triplet_binding(self):
55+
# gh-55646: '<Control-Key-up>' parses into a triplet on every platform,
56+
# so Tk rejects it in bind().
57+
mctext = self.mc(self.root)
58+
with captured_stderr() as stderr:
59+
mctext.event_add('<<test-bad>>', '<Control-Key-up>') # Must not raise.
60+
mctext.bind('<<test-bad>>', lambda e: None) # Must not raise.
61+
self.assertIn('invalid key binding', stderr.getvalue())
62+
63+
def test_invalid_nontriplet_binding(self):
64+
# gh-55646: '<Foo-Key-Up>' has no valid modifier, so MultiCall does not
65+
# parse it and falls back to Tk's event_add, which rejects it.
66+
mctext = self.mc(self.root)
67+
with captured_stderr() as stderr:
68+
mctext.event_add('<<test-bad>>', '<Foo-Key-Up>') # Must not raise.
5469
mctext.bind('<<test-bad>>', lambda e: None) # Must not raise.
5570
self.assertIn('invalid key binding', stderr.getvalue())
5671

Lib/idlelib/multicall.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,10 @@ def _triplet_to_sequence(triplet):
311311
return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
312312

313313

314-
def _warn_bad_binding(triplet, err):
315-
# Ignore a key binding invalidated by a typo in the user's config
316-
# instead of crashing IDLE (gh-55646).
317-
print(f'Warning: ignoring invalid key binding '
318-
f'{_triplet_to_sequence(triplet)!r}: {err}', file=sys.stderr)
314+
def _warn_bad_binding(sequence, err):
315+
# gh-55646: warn instead of crashing on an invalid key binding.
316+
print(f'Warning: ignoring invalid key binding {sequence!r}: {err}',
317+
file=sys.stderr)
319318

320319

321320
_multicall_dict = {}
@@ -356,7 +355,8 @@ def bind(self, sequence=None, func=None, add=None):
356355
try:
357356
self.__binders[triplet[1]].bind(triplet, func)
358357
except tkinter.TclError as err:
359-
_warn_bad_binding(triplet, err)
358+
_warn_bad_binding(_triplet_to_sequence(triplet),
359+
err)
360360
bad.append(triplet)
361361
for triplet in bad: # Drop the invalid sequences.
362362
ei[1].remove(triplet)
@@ -386,13 +386,17 @@ def event_add(self, virtual, *sequences):
386386
triplet = _parse_sequence(seq)
387387
if triplet is None:
388388
#print("Tkinter event_add(%s)" % seq, file=sys.__stderr__)
389-
widget.event_add(self, virtual, seq)
389+
try:
390+
widget.event_add(self, virtual, seq)
391+
except tkinter.TclError as err:
392+
_warn_bad_binding(seq, err)
393+
continue # Drop the invalid sequence.
390394
else:
391395
if func is not None:
392396
try:
393397
self.__binders[triplet[1]].bind(triplet, func)
394398
except tkinter.TclError as err:
395-
_warn_bad_binding(triplet, err)
399+
_warn_bad_binding(_triplet_to_sequence(triplet), err)
396400
continue # Drop the invalid sequence.
397401
triplets.append(triplet)
398402

0 commit comments

Comments
 (0)