diff -r 889ed5ff7a68 -r a094eee9f862 Debugger/DebuggerInterfacePython2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Debugger/DebuggerInterfacePython2.py Sat Sep 03 18:02:37 2016 +0200 @@ -0,0 +1,1080 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2007 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Python debugger interface for the debug server. +""" + +from __future__ import unicode_literals + +import sys +import os + +from PyQt5.QtCore import QObject, QTextCodec, QProcess, QProcessEnvironment, \ + QTimer +from PyQt5.QtWidgets import QInputDialog + +from E5Gui.E5Application import e5App +from E5Gui import E5MessageBox + +from . import DebugClientCapabilities + +import Preferences +import Utilities + +from eric6config import getConfig + + +ClientDefaultCapabilities = DebugClientCapabilities.HasAll + + +class DebuggerInterfacePython2(QObject): + """ + Class implementing the Python 2 debugger interface for the debug server. + """ + def __init__(self, debugServer, passive): + """ + Constructor + + @param debugServer reference to the debug server (DebugServer) + @param passive flag indicating passive connection mode (boolean) + """ + super(DebuggerInterfacePython2, self).__init__() + + self.__isNetworked = True + self.__autoContinue = False + + self.debugServer = debugServer + self.passive = passive + self.process = None + + self.qsock = None + self.queue = [] + + # set default values for capabilities of clients + self.clientCapabilities = ClientDefaultCapabilities + + # set translation function + self.translate = self.__identityTranslation + + self.codec = QTextCodec.codecForName( + Preferences.getSystem("StringEncoding")) + + if passive: + # set translation function + if Preferences.getDebugger("PathTranslation"): + self.translateRemote = \ + Preferences.getDebugger("PathTranslationRemote") + self.translateLocal = \ + Preferences.getDebugger("PathTranslationLocal") + self.translate = self.__remoteTranslation + else: + self.translate = self.__identityTranslation + + # attribute to remember the name of the executed script + self.__scriptName = "" + + def __identityTranslation(self, fn, remote2local=True): + """ + Private method to perform the identity path translation. + + @param fn filename to be translated (string) + @param remote2local flag indicating the direction of translation + (False = local to remote, True = remote to local [default]) + @return translated filename (string) + """ + return fn + + def __remoteTranslation(self, fn, remote2local=True): + """ + Private method to perform the path translation. + + @param fn filename to be translated (string) + @param remote2local flag indicating the direction of translation + (False = local to remote, True = remote to local [default]) + @return translated filename (string) + """ + if remote2local: + return fn.replace(self.translateRemote, self.translateLocal) + else: + return fn.replace(self.translateLocal, self.translateRemote) + + def __startProcess(self, program, arguments, environment=None): + """ + Private method to start the debugger client process. + + @param program name of the executable to start (string) + @param arguments arguments to be passed to the program (list of string) + @param environment dictionary of environment settings to pass + (dict of string) + @return the process object (QProcess) or None + """ + proc = QProcess() + if environment is not None: + env = QProcessEnvironment() + for key, value in list(environment.items()): + env.insert(key, value) + proc.setProcessEnvironment(env) + args = [] + for arg in arguments: + args.append(arg) + proc.start(program, args) + if not proc.waitForStarted(10000): + proc = None + + return proc + + def startRemote(self, port, runInConsole): + """ + Public method to start a remote Python interpreter. + + @param port portnumber the debug server is listening on (integer) + @param runInConsole flag indicating to start the debugger in a + console window (boolean) + @return client process object (QProcess), a flag to indicate + a network connection (boolean) and the name of the interpreter + in case of a local execution (string) + """ + interpreter = Preferences.getDebugger("PythonInterpreter") + if interpreter == "": + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>No Python2 interpreter configured.</p>""")) + return None, False, "" + + debugClientType = Preferences.getDebugger("DebugClientType") + if debugClientType == "standard": + debugClient = os.path.join(getConfig('ericDir'), + "DebugClients", "Python", + "DebugClient.py") + elif debugClientType == "threaded": + debugClient = os.path.join(getConfig('ericDir'), + "DebugClients", "Python", + "DebugClientThreads.py") + else: + debugClient = Preferences.getDebugger("DebugClient") + if debugClient == "": + debugClient = os.path.join(sys.path[0], + "DebugClients", "Python", + "DebugClient.py") + + redirect = str(Preferences.getDebugger("PythonRedirect")) + noencoding = Preferences.getDebugger("PythonNoEncoding") and \ + '--no-encoding' or '' + + if Preferences.getDebugger("RemoteDbgEnabled"): + ipaddr = self.debugServer.getHostAddress(False) + rexec = Preferences.getDebugger("RemoteExecution") + rhost = Preferences.getDebugger("RemoteHost") + if rhost == "": + rhost = "localhost" + if rexec: + args = Utilities.parseOptionString(rexec) + \ + [rhost, interpreter, debugClient, + noencoding, str(port), redirect, ipaddr] + args[0] = Utilities.getExecutablePath(args[0]) + process = self.__startProcess(args[0], args[1:]) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be""" + """ started.</p>""")) + + # set translation function + if Preferences.getDebugger("PathTranslation"): + self.translateRemote = \ + Preferences.getDebugger("PathTranslationRemote") + self.translateLocal = \ + Preferences.getDebugger("PathTranslationLocal") + self.translate = self.__remoteTranslation + else: + self.translate = self.__identityTranslation + return process, self.__isNetworked, "" + + # set translation function + self.translate = self.__identityTranslation + + # setup the environment for the debugger + if Preferences.getDebugger("DebugEnvironmentReplace"): + clientEnv = {} + else: + clientEnv = os.environ.copy() + envlist = Utilities.parseEnvironmentString( + Preferences.getDebugger("DebugEnvironment")) + for el in envlist: + try: + key, value = el.split('=', 1) + if value.startswith('"') or value.startswith("'"): + value = value[1:-1] + clientEnv[str(key)] = str(value) + except ValueError: + pass + + ipaddr = self.debugServer.getHostAddress(True) + if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"): + ccmd = Preferences.getDebugger("ConsoleDbgCommand") + if ccmd: + args = Utilities.parseOptionString(ccmd) + \ + [interpreter, os.path.abspath(debugClient), + noencoding, str(port), '0', ipaddr] + args[0] = Utilities.getExecutablePath(args[0]) + process = self.__startProcess(args[0], args[1:], clientEnv) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be""" + """ started.</p>""")) + return process, self.__isNetworked, interpreter + + process = self.__startProcess( + interpreter, + [debugClient, noencoding, str(port), redirect, ipaddr], + clientEnv) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be started.</p>""")) + return process, self.__isNetworked, interpreter + + def startRemoteForProject(self, port, runInConsole): + """ + Public method to start a remote Python interpreter for a project. + + @param port portnumber the debug server is listening on (integer) + @param runInConsole flag indicating to start the debugger in a + console window (boolean) + @return client process object (QProcess), a flag to indicate + a network connection (boolean) and the name of the interpreter + in case of a local execution (string) + """ + project = e5App().getObject("Project") + if not project.isDebugPropertiesLoaded(): + return None, self.__isNetworked, "" + + # start debugger with project specific settings + interpreter = project.getDebugProperty("INTERPRETER") + debugClient = project.getDebugProperty("DEBUGCLIENT") + + redirect = str(project.getDebugProperty("REDIRECT")) + noencoding = \ + project.getDebugProperty("NOENCODING") and '--no-encoding' or '' + + if project.getDebugProperty("REMOTEDEBUGGER"): + ipaddr = self.debugServer.getHostAddress(False) + rexec = project.getDebugProperty("REMOTECOMMAND") + rhost = project.getDebugProperty("REMOTEHOST") + if rhost == "": + rhost = "localhost" + if rexec: + args = Utilities.parseOptionString(rexec) + \ + [rhost, interpreter, os.path.abspath(debugClient), + noencoding, str(port), redirect, ipaddr] + args[0] = Utilities.getExecutablePath(args[0]) + process = self.__startProcess(args[0], args[1:]) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be""" + """ started.</p>""")) + # set translation function + if project.getDebugProperty("PATHTRANSLATION"): + self.translateRemote = \ + project.getDebugProperty("REMOTEPATH") + self.translateLocal = \ + project.getDebugProperty("LOCALPATH") + self.translate = self.__remoteTranslation + else: + self.translate = self.__identityTranslation + return process, self.__isNetworked, "" + + # set translation function + self.translate = self.__identityTranslation + + # setup the environment for the debugger + if project.getDebugProperty("ENVIRONMENTOVERRIDE"): + clientEnv = {} + else: + clientEnv = os.environ.copy() + envlist = Utilities.parseEnvironmentString( + project.getDebugProperty("ENVIRONMENTSTRING")) + for el in envlist: + try: + key, value = el.split('=', 1) + if value.startswith('"') or value.startswith("'"): + value = value[1:-1] + clientEnv[str(key)] = str(value) + except ValueError: + pass + + ipaddr = self.debugServer.getHostAddress(True) + if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"): + ccmd = project.getDebugProperty("CONSOLECOMMAND") or \ + Preferences.getDebugger("ConsoleDbgCommand") + if ccmd: + args = Utilities.parseOptionString(ccmd) + \ + [interpreter, os.path.abspath(debugClient), + noencoding, str(port), '0', ipaddr] + args[0] = Utilities.getExecutablePath(args[0]) + process = self.__startProcess(args[0], args[1:], clientEnv) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be""" + """ started.</p>""")) + return process, self.__isNetworked, interpreter + + process = self.__startProcess( + interpreter, + [debugClient, noencoding, str(port), redirect, ipaddr], + clientEnv) + if process is None: + E5MessageBox.critical( + None, + self.tr("Start Debugger"), + self.tr( + """<p>The debugger backend could not be started.</p>""")) + return process, self.__isNetworked, interpreter + + def getClientCapabilities(self): + """ + Public method to retrieve the debug clients capabilities. + + @return debug client capabilities (integer) + """ + return self.clientCapabilities + + def newConnection(self, sock): + """ + Public slot to handle a new connection. + + @param sock reference to the socket object (QTcpSocket) + @return flag indicating success (boolean) + """ + # If we already have a connection, refuse this one. It will be closed + # automatically. + if self.qsock is not None: + return False + + sock.disconnected.connect(self.debugServer.startClient) + sock.readyRead.connect(self.__parseClientLine) + + self.qsock = sock + + # Get the remote clients capabilities + self.remoteCapabilities() + return True + + def flush(self): + """ + Public slot to flush the queue. + """ + # Send commands that were waiting for the connection. + for cmd in self.queue: + self.qsock.write(cmd.encode('utf8', 'backslashreplace')) + + self.queue = [] + + def shutdown(self): + """ + Public method to cleanly shut down. + + It closes our socket and shuts down + the debug client. (Needed on Win OS) + """ + if self.qsock is None: + return + + # do not want any slots called during shutdown + self.qsock.disconnected.disconnect(self.debugServer.startClient) + self.qsock.readyRead.disconnect(self.__parseClientLine) + + # close down socket, and shut down client as well. + self.__sendJsonCommand("RequestShutdown", {}) + self.qsock.flush() + self.qsock.close() + + # reinitialize + self.qsock = None + self.queue = [] + + def isConnected(self): + """ + Public method to test, if a debug client has connected. + + @return flag indicating the connection status (boolean) + """ + return self.qsock is not None + + def remoteEnvironment(self, env): + """ + Public method to set the environment for a program to debug, run, ... + + @param env environment settings (dictionary) + """ + self.__sendJsonCommand("RequestEnvironment", {"environment": env}) + + def remoteLoad(self, fn, argv, wd, traceInterpreter=False, + autoContinue=True, autoFork=False, forkChild=False): + """ + Public method to load a new program to debug. + + @param fn the filename to debug (string) + @param argv the commandline arguments to pass to the program (string) + @param wd the working directory for the program (string) + @keyparam traceInterpreter flag indicating if the interpreter library + should be traced as well (boolean) + @keyparam autoContinue flag indicating, that the debugger should not + stop at the first executable line (boolean) + @keyparam autoFork flag indicating the automatic fork mode (boolean) + @keyparam forkChild flag indicating to debug the child after forking + (boolean) + """ + self.__autoContinue = autoContinue + self.__scriptName = os.path.abspath(fn) + + wd = self.translate(wd, False) + fn = self.translate(os.path.abspath(fn), False) + self.__sendJsonCommand("RequestLoad", { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "traceInterpreter": traceInterpreter, + "autofork": autoFork, + "forkChild": forkChild, + }) + + def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False): + """ + Public method to load a new program to run. + + @param fn the filename to run (string) + @param argv the commandline arguments to pass to the program (string) + @param wd the working directory for the program (string) + @keyparam autoFork flag indicating the automatic fork mode (boolean) + @keyparam forkChild flag indicating to debug the child after forking + (boolean) + """ + self.__scriptName = os.path.abspath(fn) + + wd = self.translate(wd, False) + fn = self.translate(os.path.abspath(fn), False) + self.__sendJsonCommand("RequestRun", { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "autofork": autoFork, + "forkChild": forkChild, + }) + + def remoteCoverage(self, fn, argv, wd, erase=False): + """ + Public method to load a new program to collect coverage data. + + @param fn the filename to run (string) + @param argv the commandline arguments to pass to the program (string) + @param wd the working directory for the program (string) + @keyparam erase flag indicating that coverage info should be + cleared first (boolean) + """ + self.__scriptName = os.path.abspath(fn) + + wd = self.translate(wd, False) + fn = self.translate(os.path.abspath(fn), False) + self.__sendJsonCommand("RequestCoverage", { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "erase": erase, + }) + + def remoteProfile(self, fn, argv, wd, erase=False): + """ + Public method to load a new program to collect profiling data. + + @param fn the filename to run (string) + @param argv the commandline arguments to pass to the program (string) + @param wd the working directory for the program (string) + @keyparam erase flag indicating that timing info should be cleared + first (boolean) + """ + self.__scriptName = os.path.abspath(fn) + + wd = self.translate(wd, False) + fn = self.translate(os.path.abspath(fn), False) + self.__sendJsonCommand("RequestProfile", { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "erase": erase, + }) + + def remoteStatement(self, stmt): + """ + Public method to execute a Python statement. + + @param stmt the Python statement to execute (string). It + should not have a trailing newline. + """ + self.__sendJsonCommand("ExecuteStatement", { + "statement": stmt, + }) + + def remoteStep(self): + """ + Public method to single step the debugged program. + """ + self.__sendJsonCommand("RequestStep", {}) + + def remoteStepOver(self): + """ + Public method to step over the debugged program. + """ + self.__sendJsonCommand("RequestStepOver", {}) + + def remoteStepOut(self): + """ + Public method to step out the debugged program. + """ + self.__sendJsonCommand("RequestStepOut", {}) + + def remoteStepQuit(self): + """ + Public method to stop the debugged program. + """ + self.__sendJsonCommand("RequestStepQuit", {}) + + def remoteContinue(self, special=False): + """ + Public method to continue the debugged program. + + @param special flag indicating a special continue operation (boolean) + """ + self.__sendJsonCommand("RequestContinue", { + "special": special, + }) + + def remoteBreakpoint(self, fn, line, setBreakpoint, cond=None, temp=False): + """ + Public method to set or clear a breakpoint. + + @param fn filename the breakpoint belongs to (string) + @param line linenumber of the breakpoint (int) + @param setBreakpoint flag indicating setting or resetting a + breakpoint (boolean) + @param cond condition of the breakpoint (string) + @param temp flag indicating a temporary breakpoint (boolean) + """ + self.__sendJsonCommand("RequestBreakpoint", { + "filename": self.translate(fn, False), + "line": line, + "temporary": temp, + "setBreakpoint": setBreakpoint, + "condition": cond, + }) + + def remoteBreakpointEnable(self, fn, line, enable): + """ + Public method to enable or disable a breakpoint. + + @param fn filename the breakpoint belongs to (string) + @param line linenumber of the breakpoint (int) + @param enable flag indicating enabling or disabling a breakpoint + (boolean) + """ + self.__sendJsonCommand("RequestBreakpointEnable", { + "filename": self.translate(fn, False), + "line": line, + "enable": enable, + }) + + def remoteBreakpointIgnore(self, fn, line, count): + """ + Public method to ignore a breakpoint the next couple of occurrences. + + @param fn filename the breakpoint belongs to (string) + @param line linenumber of the breakpoint (int) + @param count number of occurrences to ignore (int) + """ + self.__sendJsonCommand("RequestBreakpointIgnore", { + "filename": self.translate(fn, False), + "line": line, + "count": count, + }) + + def remoteWatchpoint(self, cond, setWatch, temp=False): + """ + Public method to set or clear a watch expression. + + @param cond expression of the watch expression (string) + @param setWatch flag indicating setting or resetting a watch expression + (boolean) + @param temp flag indicating a temporary watch expression (boolean) + """ + # cond is combination of cond and special (s. watch expression viewer) + self.__sendJsonCommand("RequestWatch", { + "temporary": temp, + "setWatch": setWatch, + "condition": cond, + }) + + def remoteWatchpointEnable(self, cond, enable): + """ + Public method to enable or disable a watch expression. + + @param cond expression of the watch expression (string) + @param enable flag indicating enabling or disabling a watch expression + (boolean) + """ + # cond is combination of cond and special (s. watch expression viewer) + self.__sendJsonCommand("RequestWatchEnable", { + "condition": cond, + "enable": enable, + }) + + def remoteWatchpointIgnore(self, cond, count): + """ + Public method to ignore a watch expression the next couple of + occurrences. + + @param cond expression of the watch expression (string) + @param count number of occurrences to ignore (int) + """ + # cond is combination of cond and special (s. watch expression viewer) + self.__sendJsonCommand("RequestWatchIgnore", { + "condition": cond, + "count": count, + }) + + def remoteRawInput(self, s): + """ + Public method to send the raw input to the debugged program. + + @param s the raw input (string) + """ + self.__sendJsonCommand("RawInput", { + "input": s, + }) + + def remoteThreadList(self): + """ + Public method to request the list of threads from the client. + """ + self.__sendJsonCommand("RequestThreadList", {}) + + def remoteSetThread(self, tid): + """ + Public method to request to set the given thread as current thread. + + @param tid id of the thread (integer) + """ + self.__sendJsonCommand("RequestThreadSet", { + "threadID": tid, + }) + + def remoteClientVariables(self, scope, filter, framenr=0): + """ + Public method to request the variables of the debugged program. + + @param scope the scope of the variables (0 = local, 1 = global) + @param filter list of variable types to filter out (list of int) + @param framenr framenumber of the variables to retrieve (int) + """ + self.__sendJsonCommand("RequestVariables", { + "frameNumber": framenr, + "scope": scope, + "filters": filter, + }) + + def remoteClientVariable(self, scope, filter, var, framenr=0): + """ + Public method to request the variables of the debugged program. + + @param scope the scope of the variables (0 = local, 1 = global) + @param filter list of variable types to filter out (list of int) + @param var list encoded name of variable to retrieve (string) + @param framenr framenumber of the variables to retrieve (int) + """ + self.__sendJsonCommand("RequestVariable", { + "variable": var, + "frameNumber": framenr, + "scope": scope, + "filters": filter, + }) + + def remoteClientSetFilter(self, scope, filter): + """ + Public method to set a variables filter list. + + @param scope the scope of the variables (0 = local, 1 = global) + @param filter regexp string for variable names to filter out (string) + """ + self.__sendJsonCommand("RequestSetFilter", { + "scope": scope, + "filter": filter, + }) + + def setCallTraceEnabled(self, on): + """ + Public method to set the call trace state. + + @param on flag indicating to enable the call trace function (boolean) + """ + self.__sendJsonCommand("RequestCallTrace", { + "enable": on, + }) + + def remoteBanner(self): + """ + Public slot to get the banner info of the remote client. + """ + self.__sendJsonCommand("RequestBanner", {}) + + def remoteCapabilities(self): + """ + Public slot to get the debug clients capabilities. + """ + self.__sendJsonCommand("RequestCapabilities", {}) + + def remoteCompletion(self, text): + """ + Public slot to get the a list of possible commandline completions + from the remote client. + + @param text the text to be completed (string) + """ + self.__sendJsonCommand("RequestCompletion", { + "text": text, + }) + + def remoteUTPrepare(self, fn, tn, tfn, failed, cov, covname, coverase): + """ + Public method to prepare a new unittest run. + + @param fn the filename to load (string) + @param tn the testname to load (string) + @param tfn the test function name to load tests from (string) + @param failed list of failed test, if only failed test should be run + (list of strings) + @param cov flag indicating collection of coverage data is requested + (boolean) + @param covname filename to be used to assemble the coverage caches + filename (string) + @param coverase flag indicating erasure of coverage data is requested + (boolean) + """ + self.__scriptName = os.path.abspath(fn) + + fn = self.translate(os.path.abspath(fn), False) + self.__sendJsonCommand("RequestUTPrepare", { + "filename": fn, + "testname": tn, + "testfunctionname": tfn, + "failed": failed, + "coverage": cov, + "coveragefile": covname, + "coverageerase": coverase, + }) + + def remoteUTRun(self): + """ + Public method to start a unittest run. + """ + self.__sendJsonCommand("RequestUTRun", {}) + + def remoteUTStop(self): + """ + Public method to stop a unittest run. + """ + self.__sendJsonCommand("RequestUTStop", {}) + + def __askForkTo(self): + """ + Private method to ask the user which branch of a fork to follow. + """ + selections = [self.tr("Parent Process"), + self.tr("Child process")] + res, ok = QInputDialog.getItem( + None, + self.tr("Client forking"), + self.tr("Select the fork branch to follow."), + selections, + 0, False) + if not ok or res == selections[0]: + self.__sendJsonCommand("ResponseForkTo", { + "target": "parent", + }) + else: + self.__sendJsonCommand("ResponseForkTo", { + "target": "child", + }) + + def __parseClientLine(self): + """ + Private method to handle data from the client. + """ + while self.qsock and self.qsock.canReadLine(): + qs = self.qsock.readLine() + if self.codec is not None: + line = self.codec.toUnicode(qs) + else: + line = bytes(qs).decode() + +## print("Server: ", line) ##debug + + self.__handleJsonCommand(line) + continue + + def __handleJsonCommand(self, jsonStr): + """ + Private method to handle a command or response serialized as a + JSON string. + + @param jsonStr string containing the command or response received + from the debug backend + @type str + """ + import json + + try: + commandDict = json.loads(jsonStr.strip()) + except json.JSONDecodeError as err: + # TODO: implement real error handling + ##print(str(err)) + return + + method = commandDict["method"] + params = commandDict["params"] + + if method == "ClientOutput": + self.debugServer.signalClientOutput(params["text"]) + + elif method in ["ResponseLine", "ResponseStack"]: + for s in params["stack"]: + s[0] = self.translate(s[0], True) + cf = params["stack"][0] + if self.__autoContinue: + self.__autoContinue = False + QTimer.singleShot(0, self.remoteContinue) + else: + self.debugServer.signalClientLine( + cf[0], int(cf[1]), + method == "ResponseStack") + self.debugServer.signalClientStack(params["stack"]) + + elif method == "CallTrace": + isCall = params["event"].lower() == "c" + fromFile, fromLineno, fromFunc = params["from"].rsplit(":", 2) + toFile, toLineno, toFunc = params["to"].rsplit(":", 2) + self.debugServer.signalClientCallTrace( + isCall, + fromFile, fromLineno, fromFunc, + toFile, toLineno, toFunc) + + elif method == "ResponseVariables": + self.debugServer.signalClientVariables( + params["scope"], params["variables"]) + + elif method == "ResponseVariable": + self.debugServer.signalClientVariable( + params["scope"], [params["variable"]] + params["variables"]) + + elif method == "ResponseThreadList": + self.debugServer.signalClientThreadList( + params["currentID"], params["threadList"]) + + elif method == "ResponseThreadSet": + self.debugServer.signalClientThreadSet() + + elif method == "ResponseCapabilities": + self.clientCapabilities = params["capabilities"] + self.debugServer.signalClientCapabilities( + params["capabilities"], params["clientType"]) + + elif method == "ResponseBanner": + self.debugServer.signalClientBanner( + params["version"], + params["platform"], + params["dbgclient"]) + + elif method == "ResponseOK": + self.debugServer.signalClientStatement(False) + + elif method == "ResponseContinue": + self.debugServer.signalClientStatement(True) + + elif method == "RequestRaw": + self.debugServer.signalClientRawInput( + params["prompt"], params["echo"]) + + elif method == "ResponseBPConditionError": + fn = self.translate(params["filename"], True) + self.debugServer.signalClientBreakConditionError( + fn, params["line"]) + + elif method == "ResponseClearBreakpoint": + fn = self.translate(params["filename"], True) + self.debugServer.signalClientClearBreak(fn, params["line"]) + + elif method == "ResponseWatchConditionError": + self.debugServer.signalClientWatchConditionError( + params["condition"]) + + elif method == "ResponseClearWatch": + self.debugServer.signalClientClearWatch(params["condition"]) + + elif method == "ResponseException": + if params: + exctype = params["type"] + excmessage = params["message"] + stack = params["stack"] + if stack: + for stackEntry in stack: + stackEntry[0] = self.translate(stackEntry[0], True) + if stack[0] and stack[0][0] == "<string>": + for stackEntry in stack: + if stackEntry[0] == "<string>": + stackEntry[0] = self.__scriptName + else: + break + else: + exctype = '' + excmessage = '' + stack = [] + + self.debugServer.signalClientException( + exctype, excmessage, stack) + + elif method == "ResponseSyntax": + self.debugServer.signalClientSyntaxError( + params["message"], self.translate(params["filename"], True), + params["linenumber"], params["characternumber"]) + + elif method == "ResponseSignal": + self.debugServer.signalClientSignal( + params["message"], self.translate(params["filename"], True), + params["linenumber"], params["function"], params["arguments"]) + + elif method == "ResponseExit": + self.__scriptName = "" + # TODO: combine these into signalClientExit + self.debugServer.signalClientExit(params["status"]) + if params["message"]: + self.debugServer.signalClientOutput(params["message"]) + + elif method == "PassiveStartup": + self.debugServer.passiveStartUp( + self.translate(params["filename"], True), params["exceptions"]) + + elif method == "ResponseCompletion": + self.debugServer.signalClientCompletionList( + params["completions"], params["text"]) + + elif method == "ResponseUTPrepared": + self.debugServer.clientUtPrepared( + params["count"], params["exception"], params["message"]) + + elif method == "ResponseUTFinished": + self.debugServer.clientUtFinished() + + elif method == "ResponseUTStartTest": + self.debugServer.clientUtStartTest( + params["testname"], params["description"]) + + elif method == "ResponseUTStopTest": + self.debugServer.clientUtStopTest() + + elif method == "ResponseUTTestFailed": + self.debugServer.clientUtTestFailed( + params["testname"], params["traceback"], params["id"]) + + elif method == "ResponseUTTestErrored": + self.debugServer.clientUtTestErrored( + params["testname"], params["traceback"], params["id"]) + + elif method == "ResponseUTTestSkipped": + self.debugServer.clientUtTestSkipped( + params["testname"], params["reason"], params["id"]) + + elif method == "ResponseUTTestFailedExpected": + self.debugServer.clientUtTestFailedExpected( + params["testname"], params["traceback"], params["id"]) + + elif method == "ResponseUTTestSucceededUnexpected": + self.debugServer.clientUtTestSucceededUnexpected( + params["testname"], params["id"]) + + elif method == "RequestForkTo": + self.__askForkTo() + + def __sendJsonCommand(self, command, params): + """ + Private method to send a single command to the client. + + @param command command name to be sent + @type str + @param params dictionary of named parameters for the command + @type dict + """ + import json + + commandDict = { + "jsonrpc": "2.0", + "method": command, + "params": params, + } + cmd = json.dumps(commandDict) + '\n' + if self.qsock is not None: + self.qsock.write(cmd.encode('utf8', 'backslashreplace')) + else: + self.queue.append(cmd) + + +def createDebuggerInterfacePython2(debugServer, passive): + """ + Module function to create a debugger interface instance. + + + @param debugServer reference to the debug server + @type DebugServer + @param passive flag indicating passive connection mode + @type bool + @return instantiated debugger interface + @rtype DebuggerInterfacePython + """ + return DebuggerInterfacePython2(debugServer, passive) + + +def getRegistryData(): + """ + Module function to get characterizing data for the debugger interface. + + @return tuple containing client type, client capabilities, client file + type associations and reference to creation function + @rtype tuple of (str, int, list of str, function) + """ + exts = [] + for ext in Preferences.getDebugger("PythonExtensions").split(): + if ext.startswith("."): + exts.append(ext) + else: + exts.append(".{0}".format(ext)) + + if exts and Preferences.getDebugger("PythonInterpreter"): + return ["Python2", ClientDefaultCapabilities, exts, + createDebuggerInterfacePython2] + else: + return ["", 0, [], None]