eric6/Debugger/DebuggerInterfacePython.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7986
2971d5d19951
child 8142
43248bafe9b2
equal deleted inserted replaced
7991:866adc8c315b 8043:0acf98cd089a
12 import logging 12 import logging
13 13
14 from PyQt5.QtCore import ( 14 from PyQt5.QtCore import (
15 QObject, QProcess, QProcessEnvironment, QTimer 15 QObject, QProcess, QProcessEnvironment, QTimer
16 ) 16 )
17 from PyQt5.QtWidgets import QInputDialog
18 17
19 from E5Gui.E5Application import e5App 18 from E5Gui.E5Application import e5App
20 from E5Gui import E5MessageBox 19 from E5Gui import E5MessageBox
21 20
22 from . import DebugClientCapabilities 21 from . import DebugClientCapabilities
46 """ 45 """
47 super(DebuggerInterfacePython, self).__init__() 46 super(DebuggerInterfacePython, self).__init__()
48 47
49 self.__isNetworked = True 48 self.__isNetworked = True
50 self.__autoContinue = False 49 self.__autoContinue = False
50 self.__autoContinued = []
51 self.__isStepCommand = False
51 52
52 self.debugServer = debugServer 53 self.debugServer = debugServer
53 self.passive = passive 54 self.passive = passive
54 self.process = None 55 self.process = None
55 self.__startedVenv = "" 56 self.__startedVenv = ""
56 57
57 self.qsock = None
58 self.queue = [] 58 self.queue = []
59 self.__master = None
60 self.__connections = {}
61 self.__pendingConnections = []
59 62
60 # set default values for capabilities of clients 63 # set default values for capabilities of clients
61 self.clientCapabilities = ClientDefaultCapabilities 64 self.clientCapabilities = ClientDefaultCapabilities
62 65
63 # set translation function 66 # set translation function
76 else: 79 else:
77 self.translate = self.__identityTranslation 80 self.translate = self.__identityTranslation
78 81
79 # attribute to remember the name of the executed script 82 # attribute to remember the name of the executed script
80 self.__scriptName = "" 83 self.__scriptName = ""
81 84
82 def __identityTranslation(self, fn, remote2local=True): 85 def __identityTranslation(self, fn, remote2local=True):
83 """ 86 """
84 Private method to perform the identity path translation. 87 Private method to perform the identity path translation.
85 88
86 @param fn filename to be translated (string) 89 @param fn filename to be translated
90 @type str
87 @param remote2local flag indicating the direction of translation 91 @param remote2local flag indicating the direction of translation
88 (False = local to remote, True = remote to local [default]) 92 (False = local to remote, True = remote to local [default])
89 @return translated filename (string) 93 @type bool
94 @return translated filename
95 @rtype str
90 """ 96 """
91 return fn 97 return fn
92 98
93 def __remoteTranslation(self, fn, remote2local=True): 99 def __remoteTranslation(self, fn, remote2local=True):
94 """ 100 """
95 Private method to perform the path translation. 101 Private method to perform the path translation.
96 102
97 @param fn filename to be translated (string) 103 @param fn filename to be translated
104 @type str
98 @param remote2local flag indicating the direction of translation 105 @param remote2local flag indicating the direction of translation
99 (False = local to remote, True = remote to local [default]) 106 (False = local to remote, True = remote to local [default])
100 @return translated filename (string) 107 @type bool
108 @return translated filename
109 @rtype str
101 """ 110 """
102 if remote2local: 111 if remote2local:
103 path = fn.replace(self.translateRemote, self.translateLocal) 112 path = fn.replace(self.translateRemote, self.translateLocal)
104 if self.translateLocalWindows: 113 if self.translateLocalWindows:
105 path = path.replace("/", "\\") 114 path = path.replace("/", "\\")
107 path = fn.replace(self.translateLocal, self.translateRemote) 116 path = fn.replace(self.translateLocal, self.translateRemote)
108 if not self.translateRemoteWindows: 117 if not self.translateRemoteWindows:
109 path = path.replace("\\", "/") 118 path = path.replace("\\", "/")
110 119
111 return path 120 return path
112 121
113 def __startProcess(self, program, arguments, environment=None, 122 def __startProcess(self, program, arguments, environment=None,
114 workingDir=None): 123 workingDir=None):
115 """ 124 """
116 Private method to start the debugger client process. 125 Private method to start the debugger client process.
117 126
138 proc.start(program, args) 147 proc.start(program, args)
139 if not proc.waitForStarted(10000): 148 if not proc.waitForStarted(10000):
140 proc = None 149 proc = None
141 150
142 return proc 151 return proc
143 152
144 def startRemote(self, port, runInConsole, venvName, originalPathString, 153 def startRemote(self, port, runInConsole, venvName, originalPathString,
145 workingDir=None): 154 workingDir=None):
146 """ 155 """
147 Public method to start a remote Python interpreter. 156 Public method to start a remote Python interpreter.
148 157
193 "DebugClient.py") 202 "DebugClient.py")
194 203
195 redirect = str(Preferences.getDebugger("Python3Redirect")) 204 redirect = str(Preferences.getDebugger("Python3Redirect"))
196 noencoding = (Preferences.getDebugger("Python3NoEncoding") and 205 noencoding = (Preferences.getDebugger("Python3NoEncoding") and
197 '--no-encoding' or '') 206 '--no-encoding' or '')
207 multiprocessEnabled = (
208 '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled")
209 else ''
210 )
198 211
199 if Preferences.getDebugger("RemoteDbgEnabled"): 212 if Preferences.getDebugger("RemoteDbgEnabled"):
200 ipaddr = self.debugServer.getHostAddress(False) 213 ipaddr = self.debugServer.getHostAddress(False)
201 rexec = Preferences.getDebugger("RemoteExecution") 214 rexec = Preferences.getDebugger("RemoteExecution")
202 rhost = Preferences.getDebugger("RemoteHost") 215 rhost = Preferences.getDebugger("RemoteHost")
203 if rhost == "": 216 if rhost == "":
204 rhost = "localhost" 217 rhost = "localhost"
205 if rexec: 218 if rexec:
206 args = Utilities.parseOptionString(rexec) + [ 219 args = Utilities.parseOptionString(rexec) + [
207 rhost, interpreter, debugClient, noencoding, str(port), 220 rhost, interpreter, debugClient]
208 redirect, ipaddr] 221 if noencoding:
222 args.append(noencoding)
223 if multiprocessEnabled:
224 args.append(multiprocessEnabled)
225 args.extend([str(port), redirect, ipaddr])
209 if Utilities.isWindowsPlatform(): 226 if Utilities.isWindowsPlatform():
210 if not os.path.splitext(args[0])[1]: 227 if not os.path.splitext(args[0])[1]:
211 for ext in [".exe", ".com", ".cmd", ".bat"]: 228 for ext in [".exe", ".com", ".cmd", ".bat"]:
212 prog = Utilities.getExecutablePath(args[0] + ext) 229 prog = Utilities.getExecutablePath(args[0] + ext)
213 if prog: 230 if prog:
268 ipaddr = self.debugServer.getHostAddress(True) 285 ipaddr = self.debugServer.getHostAddress(True)
269 if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"): 286 if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"):
270 ccmd = Preferences.getDebugger("ConsoleDbgCommand") 287 ccmd = Preferences.getDebugger("ConsoleDbgCommand")
271 if ccmd: 288 if ccmd:
272 args = Utilities.parseOptionString(ccmd) + [ 289 args = Utilities.parseOptionString(ccmd) + [
273 interpreter, os.path.abspath(debugClient), noencoding, 290 interpreter, os.path.abspath(debugClient)]
274 str(port), '0', ipaddr] 291 if noencoding:
292 args.append(noencoding)
293 if multiprocessEnabled:
294 args.append(multiprocessEnabled)
295 args.extend([str(port), '0', ipaddr])
275 args[0] = Utilities.getExecutablePath(args[0]) 296 args[0] = Utilities.getExecutablePath(args[0])
276 process = self.__startProcess(args[0], args[1:], clientEnv, 297 process = self.__startProcess(args[0], args[1:], clientEnv,
277 workingDir=workingDir) 298 workingDir=workingDir)
278 if process is None: 299 if process is None:
279 E5MessageBox.critical( 300 E5MessageBox.critical(
282 self.tr( 303 self.tr(
283 """<p>The debugger backend could not be""" 304 """<p>The debugger backend could not be"""
284 """ started.</p>""")) 305 """ started.</p>"""))
285 return process, self.__isNetworked, interpreter 306 return process, self.__isNetworked, interpreter
286 307
287 process = self.__startProcess( 308 args = [debugClient]
288 interpreter, 309 if noencoding:
289 [debugClient, noencoding, str(port), redirect, ipaddr], 310 args.append(noencoding)
290 clientEnv, 311 if multiprocessEnabled:
291 workingDir=workingDir) 312 args.append(multiprocessEnabled)
313 args.extend([str(port), redirect, ipaddr])
314 process = self.__startProcess(interpreter, args, clientEnv,
315 workingDir=workingDir)
292 if process is None: 316 if process is None:
293 self.__startedVenv = "" 317 self.__startedVenv = ""
294 E5MessageBox.critical( 318 E5MessageBox.critical(
295 None, 319 None,
296 self.tr("Start Debugger"), 320 self.tr("Start Debugger"),
298 """<p>The debugger backend could not be started.</p>""")) 322 """<p>The debugger backend could not be started.</p>"""))
299 else: 323 else:
300 self.__startedVenv = venvName 324 self.__startedVenv = venvName
301 325
302 return process, self.__isNetworked, interpreter 326 return process, self.__isNetworked, interpreter
303 327
304 def startRemoteForProject(self, port, runInConsole, venvName, 328 def startRemoteForProject(self, port, runInConsole, venvName,
305 originalPathString, workingDir=None): 329 originalPathString, workingDir=None):
306 """ 330 """
307 Public method to start a remote Python interpreter for a project. 331 Public method to start a remote Python interpreter for a project.
308 332
335 if project.getProjectLanguage() == "Python3": 359 if project.getProjectLanguage() == "Python3":
336 venvName = Preferences.getDebugger("Python3VirtualEnv") 360 venvName = Preferences.getDebugger("Python3VirtualEnv")
337 361
338 redirect = str(project.getDebugProperty("REDIRECT")) 362 redirect = str(project.getDebugProperty("REDIRECT"))
339 noencoding = ( 363 noencoding = (
340 project.getDebugProperty("NOENCODING") and '--no-encoding' or '') 364 '--no-encoding' if project.getDebugProperty("NOENCODING") else ''
365 )
366 multiprocessEnabled = (
367 '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled")
368 else ''
369 )
341 370
342 venvManager = e5App().getObject("VirtualEnvManager") 371 venvManager = e5App().getObject("VirtualEnvManager")
343 interpreter = venvManager.getVirtualenvInterpreter(venvName) 372 interpreter = venvManager.getVirtualenvInterpreter(venvName)
344 execPath = venvManager.getVirtualenvExecPath(venvName) 373 execPath = venvManager.getVirtualenvExecPath(venvName)
345 if ( 374 if (
362 rhost = project.getDebugProperty("REMOTEHOST") 391 rhost = project.getDebugProperty("REMOTEHOST")
363 if rhost == "": 392 if rhost == "":
364 rhost = "localhost" 393 rhost = "localhost"
365 if rexec: 394 if rexec:
366 args = Utilities.parseOptionString(rexec) + [ 395 args = Utilities.parseOptionString(rexec) + [
367 rhost, interpreter, debugClient, noencoding, str(port), 396 rhost, interpreter, debugClient]
368 redirect, ipaddr] 397 if noencoding:
398 args.append(noencoding)
399 if multiprocessEnabled:
400 args.append(multiprocessEnabled)
401 args.extend([str(port), redirect, ipaddr])
369 if Utilities.isWindowsPlatform(): 402 if Utilities.isWindowsPlatform():
370 if not os.path.splitext(args[0])[1]: 403 if not os.path.splitext(args[0])[1]:
371 for ext in [".exe", ".com", ".cmd", ".bat"]: 404 for ext in [".exe", ".com", ".cmd", ".bat"]:
372 prog = Utilities.getExecutablePath(args[0] + ext) 405 prog = Utilities.getExecutablePath(args[0] + ext)
373 if prog: 406 if prog:
396 self.translate = self.__identityTranslation 429 self.translate = self.__identityTranslation
397 return process, self.__isNetworked, "" 430 return process, self.__isNetworked, ""
398 else: 431 else:
399 # remote shell command is missing 432 # remote shell command is missing
400 return None, self.__isNetworked, "" 433 return None, self.__isNetworked, ""
401 434
402 # set translation function 435 # set translation function
403 self.translate = self.__identityTranslation 436 self.translate = self.__identityTranslation
404 437
405 # setup the environment for the debugger 438 # setup the environment for the debugger
406 if project.getDebugProperty("ENVIRONMENTOVERRIDE"): 439 if project.getDebugProperty("ENVIRONMENTOVERRIDE"):
430 if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"): 463 if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"):
431 ccmd = (project.getDebugProperty("CONSOLECOMMAND") or 464 ccmd = (project.getDebugProperty("CONSOLECOMMAND") or
432 Preferences.getDebugger("ConsoleDbgCommand")) 465 Preferences.getDebugger("ConsoleDbgCommand"))
433 if ccmd: 466 if ccmd:
434 args = Utilities.parseOptionString(ccmd) + [ 467 args = Utilities.parseOptionString(ccmd) + [
435 interpreter, os.path.abspath(debugClient), noencoding, 468 interpreter, os.path.abspath(debugClient)]
436 str(port), '0', ipaddr] 469 if noencoding:
470 args.append(noencoding)
471 if multiprocessEnabled:
472 args.append(multiprocessEnabled)
473 args.extend([str(port), '0', ipaddr])
437 args[0] = Utilities.getExecutablePath(args[0]) 474 args[0] = Utilities.getExecutablePath(args[0])
438 process = self.__startProcess(args[0], args[1:], clientEnv, 475 process = self.__startProcess(args[0], args[1:], clientEnv,
439 workingDir=workingDir) 476 workingDir=workingDir)
440 if process is None: 477 if process is None:
441 E5MessageBox.critical( 478 E5MessageBox.critical(
444 self.tr( 481 self.tr(
445 """<p>The debugger backend could not be""" 482 """<p>The debugger backend could not be"""
446 """ started.</p>""")) 483 """ started.</p>"""))
447 return process, self.__isNetworked, interpreter 484 return process, self.__isNetworked, interpreter
448 485
449 process = self.__startProcess( 486 args = [debugClient]
450 interpreter, 487 if noencoding:
451 [debugClient, noencoding, str(port), redirect, ipaddr], 488 args.append(noencoding)
452 clientEnv, 489 if multiprocessEnabled:
453 workingDir=workingDir) 490 args.append(multiprocessEnabled)
491 args.extend([str(port), redirect, ipaddr])
492 process = self.__startProcess(interpreter, args, clientEnv,
493 workingDir=workingDir)
454 if process is None: 494 if process is None:
455 self.__startedVenv = "" 495 self.__startedVenv = ""
456 E5MessageBox.critical( 496 E5MessageBox.critical(
457 None, 497 None,
458 self.tr("Start Debugger"), 498 self.tr("Start Debugger"),
460 """<p>The debugger backend could not be started.</p>""")) 500 """<p>The debugger backend could not be started.</p>"""))
461 else: 501 else:
462 self.__startedVenv = venvName 502 self.__startedVenv = venvName
463 503
464 return process, self.__isNetworked, interpreter 504 return process, self.__isNetworked, interpreter
465 505
466 def getClientCapabilities(self): 506 def getClientCapabilities(self):
467 """ 507 """
468 Public method to retrieve the debug clients capabilities. 508 Public method to retrieve the debug clients capabilities.
469 509
470 @return debug client capabilities (integer) 510 @return debug client capabilities
511 @rtype int
471 """ 512 """
472 return self.clientCapabilities 513 return self.clientCapabilities
473 514
474 def newConnection(self, sock): 515 def newConnection(self, sock):
475 """ 516 """
476 Public slot to handle a new connection. 517 Public slot to handle a new connection.
477 518
478 @param sock reference to the socket object (QTcpSocket) 519 @param sock reference to the socket object
479 @return flag indicating success (boolean) 520 @type QTcpSocket
480 """ 521 @return flag indicating success
481 # If we already have a connection, refuse this one. It will be closed 522 @rtype bool
482 # automatically. 523 """
483 if self.qsock is not None: 524 self.__pendingConnections.append(sock)
484 return False 525
485 526 sock.readyRead.connect(lambda: self.__parseClientLine(sock))
486 sock.disconnected.connect(self.debugServer.startClient) 527 sock.disconnected.connect(lambda: self.__socketDisconnected(sock))
487 sock.readyRead.connect(self.__parseClientLine) 528
488
489 self.qsock = sock
490
491 # Get the remote clients capabilities
492 self.remoteCapabilities()
493 return True 529 return True
494 530
495 def flush(self): 531 def __assignDebuggerId(self, sock, debuggerId):
496 """ 532 """
497 Public slot to flush the queue. 533 Private method to set the debugger id for a recent debugger connection
498 """ 534 attempt.
499 if self.qsock is not None: 535
536 @param sock reference to the socket object
537 @type QTcpSocket
538 @param debuggerId id of the connected debug client
539 @type str
540 """
541 if sock in self.__pendingConnections:
542 self.__connections[debuggerId] = sock
543 self.__pendingConnections.remove(sock)
544
545 if self.__master is None:
546 self.__master = debuggerId
547 # Get the remote clients capabilities
548 self.remoteCapabilities(debuggerId)
549
550 self.debugServer.signalClientDebuggerId(debuggerId)
551
552 if debuggerId == self.__master:
553 self.__flush()
554 self.debugServer.masterClientConnected()
555
556 self.debugServer.initializeClient(debuggerId)
557
558 # perform auto-continue except for master
559 if (
560 debuggerId != self.__master and
561 self.__autoContinue and
562 not self.__isStepCommand
563 ):
564 QTimer.singleShot(
565 0, lambda: self.remoteContinue(debuggerId))
566
567 def __socketDisconnected(self, sock):
568 """
569 Private slot handling a socket disconnecting.
570
571 @param sock reference to the disconnected socket
572 @type QTcpSocket
573 """
574 for debuggerId in self.__connections:
575 if self.__connections[debuggerId] is sock:
576 del self.__connections[debuggerId]
577 if debuggerId == self.__master:
578 self.__master = None
579 if debuggerId in self.__autoContinued:
580 self.__autoContinued.remove(debuggerId)
581 self.debugServer.signalClientDisconnected(debuggerId)
582 break
583 else:
584 if sock in self.__pendingConnections:
585 self.__pendingConnections.remove(sock)
586
587 if not self.__connections:
588 # no active connections anymore
589 try:
590 self.debugServer.signalLastClientExited()
591 except RuntimeError:
592 # debug server object might have been deleted already
593 # ignore this
594 pass
595 self.__autoContinued.clear()
596 self.debugServer.startClient()
597
598 def getDebuggerIds(self):
599 """
600 Public method to return the IDs of the connected debugger backends.
601
602 @return list of connected debugger backend IDs
603 @rtype list of str
604 """
605 return sorted(self.__connections.keys())
606
607 def __flush(self):
608 """
609 Private slot to flush the queue.
610 """
611 if self.__master:
500 # Send commands that were waiting for the connection. 612 # Send commands that were waiting for the connection.
501 for cmd in self.queue: 613 for cmd in self.queue:
502 self.__writeJsonCommandToSocket(cmd) 614 self.__writeJsonCommandToSocket(
503 615 cmd, self.__connections[self.__master])
504 self.queue = [] 616
617 self.queue = []
505 618
506 def shutdown(self): 619 def shutdown(self):
507 """ 620 """
508 Public method to cleanly shut down. 621 Public method to cleanly shut down.
509 622
510 It closes our socket and shuts down 623 It closes our sockets and shuts down the debug clients.
511 the debug client. (Needed on Win OS) 624 (Needed on Win OS)
512 """ 625 """
513 if self.qsock is None: 626 if not self.__master:
514 return 627 return
515 628
629 while self.__connections:
630 debuggerId, sock = self.__connections.popitem()
631 self.__shutdownSocket(sock)
632
633 while self.__pendingConnections:
634 sock = self.__pendingConnections.pop()
635 self.__shutdownSocket(sock)
636
637 # reinitialize
638 self.queue = []
639
640 self.__master = None
641
642 def __shutdownSocket(self, sock):
643 """
644 Private slot to shut down a socket.
645
646 @param sock reference to the socket
647 @type QTcpSocket
648 """
516 # do not want any slots called during shutdown 649 # do not want any slots called during shutdown
517 self.qsock.disconnected.disconnect(self.debugServer.startClient) 650 sock.readyRead.disconnect()
518 self.qsock.readyRead.disconnect(self.__parseClientLine) 651 sock.disconnected.disconnect()
519 652
520 # close down socket, and shut down client as well. 653 # close down socket, and shut down client as well.
521 self.__sendJsonCommand("RequestShutdown", {}) 654 self.__sendJsonCommand("RequestShutdown", {}, sock=sock)
522 self.qsock.flush() 655 sock.flush()
523 self.qsock.close() 656 sock.close()
524 657
525 # reinitialize 658 sock.setParent(None)
526 self.qsock = None 659 sock.deleteLater()
527 self.queue = [] 660 del sock
528 661
529 def isConnected(self): 662 def isConnected(self):
530 """ 663 """
531 Public method to test, if a debug client has connected. 664 Public method to test, if a debug client has connected.
532 665
533 @return flag indicating the connection status (boolean) 666 @return flag indicating the connection status
534 """ 667 @rtype bool
535 return self.qsock is not None 668 """
669 return bool(self.__connections)
536 670
537 def remoteEnvironment(self, env): 671 def remoteEnvironment(self, env):
538 """ 672 """
539 Public method to set the environment for a program to debug, run, ... 673 Public method to set the environment for a program to debug, run, ...
540 674
541 @param env environment settings (dictionary) 675 @param env environment settings
542 """ 676 @type dict
543 self.__sendJsonCommand("RequestEnvironment", {"environment": env}) 677 """
678 if self.__master:
679 self.__sendJsonCommand("RequestEnvironment", {"environment": env},
680 self.__master)
544 681
545 def remoteLoad(self, fn, argv, wd, traceInterpreter=False, 682 def remoteLoad(self, fn, argv, wd, traceInterpreter=False,
546 autoContinue=True, autoFork=False, forkChild=False): 683 autoContinue=True, enableMultiprocess=False):
547 """ 684 """
548 Public method to load a new program to debug. 685 Public method to load a new program to debug.
549 686
550 @param fn the filename to debug (string) 687 @param fn the filename to debug
551 @param argv the commandline arguments to pass to the program (string) 688 @type str
552 @param wd the working directory for the program (string) 689 @param argv the commandline arguments to pass to the program
553 @keyparam traceInterpreter flag indicating if the interpreter library 690 @type str
554 should be traced as well (boolean) 691 @param wd the working directory for the program
555 @keyparam autoContinue flag indicating, that the debugger should not 692 @type str
556 stop at the first executable line (boolean) 693 @param traceInterpreter flag indicating if the interpreter library
557 @keyparam autoFork flag indicating the automatic fork mode (boolean) 694 should be traced as well
558 @keyparam forkChild flag indicating to debug the child after forking 695 @type bool
559 (boolean) 696 @param autoContinue flag indicating, that the debugger should not
697 stop at the first executable line
698 @type bool
699 @param enableMultiprocess flag indicating to perform multiprocess
700 debugging
701 @type bool
560 """ 702 """
561 self.__autoContinue = autoContinue 703 self.__autoContinue = autoContinue
562 self.__scriptName = os.path.abspath(fn) 704 self.__scriptName = os.path.abspath(fn)
705 self.__isStepCommand = False
563 706
564 wd = self.translate(wd, False) 707 wd = self.translate(wd, False)
565 fn = self.translate(os.path.abspath(fn), False) 708 fn = self.translate(os.path.abspath(fn), False)
566 self.__sendJsonCommand("RequestLoad", { 709 self.__sendJsonCommand("RequestLoad", {
567 "workdir": wd, 710 "workdir": wd,
568 "filename": fn, 711 "filename": fn,
569 "argv": Utilities.parseOptionString(argv), 712 "argv": Utilities.parseOptionString(argv),
570 "traceInterpreter": traceInterpreter, 713 "traceInterpreter": traceInterpreter,
571 "autofork": autoFork, 714 "multiprocess": enableMultiprocess,
572 "forkChild": forkChild, 715 }, self.__master)
573 }) 716
574 717 def remoteRun(self, fn, argv, wd):
575 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False):
576 """ 718 """
577 Public method to load a new program to run. 719 Public method to load a new program to run.
578 720
579 @param fn the filename to run (string) 721 @param fn the filename to run
580 @param argv the commandline arguments to pass to the program (string) 722 @type str
581 @param wd the working directory for the program (string) 723 @param argv the commandline arguments to pass to the program
582 @keyparam autoFork flag indicating the automatic fork mode (boolean) 724 @type str
583 @keyparam forkChild flag indicating to debug the child after forking 725 @param wd the working directory for the program
584 (boolean) 726 @type str
585 """ 727 """
586 self.__scriptName = os.path.abspath(fn) 728 self.__scriptName = os.path.abspath(fn)
587 729
588 wd = self.translate(wd, False) 730 wd = self.translate(wd, False)
589 fn = self.translate(os.path.abspath(fn), False) 731 fn = self.translate(os.path.abspath(fn), False)
590 self.__sendJsonCommand("RequestRun", { 732 self.__sendJsonCommand("RequestRun", {
591 "workdir": wd, 733 "workdir": wd,
592 "filename": fn, 734 "filename": fn,
593 "argv": Utilities.parseOptionString(argv), 735 "argv": Utilities.parseOptionString(argv),
594 "autofork": autoFork, 736 }, self.__master)
595 "forkChild": forkChild,
596 })
597 737
598 def remoteCoverage(self, fn, argv, wd, erase=False): 738 def remoteCoverage(self, fn, argv, wd, erase=False):
599 """ 739 """
600 Public method to load a new program to collect coverage data. 740 Public method to load a new program to collect coverage data.
601 741
602 @param fn the filename to run (string) 742 @param fn the filename to run
603 @param argv the commandline arguments to pass to the program (string) 743 @type str
604 @param wd the working directory for the program (string) 744 @param argv the commandline arguments to pass to the program
605 @keyparam erase flag indicating that coverage info should be 745 @type str
606 cleared first (boolean) 746 @param wd the working directory for the program
747 @type str
748 @param erase flag indicating that coverage info should be
749 cleared first
750 @type bool
607 """ 751 """
608 self.__scriptName = os.path.abspath(fn) 752 self.__scriptName = os.path.abspath(fn)
609 753
610 wd = self.translate(wd, False) 754 wd = self.translate(wd, False)
611 fn = self.translate(os.path.abspath(fn), False) 755 fn = self.translate(os.path.abspath(fn), False)
612 self.__sendJsonCommand("RequestCoverage", { 756 self.__sendJsonCommand("RequestCoverage", {
613 "workdir": wd, 757 "workdir": wd,
614 "filename": fn, 758 "filename": fn,
615 "argv": Utilities.parseOptionString(argv), 759 "argv": Utilities.parseOptionString(argv),
616 "erase": erase, 760 "erase": erase,
617 }) 761 }, self.__master)
618 762
619 def remoteProfile(self, fn, argv, wd, erase=False): 763 def remoteProfile(self, fn, argv, wd, erase=False):
620 """ 764 """
621 Public method to load a new program to collect profiling data. 765 Public method to load a new program to collect profiling data.
622 766
623 @param fn the filename to run (string) 767 @param fn the filename to run
624 @param argv the commandline arguments to pass to the program (string) 768 @type str
625 @param wd the working directory for the program (string) 769 @param argv the commandline arguments to pass to the program
626 @keyparam erase flag indicating that timing info should be cleared 770 @type str
627 first (boolean) 771 @param wd the working directory for the program
772 @type str
773 @param erase flag indicating that timing info should be cleared
774 first
775 @type bool
628 """ 776 """
629 self.__scriptName = os.path.abspath(fn) 777 self.__scriptName = os.path.abspath(fn)
630 778
631 wd = self.translate(wd, False) 779 wd = self.translate(wd, False)
632 fn = self.translate(os.path.abspath(fn), False) 780 fn = self.translate(os.path.abspath(fn), False)
633 self.__sendJsonCommand("RequestProfile", { 781 self.__sendJsonCommand("RequestProfile", {
634 "workdir": wd, 782 "workdir": wd,
635 "filename": fn, 783 "filename": fn,
636 "argv": Utilities.parseOptionString(argv), 784 "argv": Utilities.parseOptionString(argv),
637 "erase": erase, 785 "erase": erase,
638 }) 786 }, self.__master)
639 787
640 def remoteStatement(self, stmt): 788 def remoteStatement(self, debuggerId, stmt):
641 """ 789 """
642 Public method to execute a Python statement. 790 Public method to execute a Python statement.
643 791
644 @param stmt the Python statement to execute (string). It 792 @param debuggerId ID of the debugger backend
645 should not have a trailing newline. 793 @type str
794 @param stmt the Python statement to execute.
795 @type str
646 """ 796 """
647 self.__sendJsonCommand("ExecuteStatement", { 797 self.__sendJsonCommand("ExecuteStatement", {
648 "statement": stmt, 798 "statement": stmt,
649 }) 799 }, debuggerId)
650 800
651 def remoteStep(self): 801 def remoteStep(self, debuggerId):
652 """ 802 """
653 Public method to single step the debugged program. 803 Public method to single step the debugged program.
654 """ 804
655 self.__sendJsonCommand("RequestStep", {}) 805 @param debuggerId ID of the debugger backend
656 806 @type str
657 def remoteStepOver(self): 807 """
808 self.__isStepCommand = True
809 self.__sendJsonCommand("RequestStep", {}, debuggerId)
810
811 def remoteStepOver(self, debuggerId):
658 """ 812 """
659 Public method to step over the debugged program. 813 Public method to step over the debugged program.
660 """ 814
661 self.__sendJsonCommand("RequestStepOver", {}) 815 @param debuggerId ID of the debugger backend
662 816 @type str
663 def remoteStepOut(self): 817 """
818 self.__isStepCommand = True
819 self.__sendJsonCommand("RequestStepOver", {}, debuggerId)
820
821 def remoteStepOut(self, debuggerId):
664 """ 822 """
665 Public method to step out the debugged program. 823 Public method to step out the debugged program.
666 """ 824
667 self.__sendJsonCommand("RequestStepOut", {}) 825 @param debuggerId ID of the debugger backend
668 826 @type str
669 def remoteStepQuit(self): 827 """
828 self.__isStepCommand = True
829 self.__sendJsonCommand("RequestStepOut", {}, debuggerId)
830
831 def remoteStepQuit(self, debuggerId):
670 """ 832 """
671 Public method to stop the debugged program. 833 Public method to stop the debugged program.
672 """ 834
673 self.__sendJsonCommand("RequestStepQuit", {}) 835 @param debuggerId ID of the debugger backend
674 836 @type str
675 def remoteContinue(self, special=False): 837 """
838 self.__isStepCommand = True
839 self.__sendJsonCommand("RequestStepQuit", {}, debuggerId)
840
841 def remoteContinue(self, debuggerId, special=False):
676 """ 842 """
677 Public method to continue the debugged program. 843 Public method to continue the debugged program.
678 844
845 @param debuggerId ID of the debugger backend
846 @type str
679 @param special flag indicating a special continue operation 847 @param special flag indicating a special continue operation
680 """ 848 @type bool
849 """
850 self.__isStepCommand = False
681 self.__sendJsonCommand("RequestContinue", { 851 self.__sendJsonCommand("RequestContinue", {
682 "special": special, 852 "special": special,
683 }) 853 }, debuggerId)
684 854
685 def remoteMoveIP(self, line): 855 def remoteContinueUntil(self, debuggerId, line):
856 """
857 Public method to continue the debugged program to the given line
858 or until returning from the current frame.
859
860 @param debuggerId ID of the debugger backend
861 @type str
862 @param line the new line, where execution should be continued to
863 @type int
864 """
865 self.__isStepCommand = False
866 self.__sendJsonCommand("RequestContinueUntil", {
867 "newLine": line,
868 }, debuggerId)
869
870 def remoteMoveIP(self, debuggerId, line):
686 """ 871 """
687 Public method to move the instruction pointer to a different line. 872 Public method to move the instruction pointer to a different line.
688 873
874 @param debuggerId ID of the debugger backend
875 @type str
689 @param line the new line, where execution should be continued 876 @param line the new line, where execution should be continued
877 @type int
690 """ 878 """
691 self.__sendJsonCommand("RequestMoveIP", { 879 self.__sendJsonCommand("RequestMoveIP", {
692 "newLine": line, 880 "newLine": line,
693 }) 881 }, debuggerId)
694 882
695 def remoteBreakpoint(self, fn, line, setBreakpoint, cond=None, temp=False): 883 def remoteBreakpoint(self, debuggerId, fn, line, setBreakpoint, cond=None,
884 temp=False):
696 """ 885 """
697 Public method to set or clear a breakpoint. 886 Public method to set or clear a breakpoint.
698 887
699 @param fn filename the breakpoint belongs to (string) 888 @param debuggerId ID of the debugger backend
700 @param line linenumber of the breakpoint (int) 889 @type str
701 @param setBreakpoint flag indicating setting or resetting a 890 @param fn filename the breakpoint belongs to
702 breakpoint (boolean) 891 @type str
703 @param cond condition of the breakpoint (string) 892 @param line linenumber of the breakpoint
704 @param temp flag indicating a temporary breakpoint (boolean) 893 @type int
705 """ 894 @param setBreakpoint flag indicating setting or resetting a breakpoint
706 self.__sendJsonCommand("RequestBreakpoint", { 895 @type bool
707 "filename": self.translate(fn, False), 896 @param cond condition of the breakpoint
708 "line": line, 897 @type str
709 "temporary": temp, 898 @param temp flag indicating a temporary breakpoint
710 "setBreakpoint": setBreakpoint, 899 @type bool
711 "condition": cond, 900 """
712 }) 901 if debuggerId:
713 902 debuggerList = [debuggerId]
714 def remoteBreakpointEnable(self, fn, line, enable): 903 else:
904 debuggerList = list(self.__connections.keys())
905 for debuggerId in debuggerList:
906 self.__sendJsonCommand("RequestBreakpoint", {
907 "filename": self.translate(fn, False),
908 "line": line,
909 "temporary": temp,
910 "setBreakpoint": setBreakpoint,
911 "condition": cond,
912 }, debuggerId)
913
914 def remoteBreakpointEnable(self, debuggerId, fn, line, enable):
715 """ 915 """
716 Public method to enable or disable a breakpoint. 916 Public method to enable or disable a breakpoint.
717 917
718 @param fn filename the breakpoint belongs to (string) 918 @param debuggerId ID of the debugger backend
719 @param line linenumber of the breakpoint (int) 919 @type str
920 @param fn filename the breakpoint belongs to
921 @type str
922 @param line linenumber of the breakpoint
923 @type int
720 @param enable flag indicating enabling or disabling a breakpoint 924 @param enable flag indicating enabling or disabling a breakpoint
721 (boolean) 925 @type bool
722 """ 926 """
723 self.__sendJsonCommand("RequestBreakpointEnable", { 927 if debuggerId:
724 "filename": self.translate(fn, False), 928 debuggerList = [debuggerId]
725 "line": line, 929 else:
726 "enable": enable, 930 debuggerList = list(self.__connections.keys())
727 }) 931 for debuggerId in debuggerList:
728 932 self.__sendJsonCommand("RequestBreakpointEnable", {
729 def remoteBreakpointIgnore(self, fn, line, count): 933 "filename": self.translate(fn, False),
934 "line": line,
935 "enable": enable,
936 }, debuggerId)
937
938 def remoteBreakpointIgnore(self, debuggerId, fn, line, count):
730 """ 939 """
731 Public method to ignore a breakpoint the next couple of occurrences. 940 Public method to ignore a breakpoint the next couple of occurrences.
732 941
733 @param fn filename the breakpoint belongs to (string) 942 @param debuggerId ID of the debugger backend
734 @param line linenumber of the breakpoint (int) 943 @type str
735 @param count number of occurrences to ignore (int) 944 @param fn filename the breakpoint belongs to
736 """ 945 @type str
737 self.__sendJsonCommand("RequestBreakpointIgnore", { 946 @param line linenumber of the breakpoint
738 "filename": self.translate(fn, False), 947 @type int
739 "line": line, 948 @param count number of occurrences to ignore
740 "count": count, 949 @type int
741 }) 950 """
742 951 if debuggerId:
743 def remoteWatchpoint(self, cond, setWatch, temp=False): 952 debuggerList = [debuggerId]
953 else:
954 debuggerList = list(self.__connections.keys())
955 for debuggerId in debuggerList:
956 self.__sendJsonCommand("RequestBreakpointIgnore", {
957 "filename": self.translate(fn, False),
958 "line": line,
959 "count": count,
960 }, debuggerId)
961
962 def remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False):
744 """ 963 """
745 Public method to set or clear a watch expression. 964 Public method to set or clear a watch expression.
746 965
747 @param cond expression of the watch expression (string) 966 @param debuggerId ID of the debugger backend
967 @type str
968 @param cond expression of the watch expression
969 @type str
748 @param setWatch flag indicating setting or resetting a watch expression 970 @param setWatch flag indicating setting or resetting a watch expression
749 (boolean) 971 @type bool
750 @param temp flag indicating a temporary watch expression (boolean) 972 @param temp flag indicating a temporary watch expression
751 """ 973 @type bool
752 # cond is combination of cond and special (s. watch expression viewer) 974 """
753 self.__sendJsonCommand("RequestWatch", { 975 if debuggerId:
754 "temporary": temp, 976 debuggerList = [debuggerId]
755 "setWatch": setWatch, 977 else:
756 "condition": cond, 978 debuggerList = list(self.__connections.keys())
757 }) 979 for debuggerId in debuggerList:
758 980 # cond is combination of cond and special (s. watch expression
759 def remoteWatchpointEnable(self, cond, enable): 981 # viewer)
982 self.__sendJsonCommand("RequestWatch", {
983 "temporary": temp,
984 "setWatch": setWatch,
985 "condition": cond,
986 }, debuggerId)
987
988 def remoteWatchpointEnable(self, debuggerId, cond, enable):
760 """ 989 """
761 Public method to enable or disable a watch expression. 990 Public method to enable or disable a watch expression.
762 991
763 @param cond expression of the watch expression (string) 992 @param debuggerId ID of the debugger backend
993 @type str
994 @param cond expression of the watch expression
995 @type str
764 @param enable flag indicating enabling or disabling a watch expression 996 @param enable flag indicating enabling or disabling a watch expression
765 (boolean) 997 @type bool
766 """ 998 """
767 # cond is combination of cond and special (s. watch expression viewer) 999 if debuggerId:
768 self.__sendJsonCommand("RequestWatchEnable", { 1000 debuggerList = [debuggerId]
769 "condition": cond, 1001 else:
770 "enable": enable, 1002 debuggerList = list(self.__connections.keys())
771 }) 1003 for debuggerId in debuggerList:
772 1004 # cond is combination of cond and special (s. watch expression
773 def remoteWatchpointIgnore(self, cond, count): 1005 # viewer)
1006 self.__sendJsonCommand("RequestWatchEnable", {
1007 "condition": cond,
1008 "enable": enable,
1009 }, debuggerId)
1010
1011 def remoteWatchpointIgnore(self, debuggerId, cond, count):
774 """ 1012 """
775 Public method to ignore a watch expression the next couple of 1013 Public method to ignore a watch expression the next couple of
776 occurrences. 1014 occurrences.
777 1015
778 @param cond expression of the watch expression (string) 1016 @param debuggerId ID of the debugger backend
779 @param count number of occurrences to ignore (int) 1017 @type str
780 """ 1018 @param cond expression of the watch expression
781 # cond is combination of cond and special (s. watch expression viewer) 1019 @type str
782 self.__sendJsonCommand("RequestWatchIgnore", { 1020 @param count number of occurrences to ignore
783 "condition": cond, 1021 @type int
784 "count": count, 1022 """
785 }) 1023 if debuggerId:
786 1024 debuggerList = [debuggerId]
787 def remoteRawInput(self, s): 1025 else:
1026 debuggerList = list(self.__connections.keys())
1027 for debuggerId in debuggerList:
1028 # cond is combination of cond and special (s. watch expression
1029 # viewer)
1030 self.__sendJsonCommand("RequestWatchIgnore", {
1031 "condition": cond,
1032 "count": count,
1033 }, debuggerId)
1034
1035 def remoteRawInput(self, debuggerId, inputString):
788 """ 1036 """
789 Public method to send the raw input to the debugged program. 1037 Public method to send the raw input to the debugged program.
790 1038
791 @param s the raw input (string) 1039 @param debuggerId ID of the debugger backend
1040 @type str
1041 @param inputString the raw input
1042 @type str
792 """ 1043 """
793 self.__sendJsonCommand("RawInput", { 1044 self.__sendJsonCommand("RawInput", {
794 "input": s, 1045 "input": inputString,
795 }) 1046 }, debuggerId)
796 1047
797 def remoteThreadList(self): 1048 def remoteThreadList(self, debuggerId):
798 """ 1049 """
799 Public method to request the list of threads from the client. 1050 Public method to request the list of threads from the client.
800 """ 1051
801 self.__sendJsonCommand("RequestThreadList", {}) 1052 @param debuggerId ID of the debugger backend
802 1053 @type str
803 def remoteSetThread(self, tid): 1054 """
1055 self.__sendJsonCommand("RequestThreadList", {}, debuggerId)
1056
1057 def remoteSetThread(self, debuggerId, tid):
804 """ 1058 """
805 Public method to request to set the given thread as current thread. 1059 Public method to request to set the given thread as current thread.
806 1060
807 @param tid id of the thread (integer) 1061 @param debuggerId ID of the debugger backend
1062 @type str
1063 @param tid id of the thread
1064 @type int
808 """ 1065 """
809 self.__sendJsonCommand("RequestThreadSet", { 1066 self.__sendJsonCommand("RequestThreadSet", {
810 "threadID": tid, 1067 "threadID": tid,
811 }) 1068 }, debuggerId)
812 1069
813 def remoteClientVariables(self, scope, filterList, framenr=0, maxSize=0): 1070 def remoteClientStack(self, debuggerId):
1071 """
1072 Public method to request the stack of the main thread.
1073
1074 @param debuggerId ID of the debugger backend
1075 @type str
1076 """
1077 self.__sendJsonCommand("RequestStack", {}, debuggerId)
1078
1079 def remoteClientVariables(self, debuggerId, scope, filterList, framenr=0,
1080 maxSize=0):
814 """ 1081 """
815 Public method to request the variables of the debugged program. 1082 Public method to request the variables of the debugged program.
816 1083
1084 @param debuggerId ID of the debugger backend
1085 @type str
817 @param scope the scope of the variables (0 = local, 1 = global) 1086 @param scope the scope of the variables (0 = local, 1 = global)
818 @type int 1087 @type int
819 @param filterList list of variable types to filter out 1088 @param filterList list of variable types to filter out
820 @type list of int 1089 @type list of str
821 @param framenr framenumber of the variables to retrieve 1090 @param framenr framenumber of the variables to retrieve
822 @type int 1091 @type int
823 @param maxSize maximum size the formatted value of a variable will 1092 @param maxSize maximum size the formatted value of a variable will
824 be shown. If it is bigger than that, a 'too big' indication will 1093 be shown. If it is bigger than that, a 'too big' indication will
825 be given (@@TOO_BIG_TO_SHOW@@). 1094 be given (@@TOO_BIG_TO_SHOW@@).
828 self.__sendJsonCommand("RequestVariables", { 1097 self.__sendJsonCommand("RequestVariables", {
829 "frameNumber": framenr, 1098 "frameNumber": framenr,
830 "scope": scope, 1099 "scope": scope,
831 "filters": filterList, 1100 "filters": filterList,
832 "maxSize": maxSize, 1101 "maxSize": maxSize,
833 }) 1102 }, debuggerId)
834 1103
835 def remoteClientVariable(self, scope, filterList, var, framenr=0, 1104 def remoteClientVariable(self, debuggerId, scope, filterList, var,
836 maxSize=0): 1105 framenr=0, maxSize=0):
837 """ 1106 """
838 Public method to request the variables of the debugged program. 1107 Public method to request the variables of the debugged program.
839 1108
1109 @param debuggerId ID of the debugger backend
1110 @type str
840 @param scope the scope of the variables (0 = local, 1 = global) 1111 @param scope the scope of the variables (0 = local, 1 = global)
841 @type int 1112 @type int
842 @param filterList list of variable types to filter out 1113 @param filterList list of variable types to filter out
843 @type list of int 1114 @type list of str
844 @param var list encoded name of variable to retrieve 1115 @param var list encoded name of variable to retrieve
845 @type list of str 1116 @type list of str
846 @param framenr framenumber of the variables to retrieve 1117 @param framenr framenumber of the variables to retrieve
847 @type int 1118 @type int
848 @param maxSize maximum size the formatted value of a variable will 1119 @param maxSize maximum size the formatted value of a variable will
854 "variable": var, 1125 "variable": var,
855 "frameNumber": framenr, 1126 "frameNumber": framenr,
856 "scope": scope, 1127 "scope": scope,
857 "filters": filterList, 1128 "filters": filterList,
858 "maxSize": maxSize, 1129 "maxSize": maxSize,
859 }) 1130 }, debuggerId)
860 1131
861 def remoteClientDisassembly(self): 1132 def remoteClientDisassembly(self, debuggerId):
862 """ 1133 """
863 Public method to ask the client for the latest traceback disassembly. 1134 Public method to ask the client for the latest traceback disassembly.
864 """ 1135
865 self.__sendJsonCommand("RequestDisassembly", {}) 1136 @param debuggerId ID of the debugger backend
866 1137 @type str
867 def remoteClientSetFilter(self, scope, filterStr): 1138 """
1139 self.__sendJsonCommand("RequestDisassembly", {}, debuggerId)
1140
1141 def remoteClientSetFilter(self, debuggerId, scope, filterStr):
868 """ 1142 """
869 Public method to set a variables filter list. 1143 Public method to set a variables filter list.
870 1144
1145 @param debuggerId ID of the debugger backend
1146 @type str
871 @param scope the scope of the variables (0 = local, 1 = global) 1147 @param scope the scope of the variables (0 = local, 1 = global)
1148 @type int
872 @param filterStr regexp string for variable names to filter out 1149 @param filterStr regexp string for variable names to filter out
873 (string) 1150 @type str
874 """ 1151 """
875 self.__sendJsonCommand("RequestSetFilter", { 1152 self.__sendJsonCommand("RequestSetFilter", {
876 "scope": scope, 1153 "scope": scope,
877 "filter": filterStr, 1154 "filter": filterStr,
878 }) 1155 }, debuggerId)
879 1156
880 def setCallTraceEnabled(self, on): 1157 def setCallTraceEnabled(self, debuggerId, on):
881 """ 1158 """
882 Public method to set the call trace state. 1159 Public method to set the call trace state.
883 1160
884 @param on flag indicating to enable the call trace function (boolean) 1161 @param debuggerId ID of the debugger backend
1162 @type str
1163 @param on flag indicating to enable the call trace function
1164 @type bool
885 """ 1165 """
886 self.__sendJsonCommand("RequestCallTrace", { 1166 self.__sendJsonCommand("RequestCallTrace", {
887 "enable": on, 1167 "enable": on,
888 }) 1168 }, debuggerId)
1169
1170 def remoteNoDebugList(self, debuggerId, noDebugList):
1171 """
1172 Public method to set a list of programs not to be debugged.
1173
1174 The programs given in the list will not be run under the control
1175 of the multi process debugger.
1176
1177 @param debuggerId ID of the debugger backend
1178 @type str
1179 @param noDebugList list of Python programs not to be debugged
1180 @type list of str
1181 """
1182 self.__sendJsonCommand("RequestSetNoDebugList", {
1183 "noDebug": noDebugList,
1184 }, debuggerId)
889 1185
890 def remoteBanner(self): 1186 def remoteBanner(self):
891 """ 1187 """
892 Public slot to get the banner info of the remote client. 1188 Public slot to get the banner info of the remote client.
893 """ 1189 """
894 self.__sendJsonCommand("RequestBanner", {}) 1190 self.__sendJsonCommand("RequestBanner", {})
895 1191
896 def remoteCapabilities(self): 1192 def remoteCapabilities(self, debuggerId):
897 """ 1193 """
898 Public slot to get the debug clients capabilities. 1194 Public slot to get the debug clients capabilities.
899 """ 1195
900 self.__sendJsonCommand("RequestCapabilities", {}) 1196 @param debuggerId ID of the debugger backend
901 1197 @type str
902 def remoteCompletion(self, text): 1198 """
1199 self.__sendJsonCommand("RequestCapabilities", {}, debuggerId)
1200
1201 def remoteCompletion(self, debuggerId, text):
903 """ 1202 """
904 Public slot to get the a list of possible commandline completions 1203 Public slot to get the a list of possible commandline completions
905 from the remote client. 1204 from the remote client.
906 1205
907 @param text the text to be completed (string) 1206 @param debuggerId ID of the debugger backend
1207 @type str
1208 @param text the text to be completed
1209 @type str
908 """ 1210 """
909 self.__sendJsonCommand("RequestCompletion", { 1211 self.__sendJsonCommand("RequestCompletion", {
910 "text": text, 1212 "text": text,
911 }) 1213 }, debuggerId)
912 1214
913 def remoteUTDiscover(self, syspath, workdir, discoveryStart): 1215 def remoteUTDiscover(self, syspath, workdir, discoveryStart):
914 """ 1216 """
915 Public method to perform a test case discovery. 1217 Public method to perform a test case discovery.
916 1218
1006 """ 1308 """
1007 Public method to stop a unittest run. 1309 Public method to stop a unittest run.
1008 """ 1310 """
1009 self.__sendJsonCommand("RequestUTStop", {}) 1311 self.__sendJsonCommand("RequestUTStop", {})
1010 1312
1011 def __askForkTo(self): 1313 def __parseClientLine(self, sock):
1012 """
1013 Private method to ask the user which branch of a fork to follow.
1014 """
1015 selections = [self.tr("Parent Process"),
1016 self.tr("Child process")]
1017 res, ok = QInputDialog.getItem(
1018 None,
1019 self.tr("Client forking"),
1020 self.tr("Select the fork branch to follow."),
1021 selections,
1022 0, False)
1023 if not ok or res == selections[0]:
1024 self.__sendJsonCommand("ResponseForkTo", {
1025 "target": "parent",
1026 })
1027 else:
1028 self.__sendJsonCommand("ResponseForkTo", {
1029 "target": "child",
1030 })
1031
1032 def __parseClientLine(self):
1033 """ 1314 """
1034 Private method to handle data from the client. 1315 Private method to handle data from the client.
1035 """ 1316
1036 while self.qsock and self.qsock.canReadLine(): 1317 @param sock reference to the socket to read data from
1037 qs = self.qsock.readLine() 1318 @type QTcpSocket
1319 """
1320 while sock and sock.canReadLine():
1321 qs = sock.readLine()
1038 line = bytes(qs).decode( 1322 line = bytes(qs).decode(
1039 encoding=Preferences.getSystem("StringEncoding")) 1323 encoding=Preferences.getSystem("StringEncoding"))
1040 1324
1041 logging.debug("<Debug-Server> %s", line) 1325 logging.debug("<Debug-Server> %s", line)
1042 ## print("Server: ", line) ##debug 1326 ## print("Server: ", line) ## debug # __IGNORE_WARNING_M891__
1043 1327
1044 self.__handleJsonCommand(line) 1328 self.__handleJsonCommand(line, sock)
1045 continue 1329
1046 1330 def __handleJsonCommand(self, jsonStr, sock):
1047 def __handleJsonCommand(self, jsonStr):
1048 """ 1331 """
1049 Private method to handle a command or response serialized as a 1332 Private method to handle a command or response serialized as a
1050 JSON string. 1333 JSON string.
1051 1334
1052 @param jsonStr string containing the command or response received 1335 @param jsonStr string containing the command or response received
1053 from the debug backend 1336 from the debug backend
1054 @type str 1337 @type str
1338 @param sock reference to the socket the data was received from
1339 @type QTcpSocket
1055 """ 1340 """
1056 import json 1341 import json
1057 1342
1058 try: 1343 try:
1059 commandDict = json.loads(jsonStr.strip()) 1344 commandDict = json.loads(jsonStr.strip())
1073 return 1358 return
1074 1359
1075 method = commandDict["method"] 1360 method = commandDict["method"]
1076 params = commandDict["params"] 1361 params = commandDict["params"]
1077 1362
1078 if method == "ClientOutput": 1363 if method == "DebuggerId":
1079 self.debugServer.signalClientOutput(params["text"]) 1364 self.__assignDebuggerId(sock, params["debuggerId"])
1365
1366 elif method == "ClientOutput":
1367 self.debugServer.signalClientOutput(
1368 params["text"], params["debuggerId"])
1080 1369
1081 elif method in ["ResponseLine", "ResponseStack"]: 1370 elif method in ["ResponseLine", "ResponseStack"]:
1082 # Check if obsolet thread was clicked 1371 # Check if obsolete thread was clicked
1083 if params["stack"] == []: 1372 if params["stack"] == []:
1084 # Request updated list 1373 # Request updated list
1085 self.remoteThreadList() 1374 self.remoteThreadList(params["debuggerId"])
1086 return 1375 return
1087 for s in params["stack"]: 1376 for s in params["stack"]:
1088 s[0] = self.translate(s[0], True) 1377 s[0] = self.translate(s[0], True)
1089 cf = params["stack"][0] 1378 cf = params["stack"][0]
1090 if self.__autoContinue: 1379 if (
1091 self.__autoContinue = False 1380 self.__autoContinue and
1092 QTimer.singleShot(0, self.remoteContinue) 1381 params["debuggerId"] not in self.__autoContinued
1382 ):
1383 self.__autoContinued.append(params["debuggerId"])
1384 QTimer.singleShot(
1385 0, lambda: self.remoteContinue(params["debuggerId"]))
1093 else: 1386 else:
1094 self.debugServer.signalClientLine( 1387 self.debugServer.signalClientLine(
1095 cf[0], int(cf[1]), 1388 cf[0], int(cf[1]), params["debuggerId"],
1096 method == "ResponseStack") 1389 method == "ResponseStack", threadName=params["threadName"])
1097 self.debugServer.signalClientStack(params["stack"]) 1390 self.debugServer.signalClientStack(
1391 params["stack"], params["debuggerId"],
1392 threadName=params["threadName"])
1098 1393
1099 elif method == "CallTrace": 1394 elif method == "CallTrace":
1100 isCall = params["event"].lower() == "c" 1395 isCall = params["event"].lower() == "c"
1101 fromInfo = params["from"] 1396 fromInfo = params["from"]
1102 toInfo = params["to"] 1397 toInfo = params["to"]
1103 self.debugServer.signalClientCallTrace( 1398 self.debugServer.signalClientCallTrace(
1104 isCall, 1399 isCall,
1105 fromInfo["filename"], str(fromInfo["linenumber"]), 1400 fromInfo["filename"], str(fromInfo["linenumber"]),
1106 fromInfo["codename"], 1401 fromInfo["codename"],
1107 toInfo["filename"], str(toInfo["linenumber"]), 1402 toInfo["filename"], str(toInfo["linenumber"]),
1108 toInfo["codename"]) 1403 toInfo["codename"],
1404 params["debuggerId"])
1109 1405
1110 elif method == "ResponseVariables": 1406 elif method == "ResponseVariables":
1111 self.debugServer.signalClientVariables( 1407 self.debugServer.signalClientVariables(
1112 params["scope"], params["variables"]) 1408 params["scope"], params["variables"], params["debuggerId"])
1113 1409
1114 elif method == "ResponseVariable": 1410 elif method == "ResponseVariable":
1115 self.debugServer.signalClientVariable( 1411 self.debugServer.signalClientVariable(
1116 params["scope"], [params["variable"]] + params["variables"]) 1412 params["scope"], [params["variable"]] + params["variables"],
1413 params["debuggerId"])
1117 1414
1118 elif method == "ResponseThreadList": 1415 elif method == "ResponseThreadList":
1119 self.debugServer.signalClientThreadList( 1416 self.debugServer.signalClientThreadList(
1120 params["currentID"], params["threadList"]) 1417 params["currentID"], params["threadList"],
1418 params["debuggerId"])
1121 1419
1122 elif method == "ResponseThreadSet": 1420 elif method == "ResponseThreadSet":
1123 self.debugServer.signalClientThreadSet() 1421 self.debugServer.signalClientThreadSet(params["debuggerId"])
1124 1422
1125 elif method == "ResponseCapabilities": 1423 elif method == "ResponseCapabilities":
1126 self.clientCapabilities = params["capabilities"] 1424 self.clientCapabilities = params["capabilities"]
1127 self.debugServer.signalClientCapabilities( 1425 if params["debuggerId"] == self.__master:
1128 params["capabilities"], 1426 # signal only for the master connection
1129 params["clientType"], 1427 self.debugServer.signalClientCapabilities(
1130 self.__startedVenv, 1428 params["capabilities"],
1131 ) 1429 params["clientType"],
1430 self.__startedVenv,
1431 )
1132 1432
1133 elif method == "ResponseBanner": 1433 elif method == "ResponseBanner":
1134 self.debugServer.signalClientBanner( 1434 if params["debuggerId"] == self.__master:
1135 params["version"], 1435 # signal only for the master connection
1136 params["platform"], 1436 self.debugServer.signalClientBanner(
1137 params["dbgclient"], 1437 params["version"],
1138 self.__startedVenv, 1438 params["platform"],
1139 ) 1439 self.__startedVenv,
1440 )
1140 1441
1141 elif method == "ResponseOK": 1442 elif method == "ResponseOK":
1142 self.debugServer.signalClientStatement(False) 1443 self.debugServer.signalClientStatement(False, params["debuggerId"])
1143 1444
1144 elif method == "ResponseContinue": 1445 elif method == "ResponseContinue":
1145 self.debugServer.signalClientStatement(True) 1446 self.debugServer.signalClientStatement(True, params["debuggerId"])
1146 1447
1147 elif method == "RequestRaw": 1448 elif method == "RequestRaw":
1148 self.debugServer.signalClientRawInput( 1449 self.debugServer.signalClientRawInput(
1149 params["prompt"], params["echo"]) 1450 params["prompt"], params["echo"], params["debuggerId"])
1150 1451
1151 elif method == "ResponseBPConditionError": 1452 elif method == "ResponseBPConditionError":
1152 fn = self.translate(params["filename"], True) 1453 fn = self.translate(params["filename"], True)
1153 self.debugServer.signalClientBreakConditionError( 1454 self.debugServer.signalClientBreakConditionError(
1154 fn, params["line"]) 1455 fn, params["line"], params["debuggerId"])
1155 1456
1156 elif method == "ResponseClearBreakpoint": 1457 elif method == "ResponseClearBreakpoint":
1157 fn = self.translate(params["filename"], True) 1458 fn = self.translate(params["filename"], True)
1158 self.debugServer.signalClientClearBreak(fn, params["line"]) 1459 self.debugServer.signalClientClearBreak(
1460 fn, params["line"], params["debuggerId"])
1159 1461
1160 elif method == "ResponseWatchConditionError": 1462 elif method == "ResponseWatchConditionError":
1161 self.debugServer.signalClientWatchConditionError( 1463 self.debugServer.signalClientWatchConditionError(
1162 params["condition"]) 1464 params["condition"], params["debuggerId"])
1163 1465
1164 elif method == "ResponseClearWatch": 1466 elif method == "ResponseClearWatch":
1165 self.debugServer.signalClientClearWatch(params["condition"]) 1467 self.debugServer.signalClientClearWatch(
1468 params["condition"], params["debuggerId"])
1166 1469
1167 elif method == "ResponseDisassembly": 1470 elif method == "ResponseDisassembly":
1168 self.debugServer.signalClientDisassembly(params["disassembly"]) 1471 self.debugServer.signalClientDisassembly(
1472 params["disassembly"], params["debuggerId"])
1169 1473
1170 elif method == "ResponseException": 1474 elif method == "ResponseException":
1171 if params: 1475 exctype = params["type"]
1172 exctype = params["type"] 1476 excmessage = params["message"]
1173 excmessage = params["message"] 1477 stack = params["stack"]
1174 stack = params["stack"] 1478 if stack:
1175 if stack: 1479 for stackEntry in stack:
1480 stackEntry[0] = self.translate(stackEntry[0], True)
1481 if stack[0] and stack[0][0] == "<string>":
1176 for stackEntry in stack: 1482 for stackEntry in stack:
1177 stackEntry[0] = self.translate(stackEntry[0], True) 1483 if stackEntry[0] == "<string>":
1178 if stack[0] and stack[0][0] == "<string>": 1484 stackEntry[0] = self.__scriptName
1179 for stackEntry in stack: 1485 else:
1180 if stackEntry[0] == "<string>": 1486 break
1181 stackEntry[0] = self.__scriptName
1182 else:
1183 break
1184 else:
1185 exctype = ''
1186 excmessage = ''
1187 stack = []
1188 1487
1189 self.debugServer.signalClientException( 1488 self.debugServer.signalClientException(
1190 exctype, excmessage, stack) 1489 exctype, excmessage, stack, params["debuggerId"],
1490 params["threadName"])
1191 1491
1192 elif method == "ResponseSyntax": 1492 elif method == "ResponseSyntax":
1193 self.debugServer.signalClientSyntaxError( 1493 self.debugServer.signalClientSyntaxError(
1194 params["message"], self.translate(params["filename"], True), 1494 params["message"], self.translate(params["filename"], True),
1195 params["linenumber"], params["characternumber"]) 1495 params["linenumber"], params["characternumber"],
1496 params["debuggerId"], params["threadName"])
1196 1497
1197 elif method == "ResponseSignal": 1498 elif method == "ResponseSignal":
1198 self.debugServer.signalClientSignal( 1499 self.debugServer.signalClientSignal(
1199 params["message"], self.translate(params["filename"], True), 1500 params["message"], self.translate(params["filename"], True),
1200 params["linenumber"], params["function"], params["arguments"]) 1501 params["linenumber"], params["function"], params["arguments"],
1502 params["debuggerId"])
1201 1503
1202 elif method == "ResponseExit": 1504 elif method == "ResponseExit":
1203 self.__scriptName = "" 1505 self.__scriptName = ""
1204 self.debugServer.signalClientExit( 1506 self.debugServer.signalClientExit(
1205 params["status"], params["message"]) 1507 params["program"], params["status"], params["message"],
1508 params["debuggerId"])
1206 1509
1207 elif method == "PassiveStartup": 1510 elif method == "PassiveStartup":
1208 self.debugServer.passiveStartUp( 1511 self.debugServer.passiveStartUp(
1209 self.translate(params["filename"], True), params["exceptions"]) 1512 self.translate(params["filename"], True), params["exceptions"],
1513 params["debuggerId"])
1210 1514
1211 elif method == "ResponseCompletion": 1515 elif method == "ResponseCompletion":
1212 self.debugServer.signalClientCompletionList( 1516 self.debugServer.signalClientCompletionList(
1213 params["completions"], params["text"]) 1517 params["completions"], params["text"], params["debuggerId"])
1518
1519 ###################################################################
1520 ## Unit test related stuff is not done with multi processing
1521 ###################################################################
1214 1522
1215 elif method == "ResponseUTDiscover": 1523 elif method == "ResponseUTDiscover":
1216 self.debugServer.clientUtDiscovered( 1524 self.debugServer.clientUtDiscovered(
1217 params["testCasesList"], params["exception"], 1525 params["testCasesList"], params["exception"],
1218 params["message"]) 1526 params["message"])
1248 params["testname"], params["traceback"], params["id"]) 1556 params["testname"], params["traceback"], params["id"])
1249 1557
1250 elif method == "ResponseUTTestSucceededUnexpected": 1558 elif method == "ResponseUTTestSucceededUnexpected":
1251 self.debugServer.clientUtTestSucceededUnexpected( 1559 self.debugServer.clientUtTestSucceededUnexpected(
1252 params["testname"], params["id"]) 1560 params["testname"], params["id"])
1253 1561
1254 elif method == "RequestForkTo": 1562 def __sendJsonCommand(self, command, params, debuggerId="", sock=None):
1255 self.__askForkTo()
1256
1257 def __sendJsonCommand(self, command, params):
1258 """ 1563 """
1259 Private method to send a single command to the client. 1564 Private method to send a single command to the client.
1260 1565
1261 @param command command name to be sent 1566 @param command command name to be sent
1262 @type str 1567 @type str
1263 @param params dictionary of named parameters for the command 1568 @param params dictionary of named parameters for the command
1264 @type dict 1569 @type dict
1570 @param debuggerId id of the debug client to send the command to
1571 @type str
1572 @param sock reference to the socket object to be used (only used if
1573 debuggerId is not given)
1574 @type QTcpSocket
1265 """ 1575 """
1266 import json 1576 import json
1267 1577
1268 commandDict = { 1578 commandDict = {
1269 "jsonrpc": "2.0", 1579 "jsonrpc": "2.0",
1270 "method": command, 1580 "method": command,
1271 "params": params, 1581 "params": params,
1272 } 1582 }
1273 cmd = json.dumps(commandDict) + '\n' 1583 cmd = json.dumps(commandDict) + '\n'
1274 if self.qsock is not None: 1584
1275 self.__writeJsonCommandToSocket(cmd) 1585 if debuggerId and debuggerId in self.__connections:
1586 sock = self.__connections[debuggerId]
1587 elif sock is None and self.__master is not None:
1588 sock = self.__connections[self.__master]
1589 if sock is not None:
1590 self.__writeJsonCommandToSocket(cmd, sock)
1276 else: 1591 else:
1277 self.queue.append(cmd) 1592 self.queue.append(cmd)
1278 1593
1279 def __writeJsonCommandToSocket(self, cmd): 1594 def __writeJsonCommandToSocket(self, cmd, sock):
1280 """ 1595 """
1281 Private method to write a JSON command to the socket. 1596 Private method to write a JSON command to the socket.
1282 1597
1283 @param cmd JSON command to be sent 1598 @param cmd JSON command to be sent
1284 @type str 1599 @type str
1600 @param sock reference to the socket to write to
1601 @type QTcpSocket
1285 """ 1602 """
1286 data = cmd.encode('utf8', 'backslashreplace') 1603 data = cmd.encode('utf8', 'backslashreplace')
1287 length = "{0:09d}".format(len(data)) 1604 length = "{0:09d}".format(len(data))
1288 self.qsock.write(length.encode() + data) 1605 sock.write(length.encode() + data)
1289 self.qsock.flush() 1606 sock.flush()
1290 1607
1291 1608
1292 def createDebuggerInterfacePython3(debugServer, passive): 1609 def createDebuggerInterfacePython3(debugServer, passive):
1293 """ 1610 """
1294 Module function to create a debugger interface instance. 1611 Module function to create a debugger interface instance.

eric ide

mercurial