src/eric7/Debugger/DebugServer.py

branch
eric7-maintenance
changeset 9264
18a7312cfdb3
parent 9192
a763d57e23bc
parent 9221
bf71ee032bb4
child 9305
3b7ef53c34c7
equal deleted inserted replaced
9241:d23e9854aea4 9264:18a7312cfdb3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the debug server.
8 """
9
10 import os
11 import shlex
12 import contextlib
13
14 from PyQt6.QtCore import pyqtSignal, pyqtSlot, QModelIndex
15 from PyQt6.QtNetwork import QTcpServer, QHostAddress, QHostInfo, QNetworkInterface
16
17 from EricWidgets.EricApplication import ericApp
18 from EricWidgets import EricMessageBox
19
20 from .BreakPointModel import BreakPointModel
21 from .WatchPointModel import WatchPointModel
22 from . import DebugClientCapabilities
23
24 import Preferences
25
26
27 DebuggerInterfaces = {
28 "Python": "DebuggerInterfacePython",
29 "None": "DebuggerInterfaceNone",
30 }
31
32
33 class DebugServer(QTcpServer):
34 """
35 Class implementing the debug server embedded within the IDE.
36
37 @signal clientProcessStdout(str) emitted after the client has sent some
38 output via stdout
39 @signal clientProcessStderr(str) emitted after the client has sent some
40 output via stderr
41 @signal clientOutput(str) emitted after the client has sent some output
42 @signal clientRawInputSent(debuggerId) emitted after the data was sent
43 to the indicated debug client
44 @signal clientLine(filename, lineno, debuggerId, threadName, forStack)
45 emitted after the debug client has executed a line of code
46 @signal clientStack(stack, debuggerId, threadName) emitted after the
47 debug client has executed a line of code
48 @signal clientThreadList(currentId, threadList, debuggerId) emitted after
49 a thread list has been received
50 @signal clientThreadSet(debuggerId) emitted after the client has
51 acknowledged the change of the current thread
52 @signal clientVariables(scope, variables, debuggerId) emitted after a
53 variables dump has been received
54 @signal clientVariable(scope, variables, debuggerId) emitted after a dump
55 for one class variable has been received
56 @signal clientStatement(continue, debuggerId) emitted after an interactive
57 command has been executed. The parameter is False to indicate that the
58 command is complete and True if it needs more input.
59 @signal clientDisassembly(disassembly, debuggerId) emitted after the client
60 has sent a disassembly of the code raising an exception
61 @signal clientException(exceptionType, exceptionMessage, stackTrace,
62 debuggerId, threadName) emitted after an exception occured on the
63 client side
64 @signal clientSyntaxError(message, filename, linenumber, characternumber,
65 debuggerId, threadName) emitted after a syntax error has been detected
66 on the client side
67 @signal clientSignal(message, filename, linenumber, function name,
68 function arguments, debuggerId) emitted after a signal has been
69 generated on the client side
70 @signal clientDisconnected(str) emitted after a debug client has
71 disconnected (i.e. closed the network socket)
72 @signal clientExit(str, int, str, bool, str) emitted after the client has
73 exited giving the program name, the exit status, an exit message, an
74 indication to be quiet and the ID of the exited client
75 @signal mainClientExit() emitted to indicate that the main client process
76 has exited
77 @signal lastClientExited() emitted to indicate that the last connected
78 debug client has terminated
79 @signal clientClearBreak(filename, lineno, debuggerId) emitted after the
80 debug client has decided to clear a temporary breakpoint
81 @signal clientBreakConditionError(fn, lineno, debuggerId) emitted after the
82 client has signaled a syntax error in a breakpoint condition
83 @signal clientClearWatch(condition, debuggerId) emitted after the debug
84 client has decided to clear a temporary watch expression
85 @signal clientWatchConditionError(condition, debuggerId) emitted after the
86 client has signaled a syntax error in a watch expression
87 @signal clientRawInput(prompt, echo, debuggerId) emitted after a raw input
88 request was received
89 @signal clientBanner(version, platform, venvname) emitted after
90 the client banner data was received
91 @signal clientCapabilities(capabilities, cltype, venvname) emitted after
92 the clients capabilities were received
93 @signal clientCompletionList(completionList, text) emitted after the
94 commandline completion list and the reworked search string was
95 received from the client
96 @signal passiveDebugStarted(str, bool) emitted after the debug client has
97 connected in passive debug mode
98 @signal clientGone(bool) emitted if the client went away (planned or
99 unplanned)
100 @signal clientInterpreterChanged(str) emitted to signal a change of the
101 client interpreter
102 @signal callTraceInfo emitted after the client reported the call trace
103 data (isCall, fromFile, fromLine, fromFunction, toFile, toLine,
104 toFunction, debuggerId)
105 @signal appendStdout(msg) emitted when a passive debug connection is
106 established or lost
107 @signal clientDebuggerId(debuggerId) emitted to indicate a newly connected
108 debugger backend
109 """
110
111 clientClearBreak = pyqtSignal(str, int, str)
112 clientClearWatch = pyqtSignal(str, str)
113 clientGone = pyqtSignal(bool)
114 clientProcessStdout = pyqtSignal(str)
115 clientProcessStderr = pyqtSignal(str)
116 clientRawInputSent = pyqtSignal(str)
117 clientOutput = pyqtSignal(str)
118 clientLine = pyqtSignal(str, int, str, str, bool)
119 clientStack = pyqtSignal(list, str, str)
120 clientThreadList = pyqtSignal("PyQt_PyObject", list, str)
121 clientThreadSet = pyqtSignal(str)
122 clientVariables = pyqtSignal(int, list, str)
123 clientVariable = pyqtSignal(int, list, str)
124 clientStatement = pyqtSignal(bool, str)
125 clientDisassembly = pyqtSignal(dict, str)
126 clientException = pyqtSignal(str, str, list, str, str)
127 clientSyntaxError = pyqtSignal(str, str, int, int, str, str)
128 clientSignal = pyqtSignal(str, str, int, str, str, str)
129 clientDisconnected = pyqtSignal(str)
130 clientExit = pyqtSignal(str, int, str, bool, str)
131 mainClientExit = pyqtSignal()
132 lastClientExited = pyqtSignal()
133 clientBreakConditionError = pyqtSignal(str, int, str)
134 clientWatchConditionError = pyqtSignal(str, str)
135 clientRawInput = pyqtSignal(str, bool, str)
136 clientBanner = pyqtSignal(str, str, str)
137 clientCapabilities = pyqtSignal(int, str, str)
138 clientCompletionList = pyqtSignal(list, str)
139 clientInterpreterChanged = pyqtSignal(str)
140 clientDebuggerId = pyqtSignal(str)
141 passiveDebugStarted = pyqtSignal(str, bool)
142 callTraceInfo = pyqtSignal(bool, str, str, str, str, str, str, str)
143 appendStdout = pyqtSignal(str)
144
145 def __init__(
146 self,
147 originalPathString,
148 preventPassiveDebugging=False,
149 project=None,
150 parent=None,
151 ):
152 """
153 Constructor
154
155 @param originalPathString original PATH environment variable
156 @type str
157 @param preventPassiveDebugging flag overriding the PassiveDbgEnabled
158 setting (defaults to False)
159 @type bool (optional)
160 @param project reference to the project object (defaults to None)
161 @type Project (optional)
162 @param parent reference to the parent object
163 @type QObject
164 """
165 super().__init__(parent)
166
167 self.__originalPathString = originalPathString
168
169 self.__debuggerInterfaces = {}
170 # the interface name is the key, a function to get the
171 # registration data is the value
172 self.__debuggerInterfaceRegistry = {}
173 # the client language is the key, a list containing the client
174 # capabilities, a list of associated file extensions, a
175 # function reference to create the debugger interface (see
176 # __createDebuggerInterface() below) and the interface name is
177 # the value
178
179 # create our models
180 self.breakpointModel = BreakPointModel(project, self)
181 self.watchpointModel = WatchPointModel(self)
182 self.watchSpecialCreated = self.tr(
183 "created", "must be same as in EditWatchpointDialog"
184 )
185 self.watchSpecialChanged = self.tr(
186 "changed", "must be same as in EditWatchpointDialog"
187 )
188
189 # arrays to track already reported issues
190 self.__reportedBreakpointIssues = []
191 self.__reportedWatchpointIssues = []
192
193 self.networkInterface = Preferences.getDebugger("NetworkInterface")
194 if self.networkInterface == "all":
195 hostAddress = QHostAddress("0.0.0.0")
196 # QHostAddress.SpecialAddress.Any) # secok
197 elif self.networkInterface == "allv6":
198 hostAddress = QHostAddress("::")
199 # QHostAddress.SpecialAddress.AnyIPv6)
200 else:
201 hostAddress = QHostAddress(self.networkInterface)
202 (
203 self.networkInterfaceName,
204 self.networkInterfaceIndex,
205 ) = self.__getNetworkInterfaceAndIndex(self.networkInterface)
206
207 if not preventPassiveDebugging and Preferences.getDebugger("PassiveDbgEnabled"):
208 sock = Preferences.getDebugger("PassiveDbgPort") # default: 42424
209 self.listen(hostAddress, sock)
210 self.passive = True
211 self.passiveClientExited = False
212 else:
213 if hostAddress.toString().lower().startswith("fe80"):
214 hostAddress.setScopeId(self.networkInterfaceName)
215 self.listen(hostAddress)
216 self.passive = False
217
218 self.debuggerInterface = None
219 self.debugging = False
220 self.running = False
221 self.clientProcess = None
222 self.clientInterpreter = ""
223 self.clientType = Preferences.getSettings().value("DebugClient/Type")
224 if self.clientType is None:
225 self.clientType = "Python3"
226
227 self.lastClientType = ""
228 self.__autoClearShell = False
229 self.__forProject = False
230
231 self.clientClearBreak.connect(self.__clientClearBreakPoint)
232 self.clientClearWatch.connect(self.__clientClearWatchPoint)
233 self.newConnection.connect(self.__newConnection)
234
235 self.breakpointModel.rowsAboutToBeRemoved.connect(self.__deleteBreakPoints)
236 self.breakpointModel.dataAboutToBeChanged.connect(
237 self.__breakPointDataAboutToBeChanged
238 )
239 self.breakpointModel.dataChanged.connect(self.__changeBreakPoints)
240 self.breakpointModel.rowsInserted.connect(self.__addBreakPoints)
241
242 self.watchpointModel.rowsAboutToBeRemoved.connect(self.__deleteWatchPoints)
243 self.watchpointModel.dataAboutToBeChanged.connect(
244 self.__watchPointDataAboutToBeChanged
245 )
246 self.watchpointModel.dataChanged.connect(self.__changeWatchPoints)
247 self.watchpointModel.rowsInserted.connect(self.__addWatchPoints)
248
249 self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
250
251 self.__multiprocessNoDebugList = []
252
253 self.__registerDebuggerInterfaces()
254
255 def getHostAddress(self, localhost):
256 """
257 Public method to get the IP address or hostname the debug server is
258 listening.
259
260 @param localhost flag indicating to return the address for localhost
261 @type bool
262 @return IP address or hostname
263 @rtype str
264 """
265 if self.networkInterface == "all":
266 if localhost:
267 return "127.0.0.1"
268 else:
269 return "{0}@@v4".format(QHostInfo.localHostName())
270 elif self.networkInterface == "allv6":
271 if localhost:
272 return "::1"
273 else:
274 return "{0}@@v6".format(QHostInfo.localHostName())
275 else:
276 return "{0}@@i{1}".format(self.networkInterface, self.networkInterfaceIndex)
277
278 def __getNetworkInterfaceAndIndex(self, address):
279 """
280 Private method to determine the network interface and the interface
281 index.
282
283 @param address address to determine the info for
284 @type str
285 @return tuple of network interface name and index
286 @rtype tuple of (str, int)
287 """
288 if address not in ["all", "allv6"]:
289 for networkInterface in QNetworkInterface.allInterfaces():
290 addressEntries = networkInterface.addressEntries()
291 if len(addressEntries) > 0:
292 for addressEntry in addressEntries:
293 if addressEntry.ip().toString().lower() == address.lower():
294 return (
295 networkInterface.humanReadableName(),
296 networkInterface.index(),
297 )
298
299 return "", 0
300
301 def preferencesChanged(self):
302 """
303 Public slot to handle the preferencesChanged signal.
304 """
305 registeredInterfaces = {}
306 for interfaceName in self.__debuggerInterfaces:
307 registeredInterfaces[interfaceName] = self.__debuggerInterfaces[
308 interfaceName
309 ]
310
311 self.__debuggerInterfaceRegistry = {}
312 for interfaceName, getRegistryData in registeredInterfaces.items():
313 self.registerDebuggerInterface(
314 interfaceName, getRegistryData, reregister=True
315 )
316
317 self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
318
319 def registerDebuggerInterface(
320 self, interfaceName, getRegistryData, reregister=False
321 ):
322 """
323 Public method to register a debugger interface.
324
325 @param interfaceName name of the debugger interface
326 @type str
327 @param getRegistryData reference to a function to be called
328 to get the debugger interface details. This method shall
329 return the client language, the client capabilities, the
330 list of associated file extensions and a function reference
331 to create the debugger interface (see __createDebuggerInterface())
332 @type function
333 @param reregister flag indicating to re-register the interface
334 @type bool
335 """
336 if interfaceName in self.__debuggerInterfaces and not reregister:
337 EricMessageBox.warning(
338 None,
339 self.tr("Register Debugger Interface"),
340 self.tr(
341 """<p>The debugger interface <b>{0}</b> has already"""
342 """ been registered. Ignoring this request.</p>"""
343 ),
344 )
345 return
346
347 if not reregister:
348 self.__debuggerInterfaces[interfaceName] = getRegistryData
349 registryDataList = getRegistryData()
350 if registryDataList:
351 for (
352 clientLanguage,
353 clientCapabilities,
354 clientExtensions,
355 interfaceCreator,
356 ) in registryDataList:
357 self.__debuggerInterfaceRegistry[clientLanguage] = [
358 clientCapabilities,
359 clientExtensions,
360 interfaceCreator,
361 interfaceName,
362 ]
363
364 def unregisterDebuggerInterface(self, interfaceName):
365 """
366 Public method to unregister a debugger interface.
367
368 @param interfaceName interfaceName of the debugger interface
369 @type str
370 """
371 if interfaceName in self.__debuggerInterfaces:
372 clientLanguages = []
373 for (
374 clientLanguage,
375 registryData,
376 ) in self.__debuggerInterfaceRegistry.items():
377 if interfaceName == registryData[-1]:
378 clientLanguages.append(clientLanguage)
379 for clientLanguage in clientLanguages:
380 del self.__debuggerInterfaceRegistry[clientLanguage]
381 del self.__debuggerInterfaces[interfaceName]
382
383 def __findLanguageForExtension(self, ext):
384 """
385 Private method to get the language associated with a file extension.
386
387 @param ext file extension
388 @type str
389 @return associated language
390 @rtype str
391 """
392 for language in self.__debuggerInterfaceRegistry:
393 if ext in self.__debuggerInterfaceRegistry[language][1]:
394 return language
395
396 return ""
397
398 def __registerDebuggerInterfaces(self):
399 """
400 Private method to register the available internal debugger interfaces.
401 """
402 for name, interface in DebuggerInterfaces.items():
403 modName = "Debugger.{0}".format(interface)
404 mod = __import__(modName)
405 components = modName.split(".")
406 for comp in components[1:]:
407 mod = getattr(mod, comp)
408
409 self.registerDebuggerInterface(name, mod.getRegistryData)
410
411 def getSupportedLanguages(self, shellOnly=False):
412 """
413 Public slot to return the supported programming languages.
414
415 @param shellOnly flag indicating only languages supporting an
416 interactive shell should be returned
417 @type bool
418 @return list of supported languages
419 @rtype list of str
420 """
421 languages = list(self.__debuggerInterfaceRegistry.keys())
422 with contextlib.suppress(ValueError):
423 languages.remove("None")
424
425 if shellOnly:
426 languages = [
427 lang
428 for lang in languages
429 if self.__debuggerInterfaceRegistry[lang][0]
430 & DebugClientCapabilities.HasShell
431 ]
432
433 return languages[:]
434
435 def getExtensions(self, language):
436 """
437 Public slot to get the extensions associated with the given language.
438
439 @param language language to get extensions for
440 @type str
441 @return tuple of extensions associated with the language
442 @rtype tuple of str
443 """
444 if language in self.__debuggerInterfaceRegistry:
445 return tuple(self.__debuggerInterfaceRegistry[language][1])
446 else:
447 return ()
448
449 def __createDebuggerInterface(self, clientType=None):
450 """
451 Private slot to create the debugger interface object.
452
453 @param clientType type of the client interface to be created
454 @type str
455 """
456 if self.lastClientType != self.clientType or clientType is not None:
457 if clientType is None:
458 clientType = self.clientType
459 if clientType in self.__debuggerInterfaceRegistry:
460 self.debuggerInterface = self.__debuggerInterfaceRegistry[clientType][
461 2
462 ](self, self.passive)
463 else:
464 self.debuggerInterface = self.__debuggerInterfaceRegistry["None"][2](
465 self, self.passive
466 )
467 self.clientType = "None"
468
469 def __setClientType(self, clType):
470 """
471 Private method to set the client type.
472
473 @param clType type of client to be started
474 @type str
475 """
476 if clType is not None and clType in self.getSupportedLanguages():
477 self.clientType = clType
478 Preferences.getSettings().setValue("DebugClient/Type", self.clientType)
479
480 def startClient(
481 self,
482 unplanned=True,
483 clType=None,
484 forProject=False,
485 runInConsole=False,
486 venvName="",
487 workingDir=None,
488 configOverride=None,
489 ):
490 """
491 Public method to start a debug client.
492
493 @param unplanned flag indicating that the client has died
494 @type bool
495 @param clType type of client to be started
496 @type str
497 @param forProject flag indicating a project related action
498 @type bool
499 @param runInConsole flag indicating to start the debugger in a
500 console window
501 @type bool
502 @param venvName name of the virtual environment to be used
503 @type str
504 @param workingDir directory to start the debugger client in
505 @type str
506 @param configOverride dictionary containing the global config override
507 data
508 @type dict
509 """
510 self.running = False
511
512 if (
513 (not self.passive or not self.passiveClientExited)
514 and self.debuggerInterface
515 and self.debuggerInterface.isConnected()
516 ):
517 self.shutdownServer()
518 self.debugging = False
519 self.clientGone.emit(unplanned and self.debugging)
520
521 if clType:
522 if clType not in self.getSupportedLanguages():
523 # a not supported client language was requested
524 return
525
526 self.__setClientType(clType)
527
528 # only start the client, if we are not in passive mode
529 if not self.passive:
530 if self.clientProcess:
531 with contextlib.suppress(RuntimeError):
532 # Workaround: The wrapped C/C++ object of type QProcess
533 # gets deleted prematurely sometimes.
534 self.clientProcess.kill()
535 self.clientProcess.waitForFinished(1000)
536 self.clientProcess.deleteLater()
537 self.clientProcess = None
538
539 self.__forProject = forProject
540 self.__createDebuggerInterface()
541 if forProject:
542 project = ericApp().getObject("Project")
543 if not project.isDebugPropertiesLoaded():
544 (
545 self.clientProcess,
546 isNetworked,
547 clientInterpreter,
548 ) = self.debuggerInterface.startRemote(
549 self.serverPort(),
550 runInConsole,
551 venvName,
552 self.__originalPathString,
553 workingDir=workingDir,
554 configOverride=configOverride,
555 )
556 else:
557 (
558 self.clientProcess,
559 isNetworked,
560 clientInterpreter,
561 ) = self.debuggerInterface.startRemoteForProject(
562 self.serverPort(),
563 runInConsole,
564 venvName,
565 self.__originalPathString,
566 workingDir=workingDir,
567 configOverride=configOverride,
568 )
569 else:
570 (
571 self.clientProcess,
572 isNetworked,
573 clientInterpreter,
574 ) = self.debuggerInterface.startRemote(
575 self.serverPort(),
576 runInConsole,
577 venvName,
578 self.__originalPathString,
579 workingDir=workingDir,
580 configOverride=configOverride,
581 )
582
583 if self.clientProcess:
584 self.clientProcess.readyReadStandardError.connect(
585 self.__clientProcessError
586 )
587 self.clientProcess.readyReadStandardOutput.connect(
588 self.__clientProcessOutput
589 )
590
591 # Perform actions necessary, if client type has changed
592 if self.lastClientType != self.clientType:
593 self.lastClientType = self.clientType
594 self.remoteBanner()
595 elif self.__autoClearShell:
596 self.__autoClearShell = False
597 self.remoteBanner()
598 else:
599 if clType and self.lastClientType:
600 self.__setClientType(self.lastClientType)
601 else:
602 self.__createDebuggerInterface("None")
603 clientInterpreter = ""
604
605 if clientInterpreter != self.clientInterpreter:
606 self.clientInterpreter = clientInterpreter
607 self.clientInterpreterChanged.emit(clientInterpreter)
608
609 def __clientProcessOutput(self):
610 """
611 Private slot to process client output received via stdout.
612 """
613 output = str(
614 self.clientProcess.readAllStandardOutput(),
615 Preferences.getSystem("IOEncoding"),
616 "replace",
617 )
618 self.clientProcessStdout.emit(output)
619
620 def __clientProcessError(self):
621 """
622 Private slot to process client output received via stderr.
623 """
624 error = str(
625 self.clientProcess.readAllStandardError(),
626 Preferences.getSystem("IOEncoding"),
627 "replace",
628 )
629 self.clientProcessStderr.emit(error)
630
631 @pyqtSlot(str, int)
632 def __clientClearBreakPoint(self, fn, lineno):
633 """
634 Private slot to handle the clientClearBreak signal.
635
636 @param fn filename of breakpoint to clear
637 @type str
638 @param lineno line number of breakpoint to clear
639 @type int
640 """
641 if self.debugging:
642 index = self.breakpointModel.getBreakPointIndex(fn, lineno)
643 self.breakpointModel.deleteBreakPointByIndex(index)
644 if (fn, lineno) in self.__reportedBreakpointIssues:
645 self.__reportedBreakpointIssues.remove((fn, lineno))
646
647 def __deleteBreakPoints(self, parentIndex, start, end):
648 """
649 Private slot to delete breakpoints.
650
651 @param parentIndex index of parent item
652 @type QModelIndex
653 @param start start row
654 @type int
655 @param end end row
656 @type int
657 """
658 if self.debugging:
659 for row in range(start, end + 1):
660 index = self.breakpointModel.index(row, 0, parentIndex)
661 fn, lineno = self.breakpointModel.getBreakPointByIndex(index)[0:2]
662 # delete the breakpoints of all connected backends
663 self.remoteBreakpoint("", fn, lineno, False)
664 if (fn, lineno) in self.__reportedBreakpointIssues:
665 self.__reportedBreakpointIssues.remove((fn, lineno))
666
667 def __changeBreakPoints(self, startIndex, endIndex):
668 """
669 Private slot to set changed breakpoints.
670
671 @param startIndex starting index of the change breakpoins
672 @type QModelIndex
673 @param endIndex ending index of the change breakpoins
674 @type QModelIndex
675 """
676 if self.debugging:
677 self.__addBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
678
679 def __breakPointDataAboutToBeChanged(self, startIndex, endIndex):
680 """
681 Private slot to handle the dataAboutToBeChanged signal of the
682 breakpoint model.
683
684 @param startIndex start index of the rows to be changed
685 @type QModelIndex
686 @param endIndex end index of the rows to be changed
687 @type QModelIndex
688 """
689 if self.debugging:
690 self.__deleteBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
691
692 def __addBreakPoints(self, parentIndex, start, end, debuggerId=""):
693 """
694 Private slot to add breakpoints.
695
696 @param parentIndex index of parent item
697 @type QModelIndex
698 @param start start row
699 @type int
700 @param end end row
701 @type int
702 @param debuggerId ID of the debugger backend to send to. If this is
703 empty, they will be broadcast to all connected backends.
704 @type str
705 """
706 if self.debugging:
707 for row in range(start, end + 1):
708 index = self.breakpointModel.index(row, 0, parentIndex)
709 (
710 fn,
711 lineno,
712 cond,
713 temp,
714 enabled,
715 ignorecount,
716 ) = self.breakpointModel.getBreakPointByIndex(index)[:6]
717
718 if (fn, lineno) in self.__reportedBreakpointIssues:
719 self.__reportedBreakpointIssues.remove((fn, lineno))
720
721 self.remoteBreakpoint(debuggerId, fn, lineno, True, cond, temp)
722 if not enabled:
723 self.__remoteBreakpointEnable(debuggerId, fn, lineno, False)
724 if ignorecount:
725 self.__remoteBreakpointIgnore(debuggerId, fn, lineno, ignorecount)
726
727 def __makeWatchCondition(self, cond, special):
728 """
729 Private method to construct the condition string.
730
731 @param cond condition
732 @type str
733 @param special special condition
734 @type str
735 @return condition string
736 @rtype str
737 """
738 if special == "":
739 _cond = cond
740 else:
741 if special == self.watchSpecialCreated:
742 _cond = "{0} ??created??".format(cond)
743 elif special == self.watchSpecialChanged:
744 _cond = "{0} ??changed??".format(cond)
745 return _cond
746
747 def __splitWatchCondition(self, cond):
748 """
749 Private method to split a remote watch expression.
750
751 @param cond remote expression
752 @type str
753 @return tuple of local expression (string) and special condition
754 @rtype str
755 """
756 if cond.endswith(" ??created??"):
757 cond, special = cond.split()
758 special = self.watchSpecialCreated
759 elif cond.endswith(" ??changed??"):
760 cond, special = cond.split()
761 special = self.watchSpecialChanged
762 else:
763 cond = cond
764 special = ""
765
766 return cond, special
767
768 @pyqtSlot(str)
769 def __clientClearWatchPoint(self, condition):
770 """
771 Private slot to handle the clientClearWatch signal.
772
773 @param condition expression of watch expression to clear
774 @type str
775 """
776 if self.debugging:
777 cond, special = self.__splitWatchCondition(condition)
778 index = self.watchpointModel.getWatchPointIndex(cond, special)
779 self.watchpointModel.deleteWatchPointByIndex(index)
780 if condition in self.__reportedWatchpointIssues:
781 self.__reportedWatchpointIssues.remove(condition)
782
783 def __deleteWatchPoints(self, parentIndex, start, end):
784 """
785 Private slot to delete watch expressions.
786
787 @param parentIndex index of parent item
788 @type QModelIndex
789 @param start start row
790 @type int
791 @param end end row
792 @type int
793 """
794 if self.debugging:
795 for row in range(start, end + 1):
796 index = self.watchpointModel.index(row, 0, parentIndex)
797 cond, special = self.watchpointModel.getWatchPointByIndex(index)[0:2]
798 cond = self.__makeWatchCondition(cond, special)
799 self.__remoteWatchpoint("", cond, False)
800 if cond in self.__reportedWatchpointIssues:
801 self.__reportedWatchpointIssues.remove(cond)
802
803 def __watchPointDataAboutToBeChanged(self, startIndex, endIndex):
804 """
805 Private slot to handle the dataAboutToBeChanged signal of the
806 watch expression model.
807
808 @param startIndex start index of the rows to be changed
809 @type QModelIndex
810 @param endIndex end index of the rows to be changed
811 @type QModelIndex
812 """
813 if self.debugging:
814 self.__deleteWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
815
816 def __addWatchPoints(self, parentIndex, start, end, debuggerId=""):
817 """
818 Private slot to set a watch expression.
819
820 @param parentIndex index of parent item
821 @type QModelIndex
822 @param start start row
823 @type int
824 @param end end row
825 @type int
826 @param debuggerId ID of the debugger backend to send to. If this is
827 empty, they will be broadcast to all connected backends.
828 @type str
829 """
830 if self.debugging:
831 for row in range(start, end + 1):
832 index = self.watchpointModel.index(row, 0, parentIndex)
833 (
834 cond,
835 special,
836 temp,
837 enabled,
838 ignorecount,
839 ) = self.watchpointModel.getWatchPointByIndex(index)[:5]
840 cond = self.__makeWatchCondition(cond, special)
841
842 if cond in self.__reportedWatchpointIssues:
843 self.__reportedWatchpointIssues.remove(cond)
844
845 self.__remoteWatchpoint(debuggerId, cond, True, temp)
846 if not enabled:
847 self.__remoteWatchpointEnable(debuggerId, cond, False)
848 if ignorecount:
849 self.__remoteWatchpointIgnore(debuggerId, cond, ignorecount)
850
851 def __changeWatchPoints(self, startIndex, endIndex):
852 """
853 Private slot to set changed watch expressions.
854
855 @param startIndex start index of the rows to be changed
856 @type QModelIndex
857 @param endIndex end index of the rows to be changed
858 @type QModelIndex
859 """
860 if self.debugging:
861 self.__addWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
862
863 def getClientCapabilities(self, clientType):
864 """
865 Public method to retrieve the debug clients capabilities.
866
867 @param clientType debug client type
868 @type str
869 @return debug client capabilities
870 @rtype int
871 """
872 try:
873 return self.__debuggerInterfaceRegistry[clientType][0]
874 except KeyError:
875 return 0 # no capabilities
876
877 def getClientInterpreter(self):
878 """
879 Public method to get the interpreter of the debug client.
880
881 @return interpreter of the debug client
882 @rtype str
883 """
884 return self.clientInterpreter
885
886 def getClientType(self):
887 """
888 Public method to get the currently running debug client type.
889
890 @return debug client type
891 @rtype str
892 """
893 return self.clientType
894
895 def isClientProcessUp(self):
896 """
897 Public method to check, if the debug client process is up.
898
899 @return flag indicating a running debug client process
900 @rtype bool
901 """
902 return self.clientProcess is not None
903
904 def __newConnection(self):
905 """
906 Private slot to handle a new connection.
907 """
908 sock = self.nextPendingConnection()
909 peerAddress = sock.peerAddress().toString()
910 if peerAddress not in Preferences.getDebugger("AllowedHosts"):
911 # the peer is not allowed to connect
912 res = EricMessageBox.yesNo(
913 None,
914 self.tr("Connection from illegal host"),
915 self.tr(
916 """<p>A connection was attempted by the illegal host"""
917 """ <b>{0}</b>. Accept this connection?</p>"""
918 ).format(peerAddress),
919 icon=EricMessageBox.Warning,
920 )
921 if not res:
922 sock.abort()
923 return
924 else:
925 allowedHosts = Preferences.getDebugger("AllowedHosts")
926 allowedHosts.append(peerAddress)
927 Preferences.setDebugger("AllowedHosts", allowedHosts)
928
929 if self.passive:
930 self.__createDebuggerInterface(Preferences.getDebugger("PassiveDbgType"))
931
932 self.debuggerInterface.newConnection(sock)
933
934 def masterClientConnected(self):
935 """
936 Public method to perform actions after the master client has finally
937 established the connection.
938 """
939 # Perform actions necessary, if client type has changed
940 if self.lastClientType != self.clientType:
941 self.lastClientType = self.clientType
942 self.remoteBanner()
943 elif self.__autoClearShell:
944 self.__autoClearShell = False
945 self.remoteBanner()
946 elif self.passive:
947 self.remoteBanner()
948
949 def shutdownServer(self):
950 """
951 Public method to cleanly shut down.
952
953 It closes our socket and shuts down
954 the debug client. (Needed on Win OS)
955 """
956 if self.debuggerInterface is not None:
957 self.debuggerInterface.shutdown()
958
959 def remoteEnvironment(self, env):
960 """
961 Public method to set the environment for a program to debug, run, ...
962
963 @param env environment settings
964 @type str
965 """
966 envlist = shlex.split(env)
967 envdict = {}
968 for el in envlist:
969 if "=" in el:
970 key, value = el.split("=", 1)
971 envdict[key] = value
972 else:
973 envdict[el] = ""
974 self.debuggerInterface.remoteEnvironment(envdict)
975
976 def remoteLoad(
977 self,
978 venvName,
979 fn,
980 argv,
981 wd,
982 env,
983 autoClearShell=True,
984 tracePython=False,
985 autoContinue=True,
986 forProject=False,
987 runInConsole=False,
988 clientType="",
989 enableCallTrace=False,
990 enableMultiprocess=False,
991 multiprocessNoDebug="",
992 configOverride=None,
993 ):
994 """
995 Public method to load a new program to debug.
996
997 @param venvName name of the virtual environment to be used
998 @type str
999 @param fn the filename to debug
1000 @type str
1001 @param argv the command line arguments to pass to the program
1002 @type str
1003 @param wd the working directory for the program
1004 @type str
1005 @param env environment parameter settings
1006 @type str
1007 @param autoClearShell flag indicating, that the interpreter window
1008 should be cleared
1009 @type bool
1010 @param tracePython flag indicating if the Python library should be
1011 traced as well
1012 @type bool
1013 @param autoContinue flag indicating, that the debugger should not
1014 stop at the first executable line
1015 @type bool
1016 @param forProject flag indicating a project related action
1017 @type bool
1018 @param runInConsole flag indicating to start the debugger in a
1019 console window
1020 @type bool
1021 @param clientType client type to be used
1022 @type str
1023 @param enableCallTrace flag indicating to enable the call trace
1024 function
1025 @type bool
1026 @param enableMultiprocess flag indicating to perform multiprocess
1027 debugging
1028 @type bool
1029 @param multiprocessNoDebug space separated list of programs not to be
1030 debugged
1031 @type str
1032 @param configOverride dictionary containing the global config override
1033 data
1034 @type dict
1035 """
1036 self.__autoClearShell = autoClearShell
1037 self.__multiprocessNoDebugList = [
1038 s.strip() for s in multiprocessNoDebug.split(os.pathsep)
1039 ]
1040
1041 if clientType not in self.getSupportedLanguages():
1042 # a not supported client language was requested
1043 EricMessageBox.critical(
1044 None,
1045 self.tr("Start Debugger"),
1046 self.tr(
1047 """<p>The debugger type <b>{0}</b> is not supported"""
1048 """ or not configured.</p>"""
1049 ).format(clientType),
1050 )
1051 return
1052
1053 # Restart the client
1054 try:
1055 if clientType:
1056 self.__setClientType(clientType)
1057 else:
1058 self.__setClientType(
1059 self.__findLanguageForExtension(os.path.splitext(fn)[1])
1060 )
1061 except KeyError:
1062 self.__setClientType("Python3") # assume it is a Python3 file
1063 self.startClient(
1064 False,
1065 forProject=forProject,
1066 runInConsole=runInConsole,
1067 venvName=venvName,
1068 configOverride=configOverride,
1069 )
1070
1071 self.setCallTraceEnabled("", enableCallTrace)
1072 self.remoteEnvironment(env)
1073
1074 self.debuggerInterface.remoteLoad(
1075 fn,
1076 argv,
1077 wd,
1078 tracePython,
1079 autoContinue,
1080 enableMultiprocess=enableMultiprocess,
1081 )
1082 self.debugging = True
1083 self.running = True
1084 self.__restoreBreakpoints()
1085 self.__restoreWatchpoints()
1086 self.__restoreNoDebugList()
1087
1088 def remoteRun(
1089 self,
1090 venvName,
1091 fn,
1092 argv,
1093 wd,
1094 env,
1095 autoClearShell=True,
1096 forProject=False,
1097 runInConsole=False,
1098 clientType="",
1099 configOverride=None,
1100 ):
1101 """
1102 Public method to load a new program to run.
1103
1104 @param venvName name of the virtual environment to be used
1105 @type str
1106 @param fn the filename to debug
1107 @type str
1108 @param argv the command line arguments to pass to the program
1109 @type str
1110 @param wd the working directory for the program
1111 @type str
1112 @param env environment parameter settings
1113 @type str
1114 @param autoClearShell flag indicating, that the interpreter window
1115 should be cleared
1116 @type bool
1117 @param forProject flag indicating a project related action
1118 @type bool
1119 @param runInConsole flag indicating to start the debugger in a
1120 console window
1121 @type bool
1122 @param clientType client type to be used
1123 @type str
1124 @param configOverride dictionary containing the global config override
1125 data
1126 @type dict
1127 """
1128 self.__autoClearShell = autoClearShell
1129
1130 if clientType not in self.getSupportedLanguages():
1131 EricMessageBox.critical(
1132 None,
1133 self.tr("Start Debugger"),
1134 self.tr(
1135 """<p>The debugger type <b>{0}</b> is not supported"""
1136 """ or not configured.</p>"""
1137 ).format(clientType),
1138 )
1139 # a not supported client language was requested
1140 return
1141
1142 # Restart the client
1143 try:
1144 if clientType:
1145 self.__setClientType(clientType)
1146 else:
1147 self.__setClientType(
1148 self.__findLanguageForExtension(os.path.splitext(fn)[1])
1149 )
1150 except KeyError:
1151 self.__setClientType("Python3") # assume it is a Python3 file
1152 self.startClient(
1153 False,
1154 forProject=forProject,
1155 runInConsole=runInConsole,
1156 venvName=venvName,
1157 configOverride=configOverride,
1158 )
1159
1160 self.remoteEnvironment(env)
1161
1162 self.debuggerInterface.remoteRun(fn, argv, wd)
1163 self.debugging = False
1164 self.running = True
1165
1166 def remoteCoverage(
1167 self,
1168 venvName,
1169 fn,
1170 argv,
1171 wd,
1172 env,
1173 autoClearShell=True,
1174 erase=False,
1175 forProject=False,
1176 runInConsole=False,
1177 clientType="",
1178 configOverride=None,
1179 ):
1180 """
1181 Public method to load a new program to collect coverage data.
1182
1183 @param venvName name of the virtual environment to be used
1184 @type str
1185 @param fn the filename to debug
1186 @type str
1187 @param argv the command line arguments to pass to the program
1188 @type str
1189 @param wd the working directory for the program
1190 @type str
1191 @param env environment parameter settings
1192 @type str
1193 @param autoClearShell flag indicating, that the interpreter window
1194 should be cleared
1195 @type bool
1196 @param erase flag indicating that coverage info should be
1197 cleared first
1198 @type bool
1199 @param forProject flag indicating a project related action
1200 @type bool
1201 @param runInConsole flag indicating to start the debugger in a
1202 console window
1203 @type bool
1204 @param clientType client type to be used
1205 @type str
1206 @param configOverride dictionary containing the global config override
1207 data
1208 @type dict
1209 """
1210 self.__autoClearShell = autoClearShell
1211
1212 if clientType not in self.getSupportedLanguages():
1213 # a not supported client language was requested
1214 EricMessageBox.critical(
1215 None,
1216 self.tr("Start Debugger"),
1217 self.tr(
1218 """<p>The debugger type <b>{0}</b> is not supported"""
1219 """ or not configured.</p>"""
1220 ).format(clientType),
1221 )
1222 return
1223
1224 # Restart the client
1225 try:
1226 if clientType:
1227 self.__setClientType(clientType)
1228 else:
1229 self.__setClientType(
1230 self.__findLanguageForExtension(os.path.splitext(fn)[1])
1231 )
1232 except KeyError:
1233 self.__setClientType("Python3") # assume it is a Python3 file
1234 self.startClient(
1235 False,
1236 forProject=forProject,
1237 runInConsole=runInConsole,
1238 venvName=venvName,
1239 configOverride=configOverride,
1240 )
1241
1242 self.remoteEnvironment(env)
1243
1244 self.debuggerInterface.remoteCoverage(fn, argv, wd, erase)
1245 self.debugging = False
1246 self.running = True
1247
1248 def remoteProfile(
1249 self,
1250 venvName,
1251 fn,
1252 argv,
1253 wd,
1254 env,
1255 autoClearShell=True,
1256 erase=False,
1257 forProject=False,
1258 runInConsole=False,
1259 clientType="",
1260 configOverride=None,
1261 ):
1262 """
1263 Public method to load a new program to collect profiling data.
1264
1265 @param venvName name of the virtual environment to be used
1266 @type str
1267 @param fn the filename to debug
1268 @type str
1269 @param argv the command line arguments to pass to the program
1270 @type str
1271 @param wd the working directory for the program
1272 @type str
1273 @param env environment parameter settings
1274 @type str
1275 @param autoClearShell flag indicating, that the interpreter window
1276 should be cleared
1277 @type bool
1278 @param erase flag indicating that coverage info should be
1279 cleared first
1280 @type bool
1281 @param forProject flag indicating a project related action
1282 @type bool
1283 @param runInConsole flag indicating to start the debugger in a
1284 console window
1285 @type bool
1286 @param clientType client type to be used
1287 @type str
1288 @param configOverride dictionary containing the global config override
1289 data
1290 @type dict
1291 """
1292 self.__autoClearShell = autoClearShell
1293
1294 if clientType not in self.getSupportedLanguages():
1295 # a not supported client language was requested
1296 EricMessageBox.critical(
1297 None,
1298 self.tr("Start Debugger"),
1299 self.tr(
1300 """<p>The debugger type <b>{0}</b> is not supported"""
1301 """ or not configured.</p>"""
1302 ).format(clientType),
1303 )
1304 return
1305
1306 # Restart the client
1307 try:
1308 if clientType:
1309 self.__setClientType(clientType)
1310 else:
1311 self.__setClientType(
1312 self.__findLanguageForExtension(os.path.splitext(fn)[1])
1313 )
1314 except KeyError:
1315 self.__setClientType("Python3") # assume it is a Python3 file
1316 self.startClient(
1317 False,
1318 forProject=forProject,
1319 runInConsole=runInConsole,
1320 venvName=venvName,
1321 configOverride=configOverride,
1322 )
1323
1324 self.remoteEnvironment(env)
1325
1326 self.debuggerInterface.remoteProfile(fn, argv, wd, erase)
1327 self.debugging = False
1328 self.running = True
1329
1330 def remoteStatement(self, debuggerId, stmt):
1331 """
1332 Public method to execute a Python statement.
1333
1334 @param debuggerId ID of the debugger backend
1335 @type str
1336 @param stmt the Python statement to execute.
1337 @type str
1338 """
1339 self.debuggerInterface.remoteStatement(debuggerId, stmt.rstrip())
1340
1341 def remoteStep(self, debuggerId):
1342 """
1343 Public method to single step the debugged program.
1344
1345 @param debuggerId ID of the debugger backend
1346 @type str
1347 """
1348 self.debuggerInterface.remoteStep(debuggerId)
1349
1350 def remoteStepOver(self, debuggerId):
1351 """
1352 Public method to step over the debugged program.
1353
1354 @param debuggerId ID of the debugger backend
1355 @type str
1356 """
1357 self.debuggerInterface.remoteStepOver(debuggerId)
1358
1359 def remoteStepOut(self, debuggerId):
1360 """
1361 Public method to step out the debugged program.
1362
1363 @param debuggerId ID of the debugger backend
1364 @type str
1365 """
1366 self.debuggerInterface.remoteStepOut(debuggerId)
1367
1368 def remoteStepQuit(self, debuggerId):
1369 """
1370 Public method to stop the debugged program.
1371
1372 @param debuggerId ID of the debugger backend
1373 @type str
1374 """
1375 self.debuggerInterface.remoteStepQuit(debuggerId)
1376
1377 def remoteContinue(self, debuggerId, special=False):
1378 """
1379 Public method to continue the debugged program.
1380
1381 @param debuggerId ID of the debugger backend
1382 @type str
1383 @param special flag indicating a special continue operation
1384 """
1385 self.debuggerInterface.remoteContinue(debuggerId, special)
1386
1387 def remoteContinueUntil(self, debuggerId, line):
1388 """
1389 Public method to continue the debugged program to the given line
1390 or until returning from the current frame.
1391
1392 @param debuggerId ID of the debugger backend
1393 @type str
1394 @param line the new line, where execution should be continued to
1395 @type int
1396 """
1397 self.debuggerInterface.remoteContinueUntil(debuggerId, line)
1398
1399 def remoteMoveIP(self, debuggerId, line):
1400 """
1401 Public method to move the instruction pointer to a different line.
1402
1403 @param debuggerId ID of the debugger backend
1404 @type str
1405 @param line the new line, where execution should be continued
1406 @type int
1407 """
1408 self.debuggerInterface.remoteMoveIP(debuggerId, line)
1409
1410 def remoteBreakpoint(
1411 self, debuggerId, fn, line, setBreakpoint, cond=None, temp=False
1412 ):
1413 """
1414 Public method to set or clear a breakpoint.
1415
1416 @param debuggerId ID of the debugger backend
1417 @type str
1418 @param fn filename the breakpoint belongs to
1419 @type str
1420 @param line linenumber of the breakpoint
1421 @type int
1422 @param setBreakpoint flag indicating setting or resetting a breakpoint
1423 @type bool
1424 @param cond condition of the breakpoint
1425 @type str
1426 @param temp flag indicating a temporary breakpoint
1427 @type bool
1428 """
1429 self.debuggerInterface.remoteBreakpoint(
1430 debuggerId, fn, line, setBreakpoint, cond, temp
1431 )
1432
1433 def __remoteBreakpointEnable(self, debuggerId, fn, line, enable):
1434 """
1435 Private method to enable or disable a breakpoint.
1436
1437 @param debuggerId ID of the debugger backend
1438 @type str
1439 @param fn filename the breakpoint belongs to
1440 @type str
1441 @param line linenumber of the breakpoint
1442 @type int
1443 @param enable flag indicating enabling or disabling a breakpoint
1444 @type bool
1445 """
1446 self.debuggerInterface.remoteBreakpointEnable(debuggerId, fn, line, enable)
1447
1448 def __remoteBreakpointIgnore(self, debuggerId, fn, line, count):
1449 """
1450 Private method to ignore a breakpoint the next couple of occurrences.
1451
1452 @param debuggerId ID of the debugger backend
1453 @type str
1454 @param fn filename the breakpoint belongs to
1455 @type str
1456 @param line linenumber of the breakpoint
1457 @type int
1458 @param count number of occurrences to ignore
1459 @type int
1460 """
1461 self.debuggerInterface.remoteBreakpointIgnore(debuggerId, fn, line, count)
1462
1463 def __remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False):
1464 """
1465 Private method to set or clear a watch expression.
1466
1467 @param debuggerId ID of the debugger backend
1468 @type str
1469 @param cond expression of the watch expression
1470 @type str
1471 @param setWatch flag indicating setting or resetting a watch expression
1472 @type bool
1473 @param temp flag indicating a temporary watch expression
1474 @type bool
1475 """
1476 # cond is combination of cond and special (s. watch expression viewer)
1477 self.debuggerInterface.remoteWatchpoint(debuggerId, cond, setWatch, temp)
1478
1479 def __remoteWatchpointEnable(self, debuggerId, cond, enable):
1480 """
1481 Private method to enable or disable a watch expression.
1482
1483 @param debuggerId ID of the debugger backend
1484 @type str
1485 @param cond expression of the watch expression
1486 @type str
1487 @param enable flag indicating enabling or disabling a watch expression
1488 @type bool
1489 """
1490 # cond is combination of cond and special (s. watch expression viewer)
1491 self.debuggerInterface.remoteWatchpointEnable(debuggerId, cond, enable)
1492
1493 def __remoteWatchpointIgnore(self, debuggerId, cond, count):
1494 """
1495 Private method to ignore a watch expression the next couple of
1496 occurrences.
1497
1498 @param debuggerId ID of the debugger backend
1499 @type str
1500 @param cond expression of the watch expression
1501 @type str
1502 @param count number of occurrences to ignore
1503 @type int
1504 """
1505 # cond is combination of cond and special (s. watch expression viewer)
1506 self.debuggerInterface.remoteWatchpointIgnore(debuggerId, cond, count)
1507
1508 def remoteRawInput(self, debuggerId, inputString):
1509 """
1510 Public method to send the raw input to the debugged program.
1511
1512 @param debuggerId ID of the debugger backend
1513 @type str
1514 @param inputString the raw input
1515 @type str
1516 """
1517 self.debuggerInterface.remoteRawInput(debuggerId, inputString)
1518 self.clientRawInputSent.emit(debuggerId)
1519
1520 def remoteThreadList(self, debuggerId):
1521 """
1522 Public method to request the list of threads from the client.
1523
1524 @param debuggerId ID of the debugger backend
1525 @type str
1526 """
1527 self.debuggerInterface.remoteThreadList(debuggerId)
1528
1529 def remoteSetThread(self, debuggerId, tid):
1530 """
1531 Public method to request to set the given thread as current thread.
1532
1533 @param debuggerId ID of the debugger backend
1534 @type str
1535 @param tid id of the thread
1536 @type int
1537 """
1538 self.debuggerInterface.remoteSetThread(debuggerId, tid)
1539
1540 def remoteClientStack(self, debuggerId):
1541 """
1542 Public method to request the stack of the main thread.
1543
1544 @param debuggerId ID of the debugger backend
1545 @type str
1546 """
1547 self.debuggerInterface.remoteClientStack(debuggerId)
1548
1549 def remoteClientVariables(self, debuggerId, scope, filterList, framenr=0):
1550 """
1551 Public method to request the variables of the debugged program.
1552
1553 @param debuggerId ID of the debugger backend
1554 @type str
1555 @param scope the scope of the variables (0 = local, 1 = global)
1556 @type int
1557 @param filterList list of variable types to filter out
1558 @type list of str
1559 @param framenr framenumber of the variables to retrieve
1560 @type int
1561 """
1562 self.debuggerInterface.remoteClientVariables(
1563 debuggerId, scope, filterList, framenr, self.__maxVariableSize
1564 )
1565
1566 def remoteClientVariable(
1567 self, debuggerId, scope, filterList, var, framenr=0, maxSize=0
1568 ):
1569 """
1570 Public method to request the variables of the debugged program.
1571
1572 @param debuggerId ID of the debugger backend
1573 @type str
1574 @param scope the scope of the variables (0 = local, 1 = global)
1575 @type int
1576 @param filterList list of variable types to filter out
1577 @type list of str
1578 @param var list encoded name of variable to retrieve
1579 @type list of str
1580 @param framenr framenumber of the variables to retrieve
1581 @type int
1582 @param maxSize maximum size the formatted value of a variable will
1583 be shown. If it is bigger than that, a 'too big' indication will
1584 be given (@@TOO_BIG_TO_SHOW@@).
1585 @type int
1586 """
1587 self.debuggerInterface.remoteClientVariable(
1588 debuggerId, scope, filterList, var, framenr, self.__maxVariableSize
1589 )
1590
1591 def remoteClientDisassembly(self, debuggerId):
1592 """
1593 Public method to ask the client for the latest traceback disassembly.
1594
1595 @param debuggerId ID of the debugger backend
1596 @type str
1597 """
1598 with contextlib.suppress(AttributeError):
1599 self.debuggerInterface.remoteClientDisassembly(debuggerId)
1600
1601 def remoteClientSetFilter(self, debuggerId, scope, filterStr):
1602 """
1603 Public method to set a variables filter list.
1604
1605 @param debuggerId ID of the debugger backend
1606 @type str
1607 @param scope the scope of the variables (0 = local, 1 = global)
1608 @type int
1609 @param filterStr regexp string for variable names to filter out
1610 @type str
1611 """
1612 self.debuggerInterface.remoteClientSetFilter(debuggerId, scope, filterStr)
1613
1614 def setCallTraceEnabled(self, debuggerId, on):
1615 """
1616 Public method to set the call trace state.
1617
1618 @param debuggerId ID of the debugger backend
1619 @type str
1620 @param on flag indicating to enable the call trace function
1621 @type bool
1622 """
1623 self.debuggerInterface.setCallTraceEnabled(debuggerId, on)
1624
1625 def remoteBanner(self):
1626 """
1627 Public slot to get the banner info of the remote client.
1628 """
1629 self.debuggerInterface.remoteBanner()
1630
1631 def remoteCapabilities(self):
1632 """
1633 Public slot to get the debug clients capabilities.
1634 """
1635 self.debuggerInterface.remoteCapabilities()
1636
1637 def remoteCompletion(self, debuggerId, text):
1638 """
1639 Public slot to get the a list of possible commandline completions
1640 from the remote client.
1641
1642 @param debuggerId ID of the debugger backend
1643 @type str
1644 @param text the text to be completed
1645 @type str
1646 """
1647 self.debuggerInterface.remoteCompletion(debuggerId, text)
1648
1649 def signalClientOutput(self, line, debuggerId):
1650 """
1651 Public method to process a line of client output.
1652
1653 @param line client output
1654 @type str
1655 @param debuggerId ID of the debugger backend
1656 @type str
1657 """
1658 if debuggerId:
1659 self.clientOutput.emit("{0}: {1}".format(debuggerId, line))
1660 else:
1661 self.clientOutput.emit(line)
1662
1663 def signalClientLine(
1664 self, filename, lineno, debuggerId, forStack=False, threadName=""
1665 ):
1666 """
1667 Public method to process client position feedback.
1668
1669 @param filename name of the file currently being executed
1670 @type str
1671 @param lineno line of code currently being executed
1672 @type int
1673 @param debuggerId ID of the debugger backend
1674 @type str
1675 @param forStack flag indicating this is for a stack dump
1676 @type bool
1677 @param threadName name of the thread signaling the event
1678 @type str
1679 """
1680 self.clientLine.emit(filename, lineno, debuggerId, threadName, forStack)
1681
1682 def signalClientStack(self, stack, debuggerId, threadName=""):
1683 """
1684 Public method to process a client's stack information.
1685
1686 @param stack list of stack entries. Each entry is a tuple of three
1687 values giving the filename, linenumber and method
1688 @type list of lists of (string, integer, string)
1689 @param debuggerId ID of the debugger backend
1690 @type str
1691 @param threadName name of the thread signaling the event
1692 @type str
1693 """
1694 self.clientStack.emit(stack, debuggerId, threadName)
1695
1696 def signalClientThreadList(self, currentId, threadList, debuggerId):
1697 """
1698 Public method to process the client thread list info.
1699
1700 @param currentId id of the current thread
1701 @type int
1702 @param threadList list of dictionaries containing the thread data
1703 @type list of dict
1704 @param debuggerId ID of the debugger backend
1705 @type str
1706 """
1707 self.clientThreadList.emit(currentId, threadList, debuggerId)
1708
1709 def signalClientThreadSet(self, debuggerId):
1710 """
1711 Public method to handle the change of the client thread.
1712
1713 @param debuggerId ID of the debugger backend
1714 @type str
1715 """
1716 self.clientThreadSet.emit(debuggerId)
1717
1718 def signalClientVariables(self, scope, variables, debuggerId):
1719 """
1720 Public method to process the client variables info.
1721
1722 @param scope scope of the variables
1723 (-2 = no frame found, -1 = empty locals, 1 = global, 0 = local)
1724 @type int
1725 @param variables the list of variables from the client
1726 @type list
1727 @param debuggerId ID of the debugger backend
1728 @type str
1729 """
1730 self.clientVariables.emit(scope, variables, debuggerId)
1731
1732 def signalClientVariable(self, scope, variables, debuggerId):
1733 """
1734 Public method to process the client variable info.
1735
1736 @param scope scope of the variables (-1 = empty global, 1 = global,
1737 0 = local)
1738 @type int
1739 @param variables the list of members of a classvariable from the client
1740 @type list
1741 @param debuggerId ID of the debugger backend
1742 @type str
1743 """
1744 self.clientVariable.emit(scope, variables, debuggerId)
1745
1746 def signalClientStatement(self, more, debuggerId):
1747 """
1748 Public method to process the input response from the client.
1749
1750 @param more flag indicating that more user input is required
1751 @type bool
1752 @param debuggerId ID of the debugger backend
1753 @type str
1754 """
1755 self.clientStatement.emit(more, debuggerId)
1756
1757 def signalClientDisassembly(self, disassembly, debuggerId):
1758 """
1759 Public method to process the disassembly info from the client.
1760
1761 @param disassembly dictionary containing the disassembly information
1762 @type dict
1763 @param debuggerId ID of the debugger backend
1764 @type str
1765 """
1766 if self.running:
1767 self.clientDisassembly.emit(disassembly, debuggerId)
1768
1769 def signalClientException(
1770 self, exceptionType, exceptionMessage, stackTrace, debuggerId, threadName=""
1771 ):
1772 """
1773 Public method to process the exception info from the client.
1774
1775 @param exceptionType type of exception raised
1776 @type str
1777 @param exceptionMessage message given by the exception
1778 @type str
1779 @param stackTrace list of stack entries with the exception position
1780 first. Each stack entry is a list giving the filename and the
1781 linenumber.
1782 @type list
1783 @param debuggerId ID of the debugger backend
1784 @type str
1785 @param threadName name of the thread signaling the event
1786 @type str
1787 """
1788 if self.running:
1789 self.clientException.emit(
1790 exceptionType, exceptionMessage, stackTrace, debuggerId, threadName
1791 )
1792
1793 def signalClientSyntaxError(
1794 self, message, filename, lineNo, characterNo, debuggerId, threadName=""
1795 ):
1796 """
1797 Public method to process a syntax error info from the client.
1798
1799 @param message message of the syntax error
1800 @type str
1801 @param filename translated filename of the syntax error position
1802 @type str
1803 @param lineNo line number of the syntax error position
1804 @type int
1805 @param characterNo character number of the syntax error position
1806 @type int
1807 @param debuggerId ID of the debugger backend
1808 @type str
1809 @param threadName name of the thread signaling the event
1810 @type str
1811 """
1812 if self.running:
1813 self.clientSyntaxError.emit(
1814 message, filename, lineNo, characterNo, debuggerId, threadName
1815 )
1816
1817 def signalClientSignal(
1818 self, message, filename, lineNo, funcName, funcArgs, debuggerId
1819 ):
1820 """
1821 Public method to process a signal generated on the client side.
1822
1823 @param message message of the syntax error
1824 @type str
1825 @param filename translated filename of the syntax error position
1826 @type str
1827 @param lineNo line number of the syntax error position
1828 @type int
1829 @param funcName name of the function causing the signal
1830 @type str
1831 @param funcArgs function arguments
1832 @type str
1833 @param debuggerId ID of the debugger backend
1834 @type str
1835 """
1836 if self.running:
1837 self.clientSignal.emit(
1838 message, filename, lineNo, funcName, funcArgs, debuggerId
1839 )
1840
1841 def signalClientDisconnected(self, debuggerId):
1842 """
1843 Public method to send a signal when a debug client has closed its
1844 connection.
1845
1846 @param debuggerId ID of the debugger backend
1847 @type str
1848 """
1849 self.clientDisconnected.emit(debuggerId)
1850
1851 def signalClientExit(self, program, status, message, debuggerId):
1852 """
1853 Public method to process the client exit status.
1854
1855 @param program name of the exited program
1856 @type str
1857 @param status exit code
1858 @type int
1859 @param message message sent with the exit
1860 @type str
1861 @param debuggerId ID of the debugger backend
1862 @type str
1863 """
1864 self.clientExit.emit(program, int(status), message, False, debuggerId)
1865
1866 def signalMainClientExit(self):
1867 """
1868 Public method to process the main client exiting.
1869 """
1870 self.mainClientExit.emit()
1871
1872 def signalLastClientExited(self):
1873 """
1874 Public method to process the last client exit event.
1875 """
1876 if self.passive:
1877 self.__passiveShutDown()
1878 self.lastClientExited.emit()
1879 if Preferences.getDebugger("AutomaticReset") or (
1880 self.running and not self.debugging
1881 ):
1882 self.debugging = False
1883 self.startClient(False, forProject=self.__forProject)
1884 if self.passive:
1885 self.__createDebuggerInterface("None")
1886 self.signalClientOutput(self.tr("\nNot connected\n"))
1887 self.signalClientStatement(False, "")
1888 self.running = False
1889
1890 def signalClientClearBreak(self, filename, lineno, debuggerId):
1891 """
1892 Public method to process the client clear breakpoint command.
1893
1894 @param filename filename of the breakpoint
1895 @type str
1896 @param lineno line umber of the breakpoint
1897 @type int
1898 @param debuggerId ID of the debugger backend
1899 @type str
1900 """
1901 self.clientClearBreak.emit(filename, lineno, debuggerId)
1902
1903 def signalClientBreakConditionError(self, filename, lineno, debuggerId):
1904 """
1905 Public method to process the client breakpoint condition error info.
1906
1907 @param filename filename of the breakpoint
1908 @type str
1909 @param lineno line umber of the breakpoint
1910 @type int
1911 @param debuggerId ID of the debugger backend
1912 @type str
1913 """
1914 if (filename, lineno) not in self.__reportedBreakpointIssues:
1915 self.__reportedBreakpointIssues.append((filename, lineno))
1916 self.clientBreakConditionError.emit(filename, lineno, debuggerId)
1917
1918 def signalClientClearWatch(self, condition, debuggerId):
1919 """
1920 Public slot to handle the clientClearWatch signal.
1921
1922 @param condition expression of watch expression to clear
1923 @type str
1924 @param debuggerId ID of the debugger backend
1925 @type str
1926 """
1927 self.clientClearWatch.emit(condition, debuggerId)
1928
1929 def signalClientWatchConditionError(self, condition, debuggerId):
1930 """
1931 Public method to process the client watch expression error info.
1932
1933 @param condition expression of watch expression to clear
1934 @type str
1935 @param debuggerId ID of the debugger backend
1936 @type str
1937 """
1938 if condition not in self.__reportedWatchpointIssues:
1939 self.__reportedWatchpointIssues.append(condition)
1940 self.clientWatchConditionError.emit(condition, debuggerId)
1941
1942 def signalClientRawInput(self, prompt, echo, debuggerId):
1943 """
1944 Public method to process the client raw input command.
1945
1946 @param prompt the input prompt
1947 @type str
1948 @param echo flag indicating an echoing of the input
1949 @type bool
1950 @param debuggerId ID of the debugger backend
1951 @type str
1952 """
1953 self.clientRawInput.emit(prompt, echo, debuggerId)
1954
1955 def signalClientBanner(self, version, platform, venvName):
1956 """
1957 Public method to process the client banner info.
1958
1959 @param version interpreter version info
1960 @type str
1961 @param platform hostname of the client
1962 @type str
1963 @param venvName name of the virtual environment
1964 @type str
1965 """
1966 self.clientBanner.emit(version, platform, venvName)
1967
1968 def signalClientCapabilities(self, capabilities, clientType, venvName):
1969 """
1970 Public method to process the client capabilities info.
1971
1972 @param capabilities bitmaks with the client capabilities
1973 @type int
1974 @param clientType type of the debug client
1975 @type str
1976 @param venvName name of the virtual environment
1977 @type str
1978 """
1979 with contextlib.suppress(KeyError):
1980 self.__debuggerInterfaceRegistry[clientType][0] = capabilities
1981 self.clientCapabilities.emit(capabilities, clientType, venvName)
1982
1983 def signalClientCompletionList(self, completionList, text, debuggerId):
1984 """
1985 Public method to process the client auto completion info.
1986
1987 @param completionList list of possible completions
1988 @type list of str
1989 @param text the text to be completed
1990 @type str
1991 @param debuggerId ID of the debugger backend
1992 @type str
1993 """
1994 self.clientCompletionList.emit(completionList, text)
1995
1996 def signalClientCallTrace(
1997 self,
1998 isCall,
1999 fromFile,
2000 fromLine,
2001 fromFunction,
2002 toFile,
2003 toLine,
2004 toFunction,
2005 debuggerId,
2006 ):
2007 """
2008 Public method to process the client call trace data.
2009
2010 @param isCall flag indicating a 'call'
2011 @type bool
2012 @param fromFile name of the originating file
2013 @type str
2014 @param fromLine line number in the originating file
2015 @type str
2016 @param fromFunction name of the originating function
2017 @type str
2018 @param toFile name of the target file
2019 @type str
2020 @param toLine line number in the target file
2021 @type str
2022 @param toFunction name of the target function
2023 @type str
2024 @param debuggerId ID of the debugger backend
2025 @type str
2026 """
2027 self.callTraceInfo.emit(
2028 isCall,
2029 fromFile,
2030 fromLine,
2031 fromFunction,
2032 toFile,
2033 toLine,
2034 toFunction,
2035 debuggerId,
2036 )
2037
2038 def passiveStartUp(self, fn, exc, debuggerId):
2039 """
2040 Public method to handle a passive debug connection.
2041
2042 @param fn filename of the debugged script
2043 @type str
2044 @param exc flag to enable exception reporting of the IDE
2045 @type bool
2046 @param debuggerId ID of the debugger backend
2047 @type str
2048 """
2049 self.appendStdout.emit(self.tr("Passive debug connection received\n"))
2050 self.passiveClientExited = False
2051 self.debugging = True
2052 self.running = True
2053 self.__restoreBreakpoints(debuggerId)
2054 self.__restoreWatchpoints(debuggerId)
2055 self.passiveDebugStarted.emit(fn, exc)
2056
2057 def __passiveShutDown(self):
2058 """
2059 Private method to shut down a passive debug connection.
2060 """
2061 self.passiveClientExited = True
2062 self.shutdownServer()
2063 self.appendStdout.emit(self.tr("Passive debug connection closed\n"))
2064
2065 def __restoreBreakpoints(self, debuggerId=""):
2066 """
2067 Private method to restore the breakpoints after a restart.
2068
2069 @param debuggerId ID of the debugger backend to send to. If this is
2070 empty, they will be broadcast to all connected backends.
2071 @type str
2072 """
2073 if self.debugging:
2074 self.__addBreakPoints(
2075 QModelIndex(), 0, self.breakpointModel.rowCount() - 1, debuggerId
2076 )
2077
2078 def __restoreWatchpoints(self, debuggerId=""):
2079 """
2080 Private method to restore the watch expressions after a restart.
2081
2082 @param debuggerId ID of the debugger backend to send to. If this is
2083 empty, they will be broadcast to all connected backends.
2084 @type str
2085 """
2086 if self.debugging:
2087 self.__addWatchPoints(
2088 QModelIndex(), 0, self.watchpointModel.rowCount() - 1, debuggerId
2089 )
2090
2091 def getBreakPointModel(self):
2092 """
2093 Public slot to get a reference to the breakpoint model object.
2094
2095 @return reference to the breakpoint model object
2096 @rtype BreakPointModel
2097 """
2098 return self.breakpointModel
2099
2100 def getWatchPointModel(self):
2101 """
2102 Public slot to get a reference to the watch expression model object.
2103
2104 @return reference to the watch expression model object
2105 @rtype WatchPointModel
2106 """
2107 return self.watchpointModel
2108
2109 def isConnected(self):
2110 """
2111 Public method to test, if the debug server is connected to a backend.
2112
2113 @return flag indicating a connection
2114 @rtype bool
2115 """
2116 return self.debuggerInterface and self.debuggerInterface.isConnected()
2117
2118 def isDebugging(self):
2119 """
2120 Public method to test, if the debug server is debugging.
2121
2122 @return flag indicating the debugging state
2123 @rtype bool
2124 """
2125 return self.debugging
2126
2127 def setDebugging(self, on):
2128 """
2129 Public method to set the debugging state.
2130
2131 @param on flag indicating the new debugging state
2132 @type bool
2133 """
2134 self.debugging = on
2135
2136 def signalClientDebuggerId(self, debuggerId):
2137 """
2138 Public method to signal the receipt of a new debugger ID.
2139
2140 This signal indicates, that a new debugger backend has connected.
2141
2142 @param debuggerId ID of the newly connected debugger backend
2143 @type str
2144 """
2145 self.clientDebuggerId.emit(debuggerId)
2146
2147 def getDebuggerIds(self):
2148 """
2149 Public method to return the IDs of the connected debugger backends.
2150
2151 @return list of connected debugger backend IDs
2152 @rtype list of str
2153 """
2154 if self.debuggerInterface:
2155 return self.debuggerInterface.getDebuggerIds()
2156 else:
2157 return []
2158
2159 def initializeClient(self, debuggerId):
2160 """
2161 Public method to initialize a freshly connected debug client.
2162
2163 @param debuggerId ID of the connected debugger
2164 @type str
2165 """
2166 self.__restoreBreakpoints(debuggerId)
2167 self.__restoreWatchpoints(debuggerId)
2168 self.__restoreNoDebugList(debuggerId)
2169
2170 def __restoreNoDebugList(self, debuggerId=""):
2171 """
2172 Private method to restore the list of scripts not to be debugged after
2173 a restart.
2174
2175 @param debuggerId ID of the debugger backend to send to. If this is
2176 empty, they will be broadcast to all connected backends.
2177 @type str
2178 """
2179 if self.debugging:
2180 self.debuggerInterface.remoteNoDebugList(
2181 debuggerId, self.__multiprocessNoDebugList
2182 )

eric ide

mercurial