src/eric7/Debugger/DebuggerInterfacePython.py

branch
server
changeset 10555
08e853c0c77b
parent 10551
d80184d38152
child 10559
64db35c6e335
--- a/src/eric7/Debugger/DebuggerInterfacePython.py	Wed Feb 07 15:28:08 2024 +0100
+++ b/src/eric7/Debugger/DebuggerInterfacePython.py	Fri Feb 09 19:54:15 2024 +0100
@@ -50,6 +50,17 @@
         self.__autoContinued = []
         self.__isStepCommand = False
 
+        self.__ericServerDebugging = False  # are we debugging via the eric-ide server?
+        try:
+            self.__ericServerDebuggerInterface = ericApp().getObject(
+                "EricServer"
+            ).getServiceInterface("Debugger")
+            self.__ericServerDebuggerInterface.debugClientResponse.connect(
+                lambda jsonStr: self.handleJsonCommand(jsonStr, None)
+            )
+        except KeyError:
+            self.__ericServerDebuggerInterface = None
+
         self.debugServer = debugServer
         self.passive = passive
         self.process = None
@@ -88,8 +99,8 @@
         @param fn filename to be translated
         @type str
         @param remote2local flag indicating the direction of translation
-            (False = local to remote, True = remote to local [default])
-        @type bool
+            (False = local to remote, True = remote to local) (defaults to True)
+        @type bool (optional)
         @return translated filename
         @rtype str
         """
@@ -102,8 +113,8 @@
         @param fn filename to be translated
         @type str
         @param remote2local flag indicating the direction of translation
-            (False = local to remote, True = remote to local [default])
-        @type bool
+            (False = local to remote, True = remote to local) (defaults to True)
+        @type bool (optional)
         @return translated filename
         @rtype str
         """
@@ -118,7 +129,24 @@
 
         return path
 
-    def __startProcess(self, program, arguments, environment=None, workingDir=None):
+    def __ericServerTranslation(self, fn, remote2local=True):
+        """
+        Private method to perform the eric-ide server path translation.
+
+        @param fn filename to be translated
+        @type str
+        @param remote2local flag indicating the direction of translation
+            (False = local to remote, True = remote to local) (defaults to True)
+        @type bool (optional)
+        @return translated filename
+        @rtype str
+        """
+        if remote2local:
+            return FileSystemUtilities.remoteFileName(fn)
+        else:
+            return FileSystemUtilities.plainFileName(fn)
+
+    def __startProcess(self, program, arguments, environment=None, workingDir=""):
         """
         Private method to start the debugger client process.
 
@@ -154,8 +182,9 @@
         runInConsole,
         venvName,
         originalPathString,
-        workingDir=None,
+        workingDir="",
         configOverride=None,
+        startRemote=False,
     ):
         """
         Public method to start a remote Python interpreter.
@@ -169,41 +198,54 @@
         @type str
         @param originalPathString original PATH environment variable
         @type str
