DebugClients/Python3/DebugClientBase.py

branch
jsonrpc
changeset 5128
b6cbdba69967
parent 5125
eb1b3e0577e4
child 5129
e4ab234cf071
--- a/DebugClients/Python3/DebugClientBase.py	Thu Sep 01 20:03:50 2016 +0200
+++ b/DebugClients/Python3/DebugClientBase.py	Fri Sep 02 19:14:41 2016 +0200
@@ -20,7 +20,7 @@
 import signal
 
 
-import DebugProtocol
+##import DebugProtocol
 import DebugClientCapabilities
 from DebugBase import setRecursionLimit, printerr   # __IGNORE_WARNING__
 from AsyncFile import AsyncFile, AsyncPendingWrite
@@ -160,6 +160,7 @@
         """
         self.breakpoints = {}
         self.redirect = True
+        self.__receiveBuffer = ""
 
         # The next couple of members are needed for the threaded version.
         # For this base class they contain static values for the non threaded
@@ -188,7 +189,7 @@
         self.globalsFilterObjects = []
         self.localsFilterObjects = []
 
-        self.pendingResponse = DebugProtocol.ResponseOK
+##        self.pendingResponse = DebugProtocol.ResponseOK
         self._fncache = {}
         self.dircache = []
 ##        self.inRawMode = False
@@ -304,7 +305,7 @@
         
 ##        self.write("{0}{1!r}\n".format(DebugProtocol.ResponseThreadList,
 ##                                       (currentId, threadList)))
-        self.__sendJsonCommand("ResponseThreadList", {
+        self.sendJsonCommand("ResponseThreadList", {
             "currentID": currentId,
             "threadList": threadList,
         })
@@ -319,7 +320,7 @@
         """
 ##        self.write("{0}{1!r}\n".format(
 ##            DebugProtocol.ResponseRaw, (prompt, echo)))
