Skip to content

Commit adef445

Browse files
authored
bpo-44282: Fix occasional test_incremental_editing failures on buildbots (pythonGH-26491)
Signed-off-by: Tal Einat <532281+taleinat@users.noreply.github.com>
1 parent 320eaa7 commit adef445

File tree

3 files changed

+49
-43
lines changed

3 files changed

+49
-43
lines changed

Lib/idlelib/idle_test/test_colorizer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ def test_long_multiline_string(self):
551551
''')
552552
self._assert_highlighting(source, {'STRING': [('1.0', '5.4')]})
553553

554-
@run_in_tk_mainloop
554+
@run_in_tk_mainloop(delay=50)
555555
def test_incremental_editing(self):
556556
text = self.text
557557
eq = self.assertEqual

Lib/idlelib/idle_test/test_sidebar.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -510,19 +510,19 @@ def test_initial_state(self):
510510
)
511511
self.assert_sidebar_lines_synced()
512512

513-
@run_in_tk_mainloop
513+
@run_in_tk_mainloop()
514514
def test_single_empty_input(self):
515515
self.do_input('\n')
516516
yield
517517
self.assert_sidebar_lines_end_with(['>>>', '>>>'])
518518

519-
@run_in_tk_mainloop
519+
@run_in_tk_mainloop()
520520
def test_single_line_statement(self):
521521
self.do_input('1\n')
522522
yield
523523
self.assert_sidebar_lines_end_with(['>>>', None, '>>>'])
524524

525-
@run_in_tk_mainloop
525+
@run_in_tk_mainloop()
526526
def test_multi_line_statement(self):
527527
# Block statements are not indented because IDLE auto-indents.
528528
self.do_input(dedent('''\
@@ -540,14 +540,14 @@ def test_multi_line_statement(self):
540540
'>>>',
541541
])
542542

543-
@run_in_tk_mainloop
543+
@run_in_tk_mainloop()
544544
def test_single_long_line_wraps(self):
545545
self.do_input('1' * 200 + '\n')
546546
yield
547547
self.assert_sidebar_lines_end_with(['>>>', None, '>>>'])
548548
self.assert_sidebar_lines_synced()
549549

550-
@run_in_tk_mainloop
550+
@run_in_tk_mainloop()
551551
def test_squeeze_multi_line_output(self):
552552
shell = self.shell
553553
text = shell.text
@@ -567,7 +567,7 @@ def test_squeeze_multi_line_output(self):
567567
self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>'])
568568
self.assert_sidebar_lines_synced()
569569

570-
@run_in_tk_mainloop
570+
@run_in_tk_mainloop()
571571
def test_interrupt_recall_undo_redo(self):
572572
text = self.shell.text
573573
# Block statements are not indented because IDLE auto-indents.
@@ -613,7 +613,7 @@ def test_interrupt_recall_undo_redo(self):
613613
['>>>', '...', '...', '...', None, '>>>']
614614
)
615615

616-
@run_in_tk_mainloop
616+
@run_in_tk_mainloop()
617617
def test_very_long_wrapped_line(self):
618618
with swap_attr(self.shell, 'squeezer', None):
619619
self.do_input('x = ' + '1'*10_000 + '\n')
@@ -678,7 +678,7 @@ def get_sidebar_colors():
678678
sidebar.update_colors()
679679
self.assertEqual(get_sidebar_colors(), test_colors)
680680

681-
@run_in_tk_mainloop
681+
@run_in_tk_mainloop()
682682
def test_mousewheel(self):
683683
sidebar = self.shell.shell_sidebar
684684
text = self.shell.text
@@ -703,7 +703,7 @@ def test_mousewheel(self):
703703
yield
704704
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
705705

706-
@run_in_tk_mainloop
706+
@run_in_tk_mainloop()
707707
def test_copy(self):
708708
sidebar = self.shell.shell_sidebar
709709
text = self.shell.text
@@ -728,7 +728,7 @@ def test_copy(self):
728728
copied_text = text.clipboard_get()
729729
self.assertEqual(copied_text, selected_text)
730730

731-
@run_in_tk_mainloop
731+
@run_in_tk_mainloop()
732732
def test_copy_with_prompts(self):
733733
sidebar = self.shell.shell_sidebar
734734
text = self.shell.text

Lib/idlelib/idle_test/tkinter_testing_utils.py

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import functools
33

44

5-
def run_in_tk_mainloop(test_method):
5+
def run_in_tk_mainloop(delay=1):
66
"""Decorator for running a test method with a real Tk mainloop.
77
88
This starts a Tk mainloop before running the test, and stops it
@@ -13,44 +13,50 @@ def run_in_tk_mainloop(test_method):
1313
using "yield" to allow the mainloop to process events and "after"
1414
callbacks, and then continue the test from that point.
1515
16+
The delay argument is passed into root.after(...) calls as the number
17+
of ms to wait before passing execution back to the generator function.
18+
1619
This also assumes that the test class has a .root attribute,
1720
which is a tkinter.Tk object.
1821
1922
For example (from test_sidebar.py):
2023
21-
@run_test_with_tk_mainloop
24+
@run_test_with_tk_mainloop()
2225
def test_single_empty_input(self):
2326
self.do_input('\n')
2427
yield
2528
self.assert_sidebar_lines_end_with(['>>>', '>>>'])
2629
"""
27-
@functools.wraps(test_method)
28-
def new_test_method(self):
29-
test_generator = test_method(self)
30-
root = self.root
31-
# Exceptions raised by self.assert...() need to be raised
32-
# outside of the after() callback in order for the test
33-
# harness to capture them.
34-
exception = None
35-
def after_callback():
36-
nonlocal exception
37-
try:
38-
next(test_generator)
39-
except StopIteration:
40-
root.quit()
41-
except Exception as exc:
42-
exception = exc
43-
root.quit()
44-
else:
45-
# Schedule the Tk mainloop to call this function again,
46-
# using a robust method of ensuring that it gets a
47-
# chance to process queued events before doing so.
48-
# See: https://stackoverflow.com/q/18499082#comment65004099_38817470
49-
root.after(1, root.after_idle, after_callback)
50-
root.after(0, root.after_idle, after_callback)
51-
root.mainloop()
52-
53-
if exception:
54-
raise exception
55-
56-
return new_test_method
30+
def decorator(test_method):
31+
@functools.wraps(test_method)
32+
def new_test_method(self):
33+
test_generator = test_method(self)
34+
root = self.root
35+
# Exceptions raised by self.assert...() need to be raised
36+
# outside of the after() callback in order for the test
37+
# harness to capture them.
38+
exception = None
39+
def after_callback():
40+
nonlocal exception
41+
try:
42+
next(test_generator)
43+
except StopIteration:
44+
root.quit()
45+
except Exception as exc:
46+
exception = exc
47+
root.quit()
48+
else:
49+
# Schedule the Tk mainloop to call this function again,
50+
# using a robust method of ensuring that it gets a
51+
# chance to process queued events before doing so.
52+
# See: https://stackoverflow.com/q/18499082#comment65004099_38817470
53+
root.after(delay, root.after_idle, after_callback)
54+
root.after(0, root.after_idle, after_callback)
55+
root.mainloop()
56+
57+
if exception:
58+
raise exception
59+
60+
return new_test_method
61+
62+
return decorator

0 commit comments

Comments
 (0)