classification
Title: Some problem with recursion handling
Type: crash Stage:
Components: Library (Lib), Tkinter Versions: Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Emmanuel.Decitre, LionKimbro, Peter.Wentworth, asvetlov, dino.viehland, flupke, gregorlingl, kbk, loewis, pitrou, python-dev, r.david.murray, serhiy.storchaka, terry.reedy, thatiparthy, vstinner, willingc
Priority: high Keywords:

Created on 2009-08-17 14:15 by gregorlingl, last changed 2018-11-02 11:09 by thatiparthy.

Files
File name Uploaded Description Edit
dragbug.py gregorlingl, 2009-08-17 14:15 script to reproduce FatalError with python 3.1
turtlecrash.py terry.reedy, 2009-10-07 04:19
tkinter_recursionbug_31.py gregorlingl, 2009-10-16 21:03 tkinter script producing the crash described here
drag_bug_is_nesting_events.py Peter.Wentworth, 2011-05-23 07:56 Demo program showing nesting of event callbacks...
test.py dino.viehland, 2011-06-14 20:21
Messages (21)
msg91664 - (view) Author: Gregor Lingl (gregorlingl) Date: 2009-08-17 14:15
I suspect that there is some deeper (more severe) issue behind the
problem I describe below. I've observed the following on Windows only.
Didn't try it with different OSs

running dragbug.py shows different behaviour with Python 3.1 compared to
Python 2.6:

Running it with Python 3.1 and performing heavy turtle dragging with the
mouse results in:

Fatal Python error: Cannot recover from stack overflow
This application has requested the Runtime to terminate it in an unusual
way. Please contact the application's support team
-- As I do at the moment ;-) 

Running the same script from Python 2.6: The error is much harder to
reproduce (only with very excessive dragging). It's a bit easier to
reproduce when running the program from a console and  if it occurs one
gets a conventional Python error message:

First a long sequence of:
Exception in Tkinter callback
Traceback (most recent call last):
Exception RuntimeError: 'maximum recursion depth exceeded while .... 
ignored

Followed by:

Traceback (most recent call last):
  File "dragbug.py", line 10, in <module>
    mainloop()
  ....
  File "c:\Python26\lib\traceback.py", line 57, in print_tb
    if hasattr(sys, 'tracebacklimit'):
AttributeError: 'module' object has no attribute 'tracebacklimit'

This problem can be overcome by setting a higher recurson limit. But I
think one should require that it doesn't produce a Fatal Error like in
Python 3.1

Regards,
Gregor
msg91665 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2009-08-17 16:16
With py3k trunk on Gentoo Linux after less than a second of flailing the
cursor around on the screen I got:

rdmurray@partner:~/python/py3k>./python dragbug.py 
Fatal Python error: Cannot recover from stack overflow.
zsh: abort      ./python dragbug.py
msg93676 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2009-10-07 04:19
For anyone trying this on another system: you must specifically click on
and drag the mouse/turtle graphic.

Running from IDLE edit, I get no traceback in the Shell Window, unlike
with the script I attached to #7074 and now here. While I am closing the
latter as probably due to the same cause, it does act differently:
adding the 'speed(0)' command present in dragbug.py appears to have
fixed the problem with turtlecrash.py.

Rewriting dragbug to use tkinter canvas directly would determine whether
the turtle module is a factor. The change of behavior with turtlecrash
with the speed command suggests to me that it might be.
msg94148 - (view) Author: Gregor Lingl (gregorlingl) Date: 2009-10-16 21:03
I've written and appended with this message a small script using
tkinter, that produces the same crash as described earlier with turtle.py:

tkinter_recursion_31.py

It occurs when heavily and fast dragging the red square. So the bug
doesn't depent on turtle.py

I'd like to mention again, that the corresponding script under Python26
does crash also, but with a regular Python Error-Message. It does not
kill IDLE when stated from within IDLE.

So I'd suggest to give this one a higher priority. Should be fixed with 3.2

