[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