[Python-checkins] cpython (merge 3.4 -> 3.5): Merge with 3.4
terry.reedy
python-checkins at python.org
Fri Nov 20 19:37:35 EST 2015
https://hg.python.org/cpython/rev/2eb9c4abfee1
changeset: 99251:2eb9c4abfee1
branch: 3.5
parent: 99247:276cf69b911e
parent: 99250:1ddd77a5e8c8
user: Terry Jan Reedy <tjreedy at udel.edu>
date: Fri Nov 20 19:37:00 2015 -0500
summary:
Merge with 3.4
files:
Lib/idlelib/Debugger.py | 60 +++++++++++++++++++++++++---
1 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/Debugger.py
--- a/Lib/idlelib/Debugger.py
+++ b/Lib/idlelib/Debugger.py
@@ -17,7 +17,10 @@
self.set_step()
return
message = self.__frame2message(frame)
- self.gui.interaction(message, frame)
+ try:
+ self.gui.interaction(message, frame)
+ except (TclError, RuntimeError):
+ pass
def user_exception(self, frame, info):
if self.in_rpc_code(frame):
@@ -59,8 +62,42 @@
self.frame = None
self.make_gui()
self.interacting = 0
+ self.nesting_level = 0
def run(self, *args):
+ # Deal with the scenario where we've already got a program running
+ # in the debugger and we want to start another. If that is the case,
+ # our second 'run' was invoked from an event dispatched not from
+ # the main event loop, but from the nested event loop in 'interaction'
+ # below. So our stack looks something like this:
+ # outer main event loop
+ # run()
+ # <running program with traces>
+ # callback to debugger's interaction()
+ # nested event loop
+ # run() for second command
+ #
+ # This kind of nesting of event loops causes all kinds of problems
+ # (see e.g. issue #24455) especially when dealing with running as a
+ # subprocess, where there's all kinds of extra stuff happening in
+ # there - insert a traceback.print_stack() to check it out.
+ #
+ # By this point, we've already called restart_subprocess() in
+ # ScriptBinding. However, we also need to unwind the stack back to
+ # that outer event loop. To accomplish this, we:
+ # - return immediately from the nested run()
+ # - abort_loop ensures the nested event loop will terminate
+ # - the debugger's interaction routine completes normally
+ # - the restart_subprocess() will have taken care of stopping
+ # the running program, which will also let the outer run complete
+ #
+ # That leaves us back at the outer main event loop, at which point our
+ # after event can fire, and we'll come back to this routine with a
+ # clean stack.
+ if self.nesting_level > 0:
+ self.abort_loop()
+ self.root.after(100, lambda: self.run(*args))
+ return
try:
self.interacting = 1
return self.idb.run(*args)
@@ -71,6 +108,7 @@
if self.interacting:
self.top.bell()
return
+ self.abort_loop()
if self.stackviewer:
self.stackviewer.close(); self.stackviewer = None
# Clean up pyshell if user clicked debugger control close widget.
@@ -191,7 +229,12 @@
b.configure(state="normal")
#
self.top.wakeup()
- self.root.mainloop()
+ # Nested main loop: Tkinter's main loop is not reentrant, so use
+ # Tcl's vwait facility, which reenters the event loop until an
+ # event handler sets the variable we're waiting on
+ self.nesting_level += 1
+ self.root.tk.call('vwait', '::idledebugwait')
+ self.nesting_level -= 1
#
for b in self.buttons:
b.configure(state="disabled")
@@ -215,23 +258,26 @@
def cont(self):
self.idb.set_continue()
- self.root.quit()
+ self.abort_loop()
def step(self):
self.idb.set_step()
- self.root.quit()
+ self.abort_loop()
def next(self):
self.idb.set_next(self.frame)
- self.root.quit()
+ self.abort_loop()
def ret(self):
self.idb.set_return(self.frame)
- self.root.quit()
+ self.abort_loop()
def quit(self):
self.idb.set_quit()
- self.root.quit()
+ self.abort_loop()
+
+ def abort_loop(self):
+ self.root.tk.call('set', '::idledebugwait', '1')
stackviewer = None
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list