src/eric7/Debugger/DebugServer.py

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

eric ide

mercurial