Continued with the multiprocess debugger. Started with QProcess support. multi_processing

Mon, 10 Feb 2020 18:49:49 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 10 Feb 2020 18:49:49 +0100
branch
multi_processing
changeset 7407
a0b6acee2c20
parent 7405
bf6be3cff6cf
child 7408
0d58e708f57b

Continued with the multiprocess debugger. Started with QProcess support.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/DebugClientBase.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/ModuleLoader.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/QProcessExtension.py file | annotate | diff | comparison | revisions
eric6/Debugger/DebugServer.py file | annotate | diff | comparison | revisions
eric6/Debugger/DebugViewer.py file | annotate | diff | comparison | revisions
eric6/Debugger/DebuggerInterfacePython.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6.e4p	Mon Feb 10 18:49:49 2020 +0100
@@ -51,6 +51,7 @@
     <Source>eric6/DebugClients/Python/FlexCompleter.py</Source>
     <Source>eric6/DebugClients/Python/ModuleLoader.py</Source>
     <Source>eric6/DebugClients/Python/PyProfile.py</Source>
+    <Source>eric6/DebugClients/Python/QProcessExtension.py</Source>
     <Source>eric6/DebugClients/Python/ThreadExtension.py</Source>
     <Source>eric6/DebugClients/Python/__init__.py</Source>
     <Source>eric6/DebugClients/Python/coverage/__init__.py</Source>
@@ -2044,6 +2045,9 @@
     <Other>eric6/APIs/MicroPython/circuitpython.api</Other>
     <Other>eric6/APIs/MicroPython/microbit.api</Other>
     <Other>eric6/APIs/MicroPython/micropython.api</Other>
+    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
+    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
+    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/Python3/PyQt4.bas</Other>
     <Other>eric6/APIs/Python3/PyQt5.bas</Other>
     <Other>eric6/APIs/Python3/PyQtChart.bas</Other>
@@ -2051,9 +2055,6 @@
     <Other>eric6/APIs/Python3/QScintilla2.bas</Other>
     <Other>eric6/APIs/Python3/eric6.api</Other>
     <Other>eric6/APIs/Python3/eric6.bas</Other>
-    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
-    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
-    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/QSS/qss.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.bas</Other>
--- a/eric6/DebugClients/Python/DebugClientBase.py	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6/DebugClients/Python/DebugClientBase.py	Mon Feb 10 18:49:49 2020 +0100
@@ -2075,7 +2075,7 @@
     
     def startProgInDebugger(self, progargs, wd='', host=None,
                             port=None, exceptions=True, tracePython=False,
-                            redirect=True):
+                            redirect=True, passive=True):
         """
         Public method used to start the remote debugger.
         
@@ -2090,6 +2090,8 @@
             (boolean)
         @param redirect flag indicating redirection of stdin, stdout and
             stderr (boolean)
+        @param passive flag indicating a passive debugging session
+        @type bool
         """
         if host is None:
             host = os.getenv('ERICHOST', 'localhost')
@@ -2112,8 +2114,9 @@
         self.__setCoding(self.running)
         self.debugging = True
         
-        self.passive = True
-        self.sendPassiveStartup(self.running, exceptions)
+        self.passive = passive
+        if passive:
+            self.sendPassiveStartup(self.running, exceptions)
         self.__interact()
         
         self.attachThread(mainThread=True)
@@ -2125,16 +2128,14 @@
         self.__interceptSignals()
         
         # This will eventually enter a local event loop.
-        # Note the use of backquotes to cause a repr of self.running. The
-        # need for this is on Windows os where backslash is the path separator.
-        # They will get inadvertantly stripped away during the eval causing
-        # IOErrors if self.running is passed as a normal str.
         self.debugMod.__dict__['__file__'] = self.running
         sys.modules['__main__'] = self.debugMod
