"Fossies" - the Fresh Open Source Software Archive

Member "eric6-20.9/eric/eric6/DebugClients/Python/DebugClientBase.py" (4 Jul 2020, 81365 Bytes) of package /linux/misc/eric6-20.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "DebugClientBase.py" see the Fossies "Dox" file reference documentation.

    1 # -*- coding: utf-8 -*-
    2 
    3 # Copyright (c) 2002 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
    4 #
    5 
    6 """
    7 Module implementing a debug client base class.
    8 """
    9 
   10 import sys
   11 import socket
   12 import select
   13 import codeop
   14 import codecs
   15 import traceback
   16 import os
   17 import json
   18 import imp
   19 import re
   20 import atexit
   21 import signal
   22 import time
   23 
   24 
   25 import DebugClientCapabilities
   26 import DebugVariables
   27 from DebugBase import setRecursionLimit, printerr   # __IGNORE_WARNING__
   28 from AsyncFile import AsyncFile, AsyncPendingWrite
   29 from DebugConfig import ConfigQtNames, ConfigVarTypeStrings
   30 from FlexCompleter import Completer
   31 from DebugUtilities import prepareJsonCommand
   32 from BreakpointWatch import Breakpoint, Watch
   33 
   34 from DebugUtilities import getargvalues, formatargvalues
   35 
   36 DebugClientInstance = None
   37 
   38 ###############################################################################
   39 
   40 
   41 def DebugClientInput(prompt=""):
   42     """
   43     Replacement for the standard input() builtin.
   44     
   45     This function works with the split debugger.
   46     
   47     @param prompt prompt to be shown
   48     @type str
   49     @return result of the input() call
   50     @rtype str
   51     """
   52     if DebugClientInstance is None or not DebugClientInstance.redirect:
   53         return DebugClientOrigInput(prompt)
   54 
   55     return DebugClientInstance.input(prompt)
   56 
   57 # Use our own input().
   58 try:
   59     DebugClientOrigInput = __builtins__.__dict__['input']
   60     __builtins__.__dict__['input'] = DebugClientInput
   61 except (AttributeError, KeyError):
   62     import __main__
   63     DebugClientOrigInput = __main__.__builtins__.__dict__['input']
   64     __main__.__builtins__.__dict__['input'] = DebugClientInput
   65 
   66 ###############################################################################
   67 
   68 
   69 def DebugClientFork():
   70     """
   71     Replacement for the standard os.fork().
   72     
   73     @return result of the fork() call
   74     """
   75     if DebugClientInstance is None:
   76         return DebugClientOrigFork()
   77     
   78     return DebugClientInstance.fork()
   79 
   80 # use our own fork().
   81 if 'fork' in dir(os):
   82     DebugClientOrigFork = os.fork
   83     os.fork = DebugClientFork
   84 
   85 ###############################################################################
   86 
   87 
   88 def DebugClientClose(fd):
   89     """
   90     Replacement for the standard os.close(fd).
   91     
   92     @param fd open file descriptor to be closed (integer)
   93     """
   94     if DebugClientInstance is None:
   95         DebugClientOrigClose(fd)
   96     
   97     DebugClientInstance.close(fd)
   98 
   99 # use our own close().
  100 if 'close' in dir(os):
  101     DebugClientOrigClose = os.close
  102     os.close = DebugClientClose
  103 
  104 ###############################################################################
  105 
  106 
  107 def DebugClientSetRecursionLimit(limit):
  108     """
  109     Replacement for the standard sys.setrecursionlimit(limit).
  110     
  111     @param limit recursion limit (integer)
  112     """
  113     rl = max(limit, 64)
  114     setRecursionLimit(rl)
  115     DebugClientOrigSetRecursionLimit(rl + 64)
  116 
  117 # use our own setrecursionlimit().
  118 if 'setrecursionlimit' in dir(sys):
  119     DebugClientOrigSetRecursionLimit = sys.setrecursionlimit
  120     sys.setrecursionlimit = DebugClientSetRecursionLimit
  121     DebugClientSetRecursionLimit(sys.getrecursionlimit())
  122 
  123 ###############################################################################
  124 
  125 
  126 class DebugClientBase(object):
  127     """
  128     Class implementing the client side of the debugger.
  129 
  130     It provides access to the Python interpeter from a debugger running in
  131     another process.
  132     
  133     The protocol between the debugger and the client is based on JSONRPC 2.0
  134     PDUs. Each one is sent on a single line, i.e. commands or responses are
  135     separated by a linefeed character.
  136 
  137     If the debugger closes the session there is no response from the client.
  138     The client may close the session at any time as a result of the script
  139     being debugged closing or crashing.
  140     
  141     <b>Note</b>: This class is meant to be subclassed by individual
  142     DebugClient classes. Do not instantiate it directly.
  143     """
  144     clientCapabilities = DebugClientCapabilities.HasAll
  145     
  146     # keep these in sync with VariablesViewer.VariableItem.Indicators
  147     Indicators = ("()", "[]", "{:}", "{}")      # __IGNORE_WARNING_M613__
  148     arrayTypes = {
  149         'list', 'tuple', 'dict', 'set', 'frozenset', "class 'dict_items'",
  150         "class 'dict_keys'", "class 'dict_values'"
  151     }
  152     
  153     def __init__(self):
  154         """
  155         Constructor
  156         """
  157         self.breakpoints = {}
  158         self.redirect = True
  159         
  160         # special objects representing the main scripts thread and frame
  161         self.mainThread = self
  162         self.framenr = 0
  163         
  164         # The context to run the debugged program in.
  165         self.debugMod = imp.new_module('__main__')
  166         self.debugMod.__dict__['__builtins__'] = __builtins__
  167 
  168         # The list of complete lines to execute.
  169         self.buffer = ''
  170         
  171         # The list of regexp objects to filter variables against
  172         self.globalsFilterObjects = []
  173         self.localsFilterObjects = []
  174 
  175         self._fncache = {}
  176         self.dircache = []
  177         self.passive = False        # used to indicate the passive mode
  178         self.running = None
  179         self.test = None
  180         self.debugging = False
  181         
  182         self.fork_auto = False
  183         self.fork_child = False
  184 
  185         self.readstream = None
  186         self.writestream = None
  187         self.errorstream = None
  188         self.pollingDisabled = False
  189         
  190         self.callTraceEnabled = None
  191         
  192         self.variant = 'You should not see this'
  193         
  194         self.compile_command = codeop.CommandCompiler()
  195         
  196         self.coding_re = re.compile(r"coding[:=]\s*([-\w_.]+)")
  197         self.defaultCoding = 'utf-8'
  198         self.__coding = self.defaultCoding
  199         self.noencoding = False
  200     
  201     def getCoding(self):
  202         """
  203         Public method to return the current coding.
  204         
  205         @return codec name (string)
  206         """
  207         return self.__coding
  208     
  209     def __setCoding(self, filename):
  210         """
  211         Private method to set the coding used by a python file.
  212         
  213         @param filename name of the file to inspect (string)
  214         """
  215         if self.noencoding:
  216             self.__coding = sys.getdefaultencoding()
  217         else:
  218             default = 'utf-8'
  219             try:
  220                 f = open(filename, 'rb')
  221                 # read the first and second line
  222                 text = f.readline()
  223                 text = "{0}{1}".format(text, f.readline())
  224                 f.close()
  225             except IOError:
  226                 self.__coding = default
  227                 return
  228             
  229             for line in text.splitlines():
  230                 m = self.coding_re.search(line)
  231                 if m:
  232                     self.__coding = m.group(1)
  233                     return
  234             self.__coding = default
  235         
  236     def input(self, prompt, echo=True):
  237         """
  238         Public method to implement input() using the event loop.
  239         
  240         @param prompt the prompt to be shown (string)
  241         @param echo Flag indicating echoing of the input (boolean)
  242         @return the entered string
  243         """
  244         self.sendJsonCommand("RequestRaw", {
  245             "prompt": prompt,
  246             "echo": echo,
  247         })
  248         self.eventLoop(True)
  249         return self.rawLine
  250 
  251     def sessionClose(self, terminate=True):
  252         """
  253         Public method to close the session with the debugger and optionally
  254         terminate.
  255         
  256         @param terminate flag indicating to terminate (boolean)
  257         """
  258         try:
  259             self.set_quit()
  260         except Exception:       # secok
  261             pass
  262 
  263         self.debugging = False
  264         
  265         # make sure we close down our end of the socket
  266         # might be overkill as normally stdin, stdout and stderr
  267         # SHOULD be closed on exit, but it does not hurt to do it here
  268         self.readstream.close(True)
  269         self.writestream.close(True)
  270         self.errorstream.close(True)
  271 
  272         if terminate:
  273             # Ok, go away.
  274             sys.exit()
  275 
  276     def __compileFileSource(self, filename, mode='exec'):
  277         """
  278         Private method to compile source code read from a file.
  279         
  280         @param filename name of the source file (string)
  281         @param mode kind of code to be generated (string, exec or eval)
  282         @return compiled code object (None in case of errors)
  283         """
  284         with codecs.open(filename, encoding=self.__coding) as fp:
  285             statement = fp.read()
  286         
  287         try:
  288             code = compile(statement + '\n', filename, mode)
  289         except SyntaxError:
  290             exctype, excval, exctb = sys.exc_info()
  291             try:
  292                 message = str(excval)
  293                 filename = excval.filename
  294                 lineno = excval.lineno
  295                 charno = excval.offset
  296                 if charno is None:
  297                     charno = 0
  298                 
  299             except (AttributeError, ValueError):
  300                 message = ""
  301                 filename = ""
  302                 lineno = 0
  303                 charno = 0
  304             
  305             self.sendSyntaxError(message, filename, lineno, charno)
  306             return None
  307         
  308         return code
  309     
  310     def handleJsonCommand(self, jsonStr):
  311         """
  312         Public method to handle a command serialized as a JSON string.
  313         
  314         @param jsonStr string containing the command received from the IDE
  315         @type str
  316         """
  317 ##        printerr(jsonStr)          ##debug
  318         
  319         try:
  320             commandDict = json.loads(jsonStr.strip())
  321         except (TypeError, ValueError) as err:
  322             printerr("Error handling command: " + jsonStr)
  323             printerr(str(err))
  324             return
  325         
  326         method = commandDict["method"]
  327         params = commandDict["params"]
  328         
  329         if method == "RequestVariables":
  330             self.__dumpVariables(
  331                 params["frameNumber"], params["scope"], params["filters"])
  332         
  333         elif method == "RequestVariable":
  334             self.__dumpVariable(
  335                 params["variable"], params["frameNumber"],
  336                 params["scope"], params["filters"])
  337         
  338         elif method == "RequestThreadList":
  339             self.dumpThreadList()
  340         
  341         elif method == "RequestThreadSet":
  342             if params["threadID"] in self.threads:
  343                 self.setCurrentThread(params["threadID"])
  344                 self.sendJsonCommand("ResponseThreadSet", {})
  345                 stack = self.currentThread.getStack()
  346                 self.sendJsonCommand("ResponseStack", {
  347                     "stack": stack,
  348                 })
  349         
  350         elif method == "RequestCapabilities":
  351             clientType = "Python3"
  352             self.sendJsonCommand("ResponseCapabilities", {
  353                 "capabilities": self.__clientCapabilities(),
  354                 "clientType": clientType
  355             })
  356         
  357         elif method == "RequestBanner":
  358             self.sendJsonCommand("ResponseBanner", {
  359                 "version": "Python {0}".format(sys.version),
  360                 "platform": socket.gethostname(),
  361                 "dbgclient": self.variant,
  362             })
  363         
  364         elif method == "RequestSetFilter":
  365             self.__generateFilterObjects(params["scope"], params["filter"])
  366         
  367         elif method == "RequestCallTrace":
  368             if params["enable"]:
  369                 callTraceEnabled = self.profile
  370             else:
  371                 callTraceEnabled = None
  372             
  373             if self.debugging:
  374                 sys.setprofile(callTraceEnabled)
  375             else:
  376                 # remember for later
  377                 self.callTraceEnabled = callTraceEnabled
  378         
  379         elif method == "RequestEnvironment":
  380             for key, value in params["environment"].items():
  381                 if key.endswith("+"):
  382                     if key[:-1] in os.environ:
  383                         os.environ[key[:-1]] += value
  384                     else:
  385                         os.environ[key[:-1]] = value
  386                 else:
  387                     os.environ[key] = value
  388         
  389         elif method == "RequestLoad":
  390             self._fncache = {}
  391             self.dircache = []
  392             sys.argv = []
  393             self.__setCoding(params["filename"])
  394             sys.argv.append(params["filename"])
  395             sys.argv.extend(params["argv"])
  396             sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
  397             if params["workdir"] == '':
  398                 os.chdir(sys.path[1])
  399             else:
  400                 os.chdir(params["workdir"])
  401             
  402             self.running = sys.argv[0]
  403             self.debugging = True
  404             
  405             self.fork_auto = params["autofork"]
  406             self.fork_child = params["forkChild"]
  407             
  408             self.threads.clear()
  409             self.attachThread(mainThread=True)
  410             
  411             # set the system exception handling function to ensure, that
  412             # we report on all unhandled exceptions
  413             sys.excepthook = self.__unhandled_exception
  414             self.__interceptSignals()
  415             
  416             # clear all old breakpoints, they'll get set after we have
  417             # started
  418             Breakpoint.clear_all_breaks()
  419             Watch.clear_all_watches()
  420             
  421             self.mainThread.tracePythonLibs(params["traceInterpreter"])
  422             
  423             # This will eventually enter a local event loop.
  424             self.debugMod.__dict__['__file__'] = self.running
  425             sys.modules['__main__'] = self.debugMod
  426             code = self.__compileFileSource(self.running)
  427             if code:
  428                 sys.setprofile(self.callTraceEnabled)
  429                 self.mainThread.run(code, self.debugMod.__dict__, debug=True)
  430 
  431         elif method == "RequestRun":
  432             sys.argv = []
  433             self.__setCoding(params["filename"])
  434             sys.argv.append(params["filename"])
  435             sys.argv.extend(params["argv"])
  436             sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
  437             if params["workdir"] == '':
  438                 os.chdir(sys.path[1])
  439             else:
  440                 os.chdir(params["workdir"])
  441 
  442             self.running = sys.argv[0]
  443             self.botframe = None
  444             
  445             self.fork_auto = params["autofork"]
  446             self.fork_child = params["forkChild"]
  447             
  448             self.threads.clear()
  449             self.attachThread(mainThread=True)
  450             
  451             # set the system exception handling function to ensure, that
  452             # we report on all unhandled exceptions
  453             sys.excepthook = self.__unhandled_exception
  454             self.__interceptSignals()
  455             
  456             self.mainThread.tracePythonLibs(False)
  457             
  458             self.debugMod.__dict__['__file__'] = sys.argv[0]
  459             sys.modules['__main__'] = self.debugMod
  460             res = 0
  461             code = self.__compileFileSource(self.running)
  462             if code:
  463                 self.mainThread.run(code, self.debugMod.__dict__, debug=False)
  464 
  465         elif method == "RequestCoverage":
  466             from coverage import Coverage
  467             sys.argv = []
  468             self.__setCoding(params["filename"])
  469             sys.argv.append(params["filename"])
  470             sys.argv.extend(params["argv"])
  471             sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
  472             if params["workdir"] == '':
  473                 os.chdir(sys.path[1])
  474             else:
  475                 os.chdir(params["workdir"])
  476             
  477             # set the system exception handling function to ensure, that
  478             # we report on all unhandled exceptions
  479             sys.excepthook = self.__unhandled_exception
  480             self.__interceptSignals()
  481             
  482             # generate a coverage object
  483             self.cover = Coverage(
  484                 auto_data=True,
  485                 data_file="{0}.coverage".format(
  486                     os.path.splitext(sys.argv[0])[0]))
  487             
  488             if params["erase"]:
  489                 self.cover.erase()
  490             sys.modules['__main__'] = self.debugMod
  491             self.debugMod.__dict__['__file__'] = sys.argv[0]
  492             code = self.__compileFileSource(sys.argv[0])
  493             if code:
  494                 self.running = sys.argv[0]
  495                 self.cover.start()
  496                 self.mainThread.run(code, self.debugMod.__dict__, debug=False)
  497                 self.cover.stop()
  498                 self.cover.save()
  499         
  500         elif method == "RequestProfile":
  501             sys.setprofile(None)
  502             import PyProfile
  503             sys.argv = []
  504             self.__setCoding(params["filename"])
  505             sys.argv.append(params["filename"])
  506             sys.argv.extend(params["argv"])
  507             sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
  508             if params["workdir"] == '':
  509                 os.chdir(sys.path[1])
  510             else:
  511                 os.chdir(params["workdir"])
  512 
  513             # set the system exception handling function to ensure, that
  514             # we report on all unhandled exceptions
  515             sys.excepthook = self.__unhandled_exception
  516             self.__interceptSignals()
  517             
  518             # generate a profile object
  519             self.prof = PyProfile.PyProfile(sys.argv[0])
  520             
  521             if params["erase"]:
  522                 self.prof.erase()
  523             self.debugMod.__dict__['__file__'] = sys.argv[0]
  524             sys.modules['__main__'] = self.debugMod
  525             script = ''
  526             with codecs.open(sys.argv[0], encoding=self.__coding) as fp:
  527                 script = fp.read()
  528             if script and not script.endswith('\n'):
  529                 script += '\n'
  530             
  531             if script:
  532                 self.running = sys.argv[0]
  533                 res = 0
  534                 try:
  535                     self.prof.run(script)
  536                     atexit._run_exitfuncs()
  537                 except SystemExit as exc:
  538                     res = exc.code
  539                     atexit._run_exitfuncs()
  540                 except Exception:
  541                     excinfo = sys.exc_info()
  542                     self.__unhandled_exception(*excinfo)
  543                 
  544                 self.prof.save()
  545                 self.progTerminated(res)
  546         
  547         elif method == "ExecuteStatement":
  548             if self.buffer:
  549                 self.buffer = self.buffer + '\n' + params["statement"]
  550             else:
  551                 self.buffer = params["statement"]
  552 
  553             try:
  554                 code = self.compile_command(self.buffer, self.readstream.name)
  555             except (OverflowError, SyntaxError, ValueError):
  556                 # Report the exception
  557                 sys.last_type, sys.last_value, sys.last_traceback = (
  558                     sys.exc_info())
  559                 self.sendJsonCommand("ClientOutput", {
  560                     "text": "".join(traceback.format_exception_only(
  561                         sys.last_type, sys.last_value))
  562                 })
  563                 self.buffer = ''
  564             else:
  565                 if code is None:
  566                     self.sendJsonCommand("ResponseContinue", {})
  567                     return
  568                 else:
  569                     self.buffer = ''
  570 
  571                     try:
  572                         if self.running is None:
  573                             exec(code, self.debugMod.__dict__)      # secok
  574                         else:
  575                             if self.currentThread is None:
  576                                 # program has terminated
  577                                 self.running = None
  578                                 _globals = self.debugMod.__dict__
  579                                 _locals = _globals
  580                             else:
  581                                 cf = self.currentThread.getCurrentFrame()
  582                                 # program has terminated
  583                                 if cf is None:
  584                                     self.running = None
  585                                     _globals = self.debugMod.__dict__
  586                                     _locals = _globals
  587                                 else:
  588                                     frmnr = self.framenr
  589                                     while cf is not None and frmnr > 0:
  590                                         cf = cf.f_back
  591                                         frmnr -= 1
  592                                     _globals = cf.f_globals
  593                                     _locals = (
  594                                         self.currentThread.getFrameLocals(
  595                                             self.framenr))
  596                             ## reset sys.stdout to our redirector
  597                             ## (unconditionally)
  598                             if "sys" in _globals:
  599                                 __stdout = _globals["sys"].stdout
  600                                 _globals["sys"].stdout = self.writestream
  601                                 exec(code, _globals, _locals)       # secok
  602                                 _globals["sys"].stdout = __stdout
  603                             elif "sys" in _locals:
  604                                 __stdout = _locals["sys"].stdout
  605                                 _locals["sys"].stdout = self.writestream
  606                                 exec(code, _globals, _locals)       # secok
  607                                 _locals["sys"].stdout = __stdout
  608                             else:
  609                                 exec(code, _globals, _locals)       # secok
  610                             
  611                             self.currentThread.storeFrameLocals(self.framenr)
  612                     except SystemExit as exc:
  613                         self.progTerminated(exc.code)
  614                     except Exception:
  615                         # Report the exception and the traceback
  616                         tlist = []
  617                         try:
  618                             exc_type, exc_value, exc_tb = sys.exc_info()
  619                             sys.last_type = exc_type
  620                             sys.last_value = exc_value
  621                             sys.last_traceback = exc_tb
  622                             tblist = traceback.extract_tb(exc_tb)
  623                             del tblist[:1]
  624                             tlist = traceback.format_list(tblist)
  625                             if tlist:
  626                                 tlist.insert(
  627                                     0, "Traceback (innermost last):\n")
  628                                 tlist.extend(traceback.format_exception_only(
  629                                     exc_type, exc_value))
  630                         finally:
  631                             tblist = exc_tb = None
  632 
  633                         self.sendJsonCommand("ClientOutput", {
  634                             "text": "".join(tlist)
  635                         })
  636             
  637             self.sendJsonCommand("ResponseOK", {})
  638         
  639         elif method == "RequestStep":
  640             self.currentThreadExec.step(True)
  641             self.eventExit = True
  642 
  643         elif method == "RequestStepOver":
  644             self.currentThreadExec.step(False)
  645             self.eventExit = True
  646         
  647         elif method == "RequestStepOut":
  648             self.currentThreadExec.stepOut()
  649             self.eventExit = True
  650         
  651         elif method == "RequestStepQuit":
  652             if self.passive:
  653                 self.progTerminated(42)
  654             else:
  655                 self.set_quit()
  656                 self.eventExit = True
  657         
  658         elif method == "RequestMoveIP":
  659             newLine = params["newLine"]
  660             self.currentThreadExec.move_instruction_pointer(newLine)
  661         
  662         elif method == "RequestContinue":
  663             self.currentThreadExec.go(params["special"])
  664             self.eventExit = True
  665         
  666         elif method == "RawInput":
  667             # If we are handling raw mode input then break out of the current
  668             # event loop.
  669             self.rawLine = params["input"]
  670             self.eventExit = True
  671         
  672         elif method == "RequestBreakpoint":
  673             if params["setBreakpoint"]:
  674                 if params["condition"] in ['None', '']:
  675                     cond = None
  676                 elif params["condition"] is not None:
  677                     try:
  678                         cond = compile(params["condition"], '<string>', 'eval')
  679                     except SyntaxError:
  680                         self.sendJsonCommand("ResponseBPConditionError", {
  681                             "filename": params["filename"],
  682                             "line": params["line"],
  683                         })
  684                         return
  685                 else:
  686                     cond = None
  687                 
  688                 Breakpoint(
  689                     params["filename"], params["line"], params["temporary"],
  690                     cond)
  691             else:
  692                 Breakpoint.clear_break(params["filename"], params["line"])
  693         
  694         elif method == "RequestBreakpointEnable":
  695             bp = Breakpoint.get_break(params["filename"], params["line"])
  696             if bp is not None:
  697                 if params["enable"]:
  698                     bp.enable()
  699                 else:
  700                     bp.disable()
  701         
  702         elif method == "RequestBreakpointIgnore":
  703             bp = Breakpoint.get_break(params["filename"], params["line"])
  704             if bp is not None:
  705                 bp.ignore = params["count"]
  706         
  707         elif method == "RequestWatch":
  708             if params["setWatch"]:
  709                 if params["condition"].endswith(
  710                         ('??created??', '??changed??')):
  711                     compiledCond, flag = params["condition"].split()
  712                 else:
  713                     compiledCond = params["condition"]
  714                     flag = ''
  715                 
  716                 try:
  717                     compiledCond = compile(compiledCond, '<string>', 'eval')
  718                 except SyntaxError:
  719                     self.sendJsonCommand("ResponseWatchConditionError", {
  720                         "condition": params["condition"],
  721                     })
  722                     return
  723                 Watch(
  724                     params["condition"], compiledCond, flag,
  725                     params["temporary"])
  726             else:
  727                 Watch.clear_watch(params["condition"])
  728         
  729         elif method == "RequestWatchEnable":
  730             wp = Watch.get_watch(params["condition"])
  731             if wp is not None:
  732                 if params["enable"]:
  733                     wp.enable()
  734                 else:
  735                     wp.disable()
  736         
  737         elif method == "RequestWatchIgnore":
  738             wp = Watch.get_watch(params["condition"])
  739             if wp is not None:
  740                 wp.ignore = params["count"]
  741         
  742         elif method == "RequestShutdown":
  743             self.sessionClose()
  744         
  745         elif method == "RequestCompletion":
  746             self.__completionList(params["text"])
  747         
  748         elif method == "RequestUTDiscover":
  749             if params["syspath"]:
  750                 sys.path = params["syspath"] + sys.path
  751             
  752             discoveryStart = params["discoverystart"]
  753             if not discoveryStart:
  754                 discoveryStart = params["workdir"]
  755             
  756             top_level_dir = params["workdir"]
  757 
  758             os.chdir(params["discoverystart"])
  759             
  760             # set the system exception handling function to ensure, that
  761             # we report on all unhandled exceptions
  762             sys.excepthook = self.__unhandled_exception
  763             self.__interceptSignals()
  764             
  765             try:
  766                 import unittest
  767                 testLoader = unittest.TestLoader()
  768                 test = testLoader.discover(
  769                     discoveryStart, top_level_dir=top_level_dir)
  770                 if (hasattr(testLoader, "errors") and
  771                         bool(testLoader.errors)):
  772                     self.sendJsonCommand("ResponseUTDiscover", {
  773                         "testCasesList": [],
  774                         "exception": "DiscoveryError",
  775                         "message": "\n\n".join(testLoader.errors),
  776                     })
  777                 else:
  778                     testsList = self.__assembleTestCasesList(test,
  779                                                              discoveryStart)
  780                     self.sendJsonCommand("ResponseUTDiscover", {
  781                         "testCasesList": testsList,
  782                         "exception": "",
  783                         "message": "",
  784                     })
  785             except Exception:
  786                 exc_type, exc_value, exc_tb = sys.exc_info()
  787                 self.sendJsonCommand("ResponseUTDiscover", {
  788                     "testCasesList": [],
  789                     "exception": exc_type.__name__,
  790                     "message": str(exc_value),
  791                 })
  792         
  793         elif method == "RequestUTPrepare":
  794             if params["syspath"]:
  795                 sys.path = params["syspath"] + sys.path
  796             sys.path.insert(
  797                 0, os.path.dirname(os.path.abspath(params["filename"])))
  798             top_level_dir = None
  799             if params["workdir"]:
  800                 os.chdir(params["workdir"])
  801                 top_level_dir = params["workdir"]
  802             else:
  803                 os.chdir(sys.path[0])
  804             
  805             # set the system exception handling function to ensure, that
  806             # we report on all unhandled exceptions
  807             sys.excepthook = self.__unhandled_exception
  808             self.__interceptSignals()
  809             
  810             try:
  811                 import unittest
  812                 testLoader = unittest.TestLoader()
  813                 if params["discover"]:
  814                     discoveryStart = params["discoverystart"]
  815                     if not discoveryStart:
  816                         discoveryStart = params["workdir"]
  817                     if params["testcases"]:
  818                         self.test = testLoader.loadTestsFromNames(
  819                             params["testcases"])
  820                     else:
  821                         self.test = testLoader.discover(
  822                             discoveryStart, top_level_dir=top_level_dir)
  823                 else:
  824                     if params["filename"]:
  825                         utModule = imp.load_source(
  826                             params["testname"], params["filename"])
  827                     else:
  828                         utModule = None
  829                     if params["failed"]:
  830                         if utModule:
  831                             failed = [t.split(".", 1)[1]
  832                                       for t in params["failed"]]
  833                         else:
  834                             failed = params["failed"][:]
  835                         self.test = testLoader.loadTestsFromNames(
  836                             failed, utModule)
  837                     else:
  838                         self.test = testLoader.loadTestsFromName(
  839                             params["testfunctionname"], utModule)
  840             except Exception:
  841                 exc_type, exc_value, exc_tb = sys.exc_info()
  842                 self.sendJsonCommand("ResponseUTPrepared", {
  843                     "count": 0,
  844                     "exception": exc_type.__name__,
  845                     "message": str(exc_value),
  846                 })
  847                 return
  848             
  849             # generate a coverage object
  850             if params["coverage"]:
  851                 from coverage import Coverage
  852                 self.cover = Coverage(
  853                     auto_data=True,
  854                     data_file="{0}.coverage".format(
  855                         os.path.splitext(params["coveragefile"])[0]))
  856                 if params["coverageerase"]:
  857                     self.cover.erase()
  858             else:
  859                 self.cover = None
  860             
  861             if params["debug"]:
  862                 Breakpoint.clear_all_breaks()
  863                 Watch.clear_all_watches()
  864             
  865             self.sendJsonCommand("ResponseUTPrepared", {
  866                 "count": self.test.countTestCases(),
  867                 "exception": "",
  868                 "message": "",
  869             })
  870         
  871         elif method == "RequestUTRun":
  872             from DCTestResult import DCTestResult
  873             self.testResult = DCTestResult(self, params["failfast"])
  874             if self.cover:
  875                 self.cover.start()
  876             self.debugging = params["debug"]
  877             if params["debug"]:
  878                 locals_ = locals()
  879                 self.threads.clear()
  880                 self.attachThread(mainThread=True)
  881                 sys.setprofile(None)
  882                 self.mainThread.run(
  883                     "result = self.test.run(self.testResult)\n",
  884                     localsDict=locals_)
  885                 result = locals_["result"]
  886             else:
  887                 result = self.test.run(self.testResult)
  888             if self.cover:
  889                 self.cover.stop()
  890                 self.cover.save()
  891             self.sendJsonCommand("ResponseUTFinished", {
  892                 "status": 0 if result.wasSuccessful() else 1,
  893             })
  894         
  895         elif method == "RequestUTStop":
  896             self.testResult.stop()
  897         
  898         elif method == "ResponseForkTo":
  899             # this results from a separate event loop
  900             self.fork_child = (params["target"] == 'child')
  901             self.eventExit = True
  902     
  903     def __assembleTestCasesList(self, suite, start):
  904         """
  905         Private method to assemble a list of test cases included in a test
  906         suite.
  907         
  908         @param suite test suite to be inspected
  909         @type unittest.TestSuite
  910         @param start name of directory discovery was started at
  911         @type str
  912         @return list of tuples containing the test case ID, a short description
  913             and the path of the test file name
  914         @rtype list of tuples of (str, str, str)
  915         """
  916         import unittest
  917         testCases = []
  918         for test in suite:
  919             if isinstance(test, unittest.TestSuite):
  920                 testCases.extend(self.__assembleTestCasesList(test, start))
  921             else:
  922                 testId = test.id()
  923                 if ("ModuleImportFailure" not in testId and
  924                     "LoadTestsFailure" not in testId and
  925                         "_FailedTest" not in testId):
  926                     filename = os.path.join(
  927                         start,
  928                         test.__module__.replace(".", os.sep) + ".py")
  929                     testCases.append(
  930                         (test.id(), test.shortDescription(), filename)
  931                     )
  932         return testCases
  933     
  934     def sendJsonCommand(self, method, params):
  935         """
  936         Public method to send a single command or response to the IDE.
  937         
  938         @param method command or response command name to be sent
  939         @type str
  940         @param params dictionary of named parameters for the command or
  941             response
  942         @type dict
  943         """
  944         cmd = prepareJsonCommand(method, params)
  945         
  946         self.writestream.write_p(cmd)
  947         self.writestream.flush()
  948     
  949     def sendClearTemporaryBreakpoint(self, filename, lineno):
  950         """
  951         Public method to signal the deletion of a temporary breakpoint.
  952         
  953         @param filename name of the file the bp belongs to
  954         @type str
  955         @param lineno linenumber of the bp
  956         @type int
  957         """
  958         self.sendJsonCommand("ResponseClearBreakpoint", {
  959             "filename": filename,
  960             "line": lineno
  961         })
  962     
  963     def sendClearTemporaryWatch(self, condition):
  964         """
  965         Public method to signal the deletion of a temporary watch expression.
  966         
  967         @param condition condition of the watch expression to be cleared
  968         @type str
  969         """
  970         self.sendJsonCommand("ResponseClearWatch", {
  971             "condition": condition,
  972         })
  973     
  974     def sendResponseLine(self, stack):
  975         """
  976         Public method to send the current call stack.
  977         
  978         @param stack call stack
  979         @type list
  980         """
  981         self.sendJsonCommand("ResponseLine", {
  982             "stack": stack,
  983         })
  984     
  985     def sendCallTrace(self, event, fromInfo, toInfo):
  986         """
  987         Public method to send a call trace entry.
  988         
  989         @param event trace event (call or return)
  990         @type str
  991         @param fromInfo dictionary containing the origin info
  992         @type dict with 'filename', 'linenumber' and 'codename'
  993             as keys
  994         @param toInfo dictionary containing the target info
  995         @type dict with 'filename', 'linenumber' and 'codename'
  996             as keys
  997         """
  998         self.sendJsonCommand("CallTrace", {
  999             "event": event[0],
 1000             "from": fromInfo,
 1001             "to": toInfo,
 1002         })
 1003     
 1004     def sendException(self, exceptionType, exceptionMessage, stack):
 1005         """
 1006         Public method to send information for an exception.
 1007         
 1008         @param exceptionType type of exception raised
 1009         @type str
 1010         @param exceptionMessage message of the exception
 1011         @type str
 1012         @param stack stack trace information
 1013         @type list
 1014         """
 1015         self.sendJsonCommand("ResponseException", {
 1016             "type": exceptionType,
 1017             "message": exceptionMessage,
 1018             "stack": stack,
 1019         })
 1020     
 1021     def sendSyntaxError(self, message, filename, lineno, charno):
 1022         """
 1023         Public method to send information for a syntax error.
 1024         
 1025         @param message syntax error message
 1026         @type str
 1027         @param filename name of the faulty file
 1028         @type str
 1029         @param lineno line number info
 1030         @type int
 1031         @param charno character number info
 1032         @type int
 1033         """
 1034         self.sendJsonCommand("ResponseSyntax", {
 1035             "message": message,
 1036             "filename": filename,
 1037             "linenumber": lineno,
 1038             "characternumber": charno,
 1039         })
 1040     
 1041     def sendPassiveStartup(self, filename, exceptions):
 1042         """
 1043         Public method to send the passive start information.
 1044         
 1045         @param filename name of the script
 1046         @type str
 1047         @param exceptions flag to enable exception reporting of the IDE
 1048         @type bool
 1049         """
 1050         self.sendJsonCommand("PassiveStartup", {
 1051             "filename": filename,
 1052             "exceptions": exceptions,
 1053         })
 1054     
 1055     def __clientCapabilities(self):
 1056         """
 1057         Private method to determine the clients capabilities.
 1058         
 1059         @return client capabilities (integer)
 1060         """
 1061         try:
 1062             import PyProfile    # __IGNORE_WARNING__
 1063             try:
 1064                 del sys.modules['PyProfile']
 1065             except KeyError:
 1066                 pass
 1067             return self.clientCapabilities
 1068         except ImportError:
 1069             return (
 1070                 self.clientCapabilities & ~DebugClientCapabilities.HasProfiler)
 1071     
 1072     def readReady(self, stream):
 1073         """
 1074         Public method called when there is data ready to be read.
 1075         
 1076         @param stream file like object that has data to be written
 1077         @return flag indicating an error condition
 1078         @rtype bool
 1079         """
 1080         error = False
 1081         
 1082         self.lockClient()
 1083         try:
 1084             command = stream.readCommand()
 1085         except Exception:
 1086             error = True
 1087             command = ""
 1088         self.unlockClient()
 1089 
 1090         if error or len(command) == 0:
 1091             self.sessionClose()
 1092         else:
 1093             self.handleJsonCommand(command)
 1094         
 1095         return error
 1096 
 1097     def writeReady(self, stream):
 1098         """
 1099         Public method called when we are ready to write data.
 1100         
 1101         @param stream file like object that has data to be written
 1102         """
 1103         stream.write_p("")
 1104         stream.flush()
 1105     
 1106     def __interact(self):
 1107         """
 1108         Private method to interact with the debugger.
 1109         """
 1110         global DebugClientInstance
 1111 
 1112         DebugClientInstance = self
 1113         self.__receiveBuffer = ""
 1114 
 1115         if not self.passive:
 1116             # At this point simulate an event loop.
 1117             self.eventLoop()
 1118 
 1119     def eventLoop(self, disablePolling=False):
 1120         """
 1121         Public method implementing our event loop.
 1122         
 1123         @param disablePolling flag indicating to enter an event loop with
 1124             polling disabled (boolean)
 1125         """
 1126         self.eventExit = False
 1127         self.pollingDisabled = disablePolling
 1128         selectErrors = 0
 1129 
 1130         while not self.eventExit:
 1131             wrdy = []
 1132 
 1133             if self.writestream.nWriteErrors > self.writestream.maxtries:
 1134                 break
 1135             
 1136             if AsyncPendingWrite(self.writestream):
 1137                 wrdy.append(self.writestream)
 1138 
 1139             if AsyncPendingWrite(self.errorstream):
 1140                 wrdy.append(self.errorstream)
 1141             
 1142             try:
 1143                 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [])
 1144             except (select.error, KeyboardInterrupt, socket.error):
 1145                 selectErrors += 1
 1146                 if selectErrors <= 10:      # arbitrarily selected
 1147                     # just carry on
 1148                     continue
 1149                 else:
 1150                     # give up for too many errors
 1151                     break
 1152             
 1153             # reset the select error counter
 1154             selectErrors = 0
 1155             
 1156             if self.readstream in rrdy:
 1157                 error = self.readReady(self.readstream)
 1158                 if error:
 1159                     break
 1160 
 1161             if self.writestream in wrdy:
 1162                 self.writeReady(self.writestream)
 1163 
 1164             if self.errorstream in wrdy:
 1165                 self.writeReady(self.errorstream)
 1166 
 1167         self.eventExit = False
 1168         self.pollingDisabled = False
 1169 
 1170     def eventPoll(self):
 1171         """
 1172         Public method to poll for events like 'set break point'.
 1173         """
 1174         if self.pollingDisabled:
 1175             return
 1176         
 1177         wrdy = []
 1178         if AsyncPendingWrite(self.writestream):
 1179             wrdy.append(self.writestream)
 1180 
 1181         if AsyncPendingWrite(self.errorstream):
 1182             wrdy.append(self.errorstream)
 1183         
 1184         # immediate return if nothing is ready.
 1185         try:
 1186             rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [], 0)
 1187         except (select.error, KeyboardInterrupt, socket.error):
 1188             return
 1189 
 1190         if self.readstream in rrdy:
 1191             self.readReady(self.readstream)
 1192 
 1193         if self.writestream in wrdy:
 1194             self.writeReady(self.writestream)
 1195 
 1196         if self.errorstream in wrdy:
 1197             self.writeReady(self.errorstream)
 1198     
 1199     def connectDebugger(self, port, remoteAddress=None, redirect=True):
 1200         """
 1201         Public method to establish a session with the debugger.
 1202         
 1203         It opens a network connection to the debugger, connects it to stdin,
 1204         stdout and stderr and saves these file objects in case the application
 1205         being debugged redirects them itself.
 1206         
 1207         @param port the port number to connect to (int)
 1208         @param remoteAddress the network address of the debug server host
 1209             (string)
 1210         @param redirect flag indicating redirection of stdin, stdout and
 1211             stderr (boolean)
 1212         """
 1213         if remoteAddress is None:
 1214             remoteAddress = "127.0.0.1"
 1215         elif "@@i" in remoteAddress:
 1216             remoteAddress = remoteAddress.split("@@i")[0]
 1217         sock = socket.create_connection((remoteAddress, port))
 1218 
 1219         self.readstream = AsyncFile(sock, sys.stdin.mode, sys.stdin.name)
 1220         self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name)
 1221         self.errorstream = AsyncFile(sock, sys.stderr.mode, sys.stderr.name)
 1222         
 1223         if redirect:
 1224             sys.stdin = self.readstream
 1225             sys.stdout = self.writestream
 1226             sys.stderr = self.errorstream
 1227         self.redirect = redirect
 1228         
 1229         # attach to the main thread here
 1230         self.attachThread(mainThread=True)
 1231 
 1232     def __unhandled_exception(self, exctype, excval, exctb):
 1233         """
 1234         Private method called to report an uncaught exception.
 1235         
 1236         @param exctype the type of the exception
 1237         @param excval data about the exception
 1238         @param exctb traceback for the exception
 1239         """
 1240         self.mainThread.user_exception((exctype, excval, exctb), True)
 1241     
 1242     def __interceptSignals(self):
 1243         """
 1244         Private method to intercept common signals.
 1245         """
 1246         for signum in [
 1247             signal.SIGABRT,                 # abnormal termination
 1248             signal.SIGFPE,                  # floating point exception
 1249             signal.SIGILL,                  # illegal instruction
 1250             signal.SIGSEGV,                 # segmentation violation
 1251         ]:
 1252             signal.signal(signum, self.__signalHandler)
 1253     
 1254     def __signalHandler(self, signalNumber, stackFrame):
 1255         """
 1256         Private method to handle signals.
 1257         
 1258         @param signalNumber number of the signal to be handled
 1259         @type int
 1260         @param stackFrame current stack frame
 1261         @type frame object
 1262         """
 1263         if signalNumber == signal.SIGABRT:
 1264             message = "Abnormal Termination"
 1265         elif signalNumber == signal.SIGFPE:
 1266             message = "Floating Point Exception"
 1267         elif signalNumber == signal.SIGILL:
 1268             message = "Illegal Instruction"
 1269         elif signalNumber == signal.SIGSEGV:
 1270             message = "Segmentation Violation"
 1271         else:
 1272             message = "Unknown Signal '{0}'".format(signalNumber)
 1273         
 1274         filename = self.absPath(stackFrame)
 1275         
 1276         linenr = stackFrame.f_lineno
 1277         ffunc = stackFrame.f_code.co_name
 1278         
 1279         if ffunc == '?':
 1280             ffunc = ''
 1281         
 1282         if ffunc and not ffunc.startswith("<"):
 1283             argInfo = getargvalues(stackFrame)
 1284             try:
 1285                 fargs = formatargvalues(
 1286                     argInfo.args, argInfo.varargs,
 1287                     argInfo.keywords, argInfo.locals)
 1288             except Exception:
 1289                 fargs = ""
 1290         else:
 1291             fargs = ""
 1292         
 1293         self.sendJsonCommand("ResponseSignal", {
 1294             "message": message,
 1295             "filename": filename,
 1296             "linenumber": linenr,
 1297             "function": ffunc,
 1298             "arguments": fargs,
 1299         })
 1300     
 1301     def absPath(self, fn):
 1302         """
 1303         Public method to convert a filename to an absolute name.
 1304 
 1305         sys.path is used as a set of possible prefixes. The name stays
 1306         relative if a file could not be found.
 1307         
 1308         @param fn filename (string)
 1309         @return the converted filename (string)
 1310         """
 1311         if os.path.isabs(fn):
 1312             return fn
 1313 
 1314         # Check the cache.
 1315         if fn in self._fncache:
 1316             return self._fncache[fn]
 1317 
 1318         # Search sys.path.
 1319         for p in sys.path:
 1320             afn = os.path.abspath(os.path.join(p, fn))
 1321             nafn = os.path.normcase(afn)
 1322 
 1323             if os.path.exists(nafn):
 1324                 self._fncache[fn] = afn
 1325                 d = os.path.dirname(afn)
 1326                 if (d not in sys.path) and (d not in self.dircache):
 1327                     self.dircache.append(d)
 1328                 return afn
 1329 
 1330         # Search the additional directory cache
 1331         for p in self.dircache:
 1332             afn = os.path.abspath(os.path.join(p, fn))
 1333             nafn = os.path.normcase(afn)
 1334             
 1335             if os.path.exists(nafn):
 1336                 self._fncache[fn] = afn
 1337                 return afn
 1338         
 1339         # Nothing found.
 1340         return fn
 1341 
 1342     def getRunning(self):
 1343         """
 1344         Public method to return the main script we are currently running.
 1345         
 1346         @return flag indicating a running debug session (boolean)
 1347         """
 1348         return self.running
 1349 
 1350     def progTerminated(self, status, message=""):
 1351         """
 1352         Public method to tell the debugger that the program has terminated.
 1353         
 1354         @param status return status
 1355         @type int
 1356         @param message status message
 1357         @type str
 1358         """
 1359         if status is None:
 1360             status = 0
 1361         elif not isinstance(status, int):
 1362             message = str(status)
 1363             status = 1
 1364 
 1365         if self.running:
 1366             self.set_quit()
 1367             self.running = None
 1368             self.sendJsonCommand("ResponseExit", {
 1369                 "status": status,
 1370                 "message": message,
 1371             })
 1372         
 1373         # reset coding
 1374         self.__coding = self.defaultCoding
 1375 
 1376     def __dumpVariables(self, frmnr, scope, filterList):
 1377         """
 1378         Private method to return the variables of a frame to the debug server.
 1379         
 1380         @param frmnr distance of frame reported on. 0 is the current frame
 1381         @type int
 1382         @param scope 1 to report global variables, 0 for local variables
 1383         @type int
 1384         @param filterList the indices of variable types to be filtered
 1385         @type list of int
 1386         """
 1387         if self.currentThread is None:
 1388             return
 1389         
 1390         self.resolverCache = [{}, {}]
 1391         frmnr += self.currentThread.skipFrames
 1392         if scope == 0:
 1393             self.framenr = frmnr
 1394         
 1395         f = self.currentThread.getCurrentFrame()
 1396         
 1397         while f is not None and frmnr > 0:
 1398             f = f.f_back
 1399             frmnr -= 1
 1400         
 1401         if f is None:
 1402             if scope:
 1403                 varDict = self.debugMod.__dict__
 1404             else:
 1405                 scope = -1
 1406         elif scope:
 1407             varDict = f.f_globals
 1408         elif f.f_globals is f.f_locals:
 1409             scope = -1
 1410         else:
 1411             varDict = f.f_locals
 1412         
 1413         if scope == -1:
 1414             varlist = []
 1415         else:
 1416             varlist = self.__formatVariablesList(varDict, scope, filterList)
 1417         
 1418         self.sendJsonCommand("ResponseVariables", {
 1419             "scope": scope,
 1420             "variables": varlist,
 1421         })
 1422     
 1423     def __dumpVariable(self, var, frmnr, scope, filterList):
 1424         """
 1425         Private method to return the variables of a frame to the debug server.
 1426         
 1427         @param var list encoded name of the requested variable
 1428         @type list of strings
 1429         @param frmnr distance of frame reported on. 0 is the current frame
 1430         @type int
 1431         @param scope 1 to report global variables, 0 for local variables
 1432         @type int
 1433         @param filterList the indices of variable types to be filtered
 1434         @type list of int
 1435         """
 1436         if self.currentThread is None:
 1437             return
 1438         
 1439         frmnr += self.currentThread.skipFrames
 1440         f = self.currentThread.getCurrentFrame()
 1441         
 1442         while f is not None and frmnr > 0:
 1443             f = f.f_back
 1444             frmnr -= 1
 1445         
 1446         if f is None:
 1447             if scope:
 1448                 varDict = self.debugMod.__dict__
 1449             else:
 1450                 scope = -1
 1451         elif scope:
 1452             varDict = f.f_globals
 1453         elif f.f_globals is f.f_locals:
 1454             scope = -1
 1455         else:
 1456             varDict = f.f_locals
 1457         
 1458         varlist = []
 1459         
 1460         if scope != -1 and str(var) in self.resolverCache[scope]:
 1461             varGen = self.resolverCache[scope][str(var)]
 1462             idx, varDict = next(varGen)
 1463             var.insert(0, idx)
 1464             varlist = self.__formatVariablesList(varDict, scope, filterList)
 1465         elif scope != -1:
 1466             variable = varDict
 1467             # Lookup the wanted attribute
 1468             for attribute in var:
 1469                 _, _, resolver = DebugVariables.getType(variable)
 1470                 if resolver:
 1471                     variable = resolver.resolve(variable, attribute)
 1472                     if variable is None:
 1473                         break
 1474                     
 1475                 else:
 1476                     break
 1477             
 1478             idx = -3  # Requested variable doesn't exist anymore
 1479             # If found, get the details of attribute
 1480             if variable is not None:
 1481                 typeName, typeStr, resolver = DebugVariables.getType(variable)
 1482                 if resolver:
 1483                     varGen = resolver.getDictionary(variable)
 1484                     self.resolverCache[scope][str(var)] = varGen
 1485                     
 1486                     idx, varDict = next(varGen)
 1487                     varlist = self.__formatVariablesList(
 1488                         varDict, scope, filterList)
 1489                 else:
 1490                     # Gently handle exception which could occure as special
 1491                     # cases, e.g. already deleted C++ objects, str conversion..
 1492                     try:
 1493                         varlist = self.__formatQtVariable(variable, typeName)
 1494                     except Exception:
 1495                         varlist = []
 1496                     idx = -1
 1497             
 1498             var.insert(0, idx)
 1499         
 1500         self.sendJsonCommand("ResponseVariable", {
 1501             "scope": scope,
 1502             "variable": var,
 1503             "variables": varlist,
 1504         })
 1505     
 1506     def __extractIndicators(self, var):
 1507         """
 1508         Private method to extract the indicator string from a variable text.
 1509         
 1510         @param var variable text
 1511         @type str
 1512         @return tuple containing the variable text without indicators and the
 1513             indicator string
 1514         @rtype tuple of two str
 1515         """
 1516         for indicator in DebugClientBase.Indicators:
 1517             if var.endswith(indicator):
 1518                 return var[:-len(indicator)], indicator
 1519         
 1520         return var, ""
 1521         
 1522     def __formatQtVariable(self, value, qttype):
 1523         """
 1524         Private method to produce a formatted output of a simple Qt5 type.
 1525         
 1526         @param value variable to be formatted
 1527         @param qttype type of the Qt variable to be formatted (string)
 1528         @return A tuple consisting of a list of formatted variables. Each
 1529             variable entry is a tuple of three elements, the variable name,
 1530             its type and value.
 1531         """
 1532         varlist = []
 1533         if qttype == 'QChar':
 1534             varlist.append(
 1535                 ("", "QChar", "{0}".format(chr(value.unicode()))))
 1536             varlist.append(("", "int", "{0:d}".format(value.unicode())))
 1537         elif qttype == 'QByteArray':
 1538             varlist.append(
 1539                 ("bytes", "QByteArray", "{0}".format(bytes(value))[2:-1]))
 1540             varlist.append(
 1541                 ("hex", "QByteArray", "{0}".format(value.toHex())[2:-1]))
 1542             varlist.append(
 1543                 ("base64", "QByteArray", "{0}".format(value.toBase64())[2:-1]))
 1544             varlist.append(("percent encoding", "QByteArray",
 1545                             "{0}".format(value.toPercentEncoding())[2:-1]))
 1546         elif qttype == 'QString':
 1547             varlist.append(("", "QString", "{0}".format(value)))
 1548         elif qttype == 'QStringList':
 1549             for i in range(value.count()):
 1550                 varlist.append(
 1551                     ("{0:d}".format(i), "QString", "{0}".format(value[i])))
 1552         elif qttype == 'QPoint':
 1553             varlist.append(("x", "int", "{0:d}".format(value.x())))
 1554             varlist.append(("y", "int", "{0:d}".format(value.y())))
 1555         elif qttype == 'QPointF':
 1556             varlist.append(("x", "float", "{0:g}".format(value.x())))
 1557             varlist.append(("y", "float", "{0:g}".format(value.y())))
 1558         elif qttype == 'QRect':
 1559             varlist.append(("x", "int", "{0:d}".format(value.x())))
 1560             varlist.append(("y", "int", "{0:d}".format(value.y())))
 1561             varlist.append(("width", "int", "{0:d}".format(value.width())))
 1562             varlist.append(("height", "int", "{0:d}".format(value.height())))
 1563         elif qttype == 'QRectF':
 1564             varlist.append(("x", "float", "{0:g}".format(value.x())))
 1565             varlist.append(("y", "float", "{0:g}".format(value.y())))
 1566             varlist.append(("width", "float", "{0:g}".format(value.width())))
 1567             varlist.append(("height", "float", "{0:g}".format(value.height())))
 1568         elif qttype == 'QSize':
 1569             varlist.append(("width", "int", "{0:d}".format(value.width())))
 1570             varlist.append(("height", "int", "{0:d}".format(value.height())))
 1571         elif qttype == 'QSizeF':
 1572             varlist.append(("width", "float", "{0:g}".format(value.width())))
 1573             varlist.append(("height", "float", "{0:g}".format(value.height())))
 1574         elif qttype == 'QColor':
 1575             varlist.append(("name", "str", "{0}".format(value.name())))
 1576             r, g, b, a = value.getRgb()
 1577             varlist.append(
 1578                 ("rgba", "int",
 1579                  "{0:d}, {1:d}, {2:d}, {3:d}".format(r, g, b, a)))
 1580             h, s, v, a = value.getHsv()
 1581             varlist.append(
 1582                 ("hsva", "int",
 1583                  "{0:d}, {1:d}, {2:d}, {3:d}".format(h, s, v, a)))
 1584             c, m, y, k, a = value.getCmyk()
 1585             varlist.append(
 1586                 ("cmyka", "int",
 1587                  "{0:d}, {1:d}, {2:d}, {3:d}, {4:d}".format(c, m, y, k, a)))
 1588         elif qttype == 'QDate':
 1589             varlist.append(("", "QDate", "{0}".format(value.toString())))
 1590         elif qttype == 'QTime':
 1591             varlist.append(("", "QTime", "{0}".format(value.toString())))
 1592         elif qttype == 'QDateTime':
 1593             varlist.append(("", "QDateTime", "{0}".format(value.toString())))
 1594         elif qttype == 'QDir':
 1595             varlist.append(("path", "str", "{0}".format(value.path())))
 1596             varlist.append(("absolutePath", "str",
 1597                             "{0}".format(value.absolutePath())))
 1598             varlist.append(("canonicalPath", "str",
 1599                             "{0}".format(value.canonicalPath())))
 1600         elif qttype == 'QFile':
 1601             varlist.append(("fileName", "str", "{0}".format(value.fileName())))
 1602         elif qttype == 'QFont':
 1603             varlist.append(("family", "str", "{0}".format(value.family())))
 1604             varlist.append(
 1605                 ("pointSize", "int", "{0:d}".format(value.pointSize())))
 1606             varlist.append(("weight", "int", "{0:d}".format(value.weight())))
 1607             varlist.append(("bold", "bool", "{0}".format(value.bold())))
 1608             varlist.append(("italic", "bool", "{0}".format(value.italic())))
 1609         elif qttype == 'QUrl':
 1610             varlist.append(("url", "str", "{0}".format(value.toString())))
 1611             varlist.append(("scheme", "str", "{0}".format(value.scheme())))
 1612             varlist.append(("user", "str", "{0}".format(value.userName())))
 1613             varlist.append(("password", "str", "{0}".format(value.password())))
 1614             varlist.append(("host", "str", "{0}".format(value.host())))
 1615             varlist.append(("port", "int", "{0:d}".format(value.port())))
 1616             varlist.append(("path", "str", "{0}".format(value.path())))
 1617         elif qttype == 'QModelIndex':
 1618             varlist.append(("valid", "bool", "{0}".format(value.isValid())))
 1619             if value.isValid():
 1620                 varlist.append(("row", "int", "{0}".format(value.row())))
 1621                 varlist.append(("column", "int", "{0}".format(value.column())))
 1622                 varlist.append(
 1623                     ("internalId", "int", "{0}".format(value.internalId())))
 1624                 varlist.append(("internalPointer", "void *",
 1625                                 "{0}".format(value.internalPointer())))
 1626         elif qttype == 'QRegExp':
 1627             varlist.append(("pattern", "str", "{0}".format(value.pattern())))
 1628         
 1629         # GUI stuff
 1630         elif qttype == 'QAction':
 1631             varlist.append(("name", "str", "{0}".format(value.objectName())))
 1632             varlist.append(("text", "str", "{0}".format(value.text())))
 1633             varlist.append(
 1634                 ("icon text", "str", "{0}".format(value.iconText())))
 1635             varlist.append(("tooltip", "str", "{0}".format(value.toolTip())))
 1636             varlist.append(
 1637                 ("whatsthis", "str", "{0}".format(value.whatsThis())))
 1638             varlist.append(
 1639                 ("shortcut", "str",
 1640                  "{0}".format(value.shortcut().toString())))
 1641         elif qttype == 'QKeySequence':
 1642             varlist.append(("value", "", "{0}".format(value.toString())))
 1643             
 1644         # XML stuff
 1645         elif qttype == 'QDomAttr':
 1646             varlist.append(("name", "str", "{0}".format(value.name())))
 1647             varlist.append(("value", "str", "{0}".format(value.value())))
 1648         elif qttype == 'QDomCharacterData':
 1649             varlist.append(("data", "str", "{0}".format(value.data())))
 1650         elif qttype == 'QDomComment':
 1651             varlist.append(("data", "str", "{0}".format(value.data())))
 1652         elif qttype == 'QDomDocument':
 1653             varlist.append(("text", "str", "{0}".format(value.toString())))
 1654         elif qttype == 'QDomElement':
 1655             varlist.append(("tagName", "str", "{0}".format(value.tagName())))
 1656             varlist.append(("text", "str", "{0}".format(value.text())))
 1657         elif qttype == 'QDomText':
 1658             varlist.append(("data", "str", "{0}".format(value.data())))
 1659             
 1660         # Networking stuff
 1661         elif qttype == 'QHostAddress':
 1662             varlist.append(
 1663                 ("address", "QHostAddress", "{0}".format(value.toString())))
 1664             
 1665         # PySide specific
 1666         elif qttype == 'EnumType':  # Not in PyQt possible
 1667             for key, value in value.values.items():
 1668                 varlist.append((key, qttype, "{0}".format(int(value))))
 1669         
 1670         return varlist
 1671     
 1672     def __formatVariablesList(self, dict_, scope, filterList=None):
 1673         """
 1674         Private method to produce a formated variables list.
 1675         
 1676         The dictionary passed in to it is scanned. Variables are
 1677         only added to the list, if their type is not contained
 1678         in the filter list and their name doesn't match any of the filter
 1679         expressions. The formated variables list (a list of tuples of 3
 1680         values) is returned.
 1681         
 1682         @param dict_ the dictionary to be scanned
 1683         @type dict
 1684         @param scope 1 to filter using the globals filter, 0 using the locals
 1685             filter.
 1686             Variables are only added to the list, if their name do not match
 1687             any of the filter expressions.
 1688         @type int
 1689         @param filterList the indices of variable types to be filtered.
 1690             Variables are only added to the list, if their type is not
 1691             contained in the filter list.
 1692         @type list of int
 1693         @return A tuple consisting of a list of formatted variables. Each
 1694             variable entry is a tuple of three elements, the variable name,
 1695             its type and value.
 1696         @rtype list of tuple of (str, str, str)
 1697         """
 1698         filterList = [] if filterList is None else filterList[:]
 1699         
 1700         varlist = []
 1701         if scope:
 1702             patternFilterObjects = self.globalsFilterObjects
 1703         else:
 1704             patternFilterObjects = self.localsFilterObjects
 1705         if type(dict_) == dict:
 1706             dict_ = dict_.items()
 1707         
 1708         for key, value in dict_:
 1709             # no more elements available
 1710             if key == -2:
 1711                 break
 1712             
 1713             # filter based on the filter pattern
 1714             matched = False
 1715             for pat in patternFilterObjects:
 1716                 if pat.match(str(key)):
 1717                     matched = True
 1718                     break
 1719             if matched:
 1720                 continue
 1721             
 1722             # filter hidden attributes (filter #0)
 1723             if 0 in filterList and str(key)[:2] == '__':
 1724                 continue
 1725             
 1726             # special handling for '__builtins__' (it's way too big)
 1727             if key == '__builtins__':
 1728                 rvalue = '<module builtins (built-in)>'
 1729                 valtype = 'module'
 1730                 if ConfigVarTypeStrings.index(valtype) in filterList:
 1731                     continue
 1732             else:
 1733                 isQt = False
 1734                 # valtypestr, e.g. class 'PyQt5.QtCore.QPoint'
 1735                 valtypestr = str(type(value))[1:-1]
 1736                 _, valtype = valtypestr.split(' ', 1)
 1737                 # valtype, e.g. PyQt5.QtCore.QPoint
 1738                 valtype = valtype[1:-1]
 1739                 # Strip 'instance' to be equal with Python 3
 1740                 if valtype == "instancemethod":
 1741                     valtype = "method"
 1742                 elif valtype == "type" or valtype == "classobj":
 1743                     valtype = "class"
 1744                 
 1745                 # valtypename, e.g. QPoint
 1746                 valtypename = type(value).__name__
 1747                 try:
 1748                     if ConfigVarTypeStrings.index(valtype) in filterList:
 1749                         continue
 1750                 except ValueError:
 1751                     if valtype in ("sip.enumtype", "sip.wrappertype"):
 1752                         if ConfigVarTypeStrings.index('class') in filterList:
 1753                             continue
 1754                     elif (valtype == "sip.methoddescriptor" or
 1755                             valtype == "method_descriptor"):
 1756                         if ConfigVarTypeStrings.index('method') in filterList:
 1757                             continue
 1758                     elif valtype in ("numpy.ndarray", "array.array"):
 1759                         if ConfigVarTypeStrings.index('list') in filterList:
 1760                             continue
 1761                     elif valtypename == "MultiValueDict":
 1762                         if ConfigVarTypeStrings.index('dict') in filterList:
 1763                             continue
 1764                     elif ConfigVarTypeStrings.index('instance') in filterList:
 1765                         continue
 1766                     
 1767                     isQt = valtype.startswith(ConfigQtNames)
 1768                     if (not valtypestr.startswith('type ') and
 1769                         valtypename not in ("ndarray", "MultiValueDict",
 1770                                             "array", "defaultdict") and
 1771                             not isQt):
 1772                         valtype = valtypestr
 1773                 
 1774                 try:
 1775                     if valtype in self.arrayTypes:
 1776                         rvalue = "{0:d}".format(len(value))
 1777                     elif valtype == 'array.array':
 1778                         rvalue = "{0:d}|{1}".format(
 1779                             len(value), value.typecode)
 1780                     elif valtype == 'collections.defaultdict':
 1781                         rvalue = "{0:d}|{1}".format(
 1782                             len(value), value.default_factory.__name__)
 1783                     elif valtype == "numpy.ndarray":
 1784                         rvalue = "x".join(str(x) for x in value.shape)
 1785                     elif valtypename == "MultiValueDict":
 1786                         rvalue = "{0:d}".format(len(value.keys()))
 1787                         valtype = "django.MultiValueDict"  # shortened type
 1788                     else:
 1789                         rvalue = repr(value)
 1790                         if valtype.startswith('class') and rvalue[0] in '{([':
 1791                             rvalue = ""
 1792                         elif (isQt and rvalue.startswith("<class '")):
 1793                             rvalue = rvalue[8:-2]
 1794                 except Exception:
 1795                     rvalue = ''
 1796             
 1797             varlist.append((key, valtype, rvalue))
 1798         
 1799         return varlist
 1800     
 1801     def __generateFilterObjects(self, scope, filterString):
 1802         """
 1803         Private slot to convert a filter string to a list of filter objects.
 1804         
 1805         @param scope 1 to generate filter for global variables, 0 for local
 1806             variables (int)
 1807         @param filterString string of filter patterns separated by ';'
 1808         """
 1809         patternFilterObjects = []
 1810         for pattern in filterString.split(';'):
 1811             patternFilterObjects.append(re.compile('^{0}$'.format(pattern)))
 1812         if scope:
 1813             self.globalsFilterObjects = patternFilterObjects[:]
 1814         else:
 1815             self.localsFilterObjects = patternFilterObjects[:]
 1816     
 1817     def __completionList(self, text):
 1818         """
 1819         Private slot to handle the request for a commandline completion list.
 1820         
 1821         @param text the text to be completed (string)
 1822         """
 1823         completerDelims = ' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?'
 1824         
 1825         completions = set()
 1826         # find position of last delim character
 1827         pos = -1
 1828         while pos >= -len(text):
 1829             if text[pos] in completerDelims:
 1830                 if pos == -1:
 1831                     text = ''
 1832                 else:
 1833                     text = text[pos + 1:]
 1834                 break
 1835             pos -= 1
 1836         
 1837         # Get local and global completions
 1838         try:
 1839             localdict = self.currentThread.getFrameLocals(self.framenr)
 1840             localCompleter = Completer(localdict).complete
 1841             self.__getCompletionList(text, localCompleter, completions)
 1842         except AttributeError:
 1843             pass
 1844         
 1845         cf = self.currentThread.getCurrentFrame()
 1846         frmnr = self.framenr
 1847         while cf is not None and frmnr > 0:
 1848             cf = cf.f_back
 1849             frmnr -= 1
 1850         
 1851         if cf is None:
 1852             globaldict = self.debugMod.__dict__
 1853         else:
 1854             globaldict = cf.f_globals
 1855         
 1856         globalCompleter = Completer(globaldict).complete
 1857         self.__getCompletionList(text, globalCompleter, completions)
 1858         
 1859         self.sendJsonCommand("ResponseCompletion", {
 1860             "completions": list(completions),
 1861             "text": text,
 1862         })
 1863 
 1864     def __getCompletionList(self, text, completer, completions):
 1865         """
 1866         Private method to create a completions list.
 1867         
 1868         @param text text to complete (string)
 1869         @param completer completer methode
 1870         @param completions set where to add new completions strings (set)
 1871         """
 1872         state = 0
 1873         try:
 1874             comp = completer(text, state)
 1875         except Exception:
 1876             comp = None
 1877         while comp is not None:
 1878             completions.add(comp)
 1879             state += 1
 1880             try:
 1881                 comp = completer(text, state)
 1882             except Exception:
 1883                 comp = None
 1884 
 1885     def startDebugger(self, filename=None, host=None, port=None,
 1886                       enableTrace=True, exceptions=True, tracePython=False,
 1887                       redirect=True):
 1888         """
 1889         Public method used to start the remote debugger.
 1890         
 1891         @param filename the program to be debugged (string)
 1892         @param host hostname of the debug server (string)
 1893         @param port portnumber of the debug server (int)
 1894         @param enableTrace flag to enable the tracing function (boolean)
 1895         @param exceptions flag to enable exception reporting of the IDE
 1896             (boolean)
 1897         @param tracePython flag to enable tracing into the Python library
 1898             (boolean)
 1899         @param redirect flag indicating redirection of stdin, stdout and
 1900             stderr (boolean)
 1901         """
 1902         global debugClient
 1903         if host is None:
 1904             host = os.getenv('ERICHOST', 'localhost')
 1905         if port is None:
 1906             port = os.getenv('ERICPORT', 42424)
 1907         
 1908         remoteAddress = self.__resolveHost(host)
 1909         self.connectDebugger(port, remoteAddress, redirect)
 1910         if filename is not None:
 1911             self.running = os.path.abspath(filename)
 1912         else:
 1913             try:
 1914                 self.running = os.path.abspath(sys.argv[0])
 1915             except IndexError:
 1916                 self.running = None
 1917         if self.running:
 1918             self.__setCoding(self.running)
 1919         self.passive = True
 1920         self.sendPassiveStartup(self.running, exceptions)
 1921         self.__interact()
 1922         
 1923         # setup the debugger variables
 1924         self._fncache = {}
 1925         self.dircache = []
 1926         self.debugging = True
 1927         
 1928         self.attachThread(mainThread=True)
 1929         self.mainThread.tracePythonLibs(tracePython)
 1930         
 1931         # set the system exception handling function to ensure, that
 1932         # we report on all unhandled exceptions
 1933         sys.excepthook = self.__unhandled_exception
 1934         self.__interceptSignals()
 1935         
 1936         # now start debugging
 1937         if enableTrace:
 1938             self.mainThread.set_trace()
 1939     
 1940     def startProgInDebugger(self, progargs, wd='', host=None,
 1941                             port=None, exceptions=True, tracePython=False,
 1942                             redirect=True):
 1943         """
 1944         Public method used to start the remote debugger.
 1945         
 1946         @param progargs commandline for the program to be debugged
 1947             (list of strings)
 1948         @param wd working directory for the program execution (string)
 1949         @param host hostname of the debug server (string)
 1950         @param port portnumber of the debug server (int)
 1951         @param exceptions flag to enable exception reporting of the IDE
 1952             (boolean)
 1953         @param tracePython flag to enable tracing into the Python library
 1954             (boolean)
 1955         @param redirect flag indicating redirection of stdin, stdout and
 1956             stderr (boolean)
 1957         """
 1958         if host is None:
 1959             host = os.getenv('ERICHOST', 'localhost')
 1960         if port is None:
 1961             port = os.getenv('ERICPORT', 42424)
 1962         
 1963         remoteAddress = self.__resolveHost(host)
 1964         self.connectDebugger(port, remoteAddress, redirect)
 1965         
 1966         self._fncache = {}
 1967         self.dircache = []
 1968         sys.argv = progargs[:]
 1969         sys.argv[0] = os.path.abspath(sys.argv[0])
 1970         sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
 1971         if wd == '':
 1972             os.chdir(sys.path[1])
 1973         else:
 1974             os.chdir(wd)
 1975         self.running = sys.argv[0]
 1976         self.__setCoding(self.running)
 1977         self.debugging = True
 1978         
 1979         self.passive = True
 1980         self.sendPassiveStartup(self.running, exceptions)
 1981         self.__interact()
 1982         
 1983         self.attachThread(mainThread=True)
 1984         self.mainThread.tracePythonLibs(tracePython)
 1985         
 1986         # set the system exception handling function to ensure, that
 1987         # we report on all unhandled exceptions
 1988         sys.excepthook = self.__unhandled_exception
 1989         self.__interceptSignals()
 1990         
 1991         # This will eventually enter a local event loop.
 1992         self.debugMod.__dict__['__file__'] = self.running
 1993         sys.modules['__main__'] = self.debugMod
 1994         code = self.__compileFileSource(self.running)
 1995         if code:
 1996             res = self.mainThread.run(code, self.debugMod.__dict__, debug=True)
 1997             self.progTerminated(res)
 1998         else:
 1999             self.progTerminated(42)     # should not happen
 2000 
 2001     def run_call(self, scriptname, func, *args):
 2002         """
 2003         Public method used to start the remote debugger and call a function.
 2004         
 2005         @param scriptname name of the script to be debugged (string)
 2006         @param func function to be called
 2007         @param *args arguments being passed to func
 2008         @return result of the function call
 2009         """
 2010         self.startDebugger(scriptname, enableTrace=False)
 2011         res = self.mainThread.runcall(func, *args)
 2012         self.progTerminated(res)
 2013         return res
 2014     
 2015     def __resolveHost(self, host):
 2016         """
 2017         Private method to resolve a hostname to an IP address.
 2018         
 2019         @param host hostname of the debug server (string)
 2020         @return IP address (string)
 2021         """
 2022         try:
 2023             host, version = host.split("@@")
 2024         except ValueError:
 2025             version = 'v4'
 2026         if version == 'v4':
 2027             family = socket.AF_INET
 2028         else:
 2029             family = socket.AF_INET6
 2030         
 2031         retryCount = 0
 2032         while retryCount < 10:
 2033             try:
 2034                 addrinfo = socket.getaddrinfo(
 2035                     host, None, family, socket.SOCK_STREAM)
 2036                 return addrinfo[0][4][0]
 2037             except Exception:
 2038                 retryCount += 1
 2039                 time.sleep(3)
 2040         return None
 2041     
 2042     def main(self):
 2043         """
 2044         Public method implementing the main method.
 2045         """
 2046         if '--' in sys.argv:
 2047             args = sys.argv[1:]
 2048             host = None
 2049             port = None
 2050             wd = ''
 2051             tracePython = False
 2052             exceptions = True
 2053             redirect = True
 2054             while args[0]:
 2055                 if args[0] == '-h':
 2056                     host = args[1]
 2057                     del args[0]
 2058                     del args[0]
 2059                 elif args[0] == '-p':
 2060                     port = int(args[1])
 2061                     del args[0]
 2062                     del args[0]
 2063                 elif args[0] == '-w':
 2064                     wd = args[1]
 2065                     del args[0]
 2066                     del args[0]
 2067                 elif args[0] == '-t':
 2068                     tracePython = True
 2069                     del args[0]
 2070                 elif args[0] == '-e':
 2071                     exceptions = False
 2072                     del args[0]
 2073                 elif args[0] == '-n':
 2074                     redirect = False
 2075                     del args[0]
 2076                 elif args[0] == '--no-encoding':
 2077                     self.noencoding = True
 2078                     del args[0]
 2079                 elif args[0] == '--fork-child':
 2080                     self.fork_auto = True
 2081                     self.fork_child = True
 2082                     del args[0]
 2083                 elif args[0] == '--fork-parent':
 2084                     self.fork_auto = True
 2085                     self.fork_child = False
 2086                     del args[0]
 2087                 elif args[0] == '--':
 2088                     del args[0]
 2089                     break
 2090                 else:   # unknown option
 2091                     del args[0]
 2092             if not args:
 2093                 print("No program given. Aborting!")
 2094                 # __IGNORE_WARNING_M801__
 2095             else:
 2096                 if not self.noencoding:
 2097                     self.__coding = self.defaultCoding
 2098                 self.startProgInDebugger(args, wd, host, port,
 2099                                          exceptions=exceptions,
 2100                                          tracePython=tracePython,
 2101                                          redirect=redirect)
 2102         else:
 2103             if sys.argv[1] == '--no-encoding':
 2104                 self.noencoding = True
 2105                 del sys.argv[1]
 2106             
 2107             if sys.argv[1] == '':
 2108                 del sys.argv[1]
 2109             
 2110             try:
 2111                 port = int(sys.argv[1])
 2112             except (ValueError, IndexError):
 2113                 port = -1
 2114             
 2115             if sys.argv[2] == "True":
 2116                 redirect = True
 2117             elif sys.argv[2] == "False":
 2118                 redirect = False
 2119             else:
 2120                 try:
 2121                     redirect = int(sys.argv[2])
 2122                 except (ValueError, IndexError):
 2123                     redirect = True
 2124             
 2125             ipOrHost = sys.argv[3]
 2126             if ':' in ipOrHost:
 2127                 # IPv6 address
 2128                 remoteAddress = ipOrHost
 2129             elif ipOrHost[0] in '0123456789':
 2130                 # IPv4 address
 2131                 remoteAddress = ipOrHost
 2132             else:
 2133                 remoteAddress = self.__resolveHost(ipOrHost)
 2134             
 2135             sys.argv = ['']
 2136             if '' not in sys.path:
 2137                 sys.path.insert(0, '')
 2138             
 2139             if port >= 0:
 2140                 if not self.noencoding:
 2141                     self.__coding = self.defaultCoding
 2142                 self.connectDebugger(port, remoteAddress, redirect)
 2143                 self.__interact()
 2144             else:
 2145                 print("No network port given. Aborting...")
 2146                 # __IGNORE_WARNING_M801__
 2147         
 2148     def fork(self):
 2149         """
 2150         Public method implementing a fork routine deciding which branch
 2151         to follow.
 2152         
 2153         @return process ID (integer)
 2154         """
 2155         if not self.fork_auto:
 2156             self.sendJsonCommand("RequestForkTo", {})
 2157             self.eventLoop(True)
 2158         pid = DebugClientOrigFork()
 2159         if pid == 0:
 2160             # child
 2161             if not self.fork_child:
 2162                 sys.settrace(None)
 2163                 sys.setprofile(None)
 2164                 self.sessionClose(False)
 2165         else:
 2166             # parent
 2167             if self.fork_child:
 2168                 sys.settrace(None)
 2169                 sys.setprofile(None)
 2170                 self.sessionClose(False)
 2171         return pid
 2172         
 2173     def close(self, fd):
 2174         """
 2175         Public method implementing a close method as a replacement for
 2176         os.close().
 2177         
 2178         It prevents the debugger connections from being closed.
 2179         
 2180         @param fd file descriptor to be closed (integer)
 2181         """
 2182         if fd in [self.readstream.fileno(), self.writestream.fileno(),
 2183                   self.errorstream.fileno()]:
 2184             return
 2185         
 2186         DebugClientOrigClose(fd)
 2187         
 2188     def __getSysPath(self, firstEntry):
 2189         """
 2190         Private slot to calculate a path list including the PYTHONPATH
 2191         environment variable.
 2192         
 2193         @param firstEntry entry to be put first in sys.path (string)
 2194         @return path list for use as sys.path (list of strings)
 2195         """
 2196         sysPath = [path for path in os.environ.get("PYTHONPATH", "")
 2197                    .split(os.pathsep)
 2198                    if path not in sys.path] + sys.path[:]
 2199         if "" in sysPath:
 2200             sysPath.remove("")
 2201         sysPath.insert(0, firstEntry)
 2202         sysPath.insert(0, '')
 2203         return sysPath