Regards,
Gregor
msg136587 - (view) Author: Peter Wentworth (Peter.Wentworth) Date: 2011-05-23 03:59
I can confirm the crash persists as of Python 3.1.3 on Windows, and would like to add my vote to prioritizing it.  

Without having delved into the code, it seems strange that the rapid stream of events is causing stack overflow / recursion limit problems.

It suggests that tkinter is allowing new event arrivals to interrupt older event handling, so that the older ones remain incomplete as the newer ones pile up on the stack. If this is the case, it is going to lead to long-term instability, and needs attention.

The usual technique is to use a queue to decouple "occurs now" from "handle immediately". (The OS puts the mouse and keyboard events onto window's event queue).  So the mainloop that services the event queue should not start handling a new queued event until the previous handling has completed.
msg136590 - (view) Author: Peter Wentworth (Peter.Wentworth) Date: 2011-05-23 06:28
Attached is a crashing program that shows that the event handler is called again before activation of the prior instance has completed.

I also have a second turtle that queues the nested events.  It doesn't crash the system, at least.

Perhaps someone can also explain why the handler doesn't work if I leave out the global declaration.
msg136592 - (view) Author: Peter Wentworth (Peter.Wentworth) Date: 2011-05-23 07:56
Oops, I wish I hadn't asked that silly question about the global declaration!

Here is the tweaked file...
msg137089 - (view) Author: Emmanuel Decitre (Emmanuel.Decitre) Date: 2011-05-27 17:10
Issue reproduceable on 3.2 (r32:88445) with drag_bug_is_nesting_events.py
msg138347 - (view) Author: Dino Viehland (dino.viehland) * (Python committer) Date: 2011-06-14 20:21
I ran into a similar issue and believe I have a simpler repro.  I think the issue here might be that when you take a stack overflow inside of a sys.settrace handler that you can then later violate the stack depth.  The attached test.py has no dependencies and crashes 3.x but works on 2.x.
msg158144 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-04-12 16:01
Yes, this is all by design. The interpreter *has* to stop: either it stops in a controlled way (the fatal error) or the stack is blown and it crashes. If you think the fatal error (basically a C abort() call) should be replaced with another way of exiting, please suggest so.

(actually, it's not the interpreter as a whole, only the current thread, but crashing a thread without affecting the others is probably impossible, due to reference leaks, resource cleanup, etc.)
msg158145 - (view) Author: Dino Viehland (dino.viehland) * (Python committer) Date: 2012-04-12 16:12
Antoine: If you're looking at my test.py then my expectation is that this doesn't crash because a RuntimeError should be raised when the maximum recursion limit is hit, and then the trace handler should be uninstalled because it leaks an exception.  And that's exactly what seems to happens on Python 2.x.  We shouldn't ever hit the OS stack limit because Python's recursion limit should be enforced even in the face of a sys.settrace handler.
msg158146 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-04-12 16:17
> Antoine: If you're looking at my test.py then my expectation is that
> this doesn't crash because a RuntimeError should be raised when the
> maximum recursion limit is hit, and then the trace handler should be
> uninstalled because it leaks an exception.

I don't understand why you say that "the trace handler leaks an
exception", since it silences it in the "try ... except" block.
msg158148 - (view) Author: Dino Viehland (dino.viehland) * (Python committer) Date: 2012-04-12 16:25
It's catching the exception when it invokes x, but the recursion enforcement should happen at a method prolog, including at the invocation of g.  Therefore if we're at or beyond the recursion limit when invoking the trace handler the limits should still be enforced and that should be the same as the trace handler raising.
msg158149 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-04-12 16:31
> It's catching the exception when it invokes x, but the recursion
> enforcement should happen at a method prolog, including at the
> invocation of g.  Therefore if we're at or beyond the recursion limit
> when invoking the trace handler the limits should still be enforced
> and that should be the same as the trace handler raising.

That's where 3.x is different: 3.x temporarily bumps up the recursion
limit a bit when it is first reached, in order to let various cleanup
handlers run as intended. This is a nice thing in the general case, but
means it can degenerate in more involved or desperate cases.

(although here it's not clear to me why a second recursion error occurs
after the first one)
msg158150 - (view) Author: Dino Viehland (dino.viehland) * (Python committer) Date: 2012-04-12 16:35
Maybe there just needs to be a max that it will bump it up?  

FYI this isn't actually causing any problems for me, I just ran into it while doing IronPython development and was surprised to be able to crash the interpreter w/ pure Python code, and my crash looked awfully similar to this bug.
msg158151 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012-04-12 16:37
> FYI this isn't actually causing any problems for me, I just ran into it 
> while doing IronPython development and was surprised to be able to
> crash the interpreter w/ pure Python code, and my crash looked awfully 
> similar to this bug.

I agree that crashing isn't ideal, and there may be more graceful ways of crashing instead of abort(), so that people don't get that horrible message box under Windows. Suggestions welcome.
msg158157 - (view) Author: Dino Viehland (dino.viehland) * (Python committer) Date: 2012-04-12 18:14
One thought might be to do a recursion check (and maybe for multiple frames) when entering a try rather than incrementing the recursion limit to allow the handlers to run.  

That would cause the exception to be more likely taken before you run the code which needs some form of cleanup and then maybe the recursion limit could be a hard limit which can't be increased forever.
msg174892 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2012-11-05 11:33
New changeset 0dfa3b09a6fe by Nick Coghlan in branch '3.2':
Record a known crasher from #6717
http://hg.python.org/cpython/rev/0dfa3b09a6fe

New changeset 509f7a53f8cc by Nick Coghlan in branch '3.3':
Merge #6717 crasher from 3.2
http://hg.python.org/cpython/rev/509f7a53f8cc

New changeset 47943fe516ec by Nick Coghlan in branch 'default':
Merge #6717 crasher from 3.3
http://hg.python.org/cpython/rev/47943fe516ec
msg319741 - (view) Author: Carol Willing (willingc) * (Python committer) Date: 2018-06-16 14:38
Hi Gregor and others,

I'm triaging 'turtle' issues. I'm recommending, if I don't hear an objection in two weeks, closing this issue with a resolution of "outdated".

Thanks.
msg319764 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-06-16 17:51
I reran Dino's test.py on current master on Win10 and got 

Traceback (most recent call last):
  File "f:/dev/tem/recursion_crash.py", line 23, in <module>
    f()
  File "f:/dev/tem/recursion_crash.py", line 18, in f
    f()
  File "f:/dev/tem/recursion_crash.py", line 18, in f
    f()
  File "f:/dev/tem/recursion_crash.py", line 18, in f
    f()
  [Previous line repeated 991 more times]
  File "f:/dev/tem/recursion_crash.py", line 17, in f
    print(sys.getrecursionlimit())
RecursionError: maximum recursion depth exceeded while calling a Python object

The request for a fix to get a nice traceback is out of date.

When I run Gregor's tkinter_recursionbug_31.py, I immediately get this:

f:\dev\3x>python f:/dev/tem/tk_recbug.py
Running Debug|Win32 interpreter...
f:/dev/tem/tk_recbug.py:14: DeprecationWarning: invalid escape sequence \P
  """

The closet I came to reproducing this is
>>> eval(r"f'\P{1}'")

Warning (from warnings module):
  File "<string>", line 1
DeprecationWarning: invalid escape sequence \P
'\\P1'

When I continue and move the red box, I eventually get

Fatal Python error: Cannot recover from stack overflow.

Current thread 0x000012c8 (most recent call first):
  File "F:\dev\3x\lib\enum.py", line 535 in __new__
  File "F:\dev\3x\lib\enum.py", line 307 in __call__
  File "F:\dev\3x\lib\tkinter\__init__.py", line 1431 in _substitute
  File "F:\dev\3x\lib\tkinter\__init__.py", line 1701 in __call__
  File "F:\dev\3x\lib\tkinter\__init__.py", line 1174 in update
  File "f:/dev/tem/tk_recbug.py", line 39 in move
  File "F:\dev\3x\lib\tkinter\__init__.py", line 1702 in __call__
  File "F:\dev\3x\lib\tkinter\__init__.py", line 1174 in update
  <repeat last 3 lines multiple times>
  ...

f:\dev\3x>

and the tk window disappears.  With pythonw, the tk window disappears with no feedback.

There is no Windows message box, so the request to not get one is fixed already.  There is a (truncated) traceback (without the code lines, but they are viewable in the source), so the requested to get one is also fulfilled.  The only thing left is 'Fatal Python error'.  From Antoine's messages, the request to get a normal exception instead is "Won't fix unless someone has a bright new idea".
msg328663 - (view) Author: Lion Kimbro (LionKimbro) Date: 2018-10-27 21:58
I confirm that dragbug.py (2009-08-17!) is failing for me, almost ten years later.  I'm using Python 3.6.1/win32 on Windows 10.

This is really disappointing, because I have students who I'm teaching Python to via turtle, and I like to show them Python working.

I have found a workaround which -- if this bug won't be fixed -- I propose mentioning in the documentation.

The work-around is something like this:

g = {"X": 0, "Y": 0}

def goto_later(x, y): g["X"] = x; g["Y"] = y

def ontick():
    goto(g["X"], g["Y"])
    ontimer(ontick, 10)

ondrag(goto_later)
ontimer(ontick, 10)

That way, there is no opening for recursion within the handler for the ondrag event.
History
Date User Action Args
2018-11-02 11:09:53thatiparthysetnosy: + thatiparthy
2018-10-27 21:58:12LionKimbrosetnosy: + LionKimbro

messages: + msg328663
versions: + Python 3.6, - Python 3.2, Python 3.3
2018-06-16 17:51:56terry.reedysetnosy: + serhiy.storchaka
messages: + msg319764
2018-06-16 14:38:59willingcsetnosy: + willingc
messages: + msg319741
2012-11-05 11:33:35python-devsetnosy: + python-dev
messages: + msg174892
2012-04-12 18:14:23dino.viehlandsetmessages: + msg158157
2012-04-12 16:37:41pitrousetnosy: + vstinner
messages: + msg158151
2012-04-12 16:35:36dino.viehlandsetmessages: + msg158150
2012-04-12 16:31:50pitrousetmessages: + msg158149
2012-04-12 16:25:16dino.viehlandsetmessages: + msg158148
2012-04-12 16:17:25pitrousetmessages: + msg158146
2012-04-12 16:12:11dino.viehlandsetmessages: + msg158145
2012-04-12 16:01:54pitrousetnosy: + pitrou, loewis
messages: + msg158144
2012-04-12 14:51:21flupkesetnosy: + flupke
2012-03-26 19:41:40asvetlovsetnosy: + asvetlov
2011-06-26 18:50:48terry.reedysetversions: + Python 3.3, - Python 3.1
2011-06-14 20:21:05dino.viehlandsetfiles: + test.py
nosy: + dino.viehland
messages: + msg138347

2011-05-27 17:10:02Emmanuel.Decitresetnosy: + Emmanuel.Decitre
messages: + msg137089
2011-05-23 07:56:27Peter.Wentworthsetfiles: + drag_bug_is_nesting_events.py

messages: + msg136592
2011-05-23 07:50:31Peter.Wentworthsetfiles: - drag_bug_is_nesting_events.py
2011-05-23 06:28:09Peter.Wentworthsetfiles: + drag_bug_is_nesting_events.py

messages: + msg136590
2011-05-23 03:59:59Peter.Wentworthsetnosy: + Peter.Wentworth
messages: + msg136587
2009-10-19 17:07:25kbksetpriority: normal -> high
nosy: + kbk
2009-10-16 21:03:16gregorlinglsetfiles: + tkinter_recursionbug_31.py

messages: + msg94148
2009-10-07 04:19:48terry.reedysetfiles: + turtlecrash.py
nosy: + terry.reedy
messages: + msg93676

2009-08-17 16:16:44r.david.murraysetpriority: normal
type: crash
components: + Tkinter
versions: + Python 3.2
2009-08-17 16:16:00r.david.murraysetnosy: + r.david.murray
messages: + msg91665
2009-08-17 14:15:59gregorlinglcreate