Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Lib/test/test_turtle.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,28 @@ def test_no_animation_calls_update_at_exit(self):
s.update.assert_not_called()
s.update.assert_called_once()

def test_update_is_not_reentrant(self):
# ondrag(goto) reenters _update() while cv.update() processes events;
# without a guard this recurses without bound (gh-50966).
s = turtle.TurtleScreen(cv=unittest.mock.MagicMock())
depth = max_depth = 0

def reenter():
nonlocal depth, max_depth
depth += 1
max_depth = max(max_depth, depth)
if depth < 50:
s._update() # as an event handler would
depth -= 1

s.cv.update.reset_mock() # ignore calls made during construction
s.cv.update.side_effect = reenter
s._update()
# cv.update() runs once; reentrant calls only flush idle tasks.
self.assertEqual(s.cv.update.call_count, 1)
self.assertEqual(max_depth, 1)
self.assertTrue(s.cv.update_idletasks.called)


class TestTurtle(unittest.TestCase):
def setUp(self):
Expand Down
12 changes: 11 additions & 1 deletion Lib/turtle.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ def __init__(self, cv):
self.canvwidth = w
self.canvheight = h
self.xscale = self.yscale = 1.0
self._updating = False

def _createpoly(self):
"""Create an invisible polygon item on canvas self.cv)
Expand Down Expand Up @@ -555,7 +556,16 @@ def _delete(self, item):
def _update(self):
"""Redraw graphics items on canvas
"""
self.cv.update()
if self._updating:
# Reentrant call (e.g. a drag handler moving the turtle,
# gh-50966): flush drawing without reprocessing input.
self.cv.update_idletasks()
return
self._updating = True
try:
self.cv.update()
finally:
self._updating = False

def _delay(self, delay):
"""Delay subsequent canvas actions for delay ms."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix unbounded recursion in :mod:`turtle` when a mouse event handler that moves
the turtle is reentered while the screen is being redrawn, for example with
``screen.ondrag(turtle.goto)``. This could previously crash the interpreter.
Loading