-        self.__sendJsonCommand("RequestRaw", {
+        self.sendJsonCommand("RequestRaw", {
             "prompt": prompt,
             "echo": echo,
         })
@@ -327,15 +328,15 @@
         self.eventLoop(True)
         return self.rawLine
 
-    def __exceptionRaised(self):
-        """
-        Private method called in the case of an exception.
-        
-        It ensures that the debug server is informed of the raised exception.
-        """
-##        self.pendingResponse = DebugProtocol.ResponseException
-        self.__sendJsonCommand("ResponseException", {})
-    
+##    def __exceptionRaised(self):
+##        """
+##        Private method called in the case of an exception.
+##        
+##        It ensures that the debug server is informed of the raised exception.
+##        """
+####        self.pendingResponse = DebugProtocol.ResponseException
+##        self.sendJsonCommand("ResponseException", {})
+##    
     def sessionClose(self, exit=True):
         """
         Public method to close the session with the debugger and optionally
@@ -348,8 +349,8 @@
         except Exception:
             pass
 
-        # clean up asyncio.
-        self.disconnect()
+##        # clean up asyncio.
+##        self.disconnect()
         self.debugging = False
         
         # make sure we close down our end of the socket
@@ -408,16 +409,16 @@
 
 ##        printerr(line)          ##debug
         
-        if "jsonrpc" in line:
+        if line.startswith("{") and "jsonrpc" in line:
             return self.handleJsonCommand(line)
-
-        eoc = line.find('<')
-
-        if eoc >= 0 and line[0] == '>':
-            # Get the command part and any argument.
-            cmd = line[:eoc + 1]
-            arg = line[eoc + 1:]
-            
+##
+##        eoc = line.find('<')
+##
+##        if eoc >= 0 and line[0] == '>':
+##            # Get the command part and any argument.
+##            cmd = line[:eoc + 1]
+##            arg = line[eoc + 1:]
+##            
 ##            if cmd == DebugProtocol.RequestVariables:
 ##                frmnr, scope, filter = eval(arg.replace("u'", "'"))
 ##                self.__dumpVariables(int(frmnr), int(scope), filter)
@@ -912,32 +913,32 @@
 ##                    str((self.test.countTestCases(), "", ""))))
 ##                return
 ##            
-            if cmd == DebugProtocol.RequestUTRun:
-                from DCTestResult import DCTestResult
-                self.testResult = DCTestResult(self)
-                if self.cover:
-                    self.cover.start()
-                self.test.run(self.testResult)
-                if self.cover:
-                    self.cover.stop()
-                    self.cover.save()
-                self.write('{0}\n'.format(DebugProtocol.ResponseUTFinished))
-                return
-            
-            if cmd == DebugProtocol.RequestUTStop:
-                self.testResult.stop()
-                return
-        
-            if cmd == DebugProtocol.ResponseForkTo:
-                # this results from a separate event loop
-                self.fork_child = (arg == 'child')
-                self.eventExit = True
-                return
-            
-            if cmd == DebugProtocol.RequestForkMode:
-                self.fork_auto, self.fork_child = eval(arg)
-                return
-        
+##            if cmd == DebugProtocol.RequestUTRun:
+##                from DCTestResult import DCTestResult
+##                self.testResult = DCTestResult(self)
+##                if self.cover:
+##                    self.cover.start()
+##                self.test.run(self.testResult)
+##                if self.cover:
+##                    self.cover.stop()
+##                    self.cover.save()
+##                self.write('{0}\n'.format(DebugProtocol.ResponseUTFinished))
+##                return
+##            
+##            if cmd == DebugProtocol.RequestUTStop:
+##                self.testResult.stop()
+##                return
+##        
+##            if cmd == DebugProtocol.ResponseForkTo:
+##                # this results from a separate event loop
+##                self.fork_child = (arg == 'child')
+##                self.eventExit = True
+##                return
+##            
+##            if cmd == DebugProtocol.RequestForkMode:
+##                self.fork_auto, self.fork_child = eval(arg)
+##                return
+##        
 ##        # If we are handling raw mode input then reset the mode and break out
 ##        # of the current event loop.
 ##        if self.inRawMode:
@@ -1046,56 +1047,48 @@
         if method == "RequestVariables":
             self.__dumpVariables(
                 params["frameNumber"], params["scope"], params["filters"])
-            return
         
-        if method == "RequestVariable":
+        elif method == "RequestVariable":
             self.__dumpVariable(
                 params["variable"], params["frameNumber"],
                 params["scope"], params["filters"])
-            return
         
-        if method == "RequestThreadList":
+        elif method == "RequestThreadList":
             self.__dumpThreadList()
-            return
         
-        if method == "RequestThreadSet":
+        elif method == "RequestThreadSet":
             if params["threadID"] in self.threads:
                 self.setCurrentThread(params["threadID"])
-                self.__sendJsonCommand("ResponseThreadSet", {})
+                self.sendJsonCommand("ResponseThreadSet", {})
                 stack = self.currentThread.getStack()
-                self.__sendJsonCommand("ResponseStack", {
+                self.sendJsonCommand("ResponseStack", {
                     "stack": stack,
                 })
-            return
         
-        if method == "RequestCapabilities":
-            self.__sendJsonCommand("ResponseCapabilities", {
+        elif method == "RequestCapabilities":
+            self.sendJsonCommand("ResponseCapabilities", {
                 "capabilities": self.__clientCapabilities(),
                 "clientType": "Python3"
             })
-            return
         
-        if method == "RequestBanner":
-            self.__sendJsonCommand("ResponseBanner", {
+        elif method == "RequestBanner":
+            self.sendJsonCommand("ResponseBanner", {
                 "version": "Python {0}".format(sys.version),
                 "platform": socket.gethostname(),
                 "dbgclient": self.variant,
             })
-            return
         
-        if method == "RequestSetFilter":
+        elif method == "RequestSetFilter":
             self.__generateFilterObjects(params["scope"], params["filter"])
-            return
         
-        if method == "RequestCallTrace":
+        elif method == "RequestCallTrace":
             if self.debugging:
                 self.callTraceEnabled = params["enable"]
             else:
                 self.__newCallTraceEnabled = params["enable"]
                 # remember for later
-            return
         
-        if method == "RequestEnvironment":
+        elif method == "RequestEnvironment":
             for key, value in params["environment"].items():
                 if key.endswith("+"):
                     if key[:-1] in os.environ:
@@ -1104,9 +1097,8 @@
                         os.environ[key[:-1]] = value
                 else:
                     os.environ[key] = value
-            return
         
-        if method == "RequestLoad":
+        elif method == "RequestLoad":
             self._fncache = {}
             self.dircache = []
             sys.argv = []
@@ -1149,9 +1141,8 @@
                 self.callTraceEnabled = self.__newCallTraceEnabled
                 res = self.mainThread.run(code, self.debugMod.__dict__)
                 self.progTerminated(res)
-            return
 
-        if method == "RequestRun":
+        elif method == "RequestRun":
             sys.argv = []
             self.__setCoding(params["filename"])
             sys.argv.append(params["filename"])
@@ -1192,9 +1183,8 @@
                     atexit._run_exitfuncs()
                 self.writestream.flush()
                 self.progTerminated(res)
-            return
 
-        if method == "RequestCoverage":
+        elif method == "RequestCoverage":
             from coverage import coverage
             sys.argv = []
             self.__setCoding(params["filename"])
@@ -1242,9 +1232,8 @@
                 self.cover.save()
                 self.writestream.flush()
                 self.progTerminated(res)
-            return
         
-        if method == "RequestProfile":
+        elif method == "RequestProfile":
             sys.setprofile(None)
             import PyProfile
             sys.argv = []
@@ -1287,9 +1276,8 @@
                 self.prof.save()
                 self.writestream.flush()
                 self.progTerminated(res)
-            return
         
-        if method == "ExecuteStatement":
+        elif method == "ExecuteStatement":
             if self.buffer:
                 self.buffer = self.buffer + '\n' + params["statement"]
             else:
@@ -1301,13 +1289,17 @@
                 # Report the exception
                 sys.last_type, sys.last_value, sys.last_traceback = \
                     sys.exc_info()
-                for l in traceback.format_exception_only(
-                        sys.last_type, sys.last_value):
-                    self.write(l)
+##                for l in traceback.format_exception_only(
+##                        sys.last_type, sys.last_value):
+##                    self.write(l)
+                self.sendJsonCommand("ClientOutput", {
+                    "text": "".join(traceback.format_exception_only(
+                        sys.last_type, sys.last_value))
+                })
                 self.buffer = ''
             else:
                 if code is None:
-                    self.__sendJsonCommand("ResponseContinue", {})
+                    self.sendJsonCommand("ResponseContinue", {})
                     return
                 else:
                     self.buffer = ''
@@ -1357,6 +1349,7 @@
                         self.progTerminated(exc.code)
                     except Exception:
                         # Report the exception and the traceback
+                        tlist = []
                         try:
                             exc_type, exc_value, exc_tb = sys.exc_info()
                             sys.last_type = exc_type
@@ -1364,57 +1357,54 @@
                             sys.last_traceback = exc_tb
                             tblist = traceback.extract_tb(exc_tb)
                             del tblist[:1]
-                            list = traceback.format_list(tblist)
-                            if list:
-                                list.insert(0, "Traceback (innermost last):\n")
-                                list[len(list):] = \
+                            tlist = traceback.format_list(tblist)
+                            if tlist:
+                                tlist.insert(
+                                    0, "Traceback (innermost last):\n")
+                                tlist[len(tlist):] = \
                                     traceback.format_exception_only(
                                         exc_type, exc_value)
                         finally:
                             tblist = exc_tb = None
 
-                        for l in list:
-                            self.write(l)
+##                        for l in list:
+##                            self.write(l)
+                        self.sendJsonCommand("ClientOutput", {
+                            "text": "".join(tlist)
+                        })
             
-            self.__sendJsonCommand("ResponseOK", {})
-            return
+            self.sendJsonCommand("ResponseOK", {})
         
-        if method == "RequestStep":
+        elif method == "RequestStep":
             self.currentThread.step(True)
             self.eventExit = True
-            return
 
-        if method == "RequestStepOver":
+        elif method == "RequestStepOver":
             self.currentThread.step(False)
             self.eventExit = True
-            return
         
-        if method == "RequestStepOut":
+        elif method == "RequestStepOut":
             self.currentThread.stepOut()
             self.eventExit = True
-            return
         
-        if method == "RequestStepQuit":
+        elif method == "RequestStepQuit":
             if self.passive:
                 self.progTerminated(42)
             else:
                 self.set_quit()
                 self.eventExit = True
-            return
         
-        if method == "RequestContinue":
+        elif method == "RequestContinue":
             self.currentThread.go(params["special"])
             self.eventExit = True
-            return
         
-        if method == "RawInput":
+        elif method == "RawInput":
             # If we are handling raw mode input then break out of the current
             # event loop.
             self.rawLine = params["input"]
             self.eventExit = True
-            return
         
-        if method == "RequestBreakpoint":
+        elif method == "RequestBreakpoint":
             if params["setBreakpoint"]:
                 if params["condition"] in ['None', '']:
                     params["condition"] = None
@@ -1422,7 +1412,7 @@
                     try:
                         compile(params["condition"], '<string>', 'eval')
                     except SyntaxError:
-                        self.__sendJsonCommand("ResponseBPConditionError", {
+                        self.sendJsonCommand("ResponseBPConditionError", {
                             "filename": params["filename"],
                             "line": params["line"],
                         })
@@ -1432,32 +1422,28 @@
                     params["condition"])
             else:
                 self.mainThread.clear_break(params["filename"], params["line"])
-
-            return
         
-        if method == "RequestBreakpointEnable":
+        elif method == "RequestBreakpointEnable":
             bp = self.mainThread.get_break(params["filename"], params["line"])
             if bp is not None:
                 if params["enable"]:
                     bp.enable()
                 else:
                     bp.disable()
-            return
         
-        if method == "RequestBreakpointIgnore":
+        elif method == "RequestBreakpointIgnore":
             bp = self.mainThread.get_break(params["filename"], params["line"])
             if bp is not None:
                 bp.ignore = params["count"]
-            return
         
-        if method == "RequestWatch":
+        elif method == "RequestWatch":
             if params["setWatch"]:
                 if not params["condition"].endswith(
                         ('??created??', '??changed??')):
                     try:
                         compile(params["condition"], '<string>', 'eval')
                     except SyntaxError:
-                        self.__sendJsonCommand("ResponseWatchConditionError", {
+                        self.sendJsonCommand("ResponseWatchConditionError", {
                             "condition": params["condition"],
                         })
                         return
@@ -1465,37 +1451,28 @@
                     params["condition"], params["temporary"])
             else:
                 self.mainThread.clear_watch(params["condition"])
-            return
         
-        if method == "RequestWatchEnable":
+        elif method == "RequestWatchEnable":
             wp = self.mainThread.get_watch(params["condition"])
             if wp is not None:
                 if params["enable"]:
                     wp.enable()
                 else:
                     wp.disable()
-            return
         
-        if method == "RequestWatchIgnore":
+        elif method == "RequestWatchIgnore":
             wp = self.mainThread.get_watch(params["condition"])
             if wp is not None:
                 wp.ignore = params["count"]
             return
         
-        if method == "RequestShutdown":
+        elif method == "RequestShutdown":
             self.sessionClose()
-            return
         
-        if method == "RequestCompletion":
+        elif method == "RequestCompletion":
             self.__completionList(params["text"])
-            return
         
-        if method == "RequestUTPrepare":
-##            fn, tn, tfn, failed, cov, covname, erase = arg.split('|')
-##            sys.path.insert(0, os.path.dirname(os.path.abspath(fn)))
-##            os.chdir(sys.path[0])
-##            failed = eval(failed)
-##
+        elif method == "RequestUTPrepare":
             # set the system exception handling function to ensure, that
             # we report on all unhandled exceptions
             sys.excepthook = self.__unhandled_exception
@@ -1518,15 +1495,12 @@
                         .loadTestsFromModule(utModule)
             except Exception:
                 exc_type, exc_value, exc_tb = sys.exc_info()
-##                self.write('{0}{1}\n'.format(
-##                    DebugProtocol.ResponseUTPrepared,
-##                    str((0, str(exc_type), str(exc_value)))))
-                self.__sendJsonCommand("ResponseUTPrepared", {
+                self.sendJsonCommand("ResponseUTPrepared", {
                     "count": 0,
                     "exception": exc_type.__name__,
                     "message": str(exc_value),
                 })
-                self.__exceptionRaised()
+##                self.__exceptionRaised()
                 return
             
             # generate a coverage object
@@ -1541,19 +1515,34 @@
             else:
                 self.cover = None
             
-##            self.write('{0}{1}\n'.format(
-##                DebugProtocol.ResponseUTPrepared,
-##                str((self.test.countTestCases(), "", ""))))
-            self.__sendJsonCommand("ResponseUTPrepared", {
+            self.sendJsonCommand("ResponseUTPrepared", {
                 "count": self.test.countTestCases(),
                 "exception": "",
                 "message": "",
             })
-            return
+        
+        elif method == "RequestUTRun":
+            from DCTestResult import DCTestResult
+            self.testResult = DCTestResult(self)
+            if self.cover:
+                self.cover.start()
+            self.test.run(self.testResult)
+            if self.cover:
+                self.cover.stop()
+                self.cover.save()
+            self.sendJsonCommand("ResponseUTFinished", {})
+        
+        elif method == "RequestUTStop":
+            self.testResult.stop()
+        
+        elif method == "ResponseForkTo":
+            # this results from a separate event loop
+            self.fork_child = (params["target"] == 'child')
+            self.eventExit = True
     
-    def __sendJsonCommand(self, command, params):
+    def sendJsonCommand(self, command, params):
         """
-        Private method to send a single command to the client.
+        Public method to send a single command to the client.
         
         @param command command name to be sent
         @type str
@@ -1568,7 +1557,9 @@
             "params": params,
         }
         cmd = json.dumps(commandDict) + '\n'
-        self.write(cmd)
+        
+        self.writestream.write_p(cmd)
+        self.writestream.flush()
     
     def sendClearTemporaryBreakpoint(self, filename, lineno):
         """
@@ -1579,7 +1570,7 @@
         @param lineno linenumber of the bp
         @type int
         """
-        self.__sendJsonCommand("ResponseClearBreakpoint", {
+        self.sendJsonCommand("ResponseClearBreakpoint", {
             "filename": filename,
             "line": lineno
         })
@@ -1591,7 +1582,7 @@
         @param condition condition of the watch expression to be cleared
         @type str
         """
-        self.__sendJsonCommand("ResponseClearWatch", {
+        self.sendJsonCommand("ResponseClearWatch", {
             "condition": condition,
         })
     
@@ -1602,7 +1593,7 @@
         @param stack call stack
         @type list
         """
-        self.__sendJsonCommand("ResponseLine", {
+        self.sendJsonCommand("ResponseLine", {
             "stack": stack,
         })
     
@@ -1617,7 +1608,7 @@
         @param toStr pre-formatted target info
         @type str
         """
-        self.__sendJsonCommand("CallTrace", {
+        self.sendJsonCommand("CallTrace", {
             "event": event[0],
             "from": fromStr,
             "to": toStr,
@@ -1634,7 +1625,7 @@
         @param stack stack trace information
         @param list
         """
-        self.__sendJsonCommand("ResponseException", {
+        self.sendJsonCommand("ResponseException", {
             "type": exceptionType,
             "message": exceptionMessage,
             "stack": stack,
@@ -1653,7 +1644,7 @@
         @param charno character number info
         @tyoe int
         """
-        self.__sendJsonCommand("ResponseSyntax", {
+        self.sendJsonCommand("ResponseSyntax", {
             "message": message,
             "filename": filename,
             "linenumber": lineno,
@@ -1669,7 +1660,7 @@
         @param exceptions flag to enable exception reporting of the IDE
         @type bool
         """
-        self.__sendJsonCommand("PassiveStartup", {
+        self.sendJsonCommand("PassiveStartup", {
             "filename": filename,
             "exceptions": exceptions,
         })
@@ -1691,23 +1682,58 @@
             return (
                 self.clientCapabilities & ~DebugClientCapabilities.HasProfiler)
     
-    def write(self, s):
+##    def write(self, s):
+##        """
+##        Public method to write data to the output stream.
+##        
+##        @param s data to be written (string)
+##        """
+##        self.writestream.write_p(s)
+##        self.writestream.flush()
+##
+    def readReady(self, stream):
         """
-        Public method to write data to the output stream.
+        Public method called when there is data ready to be read.
         
-        @param s data to be written (string)
+        @param fd file descriptor of the file that has data to be read (int)
         """
-        self.writestream.write(s)
-        self.writestream.flush()
+        try:
+            got = stream.readline_p()
+        except Exception:
+            return
+
+        if len(got) == 0:
+            self.sessionClose()
+            return
+
+        self.__receiveBuffer = self.__receiveBuffer + got
 
+        # Call handleLine for the line if it is complete.
+        eol = self.__receiveBuffer.find('\n')
+        while eol >= 0:
+            line = self.__receiveBuffer[:eol + 1]
+            self.__receiveBuffer = self.__receiveBuffer[eol + 1:]
+            self.handleLine(line)
+            eol = self.__receiveBuffer.find('\n')
+
+    def writeReady(self, stream):
+        """
+        Public method called when we are ready to write data.
+        
+        @param fd file descriptor of the file that has data to be written (int)
+        """
+        stream.write_p("")
+        stream.flush()
+    
     def __interact(self):
         """
         Private method to interact with the debugger.
         """
         global DebugClientInstance
 
-        self.setDescriptors(self.readstream, self.writestream)
+##        self.setDescriptors(self.readstream, self.writestream)
         DebugClientInstance = self
+        self.__receiveBuffer = ""
 
         if not self.passive:
             # At this point simulate an event loop.
@@ -1742,13 +1768,13 @@
                 continue
 
             if self.readstream in rrdy:
-                self.readReady(self.readstream.fileno())
+                self.readReady(self.readstream)
 
             if self.writestream in wrdy:
-                self.writeReady(self.writestream.fileno())
+                self.writeReady(self.writestream)
 
             if self.errorstream in wrdy:
-                self.writeReady(self.errorstream.fileno())
+                self.writeReady(self.errorstream)
 
         self.eventExit = None
         self.pollingDisabled = False
@@ -1786,10 +1812,10 @@
             self.readReady(self.readstream.fileno())
 
         if self.writestream in wrdy:
-            self.writeReady(self.writestream.fileno())
+            self.writeReady(self.writestream)
 
         if self.errorstream in wrdy:
-            self.writeReady(self.errorstream.fileno())
+            self.writeReady(self.errorstream)
     
     def connectDebugger(self, port, remoteAddress=None, redirect=True):
         """
@@ -1888,7 +1914,7 @@
 ##        siglist = [message, [filename, linenr, ffunc, fargs]]
 ##        
 ##        self.write("{0}{1}".format(DebugProtocol.ResponseSignal, str(siglist)))
-        self.__sendJsonCommand("ResponseSignal", {
+        self.sendJsonCommand("ResponseSignal", {
             "message": message,
             "filename": filename, 
             "linenumber": linenr, 
@@ -1969,12 +1995,14 @@
         """
         return self.running
 
-    def progTerminated(self, status):
+    def progTerminated(self, status, message=""):
         """
         Public method to tell the debugger that the program has terminated.
         
         @param status return status
         @type int
+        @param message status message
+        @type str
         """
         if status is None:
             status = 0
@@ -1988,8 +2016,9 @@
             self.set_quit()
             self.running = None
 ##            self.write('{0}{1:d}\n'.format(DebugProtocol.ResponseExit, status))
-            self.__sendJsonCommand("ResponseExit", {
+            self.sendJsonCommand("ResponseExit", {
                 "status": status,
+                "message": message,
             })
         
         # reset coding
@@ -2039,7 +2068,7 @@
         
 ##        self.write('{0}{1}\n'.format(
 ##            DebugProtocol.ResponseVariables, str(varlist)))
-        self.__sendJsonCommand("ResponseVariables", {
+        self.sendJsonCommand("ResponseVariables", {
             "scope": scope,
             "variables": varlist,
         })
@@ -2328,7 +2357,7 @@
         
 ##        self.write('{0}{1}\n'.format(
 ##            DebugProtocol.ResponseVariable, str(varlist)))
-        self.__sendJsonCommand("ResponseVariable", {
+        self.sendJsonCommand("ResponseVariable", {
             "scope": scope,
             "variable": var,
             "variables": varlist,
@@ -2630,7 +2659,7 @@
         
 ##        self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion,
 ##                                          str(list(completions)), text))
-        self.__sendJsonCommand("ResponseCompletion", {
+        self.sendJsonCommand("ResponseCompletion", {
             "completions": list(completions),
             "text": text, 
         })
@@ -2914,7 +2943,8 @@
         @return process ID (integer)
         """
         if not self.fork_auto:
-            self.write(DebugProtocol.RequestForkTo + '\n')
+##            self.write(DebugProtocol.RequestForkTo + '\n')
+            self.sendJsonCommand("RequestForkTo", {})
             self.eventLoop(True)
         pid = DebugClientOrigFork()
         if pid == 0:

eric ide

mercurial