-        res = self.mainThread.run(
-            'exec(open(' + repr(self.running) + ').read())',
-            self.debugMod.__dict__)
-        self.progTerminated(res)
+        code = self.__compileFileSource(self.running)
+        if code:
+            res = self.mainThread.run(code, self.debugMod.__dict__, debug=True)
+            self.progTerminated(res)
+        else:
+            self.progTerminated(42)     # should not happen
 
     def run_call(self, scriptname, func, *args):
         """
@@ -2189,6 +2190,7 @@
             tracePython = False
             exceptions = True
             redirect = True
+            passive = True
             while args[0]:
                 if args[0] == '-h':
                     host = args[1]
@@ -2222,6 +2224,9 @@
                     self.fork_auto = True
                     self.fork_child = False
                     del args[0]
+                elif args[0] == '--no-passive':
+                    passive = False
+                    del args[0]
                 elif args[0] == '--':
                     del args[0]
                     break
@@ -2230,6 +2235,9 @@
             if not args:
                 print("No program given. Aborting!")
                 # __IGNORE_WARNING_M801__
+            elif "-m" in args:
+                print("Running module as a script is not supported. Aborting!")
+                # __IGNORE_WARNING_M801__
             else:
                 # Store options if a process is spawn
                 # TODO: check which ones are really needed
@@ -2242,7 +2250,8 @@
                 self.startProgInDebugger(args, wd, host, port,
                                          exceptions=exceptions,
                                          tracePython=tracePython,
-                                         redirect=redirect)
+                                         redirect=redirect,
+                                         passive=passive)
         else:
             if sys.argv[1] == '--no-encoding':
                 self.noencoding = True
--- a/eric6/DebugClients/Python/ModuleLoader.py	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6/DebugClients/Python/ModuleLoader.py	Mon Feb 10 18:49:49 2020 +0100
@@ -10,6 +10,8 @@
 import sys
 import importlib
 
+from QProcessExtension import patchQProcess
+
 
 class ModuleLoader(object):
     """
@@ -38,17 +40,17 @@
                 del sys.modules[moduleName]
         
         self.__modulesToPatch = (
-                'thread', '_thread', 'threading',
-                'greenlet',
-                'PyQt4.QtCore', 'PyQt5.QtCore',
-                'PySide.QtCore', 'PySide2.QtCore',
+            'thread', '_thread', 'threading',
+            'greenlet',
+            'PyQt4.QtCore', 'PyQt5.QtCore',
+            'PySide.QtCore', 'PySide2.QtCore',
         )
         
         sys.meta_path.insert(0, self)
     
     def __loadModule(self, fullname):
         """
-        Public method to load a module.
+        Private method to load a module.
         
         @param fullname name of the module to be loaded
         @type str
@@ -82,7 +84,7 @@
             if self.__dbgClient.patchGreenlet(module):
                 module.eric6_patched = True
         
-        ## Add hook for *.QThread
+        ## Add hook for *.QThread and *.QProcess
         elif (
             fullname in ('PyQt4.QtCore', 'PyQt5.QtCore',
                          'PySide.QtCore', 'PySide2.QtCore') and
@@ -90,6 +92,7 @@
         ):
             module.eric6_patched = True
             self.__dbgClient.patchQThread(module)
+            patchQProcess(module, self.__dbgClient.startOptions)
         
         self.__enableImportHooks = True
         return module
@@ -135,7 +138,7 @@
         
         def exec_module(self, module):
             """
-            Public method to execute the created module
+            Public method to execute the created module.
             
             @param module module to be executed
             @type module
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/DebugClients/Python/QProcessExtension.py	Mon Feb 10 18:49:49 2020 +0100
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a function to patch QProcess to support debugging of the
+process.
+"""
+
+import os
+
+_startOptions = []
+
+
+def patchQProcess(module, startOptions):
+    """
+    Function to patch the QtCore module's QProcess.
+    
+    @param module reference to the imported module to be patched
+    @type module
+    @param startOptions reference to the saved start options
+    @type tuple
+    """     # __IGNORE_WARNING_D234__
+    global _startOptions
+    
+    class QProcessWrapper(module.QProcess):
+        """
+        Wrapper class for *.QProcess.
+        """
+        def __init__(self, parent=None):
+            """
+            Constructor
+            """
+            super(QProcessWrapper, self).__init__(parent)
+        
+        def __modifyArgs(self, arguments):
+            """
+            Private method to modify the arguments given to the start method.
+            
+            @param arguments list of program arguments
+            @type list of str
+            @return modified argument list
+            @rtype list of str
+            """
+            (wd, host, port, exceptions, tracePython, redirect,
+             noencoding) = _startOptions[:7]
+            modifiedArguments = [
+                os.path.join(os.path.dirname(__file__), "DebugClient.py"),
+                "-h", host,
+                "-p", str(port),
+                "--no-passive"
+            ]
+            
+            if wd:
+                modifiedArguments.extend(["-w", wd])
+            if not exceptions:
+                modifiedArguments.append("-e")
+            if tracePython:
+                modifiedArguments.append("-t")
+            if not redirect:
+                modifiedArguments.append("-n")
+            if noencoding:
+                modifiedArguments.append("--no-encoding")
+            modifiedArguments.append("--")
+            # end the arguments for DebugClient
+            modifiedArguments.extend(arguments)
+            
+            return modifiedArguments
+        
+        def start(self, *args, **kwargs):
+            """
+            Public method to start the process.
+            
+            This method patches the arguments such, that a debug client is
+            started for the Python script. A Python script is assumed, if the
+            program to be started contains the string 'python'.
+            
+            @param args arguments of the start call
+            @type list
+            @param kwargs keyword arguments of the start call
+            @type dict
+            """
+            if (
+                (len(args) >= 2 and isinstance(args[1], list)) or
+                (len(args) == 1 and not isinstance(args[0], str)) or
+                len(args) == 0
+            ):
+                # TODO: implement this
+                if len(args) >= 2:
+                    program = args[0]
+                    arguments = args[1]
+                    if len(args) > 2:
+                        mode = args[2]
+                    else:
+                        mode = module.QIODevice.ReadWrite
+                else:
+                    program = self.program()
+                    arguments = self.arguments()
+                    if len(args) == 1:
+                        mode = args[0]
+                    else:
+                        mode = module.QIODevice.ReadWrite
+                if "python" in program.lower():
+                    # assume a Python script is to be started
+                    newArgs = self.__modifyArgs(arguments)
+                    super(QProcessWrapper, self).start(program, newArgs, mode)
+                else:
+                    super(QProcessWrapper, self).start(*args, **kwargs)
+            else:
+                super(QProcessWrapper, self).start(*args, **kwargs)
+    
+    _startOptions = startOptions[:]
+    module.QProcess = QProcessWrapper
--- a/eric6/Debugger/DebugServer.py	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6/Debugger/DebugServer.py	Mon Feb 10 18:49:49 2020 +0100
@@ -965,7 +965,6 @@
         @type bool
         """
         self.__autoClearShell = autoClearShell