-        @param workingDir directory to start the debugger client in
-        @type str
-        @param configOverride dictionary containing the global config override
-            data
-        @type dict
+        @param workingDir directory to start the debugger client in (defaults to "")
+        @type str (optional)
+        @param configOverride dictionary containing the global config override data
+            (defaults to None)
+        @type dict (optional)
+        @param startRemote flag indicating to start the client via an eric-ide server
+            (defaults to False)
+        @type bool (optional)
         @return client process object, a flag to indicate a network connection
             and the name of the interpreter in case of a local execution
         @rtype tuple of (QProcess, bool, str)
         """
         global origPathEnv
 
-        if not venvName:
-            venvName = Preferences.getDebugger("Python3VirtualEnv")
-        if venvName == self.debugServer.getProjectEnvironmentString():
-            project = ericApp().getObject("Project")
-            venvName = project.getProjectVenv()
-            execPath = project.getProjectExecPath()
-            interpreter = project.getProjectInterpreter()
+        if startRemote or venvName == self.debugServer.getEricServerEnvironmentString():
+            # TODO change this once server environment definitions are supported
+            startRemote = True
+            venvName = self.debugServer.getEricServerEnvironmentString()
+            interpreter = ""
         else:
-            venvManager = ericApp().getObject("VirtualEnvManager")
-            interpreter = venvManager.getVirtualenvInterpreter(venvName)
-            execPath = venvManager.getVirtualenvExecPath(venvName)
-        if interpreter == "":
-            # use the interpreter used to run eric for identical variants
-            interpreter = PythonUtilities.getPythonExecutable()
-        if interpreter == "":
-            EricMessageBox.critical(
-                None,
-                self.tr("Start Debugger"),
-                self.tr("""<p>No suitable Python3 environment configured.</p>"""),
-            )
-            return None, False, ""
+            if not venvName:
+                venvName = Preferences.getDebugger("Python3VirtualEnv")
+            if venvName == self.debugServer.getProjectEnvironmentString():
+                project = ericApp().getObject("Project")
+                venvName = project.getProjectVenv()
+                execPath = project.getProjectExecPath()
+                interpreter = project.getProjectInterpreter()
+            else:
+                venvManager = ericApp().getObject("VirtualEnvManager")
+                interpreter = venvManager.getVirtualenvInterpreter(venvName)
+                execPath = venvManager.getVirtualenvExecPath(venvName)
+            if interpreter == "":
+                # use the interpreter used to run eric for identical variants
+                interpreter = PythonUtilities.getPythonExecutable()
+            if interpreter == "":
+                EricMessageBox.critical(
+                    None,
+                    self.tr("Start Debugger"),
+                    self.tr("""<p>No suitable Python3 environment configured.</p>"""),
+                )
+                return None, False, ""
 
         self.__inShutdown = False
 
+        self.__ericServerDebugging = False
+        self.__ericServerDebuggerInterface.stopClient()
+        self.__mainDebugger = None
+
         redirect = (
             str(configOverride["redirect"])
             if configOverride and configOverride["enable"]
@@ -290,6 +332,28 @@
                 )
                 return None, False, ""
 
+        elif startRemote and self.__ericServerDebuggerInterface is not None:
+            # debugging via an eric-ide server
+            ##self.__ericServerDebuggerInterface.stopClient()
+            ##self.__mainDebugger = None
+##
+            self.translate = self.__ericServerTranslation
+            self.__ericServerDebugging = True
+
+            args = []
+            if noencoding:
+                args.append(noencoding)
+            if multiprocessEnabled:
+                args.append(multiprocessEnabled)
+            if callTraceOptimization:
+                args.append(callTraceOptimization)
+            self.__ericServerDebuggerInterface.startClient(
+                interpreter, originalPathString, args, workingDir=workingDir,
+            )
+            self.__startedVenv = venvName
+
+            return None, self.__isNetworked, ""
+
         else:
             # local debugging code below
             debugClient = self.__determineDebugClient()
@@ -398,6 +462,7 @@
         originalPathString,
         workingDir=None,
         configOverride=None,
+        startRemote=False,
     ):
         """
         Public method to start a remote Python interpreter for a project.
@@ -416,6 +481,9 @@
         @param configOverride dictionary containing the global config override
             data
         @type dict
+        @param startRemote flag indicating to start the client via an eric-ide server
+            (defaults to False)
+        @type bool (optional)
         @return client process object, a flag to indicate a network connection
             and the name of the interpreter in case of a local execution
         @rtype tuple of (QProcess, bool, str)
@@ -460,6 +528,10 @@
 
         self.__inShutdown = False
 
+        self.__ericServerDebugging = False
+        self.__ericServerDebuggerInterface.stopClient()
+        self.__mainDebugger = None
+
         if project.getDebugProperty("REMOTEDEBUGGER"):
             # remote debugging code
             ipaddr = self.debugServer.getHostAddress(False)
@@ -517,6 +589,8 @@
                 # remote shell command is missing
                 return None, self.__isNetworked, ""
 
+        # TODO: add server debugging for projects
+
         else:
             # local debugging code below
             debugClient = project.getDebugProperty("DEBUGCLIENT")
@@ -619,7 +693,7 @@
         """
         self.__pendingConnections.append(sock)
 
-        sock.readyRead.connect(lambda: self.__parseClientLine(sock))
+        sock.readyRead.connect(lambda: self.__receiveJson(sock))
         sock.disconnected.connect(lambda: self.__socketDisconnected(sock))
 
         return True
@@ -634,30 +708,30 @@
         @param debuggerId id of the connected debug client
         @type str
         """
-        if sock in self.__pendingConnections:
+        if sock and sock in self.__pendingConnections:
             self.__connections[debuggerId] = sock
             self.__pendingConnections.remove(sock)
 
