Debugger/DebuggerInterfacePython.py

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

eric ide

mercurial