-        self.__autoContinue = autoContinue
         
         if clientType not in self.getSupportedLanguages():
             # a not supported client language was requested
@@ -1476,6 +1475,7 @@
         
         @param text the text to be completed (string)
         """
+        # TODO: send this to the currently selected debugger
         self.debuggerInterface.remoteCompletion(text)
     
     def remoteUTDiscover(self, clientType, forProject, venvName, syspath,
--- a/eric6/Debugger/DebugViewer.py	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6/Debugger/DebugViewer.py	Mon Feb 10 18:49:49 2020 +0100
@@ -65,7 +65,7 @@
         self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
         
         self.__mainLayout = QVBoxLayout()
-        self.__mainLayout.setContentsMargins(0, 0, 0, 0)
+        self.__mainLayout.setContentsMargins(0, 3, 0, 0)
         self.setLayout(self.__mainLayout)
         
         # add the viewer showing the connected debug backends
@@ -432,7 +432,8 @@
         @param debuggerId ID of the debugger backend
         @type str
         """
-        self.__setDebuggerIconAndState(debuggerId, "mediaPlaybackPause", "broken")
+        self.__setDebuggerIconAndState(debuggerId, "mediaPlaybackPause",
+                                       "broken")
     
     def __clientSyntaxError(self, message, filename, lineNo, characterNo,
                             debuggerId):
@@ -450,7 +451,8 @@
         @param debuggerId ID of the debugger backend
         @type str
         """
-        self.__setDebuggerIconAndState(debuggerId, "syntaxError22", "exception")
+        self.__setDebuggerIconAndState(debuggerId, "syntaxError22",
+                                       "exception")
     
     def __clientException(self, exceptionType, exceptionMessage, stackTrace,
                           debuggerId):
--- a/eric6/Debugger/DebuggerInterfacePython.py	Sun Feb 09 19:27:49 2020 +0100
+++ b/eric6/Debugger/DebuggerInterfacePython.py	Mon Feb 10 18:49:49 2020 +0100
@@ -51,6 +51,7 @@
         
         self.__isNetworked = True
         self.__autoContinue = False
+        self.__autoContinued = []
         
         self.debugServer = debugServer
         self.passive = passive
@@ -1335,8 +1336,11 @@
             for s in params["stack"]:
                 s[0] = self.translate(s[0], True)
             cf = params["stack"][0]
-            if self.__autoContinue:
-                self.__autoContinue = False
+            if (
+                self.__autoContinue and
+                params["debuggerId"] not in self.__autoContinued
+            ):
+                self.__autoContinued.append(params["debuggerId"])
                 QTimer.singleShot(
                     0, lambda: self.remoteContinue(params["debuggerId"]))
             else:

eric ide

mercurial