src/eric7/Debugger/DebuggerInterfacePython.py

branch
server
changeset 10555
08e853c0c77b
parent 10551
d80184d38152
child 10559
64db35c6e335
equal deleted inserted replaced
10551:d80184d38152 10555:08e853c0c77b
47 47
48 self.__isNetworked = True 48 self.__isNetworked = True
49 self.__autoContinue = False 49 self.__autoContinue = False
50 self.__autoContinued = [] 50 self.__autoContinued = []
51 self.__isStepCommand = False 51 self.__isStepCommand = False
52
53 self.__ericServerDebugging = False # are we debugging via the eric-ide server?
54 try:
55 self.__ericServerDebuggerInterface = ericApp().getObject(
56 "EricServer"
57 ).getServiceInterface("Debugger")
58 self.__ericServerDebuggerInterface.debugClientResponse.connect(
59 lambda jsonStr: self.handleJsonCommand(jsonStr, None)
60 )
61 except KeyError:
62 self.__ericServerDebuggerInterface = None
52 63
53 self.debugServer = debugServer 64 self.debugServer = debugServer
54 self.passive = passive 65 self.passive = passive
55 self.process = None 66 self.process = None
56 self.__startedVenv = "" 67 self.__startedVenv = ""
86 Private method to perform the identity path translation. 97 Private method to perform the identity path translation.
87 98
88 @param fn filename to be translated 99 @param fn filename to be translated
89 @type str 100 @type str
90 @param remote2local flag indicating the direction of translation 101 @param remote2local flag indicating the direction of translation
91 (False = local to remote, True = remote to local [default]) 102 (False = local to remote, True = remote to local) (defaults to True)
92 @type bool 103 @type bool (optional)
93 @return translated filename 104 @return translated filename
94 @rtype str 105 @rtype str
95 """ 106 """
96 return fn 107 return fn
97 108
100 Private method to perform the path translation. 111 Private method to perform the path translation.
101 112
102 @param fn filename to be translated 113 @param fn filename to be translated
103 @type str 114 @type str
104 @param remote2local flag indicating the direction of translation 115 @param remote2local flag indicating the direction of translation
105 (False = local to remote, True = remote to local [default]) 116 (False = local to remote, True = remote to local) (defaults to True)
106 @type bool 117 @type bool (optional)
107 @return translated filename 118 @return translated filename
108 @rtype str 119 @rtype str
109 """ 120 """
110 if remote2local: 121 if remote2local:
111 path = fn.replace(self.translateRemote, self.translateLocal) 122 path = fn.replace(self.translateRemote, self.translateLocal)
116 if not self.translateRemoteWindows: 127 if not self.translateRemoteWindows:
117 path = path.replace("\\", "/") 128 path = path.replace("\\", "/")
118 129
119 return path 130 return path
120 131
121 def __startProcess(self, program, arguments, environment=None, workingDir=None): 132 def __ericServerTranslation(self, fn, remote2local=True):
133 """
134 Private method to perform the eric-ide server path translation.
135
136 @param fn filename to be translated
137 @type str
138 @param remote2local flag indicating the direction of translation
139 (False = local to remote, True = remote to local) (defaults to True)
140 @type bool (optional)
141 @return translated filename
142 @rtype str
143 """
144 if remote2local:
145 return FileSystemUtilities.remoteFileName(fn)
146 else:
147 return FileSystemUtilities.plainFileName(fn)
148
149 def __startProcess(self, program, arguments, environment=None, workingDir=""):
122 """ 150 """
123 Private method to start the debugger client process. 151 Private method to start the debugger client process.
124 152
125 @param program name of the executable to start 153 @param program name of the executable to start
126 @type str 154 @type str
152 self, 180 self,
153 port, 181 port,
154 runInConsole, 182 runInConsole,
155 venvName, 183 venvName,
156 originalPathString, 184 originalPathString,
157 workingDir=None, 185 workingDir="",
158 configOverride=None, 186 configOverride=None,
187 startRemote=False,
159 ): 188 ):
160 """ 189 """
161 Public method to start a remote Python interpreter. 190 Public method to start a remote Python interpreter.
162 191
163 @param port port number the debug server is listening on 192 @param port port number the debug server is listening on
167 @type bool 196 @type bool
168 @param venvName name of the virtual environment to be used 197 @param venvName name of the virtual environment to be used
169 @type str 198 @type str
170 @param originalPathString original PATH environment variable 199 @param originalPathString original PATH environment variable
171 @type str 200 @type str
172 @param workingDir directory to start the debugger client in 201 @param workingDir directory to start the debugger client in (defaults to "")
173 @type str 202 @type str (optional)
174 @param configOverride dictionary containing the global config override 203 @param configOverride dictionary containing the global config override data
175 data 204 (defaults to None)
176 @type dict 205 @type dict (optional)
206 @param startRemote flag indicating to start the client via an eric-ide server
207 (defaults to False)
208 @type bool (optional)
177 @return client process object, a flag to indicate a network connection 209 @return client process object, a flag to indicate a network connection
178 and the name of the interpreter in case of a local execution 210 and the name of the interpreter in case of a local execution
179 @rtype tuple of (QProcess, bool, str) 211 @rtype tuple of (QProcess, bool, str)
180 """ 212 """
181 global origPathEnv 213 global origPathEnv
182 214
183 if not venvName: 215 if startRemote or venvName == self.debugServer.getEricServerEnvironmentString():
184 venvName = Preferences.getDebugger("Python3VirtualEnv") 216 # TODO change this once server environment definitions are supported
185 if venvName == self.debugServer.getProjectEnvironmentString(): 217 startRemote = True
186 project = ericApp().getObject("Project") 218 venvName = self.debugServer.getEricServerEnvironmentString()
187 venvName = project.getProjectVenv() 219 interpreter = ""
188 execPath = project.getProjectExecPath()
189 interpreter = project.getProjectInterpreter()
190 else: 220 else:
191 venvManager = ericApp().getObject("VirtualEnvManager") 221 if not venvName:
192 interpreter = venvManager.getVirtualenvInterpreter(venvName) 222 venvName = Preferences.getDebugger("Python3VirtualEnv")
193 execPath = venvManager.getVirtualenvExecPath(venvName) 223 if venvName == self.debugServer.getProjectEnvironmentString():
194 if interpreter == "": 224 project = ericApp().getObject("Project")
195 # use the interpreter used to run eric for identical variants 225 venvName = project.getProjectVenv()
196 interpreter = PythonUtilities.getPythonExecutable() 226 execPath = project.getProjectExecPath()
197 if interpreter == "": 227 interpreter = project.getProjectInterpreter()
198 EricMessageBox.critical( 228 else:
199 None, 229 venvManager = ericApp().getObject("VirtualEnvManager")
200 self.tr("Start Debugger"), 230 interpreter = venvManager.getVirtualenvInterpreter(venvName)
201 self.tr("""<p>No suitable Python3 environment configured.</p>"""), 231 execPath = venvManager.getVirtualenvExecPath(venvName)
202 ) 232 if interpreter == "":
203 return None, False, "" 233 # use the interpreter used to run eric for identical variants
234 interpreter = PythonUtilities.getPythonExecutable()
235 if interpreter == "":
236 EricMessageBox.critical(
237 None,
238 self.tr("Start Debugger"),
239 self.tr("""<p>No suitable Python3 environment configured.</p>"""),
240 )
241 return None, False, ""
204 242
205 self.__inShutdown = False 243 self.__inShutdown = False
244
245 self.__ericServerDebugging = False
246 self.__ericServerDebuggerInterface.stopClient()
247 self.__mainDebugger = None
206 248
207 redirect = ( 249 redirect = (
208 str(configOverride["redirect"]) 250 str(configOverride["redirect"])
209 if configOverride and configOverride["enable"] 251 if configOverride and configOverride["enable"]
210 else str(Preferences.getDebugger("Python3Redirect")) 252 else str(Preferences.getDebugger("Python3Redirect"))
288 " login was given.</p>" 330 " login was given.</p>"
289 ), 331 ),
290 ) 332 )
291 return None, False, "" 333 return None, False, ""
292 334
335 elif startRemote and self.__ericServerDebuggerInterface is not None:
336 # debugging via an eric-ide server
337 ##self.__ericServerDebuggerInterface.stopClient()
338 ##self.__mainDebugger = None
339 ##
340 self.translate = self.__ericServerTranslation
341 self.__ericServerDebugging = True
342
343 args = []
344 if noencoding:
345 args.append(noencoding)
346 if multiprocessEnabled:
347 args.append(multiprocessEnabled)
348 if callTraceOptimization:
349 args.append(callTraceOptimization)
350 self.__ericServerDebuggerInterface.startClient(
351 interpreter, originalPathString, args, workingDir=workingDir,
352 )
353 self.__startedVenv = venvName
354
355 return None, self.__isNetworked, ""
356
293 else: 357 else:
294 # local debugging code below 358 # local debugging code below
295 debugClient = self.__determineDebugClient() 359 debugClient = self.__determineDebugClient()
296 360
297 # set translation function 361 # set translation function
396 runInConsole, 460 runInConsole,
397 venvName, 461 venvName,
398 originalPathString, 462 originalPathString,
399 workingDir=None, 463 workingDir=None,
400 configOverride=None, 464 configOverride=None,
465 startRemote=False,
401 ): 466 ):
402 """ 467 """
403 Public method to start a remote Python interpreter for a project. 468 Public method to start a remote Python interpreter for a project.
404 469
405 @param port port number the debug server is listening on 470 @param port port number the debug server is listening on
414 @param workingDir directory to start the debugger client in 479 @param workingDir directory to start the debugger client in
415 @type str 480 @type str
416 @param configOverride dictionary containing the global config override 481 @param configOverride dictionary containing the global config override
417 data 482 data
418 @type dict 483 @type dict
484 @param startRemote flag indicating to start the client via an eric-ide server
485 (defaults to False)
486 @type bool (optional)
419 @return client process object, a flag to indicate a network connection 487 @return client process object, a flag to indicate a network connection
420 and the name of the interpreter in case of a local execution 488 and the name of the interpreter in case of a local execution
421 @rtype tuple of (QProcess, bool, str) 489 @rtype tuple of (QProcess, bool, str)
422 """ 490 """
423 global origPathEnv 491 global origPathEnv
457 self.tr("""<p>No suitable Python3 environment configured.</p>"""), 525 self.tr("""<p>No suitable Python3 environment configured.</p>"""),
458 ) 526 )
459 return None, self.__isNetworked, "" 527 return None, self.__isNetworked, ""
460 528
461 self.__inShutdown = False 529 self.__inShutdown = False
530
531 self.__ericServerDebugging = False
532 self.__ericServerDebuggerInterface.stopClient()
533 self.__mainDebugger = None
462 534
463 if project.getDebugProperty("REMOTEDEBUGGER"): 535 if project.getDebugProperty("REMOTEDEBUGGER"):
464 # remote debugging code 536 # remote debugging code
465 ipaddr = self.debugServer.getHostAddress(False) 537 ipaddr = self.debugServer.getHostAddress(False)
466 rexec = project.getDebugProperty("REMOTECOMMAND") 538 rexec = project.getDebugProperty("REMOTECOMMAND")
515 return process, self.__isNetworked, "" 587 return process, self.__isNetworked, ""
516 else: 588 else:
517 # remote shell command is missing 589 # remote shell command is missing
518 return None, self.__isNetworked, "" 590 return None, self.__isNetworked, ""
519 591
592 # TODO: add server debugging for projects
593
520 else: 594 else:
521 # local debugging code below 595 # local debugging code below
522 debugClient = project.getDebugProperty("DEBUGCLIENT") 596 debugClient = project.getDebugProperty("DEBUGCLIENT")
523 if not bool(debugClient) or not os.path.exists(debugClient): 597 if not bool(debugClient) or not os.path.exists(debugClient):
524 debugClient = self.__determineDebugClient() 598 debugClient = self.__determineDebugClient()
617 @return flag indicating success 691 @return flag indicating success
618 @rtype bool 692 @rtype bool
619 """ 693 """
620 self.__pendingConnections.append(sock) 694 self.__pendingConnections.append(sock)
621 695
622 sock.readyRead.connect(lambda: self.__parseClientLine(sock)) 696 sock.readyRead.connect(lambda: self.__receiveJson(sock))
623 sock.disconnected.connect(lambda: self.__socketDisconnected(sock)) 697 sock.disconnected.connect(lambda: self.__socketDisconnected(sock))
624 698
625 return True 699 return True
626 700
627 def __assignDebuggerId(self, sock, debuggerId): 701 def __assignDebuggerId(self, sock, debuggerId):
632 @param sock reference to the socket object 706 @param sock reference to the socket object
633 @type QTcpSocket 707 @type QTcpSocket
634 @param debuggerId id of the connected debug client 708 @param debuggerId id of the connected debug client
635 @type str 709 @type str
636 """ 710 """
637 if sock in self.__pendingConnections: 711 if sock and sock in self.__pendingConnections:
638 self.__connections[debuggerId] = sock 712 self.__connections[debuggerId] = sock
639 self.__pendingConnections.remove(sock) 713 self.__pendingConnections.remove(sock)
640 714
641 if self.__mainDebugger is None: 715 if self.__mainDebugger is None:
642 self.__mainDebugger = debuggerId 716 self.__mainDebugger = debuggerId
643 # Get the remote clients capabilities 717 # Get the remote clients capabilities
644 self.remoteCapabilities(debuggerId) 718 self.remoteCapabilities(debuggerId)
645 719
646 self.debugServer.signalClientDebuggerId(debuggerId) 720 self.debugServer.signalClientDebuggerId(debuggerId)
647 721
648 if debuggerId == self.__mainDebugger: 722 if debuggerId == self.__mainDebugger:
649 self.__flush() 723 self.__flush()
650 self.debugServer.mainClientConnected() 724 self.debugServer.mainClientConnected()
651 725
652 self.debugServer.initializeClient(debuggerId) 726 self.debugServer.initializeClient(debuggerId)
653 727
654 # perform auto-continue except for main 728 # perform auto-continue except for main
655 if ( 729 if (
656 debuggerId != self.__mainDebugger 730 debuggerId != self.__mainDebugger
657 and self.__autoContinue 731 and self.__autoContinue
658 and not self.__isStepCommand 732 and not self.__isStepCommand
659 ): 733 ):
660 QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId)) 734 QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId))
661 735
662 def __socketDisconnected(self, sock): 736 def __socketDisconnected(self, sock):
663 """ 737 """
664 Private slot handling a socket disconnecting. 738 Private slot handling a socket disconnecting.
665 739
705 """ 779 """
706 Private slot to flush the queue. 780 Private slot to flush the queue.
707 """ 781 """
708 if self.__mainDebugger: 782 if self.__mainDebugger:
709 # Send commands that were waiting for the connection. 783 # Send commands that were waiting for the connection.
710 conn = self.__connections[self.__mainDebugger] 784 if self.__ericServerDebugging:
711 for jsonStr in self.__commandQueue: 785 for jsonStr in self.__commandQueue:
712 self.__writeJsonCommandToSocket(jsonStr, conn) 786 self.__ericServerDebuggerInterface.sendClientCommand(
787 self.__mainDebugger, jsonStr
788 )
789 else:
790 conn = self.__connections[self.__mainDebugger]
791 for jsonStr in self.__commandQueue:
792 self.__writeJsonCommandToSocket(jsonStr, conn)
713 793
714 self.__commandQueue.clear() 794 self.__commandQueue.clear()
715 795
716 def shutdown(self): 796 def shutdown(self):
717 """ 797 """
1394 "text": text, 1474 "text": text,
1395 }, 1475 },
1396 debuggerId, 1476 debuggerId,
1397 ) 1477 )
1398 1478
1399 def __parseClientLine(self, sock): 1479 def __receiveJson(self, sock):
1400 """ 1480 """
1401 Private method to handle data from the client. 1481 Private method to handle data from the client.
1402 1482
1403 @param sock reference to the socket to read data from 1483 @param sock reference to the socket to read data from
1404 @type QTcpSocket 1484 @type QTcpSocket
1421 jsonStr = data.decode("utf-8", "backslashreplace") 1501 jsonStr = data.decode("utf-8", "backslashreplace")
1422 1502
1423 logging.debug("<Debug-Server> %s", jsonStr) 1503 logging.debug("<Debug-Server> %s", jsonStr)
1424 ##print("Server: ", jsonStr) ## debug # __IGNORE_WARNING_M891__ 1504 ##print("Server: ", jsonStr) ## debug # __IGNORE_WARNING_M891__
1425 1505
1426 self.__handleJsonCommand(jsonStr, sock) 1506 self.handleJsonCommand(jsonStr, sock)
1427 1507
1428 def __handleJsonCommand(self, jsonStr, sock): 1508 def handleJsonCommand(self, jsonStr, sock):
1429 """ 1509 """
1430 Private method to handle a command or response serialized as a 1510 Public method to handle a command or response serialized as a
1431 JSON string. 1511 JSON string.
1432 1512
1433 @param jsonStr string containing the command or response received 1513 @param jsonStr string containing the command or response received
1434 from the debug backend 1514 from the debug backend
1435 @type str 1515 @type str
1661 "method": command, 1741 "method": command,
1662 "params": params, 1742 "params": params,
1663 } 1743 }
1664 jsonStr = json.dumps(commandDict) 1744 jsonStr = json.dumps(commandDict)
1665 1745
1666 if debuggerId and debuggerId in self.__connections: 1746 if self.__ericServerDebugging:
1667 sock = self.__connections[debuggerId] 1747 # Debugging via the eric-ide server -> pass the command on to it
1668 elif sock is None and self.__mainDebugger is not None: 1748 if self.__mainDebugger is None:
1669 sock = self.__connections[self.__mainDebugger] 1749 # debugger has not connected yet -> queue the command
1670 if sock is not None: 1750 self.__commandQueue.append(jsonStr)
1671 self.__writeJsonCommandToSocket(jsonStr, sock) 1751 else:
1752 self.__ericServerDebuggerInterface.sendClientCommand(
1753 debuggerId, jsonStr
1754 )
1672 else: 1755 else:
1673 self.__commandQueue.append(jsonStr) 1756 # Local debugging -> send the command to the client
1757 if debuggerId and debuggerId in self.__connections:
1758 sock = self.__connections[debuggerId]
1759 elif sock is None and self.__mainDebugger is not None:
1760 sock = self.__connections[self.__mainDebugger]
1761 if sock is not None:
1762 self.__writeJsonCommandToSocket(jsonStr, sock)
1763 else:
1764 self.__commandQueue.append(jsonStr)
1674 1765
1675 def __writeJsonCommandToSocket(self, jsonCommand, sock): 1766 def __writeJsonCommandToSocket(self, jsonCommand, sock):
1676 """ 1767 """
1677 Private method to write a JSON command to the socket. 1768 Private method to write a JSON command to the socket.
1678 1769

eric ide

mercurial