Debugger/DebuggerInterfaceRuby.py

changeset 4553
a6b2acd1a355
parent 4552
b1ea4ea0190e
child 4554
f3428ddd577c
equal deleted inserted replaced
4552:b1ea4ea0190e 4553:a6b2acd1a355
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2015 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Ruby debugger interface for the debug server.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import QObject, QTextCodec, QProcess, QProcessEnvironment, \
15 QTimer
16
17 from E5Gui.E5Application import e5App
18 from E5Gui import E5MessageBox
19
20 from . import DebugProtocol
21 from . import DebugClientCapabilities
22
23 import Preferences
24 import Utilities
25
26 from eric6config import getConfig
27
28
29 ClientDefaultCapabilities = \
30 DebugClientCapabilities.HasDebugger | \
31 DebugClientCapabilities.HasShell | \
32 DebugClientCapabilities.HasInterpreter | \
33 DebugClientCapabilities.HasCompleter
34
35 ClientTypeAssociations = [".rb"]
36
37
38 def getRegistryData():
39 """
40 Module function to get characterising data for the debugger interface.
41
42 @return list of the following data. Client type (string), client
43 capabilities (integer), client type association (list of strings)
44 """
45 if Preferences.getDebugger("RubyInterpreter"):
46 return ["Ruby", ClientDefaultCapabilities, ClientTypeAssociations]
47 else:
48 return ["", 0, []]
49
50
51 class DebuggerInterfaceRuby(QObject):
52 """
53 Class implementing the Ruby debugger interface for the debug server.
54 """
55 def __init__(self, debugServer, passive):
56 """
57 Constructor
58
59 @param debugServer reference to the debug server (DebugServer)
60 @param passive flag indicating passive connection mode (boolean)
61 """
62 super(DebuggerInterfaceRuby, self).__init__()
63
64 self.__isNetworked = True
65 self.__autoContinue = not passive
66
67 self.debugServer = debugServer
68 self.passive = passive
69 self.process = None
70
71 self.qsock = None
72 self.queue = []
73
74 # set default values for capabilities of clients
75 self.clientCapabilities = ClientDefaultCapabilities
76
77 # set translation function
78 self.translate = self.__identityTranslation
79
80 self.codec = QTextCodec.codecForName(
81 str(Preferences.getSystem("StringEncoding")))
82
83 if passive:
84 # set translation function
85 if Preferences.getDebugger("PathTranslation"):
86 self.translateRemote = \
87 Preferences.getDebugger("PathTranslationRemote")
88 self.translateLocal = \
89 Preferences.getDebugger("PathTranslationLocal")
90 self.translate = self.__remoteTranslation
91 else:
92 self.translate = self.__identityTranslation
93
94 def __identityTranslation(self, fn, remote2local=True):
95 """
96 Private method to perform the identity path translation.
97
98 @param fn filename to be translated (string)
99 @param remote2local flag indicating the direction of translation
100 (False = local to remote, True = remote to local [default])
101 @return translated filename (string)
102 """
103 return fn
104
105 def __remoteTranslation(self, fn, remote2local=True):
106 """
107 Private method to perform the path translation.
108
109 @param fn filename to be translated (string)
110 @param remote2local flag indicating the direction of translation
111 (False = local to remote, True = remote to local [default])
112 @return translated filename (string)
113 """
114 if remote2local:
115 return fn.replace(self.translateRemote, self.translateLocal)
116 else:
117 return fn.replace(self.translateLocal, self.translateRemote)
118
119 def __startProcess(self, program, arguments, environment=None):
120 """
121 Private method to start the debugger client process.
122
123 @param program name of the executable to start (string)
124 @param arguments arguments to be passed to the program (list of string)
125 @param environment dictionary of environment settings to pass
126 (dict of string)
127 @return the process object (QProcess) or None
128 """
129 proc = QProcess()
130 if environment is not None:
131 env = QProcessEnvironment()
132 for key, value in list(environment.items()):
133 env.insert(key, value)
134 proc.setProcessEnvironment(env)
135 args = []
136 for arg in arguments:
137 args.append(arg)
138 proc.start(program, args)
139 if not proc.waitForStarted(10000):
140 proc = None
141
142 return proc
143
144 def startRemote(self, port, runInConsole):
145 """
146 Public method to start a remote Ruby interpreter.
147
148 @param port portnumber the debug server is listening on (integer)
149 @param runInConsole flag indicating to start the debugger in a
150 console window (boolean)
151 @return client process object (QProcess), a flag to indicate
152 a network connection (boolean) and the name of the interpreter
153 in case of a local execution (string)
154 """
155 interpreter = Preferences.getDebugger("RubyInterpreter")
156 if interpreter == "":
157 E5MessageBox.critical(
158 None,
159 self.tr("Start Debugger"),
160 self.tr("""<p>No Ruby interpreter configured.</p>"""))
161 return None, False, ""
162
163 debugClient = os.path.join(
164 getConfig('ericDir'), "DebugClients", "Ruby", "DebugClient.rb")
165
166 redirect = str(Preferences.getDebugger("RubyRedirect"))
167
168 if Preferences.getDebugger("RemoteDbgEnabled"):
169 ipaddr = self.debugServer.getHostAddress(False)[0]
170 rexec = Preferences.getDebugger("RemoteExecution")
171 rhost = Preferences.getDebugger("RemoteHost")
172 if rhost == "":
173 rhost = "localhost"
174 if rexec:
175 args = Utilities.parseOptionString(rexec) + \
176 [rhost, interpreter, os.path.abspath(debugClient),
177 str(port), redirect, ipaddr]
178 args[0] = Utilities.getExecutablePath(args[0])
179 process = self.__startProcess(args[0], args[1:])
180 if process is None:
181 E5MessageBox.critical(
182 None,
183 self.tr("Start Debugger"),
184 self.tr(
185 """<p>The debugger backend could not be"""
186 """ started.</p>"""))
187
188 # set translation function
189 if Preferences.getDebugger("PathTranslation"):
190 self.translateRemote = \
191 Preferences.getDebugger("PathTranslationRemote")
192 self.translateLocal = \
193 Preferences.getDebugger("PathTranslationLocal")
194 self.translate = self.__remoteTranslation
195 else:
196 self.translate = self.__identityTranslation
197 return process, self.__isNetworked, ""
198
199 # set translation function
200 self.translate = self.__identityTranslation
201
202 # setup the environment for the debugger
203 if Preferences.getDebugger("DebugEnvironmentReplace"):
204 clientEnv = {}
205 else:
206 clientEnv = os.environ.copy()
207 envlist = Utilities.parseEnvironmentString(
208 Preferences.getDebugger("DebugEnvironment"))
209 for el in envlist:
210 try:
211 key, value = el.split('=', 1)
212 if value.startswith('"') or value.startswith("'"):
213 value = value[1:-1]
214 clientEnv[str(key)] = str(value)
215 except ValueError:
216 pass
217
218 ipaddr = self.debugServer.getHostAddress(True)
219 if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"):
220 ccmd = Preferences.getDebugger("ConsoleDbgCommand")
221 if ccmd:
222 args = Utilities.parseOptionString(ccmd) + \
223 [interpreter, os.path.abspath(debugClient),
224 str(port), '0', ipaddr]
225 args[0] = Utilities.getExecutablePath(args[0])
226 process = self.__startProcess(args[0], args[1:], clientEnv)
227 if process is None:
228 E5MessageBox.critical(
229 None,
230 self.tr("Start Debugger"),
231 self.tr(
232 """<p>The debugger backend could not be"""
233 """ started.</p>"""))
234 return process, self.__isNetworked, interpreter
235
236 process = self.__startProcess(
237 interpreter,
238 [debugClient, str(port), redirect, ipaddr],
239 clientEnv)
240 if process is None:
241 E5MessageBox.critical(
242 None,
243 self.tr("Start Debugger"),
244 self.tr(
245 """<p>The debugger backend could not be started.</p>"""))
246 return process, self.__isNetworked, interpreter
247
248 def startRemoteForProject(self, port, runInConsole):
249 """
250 Public method to start a remote Ruby interpreter for a project.
251
252 @param port portnumber the debug server is listening on (integer)
253 @param runInConsole flag indicating to start the debugger in a
254 console window (boolean)
255 @return client process object (QProcess), a flag to indicate
256 a network connection (boolean) and the name of the interpreter
257 in case of a local execution (string)
258 """
259 project = e5App().getObject("Project")
260 if not project.isDebugPropertiesLoaded():
261 return None, self.__isNetworked, ""
262
263 # start debugger with project specific settings
264 interpreter = project.getDebugProperty("INTERPRETER")
265 debugClient = project.getDebugProperty("DEBUGCLIENT")
266
267 redirect = str(project.getDebugProperty("REDIRECT"))
268
269 if project.getDebugProperty("REMOTEDEBUGGER"):
270 ipaddr = self.debugServer.getHostAddress(False)[0]
271 rexec = project.getDebugProperty("REMOTECOMMAND")
272 rhost = project.getDebugProperty("REMOTEHOST")
273 if rhost == "":
274 rhost = "localhost"
275 if rexec:
276 args = Utilities.parseOptionString(rexec) + \
277 [rhost, interpreter, os.path.abspath(debugClient),
278 str(port), redirect, ipaddr]
279 args[0] = Utilities.getExecutablePath(args[0])
280 process = self.__startProcess(args[0], args[1:])
281 if process is None:
282 E5MessageBox.critical(
283 None,
284 self.tr("Start Debugger"),
285 self.tr(
286 """<p>The debugger backend could not be"""
287 """ started.</p>"""))
288 # set translation function
289 if project.getDebugProperty("PATHTRANSLATION"):
290 self.translateRemote = \
291 project.getDebugProperty("REMOTEPATH")
292 self.translateLocal = \
293 project.getDebugProperty("LOCALPATH")
294 self.translate = self.__remoteTranslation
295 else:
296 self.translate = self.__identityTranslation
297 return process, self.__isNetworked, ""
298
299 # set translation function
300 self.translate = self.__identityTranslation
301
302 # setup the environment for the debugger
303 if project.getDebugProperty("ENVIRONMENTOVERRIDE"):
304 clientEnv = {}
305 else:
306 clientEnv = os.environ.copy()
307 envlist = Utilities.parseEnvironmentString(
308 project.getDebugProperty("ENVIRONMENTSTRING"))
309 for el in envlist:
310 try:
311 key, value = el.split('=', 1)
312 if value.startswith('"') or value.startswith("'"):
313 value = value[1:-1]
314 clientEnv[str(key)] = str(value)
315 except ValueError:
316 pass
317
318 ipaddr = self.debugServer.getHostAddress(True)
319 if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"):
320 ccmd = project.getDebugProperty("CONSOLECOMMAND") or \
321 Preferences.getDebugger("ConsoleDbgCommand")
322 if ccmd:
323 args = Utilities.parseOptionString(ccmd) + \
324 [interpreter, os.path.abspath(debugClient),
325 str(port), '0', ipaddr]
326 args[0] = Utilities.getExecutablePath(args[0])
327 process = self.__startProcess(args[0], args[1:], clientEnv)
328 if process is None:
329 E5MessageBox.critical(
330 None,
331 self.tr("Start Debugger"),
332 self.tr(
333 """<p>The debugger backend could not be"""
334 """ started.</p>"""))
335 return process, self.__isNetworked, interpreter
336
337 process = self.__startProcess(
338 interpreter,
339 [debugClient, str(port), redirect, ipaddr],
340 clientEnv)
341 if process is None:
342 E5MessageBox.critical(
343 None,
344 self.tr("Start Debugger"),
345 self.tr(
346 """<p>The debugger backend could not be started.</p>"""))
347 return process, self.__isNetworked, interpreter
348
349 def getClientCapabilities(self):
350 """
351 Public method to retrieve the debug clients capabilities.
352
353 @return debug client capabilities (integer)
354 """
355 return self.clientCapabilities
356
357 def newConnection(self, sock):
358 """
359 Public slot to handle a new connection.
360
361 @param sock reference to the socket object (QTcpSocket)
362 @return flag indicating success (boolean)
363 """
364 # If we already have a connection, refuse this one. It will be closed
365 # automatically.
366 if self.qsock is not None:
367 return False
368
369 sock.disconnected.connect(self.debugServer.startClient)
370 sock.readyRead.connect(self.__parseClientLine)
371
372 self.qsock = sock
373
374 # Get the remote clients capabilities
375 self.remoteCapabilities()
376 return True
377
378 def flush(self):
379 """
380 Public slot to flush the queue.
381 """
382 # Send commands that were waiting for the connection.
383 for cmd in self.queue:
384 self.qsock.write(cmd.encode('utf8'))
385
386 self.queue = []
387
388 def shutdown(self):
389 """
390 Public method to cleanly shut down.
391
392 It closes our socket and shuts down
393 the debug client. (Needed on Win OS)
394 """
395 if self.qsock is None:
396 return
397
398 # do not want any slots called during shutdown
399 self.qsock.disconnected.disconnect(self.debugServer.startClient)
400 self.qsock.readyRead.disconnect(self.__parseClientLine)
401
402 # close down socket, and shut down client as well.
403 self.__sendCommand('{0}\n'.format(DebugProtocol.RequestShutdown))
404 self.qsock.flush()
405
406 self.qsock.close()
407
408 # reinitialize
409 self.qsock = None
410 self.queue = []
411
412 def isConnected(self):
413 """
414 Public method to test, if a debug client has connected.
415
416 @return flag indicating the connection status (boolean)
417 """
418 return self.qsock is not None
419
420 def remoteEnvironment(self, env):
421 """
422 Public method to set the environment for a program to debug, run, ...
423
424 @param env environment settings (dictionary)
425 """
426 self.__sendCommand('{0}{1}\n'.format(
427 DebugProtocol.RequestEnv, str(env)))
428
429 def remoteLoad(self, fn, argv, wd, traceInterpreter=False,
430 autoContinue=True, autoFork=False, forkChild=False):
431 """
432 Public method to load a new program to debug.
433
434 @param fn the filename to debug (string)
435 @param argv the commandline arguments to pass to the program (string)
436 @param wd the working directory for the program (string)
437 @keyparam traceInterpreter flag indicating if the interpreter library
438 should be traced as well (boolean)
439 @keyparam autoContinue flag indicating, that the debugger should not
440 stop at the first executable line (boolean)
441 @keyparam autoFork flag indicating the automatic fork mode (boolean)
442 (ignored)
443 @keyparam forkChild flag indicating to debug the child after forking
444 (boolean) (ignored)
445 """
446 self.__autoContinue = autoContinue
447
448 wd = self.translate(wd, False)
449 fn = self.translate(os.path.abspath(fn), False)
450 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format(
451 DebugProtocol.RequestLoad, wd, fn,
452 str(Utilities.parseOptionString(argv)),
453 traceInterpreter))
454
455 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False):
456 """
457 Public method to load a new program to run.
458
459 @param fn the filename to run (string)
460 @param argv the commandline arguments to pass to the program (string)
461 @param wd the working directory for the program (string)
462 @keyparam autoFork flag indicating the automatic fork mode (boolean)
463 (ignored)
464 @keyparam forkChild flag indicating to debug the child after forking
465 (boolean) (ignored)
466 """
467 wd = self.translate(wd, False)
468 fn = self.translate(os.path.abspath(fn), False)
469 self.__sendCommand('{0}{1}|{2}|{3}\n'.format(
470 DebugProtocol.RequestRun, wd, fn,
471 str(Utilities.parseOptionString(argv))))
472
473 def remoteCoverage(self, fn, argv, wd, erase=False):
474 """
475 Public method to load a new program to collect coverage data.
476
477 @param fn the filename to run (string)
478 @param argv the commandline arguments to pass to the program (string)
479 @param wd the working directory for the program (string)
480 @keyparam erase flag indicating that coverage info should be
481 cleared first (boolean)
482 @exception NotImplementedError raised to indicate that this interface
483 is not supported
484 """
485 raise NotImplementedError("Interface not available.")
486
487 def remoteProfile(self, fn, argv, wd, erase=False):
488 """
489 Public method to load a new program to collect profiling data.
490
491 @param fn the filename to run (string)
492 @param argv the commandline arguments to pass to the program (string)
493 @param wd the working directory for the program (string)
494 @keyparam erase flag indicating that timing info should be cleared
495 first (boolean)
496 @exception NotImplementedError raised to indicate that this interface
497 is not supported
498 """
499 raise NotImplementedError("Interface not available.")
500
501 def remoteStatement(self, stmt):
502 """
503 Public method to execute a Ruby statement.
504
505 @param stmt the Ruby statement to execute (string). It
506 should not have a trailing newline.
507 """
508 self.__sendCommand('{0}\n'.format(stmt))
509 self.__sendCommand(DebugProtocol.RequestOK + '\n')
510
511 def remoteStep(self):
512 """
513 Public method to single step the debugged program.
514 """
515 self.__sendCommand(DebugProtocol.RequestStep + '\n')
516
517 def remoteStepOver(self):
518 """
519 Public method to step over the debugged program.
520 """
521 self.__sendCommand(DebugProtocol.RequestStepOver + '\n')
522
523 def remoteStepOut(self):
524 """
525 Public method to step out the debugged program.
526 """
527 self.__sendCommand(DebugProtocol.RequestStepOut + '\n')
528
529 def remoteStepQuit(self):
530 """
531 Public method to stop the debugged program.
532 """
533 self.__sendCommand(DebugProtocol.RequestStepQuit + '\n')
534
535 def remoteContinue(self, special=False):
536 """
537 Public method to continue the debugged program.
538
539 @param special flag indicating a special continue operation (boolean)
540 """
541 self.__sendCommand('{0}{1:d}\n'.format(
542 DebugProtocol.RequestContinue, special))
543
544 def remoteBreakpoint(self, fn, line, set, cond=None, temp=False):
545 """
546 Public method to set or clear a breakpoint.
547
548 @param fn filename the breakpoint belongs to (string)
549 @param line linenumber of the breakpoint (int)
550 @param set flag indicating setting or resetting a breakpoint (boolean)
551 @param cond condition of the breakpoint (string)
552 @param temp flag indicating a temporary breakpoint (boolean)
553 """
554 fn = self.translate(fn, False)
555 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}@@{4:d}@@{5}\n'.format(
556 DebugProtocol.RequestBreak, fn, line, temp, set, cond))
557
558 def remoteBreakpointEnable(self, fn, line, enable):
559 """
560 Public method to enable or disable a breakpoint.
561
562 @param fn filename the breakpoint belongs to (string)
563 @param line linenumber of the breakpoint (int)
564 @param enable flag indicating enabling or disabling a breakpoint
565 (boolean)
566 """
567 fn = self.translate(fn, False)
568 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format(
569 DebugProtocol.RequestBreakEnable, fn, line, enable))
570
571 def remoteBreakpointIgnore(self, fn, line, count):
572 """
573 Public method to ignore a breakpoint the next couple of occurrences.
574
575 @param fn filename the breakpoint belongs to (string)
576 @param line linenumber of the breakpoint (int)
577 @param count number of occurrences to ignore (int)
578 """
579 fn = self.translate(fn, False)
580 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format(
581 DebugProtocol.RequestBreakIgnore, fn, line, count))
582
583 def remoteWatchpoint(self, cond, set, temp=False):
584 """
585 Public method to set or clear a watch expression.
586
587 @param cond expression of the watch expression (string)
588 @param set flag indicating setting or resetting a watch expression
589 (boolean)
590 @param temp flag indicating a temporary watch expression (boolean)
591 """
592 # cond is combination of cond and special (s. watch expression viewer)
593 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}\n'.format(
594 DebugProtocol.RequestWatch, cond, temp, set))
595
596 def remoteWatchpointEnable(self, cond, enable):
597 """
598 Public method to enable or disable a watch expression.
599
600 @param cond expression of the watch expression (string)
601 @param enable flag indicating enabling or disabling a watch expression
602 (boolean)
603 """
604 # cond is combination of cond and special (s. watch expression viewer)
605 self.__sendCommand('{0}{1},{2:d}\n'.format(
606 DebugProtocol.RequestWatchEnable, cond, enable))
607
608 def remoteWatchpointIgnore(self, cond, count):
609 """
610 Public method to ignore a watch expression the next couple of
611 occurrences.
612
613 @param cond expression of the watch expression (string)
614 @param count number of occurrences to ignore (int)
615 """
616 # cond is combination of cond and special (s. watch expression viewer)
617 self.__sendCommand('{0}{1},{2:d}\n'.format(
618 DebugProtocol.RequestWatchIgnore, cond, count))
619
620 def remoteRawInput(self, s):
621 """
622 Public method to send the raw input to the debugged program.
623
624 @param s the raw input (string)
625 """
626 self.__sendCommand(s + '\n')
627
628 def remoteThreadList(self):
629 """
630 Public method to request the list of threads from the client.
631 """
632 return
633
634 def remoteSetThread(self, tid):
635 """
636 Public method to request to set the given thread as current thread.
637
638 @param tid id of the thread (integer)
639 """
640 return
641
642 def remoteClientVariables(self, scope, filter, framenr=0):
643 """
644 Public method to request the variables of the debugged program.
645
646 @param scope the scope of the variables (0 = local, 1 = global)
647 @param filter list of variable types to filter out (list of int)
648 @param framenr framenumber of the variables to retrieve (int)
649 """
650 self.__sendCommand('{0}{1:d}, {2:d}, {3}\n'.format(
651 DebugProtocol.RequestVariables, framenr, scope, str(filter)))
652
653 def remoteClientVariable(self, scope, filter, var, framenr=0):
654 """
655 Public method to request the variables of the debugged program.
656
657 @param scope the scope of the variables (0 = local, 1 = global)
658 @param filter list of variable types to filter out (list of int)
659 @param var list encoded name of variable to retrieve (string)
660 @param framenr framenumber of the variables to retrieve (int)
661 """
662 self.__sendCommand('{0}{1}, {2:d}, {3:d}, {4}\n'.format(
663 DebugProtocol.RequestVariable, str(var), framenr, scope,
664 str(filter)))
665
666 def remoteClientSetFilter(self, scope, filter):
667 """
668 Public method to set a variables filter list.
669
670 @param scope the scope of the variables (0 = local, 1 = global)
671 @param filter regexp string for variable names to filter out (string)
672 """
673 self.__sendCommand('{0}{1:d}, "{2}"\n'.format(
674 DebugProtocol.RequestSetFilter, scope, filter))
675
676 def setCallTraceEnabled(self, on):
677 """
678 Public method to set the call trace state.
679
680 @param on flag indicating to enable the call trace function (boolean)
681 """
682 return
683
684 def remoteEval(self, arg):
685 """
686 Public method to evaluate arg in the current context of the debugged
687 program.
688
689 @param arg the arguments to evaluate (string)
690 """
691 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestEval, arg))
692
693 def remoteExec(self, stmt):
694 """
695 Public method to execute stmt in the current context of the debugged
696 program.
697
698 @param stmt statement to execute (string)
699 """
700 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestExec, stmt))
701
702 def remoteBanner(self):
703 """
704 Public slot to get the banner info of the remote client.
705 """
706 self.__sendCommand(DebugProtocol.RequestBanner + '\n')
707
708 def remoteCapabilities(self):
709 """
710 Public slot to get the debug clients capabilities.
711 """
712 self.__sendCommand(DebugProtocol.RequestCapabilities + '\n')
713
714 def remoteCompletion(self, text):
715 """
716 Public slot to get the a list of possible commandline completions
717 from the remote client.
718
719 @param text the text to be completed (string)
720 """
721 self.__sendCommand("{0}{1}\n".format(
722 DebugProtocol.RequestCompletion, text))
723
724 def remoteUTPrepare(self, fn, tn, tfn, failed, cov, covname, coverase):
725 """
726 Public method to prepare a new unittest run.
727
728 @param fn the filename to load (string)
729 @param tn the testname to load (string)
730 @param tfn the test function name to load tests from (string)
731 @param failed list of failed test, if only failed test should be run
732 (list of strings)
733 @param cov flag indicating collection of coverage data is requested
734 (boolean)
735 @param covname filename to be used to assemble the coverage caches
736 filename (string)
737 @param coverase flag indicating erasure of coverage data is requested
738 (boolean)
739 @exception NotImplementedError raised to indicate that this interface
740 is not supported
741 """
742 raise NotImplementedError("Interface not available.")
743
744 def remoteUTRun(self):
745 """
746 Public method to start a unittest run.
747
748 @exception NotImplementedError raised to indicate that this interface
749 is not supported
750 """
751 raise NotImplementedError("Interface not available.")
752
753 def remoteUTStop(self):
754 """
755 Public method to stop a unittest run.
756
757 @exception NotImplementedError raised to indicate that this interface
758 is not supported
759 """
760 raise NotImplementedError("Interface not available.")
761
762 def __parseClientLine(self):
763 """
764 Private method to handle data from the client.
765 """
766 while self.qsock and self.qsock.canReadLine():
767 qs = self.qsock.readLine()
768 if self.codec is not None:
769 line = self.codec.toUnicode(qs)
770 else:
771 line = bytes(qs).decode()
772 if line.endswith(DebugProtocol.EOT):
773 line = line[:-len(DebugProtocol.EOT)]
774 if not line:
775 continue
776
777 ## print("Server: ", line) ##debug
778
779 eoc = line.find('<') + 1
780
781 # Deal with case where user has written directly to stdout
782 # or stderr, but not line terminated and we stepped over the
783 # write call, in that case the >line< will not be the first
784 # string read from the socket...
785 boc = line.find('>')
786 if boc > 0 and eoc > boc:
787 self.debugServer.signalClientOutput(line[:boc])
788 line = line[boc:]
789 eoc = line.find('<') + 1
790 boc = line.find('>')
791
792 if boc >= 0 and eoc > boc:
793 resp = line[boc:eoc]
794
795 if resp == DebugProtocol.ResponseLine:
796 stack = eval(line[eoc:-1])
797 for s in stack:
798 s[0] = self.translate(s[0], True)
799 cf = stack[0]
800 if self.__autoContinue:
801 self.__autoContinue = False
802 QTimer.singleShot(0, self.remoteContinue)
803 else:
804 self.debugServer.signalClientLine(cf[0], int(cf[1]))
805 self.debugServer.signalClientStack(stack)
806 continue
807
808 if resp == DebugProtocol.ResponseVariables:
809 vlist = eval(line[eoc:-1])
810 scope = vlist[0]
811 try:
812 variables = vlist[1:]
813 except IndexError:
814 variables = []
815 self.debugServer.signalClientVariables(scope, variables)
816 continue
817
818 if resp == DebugProtocol.ResponseVariable:
819 vlist = eval(line[eoc:-1])
820 scope = vlist[0]
821 try:
822 variables = vlist[1:]
823 except IndexError:
824 variables = []
825 self.debugServer.signalClientVariable(scope, variables)
826 continue
827
828 if resp == DebugProtocol.ResponseOK:
829 self.debugServer.signalClientStatement(False)
830 continue
831
832 if resp == DebugProtocol.ResponseContinue:
833 self.debugServer.signalClientStatement(True)
834 continue
835
836 if resp == DebugProtocol.ResponseException:
837 exc = line[eoc:-1]
838 exc = self.translate(exc, True)
839 try:
840 exclist = eval(exc)
841 exctype = exclist[0]
842 excmessage = exclist[1]
843 stack = exclist[2:]
844 except (IndexError, ValueError, SyntaxError):
845 exctype = None
846 excmessage = ''
847 stack = []
848 self.debugServer.signalClientException(
849 exctype, excmessage, stack)
850 continue
851
852 if resp == DebugProtocol.ResponseSyntax:
853 exc = line[eoc:-1]
854 exc = self.translate(exc, True)
855 try:
856 message, (fn, ln, cn) = eval(exc)
857 if fn is None:
858 fn = ''
859 except (IndexError, ValueError):
860 message = None
861 fn = ''
862 ln = 0
863 cn = 0
864 self.debugServer.signalClientSyntaxError(
865 message, fn, ln, cn)
866 continue
867
868 if resp == DebugProtocol.ResponseExit:
869 self.debugServer.signalClientExit(line[eoc:-1])
870 continue
871
872 if resp == DebugProtocol.ResponseClearBreak:
873 fn, lineno = line[eoc:-1].split(',')
874 lineno = int(lineno)
875 fn = self.translate(fn, True)
876 self.debugServer.signalClientClearBreak(fn, lineno)
877 continue
878
879 if resp == DebugProtocol.ResponseClearWatch:
880 cond = line[eoc:-1]
881 self.debugServer.signalClientClearWatch(cond)
882 continue
883
884 if resp == DebugProtocol.ResponseBanner:
885 version, platform, dbgclient = eval(line[eoc:-1])
886 self.debugServer.signalClientBanner(
887 version, platform, dbgclient)
888 continue
889
890 if resp == DebugProtocol.ResponseCapabilities:
891 cap, clType = eval(line[eoc:-1])
892 self.clientCapabilities = cap
893 self.debugServer.signalClientCapabilities(cap, clType)
894 continue
895
896 if resp == DebugProtocol.ResponseCompletion:
897 clstring, text = line[eoc:-1].split('||')
898 cl = eval(clstring)
899 self.debugServer.signalClientCompletionList(cl, text)
900 continue
901
902 if resp == DebugProtocol.PassiveStartup:
903 fn, exc = line[eoc:-1].split('|')
904 exc = bool(exc)
905 fn = self.translate(fn, True)
906 self.debugServer.passiveStartUp(fn, exc)
907 continue
908
909 self.debugServer.signalClientOutput(line)
910
911 def __sendCommand(self, cmd):
912 """
913 Private method to send a single line command to the client.
914
915 @param cmd command to send to the debug client (string)
916 """
917 if self.qsock is not None:
918 self.qsock.write(cmd.encode('utf8'))
919 else:
920 self.queue.append(cmd)

eric ide

mercurial