Debugger/DebuggerInterfacePython2.py

branch
maintenance
changeset 5863
0752bdd8db77
parent 5826
ea0f0f066b1d
parent 5862
0a88812e43bf
child 5864
86ccabe751ac
equal deleted inserted replaced
5826:ea0f0f066b1d 5863:0752bdd8db77
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Python debugger interface for the debug server.
8 """
9
10 from __future__ import unicode_literals
11
12 import sys
13 import os
14
15 from PyQt5.QtCore import QObject, QTextCodec, QProcess, QProcessEnvironment, \
16 QTimer
17 from PyQt5.QtWidgets import QInputDialog
18
19 from E5Gui.E5Application import e5App
20 from E5Gui import E5MessageBox
21
22 from . import DebugClientCapabilities
23
24 import Preferences
25 import Utilities
26
27 from eric6config import getConfig
28
29
30 ClientDefaultCapabilities = DebugClientCapabilities.HasAll
31
32
33 class DebuggerInterfacePython2(QObject):
34 """
35 Class implementing the Python 2 debugger interface for the debug server.
36 """
37 def __init__(self, debugServer, passive):
38 """
39 Constructor
40
41 @param debugServer reference to the debug server (DebugServer)
42 @param passive flag indicating passive connection mode (boolean)
43 """
44 super(DebuggerInterfacePython2, self).__init__()
45
46 self.__isNetworked = True
47 self.__autoContinue = False
48
49 self.debugServer = debugServer
50 self.passive = passive
51 self.process = None
52
53 self.qsock = None
54 self.queue = []
55
56 # set default values for capabilities of clients
57 self.clientCapabilities = ClientDefaultCapabilities
58
59 # set translation function
60 self.translate = self.__identityTranslation
61
62 self.codec = QTextCodec.codecForName(
63 Preferences.getSystem("StringEncoding"))
64
65 if passive:
66 # set translation function
67 if Preferences.getDebugger("PathTranslation"):
68 self.translateRemote = \
69 Preferences.getDebugger("PathTranslationRemote")
70 self.translateLocal = \
71 Preferences.getDebugger("PathTranslationLocal")
72 self.translate = self.__remoteTranslation
73 else:
74 self.translate = self.__identityTranslation
75
76 # attribute to remember the name of the executed script
77 self.__scriptName = ""
78
79 def __identityTranslation(self, fn, remote2local=True):
80 """
81 Private method to perform the identity path translation.
82
83 @param fn filename to be translated (string)
84 @param remote2local flag indicating the direction of translation
85 (False = local to remote, True = remote to local [default])
86 @return translated filename (string)
87 """
88 return fn
89
90 def __remoteTranslation(self, fn, remote2local=True):
91 """
92 Private method to perform the path translation.
93
94 @param fn filename to be translated (string)
95 @param remote2local flag indicating the direction of translation
96 (False = local to remote, True = remote to local [default])
97 @return translated filename (string)
98 """
99 if remote2local:
100 return fn.replace(self.translateRemote, self.translateLocal)
101 else:
102 return fn.replace(self.translateLocal, self.translateRemote)
103
104 def __startProcess(self, program, arguments, environment=None):
105 """
106 Private method to start the debugger client process.
107
108 @param program name of the executable to start (string)
109 @param arguments arguments to be passed to the program (list of string)
110 @param environment dictionary of environment settings to pass
111 (dict of string)
112 @return the process object (QProcess) or None
113 """
114 proc = QProcess()
115 if environment is not None:
116 env = QProcessEnvironment()
117 for key, value in list(environment.items()):
118 env.insert(key, value)
119 proc.setProcessEnvironment(env)
120 args = []
121 for arg in arguments:
122 args.append(arg)
123 proc.start(program, args)
124 if not proc.waitForStarted(10000):
125 proc = None
126
127 return proc
128
129 def startRemote(self, port, runInConsole):
130 """
131 Public method to start a remote Python interpreter.
132
133 @param port portnumber the debug server is listening on (integer)
134 @param runInConsole flag indicating to start the debugger in a
135 console window (boolean)
136 @return client process object (QProcess), a flag to indicate
137 a network connection (boolean) and the name of the interpreter
138 in case of a local execution (string)
139 """
140 interpreter = Preferences.getDebugger("PythonInterpreter")
141 if interpreter == "":
142 E5MessageBox.critical(
143 None,
144 self.tr("Start Debugger"),
145 self.tr(
146 """<p>No Python2 interpreter configured.</p>"""))
147 return None, False, ""
148
149 debugClientType = Preferences.getDebugger("DebugClientType")
150 if debugClientType == "standard":
151 debugClient = os.path.join(getConfig('ericDir'),
152 "DebugClients", "Python",
153 "DebugClient.py")
154 else:
155 debugClient = Preferences.getDebugger("DebugClient")
156 if debugClient == "":
157 debugClient = os.path.join(sys.path[0],
158 "DebugClients", "Python",
159 "DebugClient.py")
160
161 redirect = str(Preferences.getDebugger("PythonRedirect"))
162 noencoding = Preferences.getDebugger("PythonNoEncoding") and \
163 '--no-encoding' or ''
164
165 if Preferences.getDebugger("RemoteDbgEnabled"):
166 ipaddr = self.debugServer.getHostAddress(False)
167 rexec = Preferences.getDebugger("RemoteExecution")
168 rhost = Preferences.getDebugger("RemoteHost")
169 if rhost == "":
170 rhost = "localhost"
171 if rexec:
172 args = Utilities.parseOptionString(rexec) + \
173 [rhost, interpreter, debugClient,
174 noencoding, str(port), redirect, ipaddr]
175 args[0] = Utilities.getExecutablePath(args[0])
176 process = self.__startProcess(args[0], args[1:])
177 if process is None:
178 E5MessageBox.critical(
179 None,
180 self.tr("Start Debugger"),
181 self.tr(
182 """<p>The debugger backend could not be"""
183 """ started.</p>"""))
184
185 # set translation function
186 if Preferences.getDebugger("PathTranslation"):
187 self.translateRemote = \
188 Preferences.getDebugger("PathTranslationRemote")
189 self.translateLocal = \
190 Preferences.getDebugger("PathTranslationLocal")
191 self.translate = self.__remoteTranslation
192 else:
193 self.translate = self.__identityTranslation
194 return process, self.__isNetworked, ""
195
196 # set translation function
197 self.translate = self.__identityTranslation
198
199 # setup the environment for the debugger
200 if Preferences.getDebugger("DebugEnvironmentReplace"):
201 clientEnv = {}
202 else:
203 clientEnv = os.environ.copy()
204 envlist = Utilities.parseEnvironmentString(
205 Preferences.getDebugger("DebugEnvironment"))
206 for el in envlist:
207 try:
208 key, value = el.split('=', 1)
209 if value.startswith('"') or value.startswith("'"):
210 value = value[1:-1]
211 clientEnv[str(key)] = str(value)
212 except ValueError:
213 pass
214
215 ipaddr = self.debugServer.getHostAddress(True)
216 if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"):
217 ccmd = Preferences.getDebugger("ConsoleDbgCommand")
218 if ccmd:
219 args = Utilities.parseOptionString(ccmd) + \
220 [interpreter, os.path.abspath(debugClient),
221 noencoding, str(port), '0', ipaddr]
222 args[0] = Utilities.getExecutablePath(args[0])
223 process = self.__startProcess(args[0], args[1:], clientEnv)
224 if process is None:
225 E5MessageBox.critical(
226 None,
227 self.tr("Start Debugger"),
228 self.tr(
229 """<p>The debugger backend could not be"""
230 """ started.</p>"""))
231 return process, self.__isNetworked, interpreter
232
233 process = self.__startProcess(
234 interpreter,
235 [debugClient, noencoding, str(port), redirect, ipaddr],
236 clientEnv)
237 if process is None:
238 E5MessageBox.critical(
239 None,
240 self.tr("Start Debugger"),
241 self.tr(
242 """<p>The debugger backend could not be started.</p>"""))
243 return process, self.__isNetworked, interpreter
244
245 def startRemoteForProject(self, port, runInConsole):
246 """
247 Public method to start a remote Python interpreter for a project.
248
249 @param port portnumber the debug server is listening on (integer)
250 @param runInConsole flag indicating to start the debugger in a
251 console window (boolean)
252 @return client process object (QProcess), a flag to indicate
253 a network connection (boolean) and the name of the interpreter
254 in case of a local execution (string)
255 """
256 project = e5App().getObject("Project")
257 if not project.isDebugPropertiesLoaded():
258 return None, self.__isNetworked, ""
259
260 # start debugger with project specific settings
261 interpreter = project.getDebugProperty("INTERPRETER")
262 debugClient = project.getDebugProperty("DEBUGCLIENT")
263
264 redirect = str(project.getDebugProperty("REDIRECT"))
265 noencoding = \
266 project.getDebugProperty("NOENCODING") and '--no-encoding' or ''
267
268 if project.getDebugProperty("REMOTEDEBUGGER"):
269 ipaddr = self.debugServer.getHostAddress(False)
270 rexec = project.getDebugProperty("REMOTECOMMAND")
271 rhost = project.getDebugProperty("REMOTEHOST")
272 if rhost == "":
273 rhost = "localhost"
274 if rexec:
275 args = Utilities.parseOptionString(rexec) + \
276 [rhost, interpreter, os.path.abspath(debugClient),
277 noencoding, str(port), redirect, ipaddr]
278 args[0] = Utilities.getExecutablePath(args[0])
279 process = self.__startProcess(args[0], args[1:])
280 if process is None:
281 E5MessageBox.critical(
282 None,
283 self.tr("Start Debugger"),
284 self.tr(
285 """<p>The debugger backend could not be"""
286 """ started.</p>"""))
287 # set translation function
288 if project.getDebugProperty("PATHTRANSLATION"):
289 self.translateRemote = \
290 project.getDebugProperty("REMOTEPATH")
291 self.translateLocal = \
292 project.getDebugProperty("LOCALPATH")
293 self.translate = self.__remoteTranslation
294 else:
295 self.translate = self.__identityTranslation
296 return process, self.__isNetworked, ""
297
298 # set translation function
299 self.translate = self.__identityTranslation
300
301 # setup the environment for the debugger
302 if project.getDebugProperty("ENVIRONMENTOVERRIDE"):
303 clientEnv = {}
304 else:
305 clientEnv = os.environ.copy()
306 envlist = Utilities.parseEnvironmentString(
307 project.getDebugProperty("ENVIRONMENTSTRING"))
308 for el in envlist:
309 try:
310 key, value = el.split('=', 1)
311 if value.startswith('"') or value.startswith("'"):
312 value = value[1:-1]
313 clientEnv[str(key)] = str(value)
314 except ValueError:
315 pass
316
317 ipaddr = self.debugServer.getHostAddress(True)
318 if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"):
319 ccmd = project.getDebugProperty("CONSOLECOMMAND") or \
320 Preferences.getDebugger("ConsoleDbgCommand")
321 if ccmd:
322 args = Utilities.parseOptionString(ccmd) + \
323 [interpreter, os.path.abspath(debugClient),
324 noencoding, str(port), '0', ipaddr]
325 args[0] = Utilities.getExecutablePath(args[0])
326 process = self.__startProcess(args[0], args[1:], clientEnv)
327 if process is None:
328 E5MessageBox.critical(
329 None,
330 self.tr("Start Debugger"),
331 self.tr(
332 """<p>The debugger backend could not be"""
333 """ started.</p>"""))
334 return process, self.__isNetworked, interpreter
335
336 process = self.__startProcess(
337 interpreter,
338 [debugClient, noencoding, str(port), redirect, ipaddr],
339 clientEnv)
340 if process is None:
341 E5MessageBox.critical(
342 None,
343 self.tr("Start Debugger"),
344 self.tr(
345 """<p>The debugger backend could not be started.</p>"""))
346 return process, self.__isNetworked, interpreter
347
348 def getClientCapabilities(self):
349 """
350 Public method to retrieve the debug clients capabilities.
351
352 @return debug client capabilities (integer)
353 """
354 return self.clientCapabilities
355
356 def newConnection(self, sock):
357 """
358 Public slot to handle a new connection.
359
360 @param sock reference to the socket object (QTcpSocket)
361 @return flag indicating success (boolean)
362 """
363 # If we already have a connection, refuse this one. It will be closed
364 # automatically.
365 if self.qsock is not None:
366 return False
367
368 sock.disconnected.connect(self.debugServer.startClient)
369 sock.readyRead.connect(self.__parseClientLine)
370
371 self.qsock = sock
372
373 # Get the remote clients capabilities
374 self.remoteCapabilities()
375 return True
376
377 def flush(self):
378 """
379 Public slot to flush the queue.
380 """
381 # Send commands that were waiting for the connection.
382 for cmd in self.queue:
383 self.qsock.write(cmd.encode('utf8', 'backslashreplace'))
384
385 self.queue = []
386
387 def shutdown(self):
388 """
389 Public method to cleanly shut down.
390
391 It closes our socket and shuts down
392 the debug client. (Needed on Win OS)
393 """
394 if self.qsock is None:
395 return
396
397 # do not want any slots called during shutdown
398 self.qsock.disconnected.disconnect(self.debugServer.startClient)
399 self.qsock.readyRead.disconnect(self.__parseClientLine)
400
401 # close down socket, and shut down client as well.
402 self.__sendJsonCommand("RequestShutdown", {})
403 self.qsock.flush()
404 self.qsock.close()
405
406 # reinitialize
407 self.qsock = None
408 self.queue = []
409
410 def isConnected(self):
411 """
412 Public method to test, if a debug client has connected.
413
414 @return flag indicating the connection status (boolean)
415 """
416 return self.qsock is not None
417
418 def remoteEnvironment(self, env):
419 """
420 Public method to set the environment for a program to debug, run, ...
421
422 @param env environment settings (dictionary)
423 """
424 self.__sendJsonCommand("RequestEnvironment", {"environment": env})
425
426 def remoteLoad(self, fn, argv, wd, traceInterpreter=False,
427 autoContinue=True, autoFork=False, forkChild=False):
428 """
429 Public method to load a new program to debug.
430
431 @param fn the filename to debug (string)
432 @param argv the commandline arguments to pass to the program (string)
433 @param wd the working directory for the program (string)
434 @keyparam traceInterpreter flag indicating if the interpreter library
435 should be traced as well (boolean)
436 @keyparam autoContinue flag indicating, that the debugger should not
437 stop at the first executable line (boolean)
438 @keyparam autoFork flag indicating the automatic fork mode (boolean)
439 @keyparam forkChild flag indicating to debug the child after forking
440 (boolean)
441 """
442 self.__autoContinue = autoContinue
443 self.__scriptName = os.path.abspath(fn)
444
445 wd = self.translate(wd, False)
446 fn = self.translate(os.path.abspath(fn), False)
447 self.__sendJsonCommand("RequestLoad", {
448 "workdir": wd,
449 "filename": fn,
450 "argv": Utilities.parseOptionString(argv),
451 "traceInterpreter": traceInterpreter,
452 "autofork": autoFork,
453 "forkChild": forkChild,
454 })
455
456 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False):
457 """
458 Public method to load a new program to run.
459
460 @param fn the filename to run (string)
461 @param argv the commandline arguments to pass to the program (string)
462 @param wd the working directory for the program (string)
463 @keyparam autoFork flag indicating the automatic fork mode (boolean)
464 @keyparam forkChild flag indicating to debug the child after forking
465 (boolean)
466 """
467 self.__scriptName = os.path.abspath(fn)
468
469 wd = self.translate(wd, False)
470 fn = self.translate(os.path.abspath(fn), False)
471 self.__sendJsonCommand("RequestRun", {
472 "workdir": wd,
473 "filename": fn,
474 "argv": Utilities.parseOptionString(argv),
475 "autofork": autoFork,
476 "forkChild": forkChild,
477 })
478
479 def remoteCoverage(self, fn, argv, wd, erase=False):
480 """
481 Public method to load a new program to collect coverage data.
482
483 @param fn the filename to run (string)
484 @param argv the commandline arguments to pass to the program (string)
485 @param wd the working directory for the program (string)
486 @keyparam erase flag indicating that coverage info should be
487 cleared first (boolean)
488 """
489 self.__scriptName = os.path.abspath(fn)
490
491 wd = self.translate(wd, False)
492 fn = self.translate(os.path.abspath(fn), False)
493 self.__sendJsonCommand("RequestCoverage", {
494 "workdir": wd,
495 "filename": fn,
496 "argv": Utilities.parseOptionString(argv),
497 "erase": erase,
498 })
499
500 def remoteProfile(self, fn, argv, wd, erase=False):
501 """
502 Public method to load a new program to collect profiling data.
503
504 @param fn the filename to run (string)
505 @param argv the commandline arguments to pass to the program (string)
506 @param wd the working directory for the program (string)
507 @keyparam erase flag indicating that timing info should be cleared
508 first (boolean)
509 """
510 self.__scriptName = os.path.abspath(fn)
511
512 wd = self.translate(wd, False)
513 fn = self.translate(os.path.abspath(fn), False)
514 self.__sendJsonCommand("RequestProfile", {
515 "workdir": wd,
516 "filename": fn,
517 "argv": Utilities.parseOptionString(argv),
518 "erase": erase,
519 })
520
521 def remoteStatement(self, stmt):
522 """
523 Public method to execute a Python statement.
524
525 @param stmt the Python statement to execute (string). It
526 should not have a trailing newline.
527 """
528 self.__sendJsonCommand("ExecuteStatement", {
529 "statement": stmt,
530 })
531
532 def remoteStep(self):
533 """
534 Public method to single step the debugged program.
535 """
536 self.__sendJsonCommand("RequestStep", {})
537
538 def remoteStepOver(self):
539 """
540 Public method to step over the debugged program.
541 """
542 self.__sendJsonCommand("RequestStepOver", {})
543
544 def remoteStepOut(self):
545 """
546 Public method to step out the debugged program.
547 """
548 self.__sendJsonCommand("RequestStepOut", {})
549
550 def remoteStepQuit(self):
551 """
552 Public method to stop the debugged program.
553 """
554 self.__sendJsonCommand("RequestStepQuit", {})
555
556 def remoteContinue(self, special=False):
557 """
558 Public method to continue the debugged program.
559
560 @param special flag indicating a special continue operation (boolean)
561 """
562 self.__sendJsonCommand("RequestContinue", {
563 "special": special,
564 })
565
566 def remoteMoveIP(self, line):
567 """
568 Public method to move the instruction pointer to a different line.
569
570 @param line the new line, where execution should be continued
571 """
572 self.__sendJsonCommand("RequestMoveIP", {
573 "newLine": line,
574 })
575
576 def remoteBreakpoint(self, fn, line, setBreakpoint, cond=None, temp=False):
577 """
578 Public method to set or clear a breakpoint.
579
580 @param fn filename the breakpoint belongs to (string)
581 @param line linenumber of the breakpoint (int)
582 @param setBreakpoint flag indicating setting or resetting a
583 breakpoint (boolean)
584 @param cond condition of the breakpoint (string)
585 @param temp flag indicating a temporary breakpoint (boolean)
586 """
587 self.__sendJsonCommand("RequestBreakpoint", {
588 "filename": self.translate(fn, False),
589 "line": line,
590 "temporary": temp,
591 "setBreakpoint": setBreakpoint,
592 "condition": cond,
593 })
594
595 def remoteBreakpointEnable(self, fn, line, enable):
596 """
597 Public method to enable or disable a breakpoint.
598
599 @param fn filename the breakpoint belongs to (string)
600 @param line linenumber of the breakpoint (int)
601 @param enable flag indicating enabling or disabling a breakpoint
602 (boolean)
603 """
604 self.__sendJsonCommand("RequestBreakpointEnable", {
605 "filename": self.translate(fn, False),
606 "line": line,
607 "enable": enable,
608 })
609
610 def remoteBreakpointIgnore(self, fn, line, count):
611 """
612 Public method to ignore a breakpoint the next couple of occurrences.
613
614 @param fn filename the breakpoint belongs to (string)
615 @param line linenumber of the breakpoint (int)
616 @param count number of occurrences to ignore (int)
617 """
618 self.__sendJsonCommand("RequestBreakpointIgnore", {
619 "filename": self.translate(fn, False),
620 "line": line,
621 "count": count,
622 })
623
624 def remoteWatchpoint(self, cond, setWatch, temp=False):
625 """
626 Public method to set or clear a watch expression.
627
628 @param cond expression of the watch expression (string)
629 @param setWatch flag indicating setting or resetting a watch expression
630 (boolean)
631 @param temp flag indicating a temporary watch expression (boolean)
632 """
633 # cond is combination of cond and special (s. watch expression viewer)
634 self.__sendJsonCommand("RequestWatch", {
635 "temporary": temp,
636 "setWatch": setWatch,
637 "condition": cond,
638 })
639
640 def remoteWatchpointEnable(self, cond, enable):
641 """
642 Public method to enable or disable a watch expression.
643
644 @param cond expression of the watch expression (string)
645 @param enable flag indicating enabling or disabling a watch expression
646 (boolean)
647 """
648 # cond is combination of cond and special (s. watch expression viewer)
649 self.__sendJsonCommand("RequestWatchEnable", {
650 "condition": cond,
651 "enable": enable,
652 })
653
654 def remoteWatchpointIgnore(self, cond, count):
655 """
656 Public method to ignore a watch expression the next couple of
657 occurrences.
658
659 @param cond expression of the watch expression (string)
660 @param count number of occurrences to ignore (int)
661 """
662 # cond is combination of cond and special (s. watch expression viewer)
663 self.__sendJsonCommand("RequestWatchIgnore", {
664 "condition": cond,
665 "count": count,
666 })
667
668 def remoteRawInput(self, s):
669 """
670 Public method to send the raw input to the debugged program.
671
672 @param s the raw input (string)
673 """
674 self.__sendJsonCommand("RawInput", {
675 "input": s,
676 })
677
678 def remoteThreadList(self):
679 """
680 Public method to request the list of threads from the client.
681 """
682 self.__sendJsonCommand("RequestThreadList", {})
683
684 def remoteSetThread(self, tid):
685 """
686 Public method to request to set the given thread as current thread.
687
688 @param tid id of the thread (integer)
689 """
690 self.__sendJsonCommand("RequestThreadSet", {
691 "threadID": tid,
692 })
693
694 def remoteClientVariables(self, scope, filterList, framenr=0):
695 """
696 Public method to request the variables of the debugged program.
697
698 @param scope the scope of the variables (0 = local, 1 = global)
699 @param filterList list of variable types to filter out (list of int)
700 @param framenr framenumber of the variables to retrieve (int)
701 """
702 self.__sendJsonCommand("RequestVariables", {
703 "frameNumber": framenr,
704 "scope": scope,
705 "filters": filterList,
706 })
707
708 def remoteClientVariable(self, scope, filterList, var, framenr=0):
709 """
710 Public method to request the variables of the debugged program.
711
712 @param scope the scope of the variables (0 = local, 1 = global)
713 @param filterList list of variable types to filter out (list of int)
714 @param var list encoded name of variable to retrieve (string)
715 @param framenr framenumber of the variables to retrieve (int)
716 """
717 self.__sendJsonCommand("RequestVariable", {
718 "variable": var,
719 "frameNumber": framenr,
720 "scope": scope,
721 "filters": filterList,
722 })
723
724 def remoteClientSetFilter(self, scope, filterStr):
725 """
726 Public method to set a variables filter list.
727
728 @param scope the scope of the variables (0 = local, 1 = global)
729 @param filterStr regexp string for variable names to filter out
730 (string)
731 """
732 self.__sendJsonCommand("RequestSetFilter", {
733 "scope": scope,
734 "filter": filterStr,
735 })
736
737 def setCallTraceEnabled(self, on):
738 """
739 Public method to set the call trace state.
740
741 @param on flag indicating to enable the call trace function (boolean)
742 """
743 self.__sendJsonCommand("RequestCallTrace", {
744 "enable": on,
745 })
746
747 def remoteBanner(self):
748 """
749 Public slot to get the banner info of the remote client.
750 """
751 self.__sendJsonCommand("RequestBanner", {})
752
753 def remoteCapabilities(self):
754 """
755 Public slot to get the debug clients capabilities.
756 """
757 self.__sendJsonCommand("RequestCapabilities", {})
758
759 def remoteCompletion(self, text):
760 """
761 Public slot to get the a list of possible commandline completions
762 from the remote client.
763
764 @param text the text to be completed (string)
765 """
766 self.__sendJsonCommand("RequestCompletion", {
767 "text": text,
768 })
769
770 def remoteUTPrepare(self, fn, tn, tfn, failed, cov, covname, coverase):
771 """
772 Public method to prepare a new unittest run.
773
774 @param fn the filename to load (string)
775 @param tn the testname to load (string)
776 @param tfn the test function name to load tests from (string)
777 @param failed list of failed test, if only failed test should be run
778 (list of strings)
779 @param cov flag indicating collection of coverage data is requested
780 (boolean)
781 @param covname filename to be used to assemble the coverage caches
782 filename (string)
783 @param coverase flag indicating erasure of coverage data is requested
784 (boolean)
785 """
786 self.__scriptName = os.path.abspath(fn)
787
788 fn = self.translate(os.path.abspath(fn), False)
789 self.__sendJsonCommand("RequestUTPrepare", {
790 "filename": fn,
791 "testname": tn,
792 "testfunctionname": tfn,
793 "failed": failed,
794 "coverage": cov,
795 "coveragefile": covname,
796 "coverageerase": coverase,
797 })
798
799 def remoteUTRun(self):
800 """
801 Public method to start a unittest run.
802 """
803 self.__sendJsonCommand("RequestUTRun", {})
804
805 def remoteUTStop(self):
806 """
807 Public method to stop a unittest run.
808 """
809 self.__sendJsonCommand("RequestUTStop", {})
810
811 def __askForkTo(self):
812 """
813 Private method to ask the user which branch of a fork to follow.
814 """
815 selections = [self.tr("Parent Process"),
816 self.tr("Child process")]
817 res, ok = QInputDialog.getItem(
818 None,
819 self.tr("Client forking"),
820 self.tr("Select the fork branch to follow."),
821 selections,
822 0, False)
823 if not ok or res == selections[0]:
824 self.__sendJsonCommand("ResponseForkTo", {
825 "target": "parent",
826 })
827 else:
828 self.__sendJsonCommand("ResponseForkTo", {
829 "target": "child",
830 })
831
832 def __parseClientLine(self):
833 """
834 Private method to handle data from the client.
835 """
836 while self.qsock and self.qsock.canReadLine():
837 qs = self.qsock.readLine()
838 if self.codec is not None:
839 line = self.codec.toUnicode(qs)
840 else:
841 line = bytes(qs).decode()
842
843 ## print("Server: ", line) ##debug
844
845 self.__handleJsonCommand(line)
846 continue
847
848 def __handleJsonCommand(self, jsonStr):
849 """
850 Private method to handle a command or response serialized as a
851 JSON string.
852
853 @param jsonStr string containing the command or response received
854 from the debug backend
855 @type str
856 """
857 import json
858
859 try:
860 commandDict = json.loads(jsonStr.strip())
861 except (TypeError, ValueError) as err:
862 E5MessageBox.critical(
863 None,
864 self.tr("Debug Protocol Error"),
865 self.tr("""<p>The response received from the debugger"""
866 """ backend could not be decoded. Please report"""
867 """ this issue with the received data to the"""
868 """ eric bugs email address.</p>"""
869 """<p>Error: {0}</p>"""
870 """<p>Data:<br/>{0}</p>""").format(
871 str(err), Utilities.html_encode(jsonStr.strip())),
872 E5MessageBox.StandardButtons(
873 E5MessageBox.Ok))
874 return
875
876 method = commandDict["method"]
877 params = commandDict["params"]
878
879 if method == "ClientOutput":
880 self.debugServer.signalClientOutput(params["text"])
881
882 elif method in ["ResponseLine", "ResponseStack"]:
883 # Check if obsolet thread was clicked
884 if params["stack"] == []:
885 # Request updated list
886 self.remoteThreadList()
887 return
888 for s in params["stack"]:
889 s[0] = self.translate(s[0], True)
890 cf = params["stack"][0]
891 if self.__autoContinue:
892 self.__autoContinue = False
893 QTimer.singleShot(0, self.remoteContinue)
894 else:
895 self.debugServer.signalClientLine(
896 cf[0], int(cf[1]),
897 method == "ResponseStack")
898 self.debugServer.signalClientStack(params["stack"])
899
900 elif method == "CallTrace":
901 isCall = params["event"].lower() == "c"
902 fromInfo = params["from"]
903 toInfo = params["to"]
904 self.debugServer.signalClientCallTrace(
905 isCall,
906 fromInfo["filename"], str(fromInfo["linenumber"]),
907 fromInfo["codename"],
908 toInfo["filename"], str(toInfo["linenumber"]),
909 toInfo["codename"])
910
911 elif method == "ResponseVariables":
912 self.debugServer.signalClientVariables(
913 params["scope"], params["variables"])
914
915 elif method == "ResponseVariable":
916 self.debugServer.signalClientVariable(
917 params["scope"], [params["variable"]] + params["variables"])
918
919 elif method == "ResponseThreadList":
920 self.debugServer.signalClientThreadList(
921 params["currentID"], params["threadList"])
922
923 elif method == "ResponseThreadSet":
924 self.debugServer.signalClientThreadSet()
925
926 elif method == "ResponseCapabilities":
927 self.clientCapabilities = params["capabilities"]
928 self.debugServer.signalClientCapabilities(
929 params["capabilities"], params["clientType"])
930
931 elif method == "ResponseBanner":
932 self.debugServer.signalClientBanner(
933 params["version"],
934 params["platform"],
935 params["dbgclient"])
936
937 elif method == "ResponseOK":
938 self.debugServer.signalClientStatement(False)
939
940 elif method == "ResponseContinue":
941 self.debugServer.signalClientStatement(True)
942
943 elif method == "RequestRaw":
944 self.debugServer.signalClientRawInput(
945 params["prompt"], params["echo"])
946
947 elif method == "ResponseBPConditionError":
948 fn = self.translate(params["filename"], True)
949 self.debugServer.signalClientBreakConditionError(
950 fn, params["line"])
951
952 elif method == "ResponseClearBreakpoint":
953 fn = self.translate(params["filename"], True)
954 self.debugServer.signalClientClearBreak(fn, params["line"])
955
956 elif method == "ResponseWatchConditionError":
957 self.debugServer.signalClientWatchConditionError(
958 params["condition"])
959
960 elif method == "ResponseClearWatch":
961 self.debugServer.signalClientClearWatch(params["condition"])
962
963 elif method == "ResponseException":
964 if params:
965 exctype = params["type"]
966 excmessage = params["message"]
967 stack = params["stack"]
968 if stack:
969 for stackEntry in stack:
970 stackEntry[0] = self.translate(stackEntry[0], True)
971 if stack[0] and stack[0][0] == "<string>":
972 for stackEntry in stack:
973 if stackEntry[0] == "<string>":
974 stackEntry[0] = self.__scriptName
975 else:
976 break
977 else:
978 exctype = ''
979 excmessage = ''
980 stack = []
981
982 self.debugServer.signalClientException(
983 exctype, excmessage, stack)
984
985 elif method == "ResponseSyntax":
986 self.debugServer.signalClientSyntaxError(
987 params["message"], self.translate(params["filename"], True),
988 params["linenumber"], params["characternumber"])
989
990 elif method == "ResponseSignal":
991 self.debugServer.signalClientSignal(
992 params["message"], self.translate(params["filename"], True),
993 params["linenumber"], params["function"], params["arguments"])
994
995 elif method == "ResponseExit":
996 self.__scriptName = ""
997 self.debugServer.signalClientExit(
998 params["status"], params["message"])
999
1000 elif method == "PassiveStartup":
1001 self.debugServer.passiveStartUp(
1002 self.translate(params["filename"], True), params["exceptions"])
1003
1004 elif method == "ResponseCompletion":
1005 self.debugServer.signalClientCompletionList(
1006 params["completions"], params["text"])
1007
1008 elif method == "ResponseUTPrepared":
1009 self.debugServer.clientUtPrepared(
1010 params["count"], params["exception"], params["message"])
1011
1012 elif method == "ResponseUTFinished":
1013 self.debugServer.clientUtFinished()
1014
1015 elif method == "ResponseUTStartTest":
1016 self.debugServer.clientUtStartTest(
1017 params["testname"], params["description"])
1018
1019 elif method == "ResponseUTStopTest":
1020 self.debugServer.clientUtStopTest()
1021
1022 elif method == "ResponseUTTestFailed":
1023 self.debugServer.clientUtTestFailed(
1024 params["testname"], params["traceback"], params["id"])
1025
1026 elif method == "ResponseUTTestErrored":
1027 self.debugServer.clientUtTestErrored(
1028 params["testname"], params["traceback"], params["id"])
1029
1030 elif method == "ResponseUTTestSkipped":
1031 self.debugServer.clientUtTestSkipped(
1032 params["testname"], params["reason"], params["id"])
1033
1034 elif method == "ResponseUTTestFailedExpected":
1035 self.debugServer.clientUtTestFailedExpected(
1036 params["testname"], params["traceback"], params["id"])
1037
1038 elif method == "ResponseUTTestSucceededUnexpected":
1039 self.debugServer.clientUtTestSucceededUnexpected(
1040 params["testname"], params["id"])
1041
1042 elif method == "RequestForkTo":
1043 self.__askForkTo()
1044
1045 def __sendJsonCommand(self, command, params):
1046 """
1047 Private method to send a single command to the client.
1048
1049 @param command command name to be sent
1050 @type str
1051 @param params dictionary of named parameters for the command
1052 @type dict
1053 """
1054 import json
1055
1056 commandDict = {
1057 "jsonrpc": "2.0",
1058 "method": command,
1059 "params": params,
1060 }
1061 cmd = json.dumps(commandDict) + '\n'
1062 if self.qsock is not None:
1063 self.qsock.write(cmd.encode('utf8', 'backslashreplace'))
1064 else:
1065 self.queue.append(cmd)
1066
1067
1068 def createDebuggerInterfacePython2(debugServer, passive):
1069 """
1070 Module function to create a debugger interface instance.
1071
1072
1073 @param debugServer reference to the debug server
1074 @type DebugServer
1075 @param passive flag indicating passive connection mode
1076 @type bool
1077 @return instantiated debugger interface
1078 @rtype DebuggerInterfacePython
1079 """
1080 return DebuggerInterfacePython2(debugServer, passive)
1081
1082
1083 def getRegistryData():
1084 """
1085 Module function to get characterizing data for the debugger interface.
1086
1087 @return tuple containing client type, client capabilities, client file
1088 type associations and reference to creation function
1089 @rtype tuple of (str, int, list of str, function)
1090 """
1091 exts = []
1092 for ext in Preferences.getDebugger("PythonExtensions").split():
1093 if ext.startswith("."):
1094 exts.append(ext)
1095 else:
1096 exts.append(".{0}".format(ext))
1097
1098 if exts and Preferences.getDebugger("PythonInterpreter"):
1099 return ["Python2", ClientDefaultCapabilities, exts,
1100 createDebuggerInterfacePython2]
1101 else:
1102 return ["", 0, [], None]

eric ide

mercurial