src/eric7/Debugger/DebugServer.py

branch
eric7-maintenance
changeset 9264
18a7312cfdb3
parent 9192
a763d57e23bc
parent 9221
bf71ee032bb4
child 9305
3b7ef53c34c7
diff -r d23e9854aea4 -r 18a7312cfdb3 src/eric7/Debugger/DebugServer.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Debugger/DebugServer.py	Sun Jul 24 11:29:56 2022 +0200
@@ -0,0 +1,2182 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2007 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the debug server.
+"""
+
+import os
+import shlex
+import contextlib
+
+from PyQt6.QtCore import pyqtSignal, pyqtSlot, QModelIndex
+from PyQt6.QtNetwork import QTcpServer, QHostAddress, QHostInfo, QNetworkInterface
+
+from EricWidgets.EricApplication import ericApp
+from EricWidgets import EricMessageBox
+
+from .BreakPointModel import BreakPointModel
+from .WatchPointModel import WatchPointModel
+from . import DebugClientCapabilities
+
+import Preferences
+
+
+DebuggerInterfaces = {
+    "Python": "DebuggerInterfacePython",
+    "None": "DebuggerInterfaceNone",
+}
+
+
+class DebugServer(QTcpServer):
+    """
+    Class implementing the debug server embedded within the IDE.
+
+    @signal clientProcessStdout(str) emitted after the client has sent some
+        output via stdout
+    @signal clientProcessStderr(str) emitted after the client has sent some
+        output via stderr
+    @signal clientOutput(str) emitted after the client has sent some output
+    @signal clientRawInputSent(debuggerId) emitted after the data was sent
+        to the indicated debug client
+    @signal clientLine(filename, lineno, debuggerId, threadName, forStack)
+        emitted after the debug client has executed a line of code
+    @signal clientStack(stack, debuggerId, threadName) emitted after the
+        debug client has executed a line of code
+    @signal clientThreadList(currentId, threadList, debuggerId) emitted after
+        a thread list has been received
+    @signal clientThreadSet(debuggerId) emitted after the client has
+        acknowledged the change of the current thread
+    @signal clientVariables(scope, variables, debuggerId) emitted after a
+        variables dump has been received
+    @signal clientVariable(scope, variables, debuggerId) emitted after a dump
+        for one class variable has been received
+    @signal clientStatement(continue, debuggerId) emitted after an interactive
+        command has been executed. The parameter is False to indicate that the
+        command is complete and True if it needs more input.
+    @signal clientDisassembly(disassembly, debuggerId) emitted after the client
+        has sent a disassembly of the code raising an exception
+    @signal clientException(exceptionType, exceptionMessage, stackTrace,
+        debuggerId, threadName) emitted after an exception occured on the
+        client side
+    @signal clientSyntaxError(message, filename, linenumber, characternumber,
+        debuggerId, threadName) emitted after a syntax error has been detected
+        on the client side
+    @signal clientSignal(message, filename, linenumber, function name,
+        function arguments, debuggerId) emitted after a signal has been
+        generated on the client side
+    @signal clientDisconnected(str) emitted after a debug client has
+        disconnected (i.e. closed the network socket)
+    @signal clientExit(str, int, str, bool, str) emitted after the client has
+        exited giving the program name, the exit status, an exit message, an
+        indication to be quiet and the ID of the exited client
+    @signal mainClientExit() emitted to indicate that the main client process
+        has exited
+    @signal lastClientExited() emitted to indicate that the last connected
+        debug client has terminated
+    @signal clientClearBreak(filename, lineno, debuggerId) emitted after the
+        debug client has decided to clear a temporary breakpoint
+    @signal clientBreakConditionError(fn, lineno, debuggerId) emitted after the
+        client has signaled a syntax error in a breakpoint condition
+    @signal clientClearWatch(condition, debuggerId) emitted after the debug
+        client has decided to clear a temporary watch expression
+    @signal clientWatchConditionError(condition, debuggerId) emitted after the
+        client has signaled a syntax error in a watch expression
+    @signal clientRawInput(prompt, echo, debuggerId) emitted after a raw input
+        request was received
+    @signal clientBanner(version, platform, venvname) emitted after
+        the client banner data was received
+    @signal clientCapabilities(capabilities, cltype, venvname) emitted after
+        the clients capabilities were received
+    @signal clientCompletionList(completionList, text) emitted after the
+        commandline completion list and the reworked search string was
+        received from the client
+    @signal passiveDebugStarted(str, bool) emitted after the debug client has
+        connected in passive debug mode
+    @signal clientGone(bool) emitted if the client went away (planned or
+        unplanned)
+    @signal clientInterpreterChanged(str) emitted to signal a change of the
+        client interpreter
+    @signal callTraceInfo emitted after the client reported the call trace
+        data (isCall, fromFile, fromLine, fromFunction, toFile, toLine,
+        toFunction, debuggerId)
+    @signal appendStdout(msg) emitted when a passive debug connection is
+        established or lost
+    @signal clientDebuggerId(debuggerId) emitted to indicate a newly connected
+        debugger backend
+    """
+
+    clientClearBreak = pyqtSignal(str, int, str)
+    clientClearWatch = pyqtSignal(str, str)
+    clientGone = pyqtSignal(bool)
+    clientProcessStdout = pyqtSignal(str)
+    clientProcessStderr = pyqtSignal(str)
+    clientRawInputSent = pyqtSignal(str)
+    clientOutput = pyqtSignal(str)
+    clientLine = pyqtSignal(str, int, str, str, bool)
+    clientStack = pyqtSignal(list, str, str)
+    clientThreadList = pyqtSignal("PyQt_PyObject", list, str)
+    clientThreadSet = pyqtSignal(str)
+    clientVariables = pyqtSignal(int, list, str)
+    clientVariable = pyqtSignal(int, list, str)
+    clientStatement = pyqtSignal(bool, str)
+    clientDisassembly = pyqtSignal(dict, str)
+    clientException = pyqtSignal(str, str, list, str, str)
+    clientSyntaxError = pyqtSignal(str, str, int, int, str, str)
+    clientSignal = pyqtSignal(str, str, int, str, str, str)
+    clientDisconnected = pyqtSignal(str)
+    clientExit = pyqtSignal(str, int, str, bool, str)
+    mainClientExit = pyqtSignal()
+    lastClientExited = pyqtSignal()
+    clientBreakConditionError = pyqtSignal(str, int, str)
+    clientWatchConditionError = pyqtSignal(str, str)
+    clientRawInput = pyqtSignal(str, bool, str)
+    clientBanner = pyqtSignal(str, str, str)
+    clientCapabilities = pyqtSignal(int, str, str)
+    clientCompletionList = pyqtSignal(list, str)
+    clientInterpreterChanged = pyqtSignal(str)
+    clientDebuggerId = pyqtSignal(str)
+    passiveDebugStarted = pyqtSignal(str, bool)
+    callTraceInfo = pyqtSignal(bool, str, str, str, str, str, str, str)
+    appendStdout = pyqtSignal(str)
+
+    def __init__(
+        self,
+        originalPathString,
+        preventPassiveDebugging=False,
+        project=None,
+        parent=None,
+    ):
+        """
+        Constructor
+
+        @param originalPathString original PATH environment variable
+        @type str
+        @param preventPassiveDebugging flag overriding the PassiveDbgEnabled
+            setting (defaults to False)
+        @type bool (optional)
+        @param project reference to the project object (defaults to None)
+        @type Project (optional)
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super().__init__(parent)
+
+        self.__originalPathString = originalPathString
+
+        self.__debuggerInterfaces = {}
+        # the interface name is the key, a function to get the
+        # registration data is the value
+        self.__debuggerInterfaceRegistry = {}
+        # the client language is the key, a list containing the client
+        # capabilities, a list of associated file extensions, a
+        # function reference to create the debugger interface (see
+        # __createDebuggerInterface() below) and the interface name is
+        # the value
+
+        # create our models
+        self.breakpointModel = BreakPointModel(project, self)
+        self.watchpointModel = WatchPointModel(self)
+        self.watchSpecialCreated = self.tr(
+            "created", "must be same as in EditWatchpointDialog"
+        )
+        self.watchSpecialChanged = self.tr(
+            "changed", "must be same as in EditWatchpointDialog"
+        )
+
+        # arrays to track already reported issues
+        self.__reportedBreakpointIssues = []
+        self.__reportedWatchpointIssues = []
+
+        self.networkInterface = Preferences.getDebugger("NetworkInterface")
+        if self.networkInterface == "all":
+            hostAddress = QHostAddress("0.0.0.0")
+            # QHostAddress.SpecialAddress.Any)  # secok
+        elif self.networkInterface == "allv6":
+            hostAddress = QHostAddress("::")
+            # QHostAddress.SpecialAddress.AnyIPv6)
+        else:
+            hostAddress = QHostAddress(self.networkInterface)
+        (
+            self.networkInterfaceName,
+            self.networkInterfaceIndex,
+        ) = self.__getNetworkInterfaceAndIndex(self.networkInterface)
+
+        if not preventPassiveDebugging and Preferences.getDebugger("PassiveDbgEnabled"):
+            sock = Preferences.getDebugger("PassiveDbgPort")  # default: 42424
+            self.listen(hostAddress, sock)
+            self.passive = True
+            self.passiveClientExited = False
+        else:
+            if hostAddress.toString().lower().startswith("fe80"):
+                hostAddress.setScopeId(self.networkInterfaceName)
+            self.listen(hostAddress)
+            self.passive = False
+
+        self.debuggerInterface = None
+        self.debugging = False
+        self.running = False
+        self.clientProcess = None
+        self.clientInterpreter = ""
+        self.clientType = Preferences.getSettings().value("DebugClient/Type")
+        if self.clientType is None:
+            self.clientType = "Python3"
+
+        self.lastClientType = ""
+        self.__autoClearShell = False
+        self.__forProject = False
+
+        self.clientClearBreak.connect(self.__clientClearBreakPoint)
+        self.clientClearWatch.connect(self.__clientClearWatchPoint)
+        self.newConnection.connect(self.__newConnection)
+
+        self.breakpointModel.rowsAboutToBeRemoved.connect(self.__deleteBreakPoints)
+        self.breakpointModel.dataAboutToBeChanged.connect(
+            self.__breakPointDataAboutToBeChanged
+        )
+        self.breakpointModel.dataChanged.connect(self.__changeBreakPoints)
+        self.breakpointModel.rowsInserted.connect(self.__addBreakPoints)
+
+        self.watchpointModel.rowsAboutToBeRemoved.connect(self.__deleteWatchPoints)
+        self.watchpointModel.dataAboutToBeChanged.connect(
+            self.__watchPointDataAboutToBeChanged
+        )
+        self.watchpointModel.dataChanged.connect(self.__changeWatchPoints)
+        self.watchpointModel.rowsInserted.connect(self.__addWatchPoints)
+
+        self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
+
+        self.__multiprocessNoDebugList = []
+
+        self.__registerDebuggerInterfaces()
+
+    def getHostAddress(self, localhost):
+        """
+        Public method to get the IP address or hostname the debug server is
+        listening.
+
+        @param localhost flag indicating to return the address for localhost
+        @type bool
+        @return IP address or hostname
+        @rtype str
+        """
+        if self.networkInterface == "all":
+            if localhost:
+                return "127.0.0.1"
+            else:
+                return "{0}@@v4".format(QHostInfo.localHostName())
+        elif self.networkInterface == "allv6":
+            if localhost:
+                return "::1"
+            else:
+                return "{0}@@v6".format(QHostInfo.localHostName())
+        else:
+            return "{0}@@i{1}".format(self.networkInterface, self.networkInterfaceIndex)
+
+    def __getNetworkInterfaceAndIndex(self, address):
+        """
+        Private method to determine the network interface and the interface
+        index.
+
+        @param address address to determine the info for
+        @type str
+        @return tuple of network interface name and index
+        @rtype tuple of (str, int)
+        """
+        if address not in ["all", "allv6"]:
+            for networkInterface in QNetworkInterface.allInterfaces():
+                addressEntries = networkInterface.addressEntries()
+                if len(addressEntries) > 0:
+                    for addressEntry in addressEntries:
+                        if addressEntry.ip().toString().lower() == address.lower():
+                            return (
+                                networkInterface.humanReadableName(),
+                                networkInterface.index(),
+                            )
+
+        return "", 0
+
+    def preferencesChanged(self):
+        """
+        Public slot to handle the preferencesChanged signal.
+        """
+        registeredInterfaces = {}
+        for interfaceName in self.__debuggerInterfaces:
+            registeredInterfaces[interfaceName] = self.__debuggerInterfaces[
+                interfaceName
+            ]
+
+        self.__debuggerInterfaceRegistry = {}
+        for interfaceName, getRegistryData in registeredInterfaces.items():
+            self.registerDebuggerInterface(
+                interfaceName, getRegistryData, reregister=True
+            )
+
+        self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
+
+    def registerDebuggerInterface(
+        self, interfaceName, getRegistryData, reregister=False
+    ):
+        """
+        Public method to register a debugger interface.
+
+        @param interfaceName name of the debugger interface
+        @type str
+        @param getRegistryData reference to a function to be called
+            to get the debugger interface details. This method shall
+            return the client language, the client capabilities, the
+            list of associated file extensions and a function reference
+            to create the debugger interface (see __createDebuggerInterface())
+        @type function
+        @param reregister flag indicating to re-register the interface
+        @type bool
+        """
+        if interfaceName in self.__debuggerInterfaces and not reregister:
+            EricMessageBox.warning(
+                None,
+                self.tr("Register Debugger Interface"),
+                self.tr(
+                    """<p>The debugger interface <b>{0}</b> has already"""
+                    """ been registered. Ignoring this request.</p>"""
+                ),
+            )
+            return
+
+        if not reregister:
+            self.__debuggerInterfaces[interfaceName] = getRegistryData
+        registryDataList = getRegistryData()
+        if registryDataList:
+            for (
+                clientLanguage,
+                clientCapabilities,
+                clientExtensions,
+                interfaceCreator,
+            ) in registryDataList:
+                self.__debuggerInterfaceRegistry[clientLanguage] = [
+                    clientCapabilities,
+                    clientExtensions,
+                    interfaceCreator,
+                    interfaceName,
+                ]
+
+    def unregisterDebuggerInterface(self, interfaceName):
+        """
+        Public method to unregister a debugger interface.
+
+        @param interfaceName interfaceName of the debugger interface
+        @type str
+        """
+        if interfaceName in self.__debuggerInterfaces:
+            clientLanguages = []
+            for (
+                clientLanguage,
+                registryData,
+            ) in self.__debuggerInterfaceRegistry.items():
+                if interfaceName == registryData[-1]:
+                    clientLanguages.append(clientLanguage)
+            for clientLanguage in clientLanguages:
+                del self.__debuggerInterfaceRegistry[clientLanguage]
+            del self.__debuggerInterfaces[interfaceName]
+
+    def __findLanguageForExtension(self, ext):
+        """
+        Private method to get the language associated with a file extension.
+
+        @param ext file extension
+        @type str
+        @return associated language
+        @rtype str
+        """
+        for language in self.__debuggerInterfaceRegistry:
+            if ext in self.__debuggerInterfaceRegistry[language][1]:
+                return language
+
+        return ""
+
+    def __registerDebuggerInterfaces(self):
+        """
+        Private method to register the available internal debugger interfaces.
+        """
+        for name, interface in DebuggerInterfaces.items():
+            modName = "Debugger.{0}".format(interface)
+            mod = __import__(modName)
+            components = modName.split(".")
+            for comp in components[1:]:
+                mod = getattr(mod, comp)
+
+            self.registerDebuggerInterface(name, mod.getRegistryData)
+
+    def getSupportedLanguages(self, shellOnly=False):
+        """
+        Public slot to return the supported programming languages.
+
+        @param shellOnly flag indicating only languages supporting an
+            interactive shell should be returned
+        @type bool
+        @return list of supported languages
+        @rtype list of str
+        """
+        languages = list(self.__debuggerInterfaceRegistry.keys())
+        with contextlib.suppress(ValueError):
+            languages.remove("None")
+
+        if shellOnly:
+            languages = [
+                lang
+                for lang in languages
+                if self.__debuggerInterfaceRegistry[lang][0]
+                & DebugClientCapabilities.HasShell
+            ]
+
+        return languages[:]
+
+    def getExtensions(self, language):
+        """
+        Public slot to get the extensions associated with the given language.
+
+        @param language language to get extensions for
+        @type str
+        @return tuple of extensions associated with the language
+        @rtype tuple of str
+        """
+        if language in self.__debuggerInterfaceRegistry:
+            return tuple(self.__debuggerInterfaceRegistry[language][1])
+        else:
+            return ()
+
+    def __createDebuggerInterface(self, clientType=None):
+        """
+        Private slot to create the debugger interface object.
+
+        @param clientType type of the client interface to be created
+        @type str
+        """
+        if self.lastClientType != self.clientType or clientType is not None:
+            if clientType is None:
+                clientType = self.clientType
+            if clientType in self.__debuggerInterfaceRegistry:
+                self.debuggerInterface = self.__debuggerInterfaceRegistry[clientType][
+                    2
+                ](self, self.passive)
+            else:
+                self.debuggerInterface = self.__debuggerInterfaceRegistry["None"][2](
+                    self, self.passive
+                )
+                self.clientType = "None"
+
+    def __setClientType(self, clType):
+        """
+        Private method to set the client type.
+
+        @param clType type of client to be started
+        @type str
+        """
+        if clType is not None and clType in self.getSupportedLanguages():
+            self.clientType = clType
+            Preferences.getSettings().setValue("DebugClient/Type", self.clientType)
+
+    def startClient(
+        self,
+        unplanned=True,
+        clType=None,
+        forProject=False,
+        runInConsole=False,
+        venvName="",
+        workingDir=None,
+        configOverride=None,
+    ):
+        """
+        Public method to start a debug client.
+
+        @param unplanned flag indicating that the client has died
+        @type bool
+        @param clType type of client to be started
+        @type str
+        @param forProject flag indicating a project related action
+        @type bool
+        @param runInConsole flag indicating to start the debugger in a
+            console window
+        @type bool
+        @param venvName name of the virtual environment to be used
+        @type str
+        @param workingDir directory to start the debugger client in
+        @type str
+        @param configOverride dictionary containing the global config override
+            data
+        @type dict
+        """
+        self.running = False
+
+        if (
+            (not self.passive or not self.passiveClientExited)
+            and self.debuggerInterface
+            and self.debuggerInterface.isConnected()
+        ):
+            self.shutdownServer()
+            self.debugging = False
+            self.clientGone.emit(unplanned and self.debugging)
+
+        if clType:
+            if clType not in self.getSupportedLanguages():
+                # a not supported client language was requested
+                return
+
+            self.__setClientType(clType)
+
+        # only start the client, if we are not in passive mode
+        if not self.passive:
+            if self.clientProcess:
+                with contextlib.suppress(RuntimeError):
+                    # Workaround: The wrapped C/C++ object of type QProcess
+                    # gets deleted prematurely sometimes.
+                    self.clientProcess.kill()
+                    self.clientProcess.waitForFinished(1000)
+                    self.clientProcess.deleteLater()
+                self.clientProcess = None
+
+            self.__forProject = forProject
+            self.__createDebuggerInterface()
+            if forProject:
+                project = ericApp().getObject("Project")
+                if not project.isDebugPropertiesLoaded():
+                    (
+                        self.clientProcess,
+                        isNetworked,
+                        clientInterpreter,
+                    ) = self.debuggerInterface.startRemote(
+                        self.serverPort(),
+                        runInConsole,
+                        venvName,
+                        self.__originalPathString,
+                        workingDir=workingDir,
+                        configOverride=configOverride,
+                    )
+                else:
+                    (
+                        self.clientProcess,
+                        isNetworked,
+                        clientInterpreter,
+                    ) = self.debuggerInterface.startRemoteForProject(
+                        self.serverPort(),
+                        runInConsole,
+                        venvName,
+                        self.__originalPathString,
+                        workingDir=workingDir,
+                        configOverride=configOverride,
+                    )
+            else:
+                (
+                    self.clientProcess,
+                    isNetworked,
+                    clientInterpreter,
+                ) = self.debuggerInterface.startRemote(
+                    self.serverPort(),
+                    runInConsole,
+                    venvName,
+                    self.__originalPathString,
+                    workingDir=workingDir,
+                    configOverride=configOverride,
+                )
+
+            if self.clientProcess:
+                self.clientProcess.readyReadStandardError.connect(
+                    self.__clientProcessError
+                )
+                self.clientProcess.readyReadStandardOutput.connect(
+                    self.__clientProcessOutput
+                )
+
+                # Perform actions necessary, if client type has changed
+                if self.lastClientType != self.clientType:
+                    self.lastClientType = self.clientType
+                    self.remoteBanner()
+                elif self.__autoClearShell:
+                    self.__autoClearShell = False
+                    self.remoteBanner()
+            else:
+                if clType and self.lastClientType:
+                    self.__setClientType(self.lastClientType)
+        else:
+            self.__createDebuggerInterface("None")
+            clientInterpreter = ""
+
+        if clientInterpreter != self.clientInterpreter:
+            self.clientInterpreter = clientInterpreter
+            self.clientInterpreterChanged.emit(clientInterpreter)
+
+    def __clientProcessOutput(self):
+        """
+        Private slot to process client output received via stdout.
+        """
+        output = str(
+            self.clientProcess.readAllStandardOutput(),
+            Preferences.getSystem("IOEncoding"),
+            "replace",
+        )
+        self.clientProcessStdout.emit(output)
+
+    def __clientProcessError(self):
+        """
+        Private slot to process client output received via stderr.
+        """
+        error = str(
+            self.clientProcess.readAllStandardError(),
+            Preferences.getSystem("IOEncoding"),
+            "replace",
+        )
+        self.clientProcessStderr.emit(error)
+
+    @pyqtSlot(str, int)
+    def __clientClearBreakPoint(self, fn, lineno):
+        """
+        Private slot to handle the clientClearBreak signal.
+
+        @param fn filename of breakpoint to clear
+        @type str
+        @param lineno line number of breakpoint to clear
+        @type int
+        """
+        if self.debugging:
+            index = self.breakpointModel.getBreakPointIndex(fn, lineno)
+            self.breakpointModel.deleteBreakPointByIndex(index)
+            if (fn, lineno) in self.__reportedBreakpointIssues:
+                self.__reportedBreakpointIssues.remove((fn, lineno))
+
+    def __deleteBreakPoints(self, parentIndex, start, end):
+        """
+        Private slot to delete breakpoints.
+
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        """
+        if self.debugging:
+            for row in range(start, end + 1):
+                index = self.breakpointModel.index(row, 0, parentIndex)
+                fn, lineno = self.breakpointModel.getBreakPointByIndex(index)[0:2]
+                # delete the breakpoints of all connected backends
+                self.remoteBreakpoint("", fn, lineno, False)
+                if (fn, lineno) in self.__reportedBreakpointIssues:
+                    self.__reportedBreakpointIssues.remove((fn, lineno))
+
+    def __changeBreakPoints(self, startIndex, endIndex):
+        """
+        Private slot to set changed breakpoints.
+
+        @param startIndex starting index of the change breakpoins
+        @type QModelIndex
+        @param endIndex ending index of the change breakpoins
+        @type QModelIndex
+        """
+        if self.debugging:
+            self.__addBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
+
+    def __breakPointDataAboutToBeChanged(self, startIndex, endIndex):
+        """
+        Private slot to handle the dataAboutToBeChanged signal of the
+        breakpoint model.
+
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
+        """
+        if self.debugging:
+            self.__deleteBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
+
+    def __addBreakPoints(self, parentIndex, start, end, debuggerId=""):
+        """
+        Private slot to add breakpoints.
+
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            for row in range(start, end + 1):
+                index = self.breakpointModel.index(row, 0, parentIndex)
+                (
+                    fn,
+                    lineno,
+                    cond,
+                    temp,
+                    enabled,
+                    ignorecount,
+                ) = self.breakpointModel.getBreakPointByIndex(index)[:6]
+
+                if (fn, lineno) in self.__reportedBreakpointIssues:
+                    self.__reportedBreakpointIssues.remove((fn, lineno))
+
+                self.remoteBreakpoint(debuggerId, fn, lineno, True, cond, temp)
+                if not enabled:
+                    self.__remoteBreakpointEnable(debuggerId, fn, lineno, False)
+                if ignorecount:
+                    self.__remoteBreakpointIgnore(debuggerId, fn, lineno, ignorecount)
+
+    def __makeWatchCondition(self, cond, special):
+        """
+        Private method to construct the condition string.
+
+        @param cond condition
+        @type str
+        @param special special condition
+        @type str
+        @return condition string
+        @rtype str
+        """
+        if special == "":
+            _cond = cond
+        else:
+            if special == self.watchSpecialCreated:
+                _cond = "{0} ??created??".format(cond)
+            elif special == self.watchSpecialChanged:
+                _cond = "{0} ??changed??".format(cond)
+        return _cond
+
+    def __splitWatchCondition(self, cond):
+        """
+        Private method to split a remote watch expression.
+
+        @param cond remote expression
+        @type str
+        @return tuple of local expression (string) and special condition
+        @rtype str
+        """
+        if cond.endswith(" ??created??"):
+            cond, special = cond.split()
+            special = self.watchSpecialCreated
+        elif cond.endswith(" ??changed??"):
+            cond, special = cond.split()
+            special = self.watchSpecialChanged
+        else:
+            cond = cond
+            special = ""
+
+        return cond, special
+
+    @pyqtSlot(str)
+    def __clientClearWatchPoint(self, condition):
+        """
+        Private slot to handle the clientClearWatch signal.
+
+        @param condition expression of watch expression to clear
+        @type str
+        """
+        if self.debugging:
+            cond, special = self.__splitWatchCondition(condition)
+            index = self.watchpointModel.getWatchPointIndex(cond, special)
+            self.watchpointModel.deleteWatchPointByIndex(index)
+            if condition in self.__reportedWatchpointIssues:
+                self.__reportedWatchpointIssues.remove(condition)
+
+    def __deleteWatchPoints(self, parentIndex, start, end):
+        """
+        Private slot to delete watch expressions.
+
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        """
+        if self.debugging:
+            for row in range(start, end + 1):
+                index = self.watchpointModel.index(row, 0, parentIndex)
+                cond, special = self.watchpointModel.getWatchPointByIndex(index)[0:2]
+                cond = self.__makeWatchCondition(cond, special)
+                self.__remoteWatchpoint("", cond, False)
+                if cond in self.__reportedWatchpointIssues:
+                    self.__reportedWatchpointIssues.remove(cond)
+
+    def __watchPointDataAboutToBeChanged(self, startIndex, endIndex):
+        """
+        Private slot to handle the dataAboutToBeChanged signal of the
+        watch expression model.
+
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
+        """
+        if self.debugging:
+            self.__deleteWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
+
+    def __addWatchPoints(self, parentIndex, start, end, debuggerId=""):
+        """
+        Private slot to set a watch expression.
+
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            for row in range(start, end + 1):
+                index = self.watchpointModel.index(row, 0, parentIndex)
+                (
+                    cond,
+                    special,
+                    temp,
+                    enabled,
+                    ignorecount,
+                ) = self.watchpointModel.getWatchPointByIndex(index)[:5]
+                cond = self.__makeWatchCondition(cond, special)
+
+                if cond in self.__reportedWatchpointIssues:
+                    self.__reportedWatchpointIssues.remove(cond)
+
+                self.__remoteWatchpoint(debuggerId, cond, True, temp)
+                if not enabled:
+                    self.__remoteWatchpointEnable(debuggerId, cond, False)
+                if ignorecount:
+                    self.__remoteWatchpointIgnore(debuggerId, cond, ignorecount)
+
+    def __changeWatchPoints(self, startIndex, endIndex):
+        """
+        Private slot to set changed watch expressions.
+
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
+        """
+        if self.debugging:
+            self.__addWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
+
+    def getClientCapabilities(self, clientType):
+        """
+        Public method to retrieve the debug clients capabilities.
+
+        @param clientType debug client type
+        @type str
+        @return debug client capabilities
+        @rtype int
+        """
+        try:
+            return self.__debuggerInterfaceRegistry[clientType][0]
+        except KeyError:
+            return 0  # no capabilities
+
+    def getClientInterpreter(self):
+        """
+        Public method to get the interpreter of the debug client.
+
+        @return interpreter of the debug client
+        @rtype str
+        """
+        return self.clientInterpreter
+
+    def getClientType(self):
+        """
+        Public method to get the currently running debug client type.
+
+        @return debug client type
+        @rtype str
+        """
+        return self.clientType
+
+    def isClientProcessUp(self):
+        """
+        Public method to check, if the debug client process is up.
+
+        @return flag indicating a running debug client process
+        @rtype bool
+        """
+        return self.clientProcess is not None
+
+    def __newConnection(self):
+        """
+        Private slot to handle a new connection.
+        """
+        sock = self.nextPendingConnection()
+        peerAddress = sock.peerAddress().toString()
+        if peerAddress not in Preferences.getDebugger("AllowedHosts"):
+            # the peer is not allowed to connect
+            res = EricMessageBox.yesNo(
+                None,
+                self.tr("Connection from illegal host"),
+                self.tr(
+                    """<p>A connection was attempted by the illegal host"""
+                    """ <b>{0}</b>. Accept this connection?</p>"""
+                ).format(peerAddress),
+                icon=EricMessageBox.Warning,
+            )
+            if not res:
+                sock.abort()
+                return
+            else:
+                allowedHosts = Preferences.getDebugger("AllowedHosts")
+                allowedHosts.append(peerAddress)
+                Preferences.setDebugger("AllowedHosts", allowedHosts)
+
+        if self.passive:
+            self.__createDebuggerInterface(Preferences.getDebugger("PassiveDbgType"))
+
+        self.debuggerInterface.newConnection(sock)
+
+    def masterClientConnected(self):
+        """
+        Public method to perform actions after the master client has finally
+        established the connection.
+        """
+        # Perform actions necessary, if client type has changed
+        if self.lastClientType != self.clientType:
+            self.lastClientType = self.clientType
+            self.remoteBanner()
+        elif self.__autoClearShell:
+            self.__autoClearShell = False
+            self.remoteBanner()
+        elif self.passive:
+            self.remoteBanner()
+
+    def shutdownServer(self):
+        """
+        Public method to cleanly shut down.
+
+        It closes our socket and shuts down
+        the debug client. (Needed on Win OS)
+        """
+        if self.debuggerInterface is not None:
+            self.debuggerInterface.shutdown()
+
+    def remoteEnvironment(self, env):
+        """
+        Public method to set the environment for a program to debug, run, ...
+
+        @param env environment settings
+        @type str
+        """
+        envlist = shlex.split(env)
+        envdict = {}
+        for el in envlist:
+            if "=" in el:
+                key, value = el.split("=", 1)
+                envdict[key] = value
+            else:
+                envdict[el] = ""
+        self.debuggerInterface.remoteEnvironment(envdict)
+
+    def remoteLoad(
+        self,
+        venvName,
+        fn,
+        argv,
+        wd,
+        env,
+        autoClearShell=True,
+        tracePython=False,
+        autoContinue=True,
+        forProject=False,
+        runInConsole=False,
+        clientType="",
+        enableCallTrace=False,
+        enableMultiprocess=False,
+        multiprocessNoDebug="",
+        configOverride=None,
+    ):
+        """
+        Public method to load a new program to debug.
+
+        @param venvName name of the virtual environment to be used
+        @type str
+        @param fn the filename to debug
+        @type str
+        @param argv the command line arguments to pass to the program
+        @type str
+        @param wd the working directory for the program
+        @type str
+        @param env environment parameter settings
+        @type str
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared
+        @type bool
+        @param tracePython flag indicating if the Python library should be
+            traced as well
+        @type bool
+        @param autoContinue flag indicating, that the debugger should not
+            stop at the first executable line
+        @type bool
+        @param forProject flag indicating a project related action
+        @type bool
+        @param runInConsole flag indicating to start the debugger in a
+            console window
+        @type bool
+        @param clientType client type to be used
+        @type str
+        @param enableCallTrace flag indicating to enable the call trace
+            function
+        @type bool
+        @param enableMultiprocess flag indicating to perform multiprocess
+            debugging
+        @type bool
+        @param multiprocessNoDebug space separated list of programs not to be
+            debugged
+        @type str
+        @param configOverride dictionary containing the global config override
+            data
+        @type dict
+        """
+        self.__autoClearShell = autoClearShell
+        self.__multiprocessNoDebugList = [
+            s.strip() for s in multiprocessNoDebug.split(os.pathsep)
+        ]
+
+        if clientType not in self.getSupportedLanguages():
+            # a not supported client language was requested
+            EricMessageBox.critical(
+                None,
+                self.tr("Start Debugger"),
+                self.tr(
+                    """<p>The debugger type <b>{0}</b> is not supported"""
+                    """ or not configured.</p>"""
+                ).format(clientType),
+            )
+            return
+
+        # Restart the client
+        try:
+            if clientType:
+                self.__setClientType(clientType)
+            else:
+                self.__setClientType(
+                    self.__findLanguageForExtension(os.path.splitext(fn)[1])
+                )
+        except KeyError:
+            self.__setClientType("Python3")  # assume it is a Python3 file
+        self.startClient(
+            False,
+            forProject=forProject,
+            runInConsole=runInConsole,
+            venvName=venvName,
+            configOverride=configOverride,
+        )
+
+        self.setCallTraceEnabled("", enableCallTrace)
+        self.remoteEnvironment(env)
+
+        self.debuggerInterface.remoteLoad(
+            fn,
+            argv,
+            wd,
+            tracePython,
+            autoContinue,
+            enableMultiprocess=enableMultiprocess,
+        )
+        self.debugging = True
+        self.running = True
+        self.__restoreBreakpoints()
+        self.__restoreWatchpoints()
+        self.__restoreNoDebugList()
+
+    def remoteRun(
+        self,
+        venvName,
+        fn,
+        argv,
+        wd,
+        env,
+        autoClearShell=True,
+        forProject=False,
+        runInConsole=False,
+        clientType="",
+        configOverride=None,
+    ):
+        """
+        Public method to load a new program to run.
+
+        @param venvName name of the virtual environment to be used
+        @type str
+        @param fn the filename to debug
+        @type str
+        @param argv the command line arguments to pass to the program
+        @type str
+        @param wd the working directory for the program
+        @type str
+        @param env environment parameter settings
+        @type str
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared
+        @type bool
+        @param forProject flag indicating a project related action
+        @type bool
+        @param runInConsole flag indicating to start the debugger in a
+            console window
+        @type bool
+        @param clientType client type to be used
+        @type str
+        @param configOverride dictionary containing the global config override
+            data
+        @type dict
+        """
+        self.__autoClearShell = autoClearShell
+
+        if clientType not in self.getSupportedLanguages():
+            EricMessageBox.critical(
+                None,
+                self.tr("Start Debugger"),
+                self.tr(
+                    """<p>The debugger type <b>{0}</b> is not supported"""
+                    """ or not configured.</p>"""
+                ).format(clientType),
+            )
+            # a not supported client language was requested
+            return
+
+        # Restart the client
+        try:
+            if clientType:
+                self.__setClientType(clientType)
+            else:
+                self.__setClientType(
+                    self.__findLanguageForExtension(os.path.splitext(fn)[1])
+                )
+        except KeyError:
+            self.__setClientType("Python3")  # assume it is a Python3 file
+        self.startClient(
+            False,
+            forProject=forProject,
+            runInConsole=runInConsole,
+            venvName=venvName,
+            configOverride=configOverride,
+        )
+
+        self.remoteEnvironment(env)
+
+        self.debuggerInterface.remoteRun(fn, argv, wd)
+        self.debugging = False
+        self.running = True
+
+    def remoteCoverage(
+        self,
+        venvName,
+        fn,
+        argv,
+        wd,
+        env,
+        autoClearShell=True,
+        erase=False,
+        forProject=False,
+        runInConsole=False,
+        clientType="",
+        configOverride=None,
+    ):
+        """
+        Public method to load a new program to collect coverage data.
+
+        @param venvName name of the virtual environment to be used
+        @type str
+        @param fn the filename to debug
+        @type str
+        @param argv the command line arguments to pass to the program
+        @type str
+        @param wd the working directory for the program
+        @type str
+        @param env environment parameter settings
+        @type str
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared
+        @type bool
+        @param erase flag indicating that coverage info should be
+            cleared first
+        @type bool
+        @param forProject flag indicating a project related action
+        @type bool
+        @param runInConsole flag indicating to start the debugger in a
+            console window
+        @type bool
+        @param clientType client type to be used
+        @type str
+        @param configOverride dictionary containing the global config override
+            data
+        @type dict
+        """
+        self.__autoClearShell = autoClearShell
+
+        if clientType not in self.getSupportedLanguages():
+            # a not supported client language was requested
+            EricMessageBox.critical(
+                None,
+                self.tr("Start Debugger"),
+                self.tr(
+                    """<p>The debugger type <b>{0}</b> is not supported"""
+                    """ or not configured.</p>"""
+                ).format(clientType),
+            )
+            return
+
+        # Restart the client
+        try:
+            if clientType:
+                self.__setClientType(clientType)
+            else:
+                self.__setClientType(
+                    self.__findLanguageForExtension(os.path.splitext(fn)[1])
+                )
+        except KeyError:
+            self.__setClientType("Python3")  # assume it is a Python3 file
+        self.startClient(
+            False,
+            forProject=forProject,
+            runInConsole=runInConsole,
+            venvName=venvName,
+            configOverride=configOverride,
+        )
+
+        self.remoteEnvironment(env)
+
+        self.debuggerInterface.remoteCoverage(fn, argv, wd, erase)
+        self.debugging = False
+        self.running = True
+
+    def remoteProfile(
+        self,
+        venvName,
+        fn,
+        argv,
+        wd,
+        env,
+        autoClearShell=True,
+        erase=False,
+        forProject=False,
+        runInConsole=False,
+        clientType="",
+        configOverride=None,
+    ):
+        """
+        Public method to load a new program to collect profiling data.
+
+        @param venvName name of the virtual environment to be used
+        @type str
+        @param fn the filename to debug
+        @type str
+        @param argv the command line arguments to pass to the program
+        @type str
+        @param wd the working directory for the program
+        @type str
+        @param env environment parameter settings
+        @type str
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared
+        @type bool
+        @param erase flag indicating that coverage info should be
+            cleared first
+        @type bool
+        @param forProject flag indicating a project related action
+        @type bool
+        @param runInConsole flag indicating to start the debugger in a
+            console window
+        @type bool
+        @param clientType client type to be used
+        @type str
+        @param configOverride dictionary containing the global config override
+            data
+        @type dict
+        """
+        self.__autoClearShell = autoClearShell
+
+        if clientType not in self.getSupportedLanguages():
+            # a not supported client language was requested
+            EricMessageBox.critical(
+                None,
+                self.tr("Start Debugger"),
+                self.tr(
+                    """<p>The debugger type <b>{0}</b> is not supported"""
+                    """ or not configured.</p>"""
+                ).format(clientType),
+            )
+            return
+
+        # Restart the client
+        try:
+            if clientType:
+                self.__setClientType(clientType)
+            else:
+                self.__setClientType(
+                    self.__findLanguageForExtension(os.path.splitext(fn)[1])
+                )
+        except KeyError:
+            self.__setClientType("Python3")  # assume it is a Python3 file
+        self.startClient(
+            False,
+            forProject=forProject,
+            runInConsole=runInConsole,
+            venvName=venvName,
+            configOverride=configOverride,
+        )
+
+        self.remoteEnvironment(env)
+
+        self.debuggerInterface.remoteProfile(fn, argv, wd, erase)
+        self.debugging = False
+        self.running = True
+
+    def remoteStatement(self, debuggerId, stmt):
+        """
+        Public method to execute a Python statement.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param stmt the Python statement to execute.
+        @type str
+        """
+        self.debuggerInterface.remoteStatement(debuggerId, stmt.rstrip())
+
+    def remoteStep(self, debuggerId):
+        """
+        Public method to single step the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteStep(debuggerId)
+
+    def remoteStepOver(self, debuggerId):
+        """
+        Public method to step over the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteStepOver(debuggerId)
+
+    def remoteStepOut(self, debuggerId):
+        """
+        Public method to step out the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteStepOut(debuggerId)
+
+    def remoteStepQuit(self, debuggerId):
+        """
+        Public method to stop the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteStepQuit(debuggerId)
+
+    def remoteContinue(self, debuggerId, special=False):
+        """
+        Public method to continue the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param special flag indicating a special continue operation
+        """
+        self.debuggerInterface.remoteContinue(debuggerId, special)
+
+    def remoteContinueUntil(self, debuggerId, line):
+        """
+        Public method to continue the debugged program to the given line
+        or until returning from the current frame.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param line the new line, where execution should be continued to
+        @type int
+        """
+        self.debuggerInterface.remoteContinueUntil(debuggerId, line)
+
+    def remoteMoveIP(self, debuggerId, line):
+        """
+        Public method to move the instruction pointer to a different line.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param line the new line, where execution should be continued
+        @type int
+        """
+        self.debuggerInterface.remoteMoveIP(debuggerId, line)
+
+    def remoteBreakpoint(
+        self, debuggerId, fn, line, setBreakpoint, cond=None, temp=False
+    ):
+        """
+        Public method to set or clear a breakpoint.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
+        @param setBreakpoint flag indicating setting or resetting a breakpoint
+        @type bool
+        @param cond condition of the breakpoint
+        @type str
+        @param temp flag indicating a temporary breakpoint
+        @type bool
+        """
+        self.debuggerInterface.remoteBreakpoint(
+            debuggerId, fn, line, setBreakpoint, cond, temp
+        )
+
+    def __remoteBreakpointEnable(self, debuggerId, fn, line, enable):
+        """
+        Private method to enable or disable a breakpoint.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
+        @param enable flag indicating enabling or disabling a breakpoint
+        @type bool
+        """
+        self.debuggerInterface.remoteBreakpointEnable(debuggerId, fn, line, enable)
+
+    def __remoteBreakpointIgnore(self, debuggerId, fn, line, count):
+        """
+        Private method to ignore a breakpoint the next couple of occurrences.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
+        @param count number of occurrences to ignore
+        @type int
+        """
+        self.debuggerInterface.remoteBreakpointIgnore(debuggerId, fn, line, count)
+
+    def __remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False):
+        """
+        Private method to set or clear a watch expression.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
+        @param setWatch flag indicating setting or resetting a watch expression
+        @type bool
+        @param temp flag indicating a temporary watch expression
+        @type bool
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpoint(debuggerId, cond, setWatch, temp)
+
+    def __remoteWatchpointEnable(self, debuggerId, cond, enable):
+        """
+        Private method to enable or disable a watch expression.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
+        @param enable flag indicating enabling or disabling a watch expression
+        @type bool
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpointEnable(debuggerId, cond, enable)
+
+    def __remoteWatchpointIgnore(self, debuggerId, cond, count):
+        """
+        Private method to ignore a watch expression the next couple of
+        occurrences.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
+        @param count number of occurrences to ignore
+        @type int
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpointIgnore(debuggerId, cond, count)
+
+    def remoteRawInput(self, debuggerId, inputString):
+        """
+        Public method to send the raw input to the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param inputString the raw input
+        @type str
+        """
+        self.debuggerInterface.remoteRawInput(debuggerId, inputString)
+        self.clientRawInputSent.emit(debuggerId)
+
+    def remoteThreadList(self, debuggerId):
+        """
+        Public method to request the list of threads from the client.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteThreadList(debuggerId)
+
+    def remoteSetThread(self, debuggerId, tid):
+        """
+        Public method to request to set the given thread as current thread.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param tid id of the thread
+        @type int
+        """
+        self.debuggerInterface.remoteSetThread(debuggerId, tid)
+
+    def remoteClientStack(self, debuggerId):
+        """
+        Public method to request the stack of the main thread.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteClientStack(debuggerId)
+
+    def remoteClientVariables(self, debuggerId, scope, filterList, framenr=0):
+        """
+        Public method to request the variables of the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @type int
+        @param filterList list of variable types to filter out
+        @type list of str
+        @param framenr framenumber of the variables to retrieve
+        @type int
+        """
+        self.debuggerInterface.remoteClientVariables(
+            debuggerId, scope, filterList, framenr, self.__maxVariableSize
+        )
+
+    def remoteClientVariable(
+        self, debuggerId, scope, filterList, var, framenr=0, maxSize=0
+    ):
+        """
+        Public method to request the variables of the debugged program.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @type int
+        @param filterList list of variable types to filter out
+        @type list of str
+        @param var list encoded name of variable to retrieve
+        @type list of str
+        @param framenr framenumber of the variables to retrieve
+        @type int
+        @param maxSize maximum size the formatted value of a variable will
+            be shown. If it is bigger than that, a 'too big' indication will
+            be given (@@TOO_BIG_TO_SHOW@@).
+        @type int
+        """
+        self.debuggerInterface.remoteClientVariable(
+            debuggerId, scope, filterList, var, framenr, self.__maxVariableSize
+        )
+
+    def remoteClientDisassembly(self, debuggerId):
+        """
+        Public method to ask the client for the latest traceback disassembly.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        with contextlib.suppress(AttributeError):
+            self.debuggerInterface.remoteClientDisassembly(debuggerId)
+
+    def remoteClientSetFilter(self, debuggerId, scope, filterStr):
+        """
+        Public method to set a variables filter list.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @type int
+        @param filterStr regexp string for variable names to filter out
+        @type str
+        """
+        self.debuggerInterface.remoteClientSetFilter(debuggerId, scope, filterStr)
+
+    def setCallTraceEnabled(self, debuggerId, on):
+        """
+        Public method to set the call trace state.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param on flag indicating to enable the call trace function
+        @type bool
+        """
+        self.debuggerInterface.setCallTraceEnabled(debuggerId, on)
+
+    def remoteBanner(self):
+        """
+        Public slot to get the banner info of the remote client.
+        """
+        self.debuggerInterface.remoteBanner()
+
+    def remoteCapabilities(self):
+        """
+        Public slot to get the debug clients capabilities.
+        """
+        self.debuggerInterface.remoteCapabilities()
+
+    def remoteCompletion(self, debuggerId, text):
+        """
+        Public slot to get the a list of possible commandline completions
+        from the remote client.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param text the text to be completed
+        @type str
+        """
+        self.debuggerInterface.remoteCompletion(debuggerId, text)
+
+    def signalClientOutput(self, line, debuggerId):
+        """
+        Public method to process a line of client output.
+
+        @param line client output
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if debuggerId:
+            self.clientOutput.emit("{0}: {1}".format(debuggerId, line))
+        else:
+            self.clientOutput.emit(line)
+
+    def signalClientLine(
+        self, filename, lineno, debuggerId, forStack=False, threadName=""
+    ):
+        """
+        Public method to process client position feedback.
+
+        @param filename name of the file currently being executed
+        @type str
+        @param lineno line of code currently being executed
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param forStack flag indicating this is for a stack dump
+        @type bool
+        @param threadName name of the thread signaling the event
+        @type str
+        """
+        self.clientLine.emit(filename, lineno, debuggerId, threadName, forStack)
+
+    def signalClientStack(self, stack, debuggerId, threadName=""):
+        """
+        Public method to process a client's stack information.
+
+        @param stack list of stack entries. Each entry is a tuple of three
+            values giving the filename, linenumber and method
+        @type list of lists of (string, integer, string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
+        """
+        self.clientStack.emit(stack, debuggerId, threadName)
+
+    def signalClientThreadList(self, currentId, threadList, debuggerId):
+        """
+        Public method to process the client thread list info.
+
+        @param currentId id of the current thread
+        @type int
+        @param threadList list of dictionaries containing the thread data
+        @type list of dict
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientThreadList.emit(currentId, threadList, debuggerId)
+
+    def signalClientThreadSet(self, debuggerId):
+        """
+        Public method to handle the change of the client thread.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientThreadSet.emit(debuggerId)
+
+    def signalClientVariables(self, scope, variables, debuggerId):
+        """
+        Public method to process the client variables info.
+
+        @param scope scope of the variables
+            (-2 = no frame found, -1 = empty locals, 1 = global, 0 = local)
+        @type int
+        @param variables the list of variables from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientVariables.emit(scope, variables, debuggerId)
+
+    def signalClientVariable(self, scope, variables, debuggerId):
+        """
+        Public method to process the client variable info.
+
+        @param scope scope of the variables (-1 = empty global, 1 = global,
+            0 = local)
+        @type int
+        @param variables the list of members of a classvariable from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientVariable.emit(scope, variables, debuggerId)
+
+    def signalClientStatement(self, more, debuggerId):
+        """
+        Public method to process the input response from the client.
+
+        @param more flag indicating that more user input is required
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientStatement.emit(more, debuggerId)
+
+    def signalClientDisassembly(self, disassembly, debuggerId):
+        """
+        Public method to process the disassembly info from the client.
+
+        @param disassembly dictionary containing the disassembly information
+        @type dict
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if self.running:
+            self.clientDisassembly.emit(disassembly, debuggerId)
+
+    def signalClientException(
+        self, exceptionType, exceptionMessage, stackTrace, debuggerId, threadName=""
+    ):
+        """
+        Public method to process the exception info from the client.
+
+        @param exceptionType type of exception raised
+        @type str
+        @param exceptionMessage message given by the exception
+        @type str
+        @param stackTrace list of stack entries with the exception position
+            first. Each stack entry is a list giving the filename and the
+            linenumber.
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
+        """
+        if self.running:
+            self.clientException.emit(
+                exceptionType, exceptionMessage, stackTrace, debuggerId, threadName
+            )
+
+    def signalClientSyntaxError(
+        self, message, filename, lineNo, characterNo, debuggerId, threadName=""
+    ):
+        """
+        Public method to process a syntax error info from the client.
+
+        @param message message of the syntax error
+        @type str
+        @param filename translated filename of the syntax error position
+        @type str
+        @param lineNo line number of the syntax error position
+        @type int
+        @param characterNo character number of the syntax error position
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
+        """
+        if self.running:
+            self.clientSyntaxError.emit(
+                message, filename, lineNo, characterNo, debuggerId, threadName
+            )
+
+    def signalClientSignal(
+        self, message, filename, lineNo, funcName, funcArgs, debuggerId
+    ):
+        """
+        Public method to process a signal generated on the client side.
+
+        @param message message of the syntax error
+        @type str
+        @param filename translated filename of the syntax error position
+        @type str
+        @param lineNo line number of the syntax error position
+        @type int
+        @param funcName name of the function causing the signal
+        @type str
+        @param funcArgs function arguments
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if self.running:
+            self.clientSignal.emit(
+                message, filename, lineNo, funcName, funcArgs, debuggerId
+            )
+
+    def signalClientDisconnected(self, debuggerId):
+        """
+        Public method to send a signal when a debug client has closed its
+        connection.
+
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientDisconnected.emit(debuggerId)
+
+    def signalClientExit(self, program, status, message, debuggerId):
+        """
+        Public method to process the client exit status.
+
+        @param program name of the exited program
+        @type str
+        @param status exit code
+        @type int
+        @param message message sent with the exit
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientExit.emit(program, int(status), message, False, debuggerId)
+
+    def signalMainClientExit(self):
+        """
+        Public method to process the main client exiting.
+        """
+        self.mainClientExit.emit()
+
+    def signalLastClientExited(self):
+        """
+        Public method to process the last client exit event.
+        """
+        if self.passive:
+            self.__passiveShutDown()
+        self.lastClientExited.emit()
+        if Preferences.getDebugger("AutomaticReset") or (
+            self.running and not self.debugging
+        ):
+            self.debugging = False
+            self.startClient(False, forProject=self.__forProject)
+        if self.passive:
+            self.__createDebuggerInterface("None")
+            self.signalClientOutput(self.tr("\nNot connected\n"))
+            self.signalClientStatement(False, "")
+        self.running = False
+
+    def signalClientClearBreak(self, filename, lineno, debuggerId):
+        """
+        Public method to process the client clear breakpoint command.
+
+        @param filename filename of the breakpoint
+        @type str
+        @param lineno line umber of the breakpoint
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientClearBreak.emit(filename, lineno, debuggerId)
+
+    def signalClientBreakConditionError(self, filename, lineno, debuggerId):
+        """
+        Public method to process the client breakpoint condition error info.
+
+        @param filename filename of the breakpoint
+        @type str
+        @param lineno line umber of the breakpoint
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if (filename, lineno) not in self.__reportedBreakpointIssues:
+            self.__reportedBreakpointIssues.append((filename, lineno))
+            self.clientBreakConditionError.emit(filename, lineno, debuggerId)
+
+    def signalClientClearWatch(self, condition, debuggerId):
+        """
+        Public slot to handle the clientClearWatch signal.
+
+        @param condition expression of watch expression to clear
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientClearWatch.emit(condition, debuggerId)
+
+    def signalClientWatchConditionError(self, condition, debuggerId):
+        """
+        Public method to process the client watch expression error info.
+
+        @param condition expression of watch expression to clear
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if condition not in self.__reportedWatchpointIssues:
+            self.__reportedWatchpointIssues.append(condition)
+            self.clientWatchConditionError.emit(condition, debuggerId)
+
+    def signalClientRawInput(self, prompt, echo, debuggerId):
+        """
+        Public method to process the client raw input command.
+
+        @param prompt the input prompt
+        @type str
+        @param echo flag indicating an echoing of the input
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientRawInput.emit(prompt, echo, debuggerId)
+
+    def signalClientBanner(self, version, platform, venvName):
+        """
+        Public method to process the client banner info.
+
+        @param version interpreter version info
+        @type str
+        @param platform hostname of the client
+        @type str
+        @param venvName name of the virtual environment
+        @type str
+        """
+        self.clientBanner.emit(version, platform, venvName)
+
+    def signalClientCapabilities(self, capabilities, clientType, venvName):
+        """
+        Public method to process the client capabilities info.
+
+        @param capabilities bitmaks with the client capabilities
+        @type int
+        @param clientType type of the debug client
+        @type str
+        @param venvName name of the virtual environment
+        @type str
+        """
+        with contextlib.suppress(KeyError):
+            self.__debuggerInterfaceRegistry[clientType][0] = capabilities
+            self.clientCapabilities.emit(capabilities, clientType, venvName)
+
+    def signalClientCompletionList(self, completionList, text, debuggerId):
+        """
+        Public method to process the client auto completion info.
+
+        @param completionList list of possible completions
+        @type list of str
+        @param text the text to be completed
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientCompletionList.emit(completionList, text)
+
+    def signalClientCallTrace(
+        self,
+        isCall,
+        fromFile,
+        fromLine,
+        fromFunction,
+        toFile,
+        toLine,
+        toFunction,
+        debuggerId,
+    ):
+        """
+        Public method to process the client call trace data.
+
+        @param isCall flag indicating a 'call'
+        @type bool
+        @param fromFile name of the originating file
+        @type str
+        @param fromLine line number in the originating file
+        @type str
+        @param fromFunction name of the originating function
+        @type str
+        @param toFile name of the target file
+        @type str
+        @param toLine line number in the target file
+        @type str
+        @param toFunction name of the target function
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.callTraceInfo.emit(
+            isCall,
+            fromFile,
+            fromLine,
+            fromFunction,
+            toFile,
+            toLine,
+            toFunction,
+            debuggerId,
+        )
+
+    def passiveStartUp(self, fn, exc, debuggerId):
+        """
+        Public method to handle a passive debug connection.
+
+        @param fn filename of the debugged script
+        @type str
+        @param exc flag to enable exception reporting of the IDE
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.appendStdout.emit(self.tr("Passive debug connection received\n"))
+        self.passiveClientExited = False
+        self.debugging = True
+        self.running = True
+        self.__restoreBreakpoints(debuggerId)
+        self.__restoreWatchpoints(debuggerId)
+        self.passiveDebugStarted.emit(fn, exc)
+
+    def __passiveShutDown(self):
+        """
+        Private method to shut down a passive debug connection.
+        """
+        self.passiveClientExited = True
+        self.shutdownServer()
+        self.appendStdout.emit(self.tr("Passive debug connection closed\n"))
+
+    def __restoreBreakpoints(self, debuggerId=""):
+        """
+        Private method to restore the breakpoints after a restart.
+
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            self.__addBreakPoints(
+                QModelIndex(), 0, self.breakpointModel.rowCount() - 1, debuggerId
+            )
+
+    def __restoreWatchpoints(self, debuggerId=""):
+        """
+        Private method to restore the watch expressions after a restart.
+
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            self.__addWatchPoints(
+                QModelIndex(), 0, self.watchpointModel.rowCount() - 1, debuggerId
+            )
+
+    def getBreakPointModel(self):
+        """
+        Public slot to get a reference to the breakpoint model object.
+
+        @return reference to the breakpoint model object
+        @rtype BreakPointModel
+        """
+        return self.breakpointModel
+
+    def getWatchPointModel(self):
+        """
+        Public slot to get a reference to the watch expression model object.
+
+        @return reference to the watch expression model object
+        @rtype WatchPointModel
+        """
+        return self.watchpointModel
+
+    def isConnected(self):
+        """
+        Public method to test, if the debug server is connected to a backend.
+
+        @return flag indicating a connection
+        @rtype bool
+        """
+        return self.debuggerInterface and self.debuggerInterface.isConnected()
+
+    def isDebugging(self):
+        """
+        Public method to test, if the debug server is debugging.
+
+        @return flag indicating the debugging state
+        @rtype bool
+        """
+        return self.debugging
+
+    def setDebugging(self, on):
+        """
+        Public method to set the debugging state.
+
+        @param on flag indicating the new debugging state
+        @type bool
+        """
+        self.debugging = on
+
+    def signalClientDebuggerId(self, debuggerId):
+        """
+        Public method to signal the receipt of a new debugger ID.
+
+        This signal indicates, that a new debugger backend has connected.
+
+        @param debuggerId ID of the newly connected debugger backend
+        @type str
+        """
+        self.clientDebuggerId.emit(debuggerId)
+
+    def getDebuggerIds(self):
+        """
+        Public method to return the IDs of the connected debugger backends.
+
+        @return list of connected debugger backend IDs
+        @rtype list of str
+        """
+        if self.debuggerInterface:
+            return self.debuggerInterface.getDebuggerIds()
+        else:
+            return []
+
+    def initializeClient(self, debuggerId):
+        """
+        Public method to initialize a freshly connected debug client.
+
+        @param debuggerId ID of the connected debugger
+        @type str
+        """
+        self.__restoreBreakpoints(debuggerId)
+        self.__restoreWatchpoints(debuggerId)
+        self.__restoreNoDebugList(debuggerId)
+
+    def __restoreNoDebugList(self, debuggerId=""):
+        """
+        Private method to restore the list of scripts not to be debugged after
+        a restart.
+
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            self.debuggerInterface.remoteNoDebugList(
+                debuggerId, self.__multiprocessNoDebugList
+            )

eric ide

mercurial