-            if self.__mainDebugger is None:
-                self.__mainDebugger = debuggerId
-                # Get the remote clients capabilities
-                self.remoteCapabilities(debuggerId)
+        if self.__mainDebugger is None:
+            self.__mainDebugger = debuggerId
+            # Get the remote clients capabilities
+            self.remoteCapabilities(debuggerId)
 
-            self.debugServer.signalClientDebuggerId(debuggerId)
+        self.debugServer.signalClientDebuggerId(debuggerId)
 
-            if debuggerId == self.__mainDebugger:
-                self.__flush()
-                self.debugServer.mainClientConnected()
+        if debuggerId == self.__mainDebugger:
+            self.__flush()
+            self.debugServer.mainClientConnected()
 
-            self.debugServer.initializeClient(debuggerId)
+        self.debugServer.initializeClient(debuggerId)
 
-            # perform auto-continue except for main
-            if (
-                debuggerId != self.__mainDebugger
-                and self.__autoContinue
-                and not self.__isStepCommand
-            ):
-                QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId))
+        # perform auto-continue except for main
+        if (
+            debuggerId != self.__mainDebugger
+            and self.__autoContinue
+            and not self.__isStepCommand
+        ):
+            QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId))
 
     def __socketDisconnected(self, sock):
         """
@@ -707,9 +781,15 @@
         """
         if self.__mainDebugger:
             # Send commands that were waiting for the connection.
-            conn = self.__connections[self.__mainDebugger]
-            for jsonStr in self.__commandQueue:
-                self.__writeJsonCommandToSocket(jsonStr, conn)
+            if self.__ericServerDebugging:
+                for jsonStr in self.__commandQueue:
+                    self.__ericServerDebuggerInterface.sendClientCommand(
+                        self.__mainDebugger, jsonStr
+                    )
+            else:
+                conn = self.__connections[self.__mainDebugger]
+                for jsonStr in self.__commandQueue:
+                    self.__writeJsonCommandToSocket(jsonStr, conn)
 
         self.__commandQueue.clear()
 
@@ -1396,7 +1476,7 @@
             debuggerId,
         )
 
-    def __parseClientLine(self, sock):
+    def __receiveJson(self, sock):
         """
         Private method to handle data from the client.
 
@@ -1423,11 +1503,11 @@
             logging.debug("<Debug-Server> %s", jsonStr)
             ##print("Server: ", jsonStr)    ## debug       # __IGNORE_WARNING_M891__
 
-            self.__handleJsonCommand(jsonStr, sock)
+            self.handleJsonCommand(jsonStr, sock)
 
-    def __handleJsonCommand(self, jsonStr, sock):
+    def handleJsonCommand(self, jsonStr, sock):
         """
-        Private method to handle a command or response serialized as a
+        Public method to handle a command or response serialized as a
         JSON string.
 
         @param jsonStr string containing the command or response received
@@ -1663,14 +1743,25 @@
         }
         jsonStr = json.dumps(commandDict)
 
-        if debuggerId and debuggerId in self.__connections:
-            sock = self.__connections[debuggerId]
-        elif sock is None and self.__mainDebugger is not None:
-            sock = self.__connections[self.__mainDebugger]
-        if sock is not None:
-            self.__writeJsonCommandToSocket(jsonStr, sock)
+        if self.__ericServerDebugging:
+            # Debugging via the eric-ide server -> pass the command on to it
+            if self.__mainDebugger is None:
+                # debugger has not connected yet -> queue the command
+                self.__commandQueue.append(jsonStr)
+            else:
+                self.__ericServerDebuggerInterface.sendClientCommand(
+                    debuggerId, jsonStr
+                )
         else:
-            self.__commandQueue.append(jsonStr)
+            # Local debugging -> send the command to the client
+            if debuggerId and debuggerId in self.__connections:
+                sock = self.__connections[debuggerId]
+            elif sock is None and self.__mainDebugger is not None:
+                sock = self.__connections[self.__mainDebugger]
+            if sock is not None:
+                self.__writeJsonCommandToSocket(jsonStr, sock)
+            else:
+                self.__commandQueue.append(jsonStr)
 
     def __writeJsonCommandToSocket(self, jsonCommand, sock):
         """

eric ide

mercurial