DebugClients/Python/DebugClientBase.py

changeset 0
de9c2efb9d02
child 5
fa319c01b604
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a debug client base class.
8 """
9
10 import sys
11 import socket
12 import select
13 import codeop
14 import traceback
15 import os
16 import time
17 import imp
18 import re
19 import distutils.sysconfig
20
21
22 from DebugProtocol import *
23 import DebugClientCapabilities
24 from DebugBase import setRecursionLimit, printerr
25 from AsyncFile import *
26 from DebugConfig import ConfigVarTypeStrings
27 from FlexCompleter import Completer
28
29
30 DebugClientInstance = None
31
32 ################################################################################
33
34 def DebugClientRawInput(prompt="", echo=1):
35 """
36 Replacement for the standard raw_input builtin.
37
38 This function works with the split debugger.
39
40 @param prompt The prompt to be shown. (string)
41 @param echo Flag indicating echoing of the input (boolean)
42 """
43 if DebugClientInstance is None or DebugClientInstance.redirect == 0:
44 return DebugClientOrigRawInput(prompt)
45
46 return DebugClientInstance.raw_input(prompt, echo)
47
48 # Use our own raw_input().
49 try:
50 DebugClientOrigRawInput = __builtins__.__dict__['raw_input']
51 __builtins__.__dict__['raw_input'] = DebugClientRawInput
52 except (AttributeError, KeyError):
53 import __main__
54 DebugClientOrigRawInput = __main__.__builtins__.__dict__['raw_input']
55 __main__.__builtins__.__dict__['raw_input'] = DebugClientRawInput
56
57 ################################################################################
58
59 def DebugClientInput(prompt=""):
60 """
61 Replacement for the standard input builtin.
62
63 This function works with the split debugger.
64
65 @param prompt The prompt to be shown. (string)
66 """
67 if DebugClientInstance is None or DebugClientInstance.redirect == 0:
68 return DebugClientOrigInput(prompt)
69
70 return DebugClientInstance.input(prompt)
71
72 # Use our own input().
73 try:
74 DebugClientOrigInput = __builtins__.__dict__['input']
75 __builtins__.__dict__['input'] = DebugClientInput
76 except (AttributeError, KeyError):
77 import __main__
78 DebugClientOrigInput = __main__.__builtins__.__dict__['input']
79 __main__.__builtins__.__dict__['input'] = DebugClientInput
80
81 ################################################################################
82
83 def DebugClientFork():
84 """
85 Replacement for the standard os.fork().
86 """
87 if DebugClientInstance is None:
88 return DebugClientOrigFork()
89
90 return DebugClientInstance.fork()
91
92 # use our own fork().
93 if 'fork' in dir(os):
94 DebugClientOrigFork = os.fork
95 os.fork = DebugClientFork
96
97 ################################################################################
98
99 def DebugClientClose(fd):
100 """
101 Replacement for the standard os.close(fd).
102
103 @param fd open file descriptor to be closed (integer)
104 """
105 if DebugClientInstance is None:
106 DebugClientOrigClose(fd)
107
108 DebugClientInstance.close(fd)
109
110 # use our own close().
111 if 'close' in dir(os):
112 DebugClientOrigClose = os.close
113 os.close = DebugClientClose
114
115 ################################################################################
116
117 def DebugClientSetRecursionLimit(limit):
118 """
119 Replacement for the standard sys.setrecursionlimit(limit).
120
121 @param limit recursion limit (integer)
122 """
123 rl = max(limit, 64)
124 setRecursionLimit(rl)
125 DebugClientOrigSetRecursionLimit(rl + 64)
126
127 # use our own setrecursionlimit().
128 if 'setrecursionlimit' in dir(sys):
129 DebugClientOrigSetRecursionLimit = sys.setrecursionlimit
130 sys.setrecursionlimit = DebugClientSetRecursionLimit
131 DebugClientSetRecursionLimit(sys.getrecursionlimit())
132
133 ################################################################################
134
135 class DebugClientBase(object):
136 """
137 Class implementing the client side of the debugger.
138
139 It provides access to the Python interpeter from a debugger running in another
140 process whether or not the Qt event loop is running.
141
142 The protocol between the debugger and the client assumes that there will be
143 a single source of debugger commands and a single source of Python
144 statements. Commands and statement are always exactly one line and may be
145 interspersed.
146
147 The protocol is as follows. First the client opens a connection to the
148 debugger and then sends a series of one line commands. A command is either
149 &gt;Load&lt;, &gt;Step&lt;, &gt;StepInto&lt;, ... or a Python statement.
150 See DebugProtocol.py for a listing of valid protocol tokens.
151
152 A Python statement consists of the statement to execute, followed (in a
153 separate line) by &gt;OK?&lt;. If the statement was incomplete then the response
154 is &gt;Continue&lt;. If there was an exception then the response is
155 &gt;Exception&lt;.
156 Otherwise the response is &gt;OK&lt;. The reason for the &gt;OK?&lt; part is to
157 provide a sentinal (ie. the responding &gt;OK&lt;) after any possible output as a
158 result of executing the command.
159
160 The client may send any other lines at any other time which should be
161 interpreted as program output.
162
163 If the debugger closes the session there is no response from the client.
164 The client may close the session at any time as a result of the script
165 being debugged closing or crashing.
166
167 <b>Note</b>: This class is meant to be subclassed by individual DebugClient classes.
168 Do not instantiate it directly.
169 """
170 clientCapabilities = DebugClientCapabilities.HasAll
171
172 def __init__(self):
173 """
174 Constructor
175 """
176 self.breakpoints = {}
177 self.redirect = 1
178
179 # The next couple of members are needed for the threaded version.
180 # For this base class they contain static values for the non threaded
181 # debugger
182
183 # dictionary of all threads running
184 self.threads = {}
185
186 # the "current" thread, basically the thread we are at a breakpoint for.
187 self.currentThread = self
188
189 # special objects representing the main scripts thread and frame
190 self.mainThread = self
191 self.mainFrame = None
192 self.framenr = 0
193
194 # The context to run the debugged program in.
195 self.debugMod = imp.new_module('__main__')
196
197 # The list of complete lines to execute.
198 self.buffer = ''
199
200 # The list of regexp objects to filter variables against
201 self.globalsFilterObjects = []
202 self.localsFilterObjects = []
203
204 self.pendingResponse = ResponseOK
205 self.fncache = {}
206 self.dircache = []
207 self.inRawMode = 0
208 self.mainProcStr = None # used for the passive mode
209 self.passive = 0 # used to indicate the passive mode
210 self.running = None
211 self.test = None
212 self.tracePython = 0
213 self.debugging = 0
214
215 self.fork_auto = False
216 self.fork_child = False
217
218 self.readstream = None
219 self.writestream = None
220 self.errorstream = None
221 self.pollingDisabled = False
222
223 self.skipdirs = sys.path[:]
224
225 self.variant = 'You should not see this'
226
227 # commandline completion stuff
228 self.complete = Completer(self.debugMod.__dict__).complete
229
230 if sys.hexversion < 0x2020000:
231 self.compile_command = codeop.compile_command
232 else:
233 self.compile_command = codeop.CommandCompiler()
234
235 self.coding_re = re.compile(r"coding[:=]\s*([-\w_.]+)")
236 self.defaultCoding = 'utf-8'
237 self.__coding = self.defaultCoding
238 self.noencoding = False
239
240 def getCoding(self):
241 """
242 Public method to return the current coding.
243
244 @return codec name (string)
245 """
246 return self.__coding
247
248 def __setCoding(self, filename):
249 """
250 Private method to set the coding used by a python file.
251
252 @param filename name of the file to inspect (string)
253 """
254 if self.noencoding:
255 self.__coding = sys.getdefaultencoding()
256 else:
257 default = 'latin-1'
258 try:
259 f = open(filename, 'rb')
260 # read the first and second line
261 text = f.readline()
262 text = "%s%s" % (text, f.readline())
263 f.close()
264 except IOError:
265 self.__coding = default
266 return
267
268 for l in text.splitlines():
269 m = self.coding_re.search(l)
270 if m:
271 self.__coding = m.group(1)
272 return
273 self.__coding = default
274
275 def attachThread(self, target = None, args = None, kwargs = None, mainThread = 0):
276 """
277 Public method to setup a thread for DebugClient to debug.
278
279 If mainThread is non-zero, then we are attaching to the already
280 started mainthread of the app and the rest of the args are ignored.
281
282 This is just an empty function and is overridden in the threaded
283 debugger.
284
285 @param target the start function of the target thread (i.e. the user code)
286 @param args arguments to pass to target
287 @param kwargs keyword arguments to pass to target
288 @param mainThread non-zero, if we are attaching to the already
289 started mainthread of the app
290 @return The identifier of the created thread
291 """
292 if self.debugging:
293 sys.setprofile(self.profile)
294
295 def __dumpThreadList(self):
296 """
297 Public method to send the list of threads.
298 """
299 threadList = []
300 if self.threads: # indication for the threaded debugger
301 currentId = self.currentThread.get_ident()
302 for t in self.threads.values():
303 d = {}
304 d["id"] = t.get_ident()
305 d["name"] = t.get_name()
306 d["broken"] = t.isBroken()
307 threadList.append(d)
308 else:
309 currentId = -1
310 d = {}
311 d["id"] = -1
312 d["name"] = "MainThread"
313 d["broken"] = self.isBroken()
314 threadList.append(d)
315
316 self.write('%s%s\n' % (ResponseThreadList, unicode((currentId, threadList))))
317
318 def raw_input(self,prompt,echo):
319 """
320 Public method to implement raw_input() using the event loop.
321
322 @param prompt the prompt to be shown (string)
323 @param echo Flag indicating echoing of the input (boolean)
324 @return the entered string
325 """
326 self.write("%s%s\n" % (ResponseRaw, unicode((prompt, echo))))
327 self.inRawMode = 1
328 self.eventLoop(True)
329 return self.rawLine
330
331 def input(self,prompt):
332 """
333 Public method to implement input() using the event loop.
334
335 @param prompt the prompt to be shown (string)
336 @return the entered string evaluated as a Python expresion
337 """
338 return eval(self.raw_input(prompt, 1))
339
340 def __exceptionRaised(self):
341 """
342 Private method called in the case of an exception
343
344 It ensures that the debug server is informed of the raised exception.
345 """
346 self.pendingResponse = ResponseException
347
348 def sessionClose(self, exit = 1):
349 """
350 Public method to close the session with the debugger and optionally terminate.
351
352 @param exit flag indicating to terminate (boolean)
353 """
354 try:
355 self.set_quit()
356 except:
357 pass
358
359 # clean up asyncio.
360 self.disconnect()
361 self.debugging = 0
362
363 # make sure we close down our end of the socket
364 # might be overkill as normally stdin, stdout and stderr
365 # SHOULD be closed on exit, but it does not hurt to do it here
366 self.readstream.close(1)
367 self.writestream.close(1)
368 self.errorstream.close(1)
369
370 if exit:
371 # Ok, go away.
372 sys.exit()
373
374 def handleLine(self,line):
375 """
376 Public method to handle the receipt of a complete line.
377
378 It first looks for a valid protocol token at the start of the line. Thereafter
379 it trys to execute the lines accumulated so far.
380
381 @param line the received line
382 """
383 # Remove any newline.
384 if line[-1] == '\n':
385 line = line[:-1]
386
387 ## printerr(line) ##debug
388
389 eoc = line.find('<')
390
391 if eoc >= 0 and line[0] == '>':
392 # Get the command part and any argument.
393 cmd = line[:eoc + 1]
394 arg = line[eoc + 1:]
395
396 if cmd == RequestVariables:
397 frmnr, scope, filter = eval(arg)
398 self.__dumpVariables(int(frmnr), int(scope), filter)
399 return
400
401 if cmd == RequestVariable:
402 var, frmnr, scope, filter = eval(arg)
403 self.__dumpVariable(var, int(frmnr), int(scope), filter)
404 return
405
406 if cmd == RequestThreadList:
407 self.__dumpThreadList()
408 return
409
410 if cmd == RequestThreadSet:
411 tid = eval(arg)
412 if tid in self.threads:
413 self.setCurrentThread(tid)
414 self.write(ResponseThreadSet + '\n')
415 stack = self.currentThread.getStack()
416 self.write('%s%s\n' % (ResponseStack, unicode(stack)))
417 return
418
419 if cmd == RequestStep:
420 self.currentThread.step(1)
421 self.eventExit = 1
422 return
423
424 if cmd == RequestStepOver:
425 self.currentThread.step(0)
426 self.eventExit = 1
427 return
428
429 if cmd == RequestStepOut:
430 self.currentThread.stepOut()
431 self.eventExit = 1
432 return
433
434 if cmd == RequestStepQuit:
435 if self.passive:
436 self.progTerminated(42)
437 else:
438 self.set_quit()
439 self.eventExit = 1
440 return
441
442 if cmd == RequestContinue:
443 special = int(arg)
444 self.currentThread.go(special)
445 self.eventExit = 1
446 return
447
448 if cmd == RequestOK:
449 self.write(self.pendingResponse + '\n')
450 self.pendingResponse = ResponseOK
451 return
452
453 if cmd == RequestEnv:
454 env = eval(arg)
455 for key, value in env.items():
456 if key.endswith("+"):
457 if os.environ.has_key(key[:-1]):
458 os.environ[key[:-1]] += value
459 else:
460 os.environ[key[:-1]] = value
461 else:
462 os.environ[key] = value
463 return
464
465 if cmd == RequestLoad:
466 self.fncache = {}
467 self.dircache = []
468 sys.argv = []
469 wd, fn, args, tracePython = arg.split('|')
470 self.__setCoding(fn)
471 try:
472 sys.setappdefaultencoding(self.__coding)
473 except AttributeError:
474 pass
475 sys.argv.append(fn)
476 sys.argv.extend(eval(args))
477 sys.path[0] = os.path.dirname(sys.argv[0])
478 sys.path.insert(0, '')
479 if wd == '':
480 os.chdir(sys.path[1])
481 else:
482 os.chdir(wd)
483 tracePython = int(tracePython)
484 self.running = sys.argv[0]
485 self.mainFrame = None
486 self.inRawMode = 0
487 self.debugging = 1
488
489 self.threads.clear()
490 self.attachThread(mainThread = 1)
491
492 # set the system exception handling function to ensure, that
493 # we report on all unhandled exceptions
494 sys.excepthook = self.__unhandled_exception
495
496 # clear all old breakpoints, they'll get set after we have started
497 self.mainThread.clear_all_breaks()
498
499 self.mainThread.tracePython = tracePython
500
501 # This will eventually enter a local event loop.
502 # Note the use of backquotes to cause a repr of self.running. The
503 # need for this is on Windows os where backslash is the path separator.
504 # They will get inadvertantly stripped away during the eval causing
505 # IOErrors, if self.running is passed as a normal str.
506 self.debugMod.__dict__['__file__'] = self.running
507 sys.modules['__main__'] = self.debugMod
508 res = self.mainThread.run('execfile(' + `self.running` + ')',
509 self.debugMod.__dict__)
510 self.progTerminated(res)
511 return
512
513 if cmd == RequestRun:
514 sys.argv = []
515 wd, fn, args = arg.split('|')
516 self.__setCoding(fn)
517 try:
518 sys.setappdefaultencoding(self.__coding)
519 except AttributeError:
520 pass
521 sys.argv.append(fn)
522 sys.argv.extend(eval(args))
523 sys.path[0] = os.path.dirname(sys.argv[0])
524 sys.path.insert(0, '')
525 if wd == '':
526 os.chdir(sys.path[1])
527 else:
528 os.chdir(wd)
529
530 self.running = sys.argv[0]
531 self.mainFrame = None
532 self.botframe = None
533 self.inRawMode = 0
534
535 self.threads.clear()
536 self.attachThread(mainThread = 1)
537
538 # set the system exception handling function to ensure, that
539 # we report on all unhandled exceptions
540 sys.excepthook = self.__unhandled_exception
541
542 self.mainThread.tracePython = 0
543
544 self.debugMod.__dict__['__file__'] = sys.argv[0]
545 sys.modules['__main__'] = self.debugMod
546 execfile(sys.argv[0], self.debugMod.__dict__)
547 self.writestream.flush()
548 return
549
550 if cmd == RequestCoverage:
551 from coverage import coverage
552 sys.argv = []
553 wd, fn, args, erase = arg.split('@@')
554 self.__setCoding(fn)
555 try:
556 sys.setappdefaultencoding(self.__coding)
557 except AttributeError:
558 pass
559 sys.argv.append(fn)
560 sys.argv.extend(eval(args))
561 sys.path[0] = os.path.dirname(sys.argv[0])
562 sys.path.insert(0, '')
563 if wd == '':
564 os.chdir(sys.path[1])
565 else:
566 os.chdir(wd)
567
568 # set the system exception handling function to ensure, that
569 # we report on all unhandled exceptions
570 sys.excepthook = self.__unhandled_exception
571
572 # generate a coverage object
573 self.cover = coverage(auto_data = True,
574 data_file = "%s.coverage" % os.path.splitext(sys.argv[0])[0])
575 self.cover.use_cache(True)
576
577 if int(erase):
578 self.cover.erase()
579 sys.modules['__main__'] = self.debugMod
580 self.debugMod.__dict__['__file__'] = sys.argv[0]
581 self.cover.start()
582 execfile(sys.argv[0], self.debugMod.__dict__)
583 self.cover.stop()
584 self.cover.save()
585 self.writestream.flush()
586 return
587
588 if cmd == RequestProfile:
589 sys.setprofile(None)
590 import PyProfile
591 sys.argv = []
592 wd, fn, args, erase = arg.split('|')
593 self.__setCoding(fn)
594 try:
595 sys.setappdefaultencoding(self.__coding)
596 except AttributeError:
597 pass
598 sys.argv.append(fn)
599 sys.argv.extend(eval(args))
600 sys.path[0] = os.path.dirname(sys.argv[0])
601 sys.path.insert(0, '')
602 if wd == '':
603 os.chdir(sys.path[1])
604 else:
605 os.chdir(wd)
606
607 # set the system exception handling function to ensure, that
608 # we report on all unhandled exceptions
609 sys.excepthook = self.__unhandled_exception
610
611 # generate a profile object
612 self.prof = PyProfile.PyProfile(sys.argv[0])
613
614 if int(erase):
615 self.prof.erase()
616 self.debugMod.__dict__['__file__'] = sys.argv[0]
617 sys.modules['__main__'] = self.debugMod
618 self.prof.run('execfile(%r)' % sys.argv[0])
619 self.prof.save()
620 self.writestream.flush()
621 return
622
623 if cmd == RequestShutdown:
624 self.sessionClose()
625 return
626
627 if cmd == RequestBreak:
628 fn, line, temporary, set, cond = arg.split('@@')
629 line = int(line)
630 set = int(set)
631 temporary = int(temporary)
632
633 if set:
634 if cond == 'None' or cond == '':
635 cond = None
636 else:
637 try:
638 compile(cond, '<string>', 'eval')
639 except SyntaxError:
640 self.write('%s%s,%d\n' % \
641 (ResponseBPConditionError, fn, line))
642 return
643 self.mainThread.set_break(fn, line, temporary, cond)
644 else:
645 self.mainThread.clear_break(fn, line)
646
647 return
648
649 if cmd == RequestBreakEnable:
650 fn, line, enable = arg.split(',')
651 line = int(line)
652 enable = int(enable)
653
654 bp = self.mainThread.get_break(fn, line)
655 if bp is not None:
656 if enable:
657 bp.enable()
658 else:
659 bp.disable()
660
661 return
662
663 if cmd == RequestBreakIgnore:
664 fn, line, count = arg.split(',')
665 line = int(line)
666 count = int(count)
667
668 bp = self.mainThread.get_break(fn, line)
669 if bp is not None:
670 bp.ignore = count
671
672 return
673
674 if cmd == RequestWatch:
675 cond, temporary, set = arg.split('@@')
676 set = int(set)
677 temporary = int(temporary)
678
679 if set:
680 if not cond.endswith('??created??') and \
681 not cond.endswith('??changed??'):
682 try:
683 compile(cond, '<string>', 'eval')
684 except SyntaxError:
685 self.write('%s%s\n' % (ResponseWPConditionError, cond))
686 return
687 self.mainThread.set_watch(cond, temporary)
688 else:
689 self.mainThread.clear_watch(cond)
690
691 return
692
693 if cmd == RequestWatchEnable:
694 cond, enable = arg.split(',')
695 enable = int(enable)
696
697 bp = self.mainThread.get_watch(cond)
698 if bp is not None:
699 if enable:
700 bp.enable()
701 else:
702 bp.disable()
703
704 return
705
706 if cmd == RequestWatchIgnore:
707 cond, count = arg.split(',')
708 count = int(count)
709
710 bp = self.mainThread.get_watch(cond)
711 if bp is not None:
712 bp.ignore = count
713
714 return
715
716 if cmd == RequestEval:
717 try:
718 value = eval(arg, self.currentThread.getCurrentFrame().f_globals,
719 self.currentThread.getCurrentFrame().f_locals)
720 except:
721 # Report the exception and the traceback
722 try:
723 type, value, tb = sys.exc_info()
724 sys.last_type = type
725 sys.last_value = value
726 sys.last_traceback = tb
727 tblist = traceback.extract_tb(tb)
728 del tblist[:1]
729 list = traceback.format_list(tblist)
730 if list:
731 list.insert(0, "Traceback (innermost last):\n")
732 list[len(list):] = \
733 traceback.format_exception_only(type, value)
734 finally:
735 tblist = tb = None
736
737 map(self.write,list)
738
739 self.write(ResponseException + '\n')
740
741 else:
742 self.write(unicode(value) + '\n')
743 self.write(ResponseOK + '\n')
744
745 return
746
747 if cmd == RequestExec:
748 _globals = self.currentThread.getCurrentFrame().f_globals
749 _locals = self.currentThread.getCurrentFrame().f_locals
750 try:
751 code = compile(arg + '\n', '<stdin>', 'single')
752 exec code in _globals, _locals
753 except:
754 # Report the exception and the traceback
755 try:
756 type, value, tb = sys.exc_info()
757 sys.last_type = type
758 sys.last_value = value
759 sys.last_traceback = tb
760 tblist = traceback.extract_tb(tb)
761 del tblist[:1]
762 list = traceback.format_list(tblist)
763 if list:
764 list.insert(0, "Traceback (innermost last):\n")
765 list[len(list):] = \
766 traceback.format_exception_only(type, value)
767 finally:
768 tblist = tb = None
769
770 map(self.write, list)
771
772 self.write(ResponseException + '\n')
773
774 return
775
776 if cmd == RequestBanner:
777 self.write('%s%s\n' % (ResponseBanner,
778 unicode(("Python %s" % sys.version, socket.gethostname(),
779 self.variant))))
780 return
781
782 if cmd == RequestCapabilities:
783 self.write('%s%d, "Python"\n' % (ResponseCapabilities,
784 self.__clientCapabilities()))
785 return
786
787 if cmd == RequestCompletion:
788 self.__completionList(arg)
789 return
790
791 if cmd == RequestSetFilter:
792 scope, filterString = eval(arg)
793 self.__generateFilterObjects(int(scope), filterString)
794 return
795
796 if cmd == RequestUTPrepare:
797 fn, tn, tfn, cov, covname, erase = arg.split('|')
798 sys.path.insert(0, os.path.dirname(os.path.abspath(fn)))
799 os.chdir(sys.path[0])
800
801 # set the system exception handling function to ensure, that
802 # we report on all unhandled exceptions
803 sys.excepthook = self.__unhandled_exception
804
805 try:
806 import unittest
807 utModule = __import__(tn)
808 try:
809 self.test = unittest.defaultTestLoader\
810 .loadTestsFromName(tfn, utModule)
811 except AttributeError:
812 self.test = unittest.defaultTestLoader\
813 .loadTestsFromModule(utModule)
814 except:
815 exc_type, exc_value, exc_tb = sys.exc_info()
816 self.write('%s%s\n' % (ResponseUTPrepared,
817 unicode((0, str(exc_type), str(exc_value)))))
818 self.__exceptionRaised()
819 return
820
821 # generate a coverage object
822 if int(cov):
823 from coverage import coverage
824 self.cover = coverage(auto_data = True,
825 data_file = "%s.coverage" % os.path.splitext(covname)[0])
826 self.cover.use_cache(True)
827 if int(erase):
828 self.cover.erase()
829 else:
830 self.cover = None
831
832 self.write('%s%s\n' % (ResponseUTPrepared,
833 unicode((self.test.countTestCases(), "", ""))))
834 return
835
836 if cmd == RequestUTRun:
837 from DCTestResult import DCTestResult
838 self.testResult = DCTestResult(self)
839 if self.cover:
840 self.cover.start()
841 self.test.run(self.testResult)
842 if self.cover:
843 self.cover.stop()
844 self.cover.save()
845 self.write('%s\n' % ResponseUTFinished)
846 return
847
848 if cmd == RequestUTStop:
849 self.testResult.stop()
850 return
851
852 if cmd == ResponseForkTo:
853 # this results from a separate event loop
854 self.fork_child = (arg == 'child')
855 self.eventExit = 1
856 return
857
858 if cmd == RequestForkMode:
859 self.fork_auto, self.fork_child = eval(arg)
860 return
861
862 # If we are handling raw mode input then reset the mode and break out
863 # of the current event loop.
864 if self.inRawMode:
865 self.inRawMode = 0
866 self.rawLine = line
867 self.eventExit = 1
868 return
869
870 if self.buffer:
871 self.buffer = self.buffer + '\n' + line
872 else:
873 self.buffer = line
874
875 try:
876 code = self.compile_command(self.buffer, self.readstream.name)
877 except (OverflowError, SyntaxError, ValueError):
878 # Report the exception
879 sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
880 map(self.write,traceback.format_exception_only(sys.last_type,sys.last_value))
881 self.buffer = ''
882
883 self.__exceptionRaised()
884 else:
885 if code is None:
886 self.pendingResponse = ResponseContinue
887 else:
888 self.buffer = ''
889
890 try:
891 if self.running is None:
892 exec code in self.debugMod.__dict__
893 else:
894 if self.currentThread is None:
895 # program has terminated
896 self.running = None
897 _globals = self.debugMod.__dict__
898 _locals = _globals
899 else:
900 cf = self.currentThread.getCurrentFrame()
901 # program has terminated
902 if cf is None:
903 self.running = None
904 _globals = self.debugMod.__dict__
905 _locals = _globals
906 else:
907 frmnr = self.framenr
908 while cf is not None and frmnr > 0:
909 cf = cf.f_back
910 frmnr -= 1
911 _globals = cf.f_globals
912 _locals = cf.f_locals
913 # reset sys.stdout to our redirector (unconditionally)
914 if _globals.has_key("sys"):
915 __stdout = _globals["sys"].stdout
916 _globals["sys"].stdout = self.writestream
917 exec code in _globals, _locals
918 _globals["sys"].stdout = __stdout
919 elif _locals.has_key("sys"):
920 __stdout = _locals["sys"].stdout
921 _locals["sys"].stdout = self.writestream
922 exec code in _globals, _locals
923 _locals["sys"].stdout = __stdout
924 else:
925 exec code in _globals, _locals
926 except SystemExit, exc:
927 self.progTerminated(exc.code)
928 except:
929 # Report the exception and the traceback
930 try:
931 type, value, tb = sys.exc_info()
932 sys.last_type = type
933 sys.last_value = value
934 sys.last_traceback = tb
935 tblist = traceback.extract_tb(tb)
936 del tblist[:1]
937 list = traceback.format_list(tblist)
938 if list:
939 list.insert(0, "Traceback (innermost last):\n")
940 list[len(list):] = \
941 traceback.format_exception_only(type, value)
942 finally:
943 tblist = tb = None
944
945 map(self.write, list)
946
947 self.__exceptionRaised()
948
949 def __clientCapabilities(self):
950 """
951 Private method to determine the clients capabilities.
952
953 @return client capabilities (integer)
954 """
955 try:
956 import PyProfile
957 try:
958 del sys.modules['PyProfile']
959 except KeyError:
960 pass
961 return self.clientCapabilities
962 except ImportError:
963 return self.clientCapabilities & ~DebugClientCapabilities.HasProfiler
964
965 def write(self,s):
966 """
967 Public method to write data to the output stream.
968
969 @param s data to be written (string)
970 """
971 self.writestream.write(s)
972 self.writestream.flush()
973
974 def __interact(self):
975 """
976 Private method to Interact with the debugger.
977 """
978 global DebugClientInstance
979
980 self.setDescriptors(self.readstream,self.writestream)
981 DebugClientInstance = self
982
983 if not self.passive:
984 # At this point simulate an event loop.
985 self.eventLoop()
986
987 def eventLoop(self, disablePolling = False):
988 """
989 Public method implementing our event loop.
990
991 @param disablePolling flag indicating to enter an event loop with
992 polling disabled (boolean)
993 """
994 self.eventExit = None
995 self.pollingDisabled = disablePolling
996
997 while self.eventExit is None:
998 wrdy = []
999
1000 if AsyncPendingWrite(self.writestream):
1001 wrdy.append(self.writestream)
1002
1003 if AsyncPendingWrite(self.errorstream):
1004 wrdy.append(self.errorstream)
1005
1006 try:
1007 rrdy, wrdy, xrdy = select.select([self.readstream],wrdy,[])
1008 except (select.error, KeyboardInterrupt, socket.error):
1009 # just carry on
1010 continue
1011
1012 if self.readstream in rrdy:
1013 self.readReady(self.readstream.fileno())
1014
1015 if self.writestream in wrdy:
1016 self.writeReady(self.writestream.fileno())
1017
1018 if self.errorstream in wrdy:
1019 self.writeReady(self.errorstream.fileno())
1020
1021 self.eventExit = None
1022 self.pollingDisabled = False
1023
1024 def eventPoll(self):
1025 """
1026 Public method to poll for events like 'set break point'.
1027 """
1028 if self.pollingDisabled:
1029 return
1030
1031 # the choice of a ~0.5 second poll interval is arbitrary.
1032 lasteventpolltime = getattr(self, 'lasteventpolltime', time.time())
1033 now = time.time()
1034 if now - lasteventpolltime < 0.5:
1035 self.lasteventpolltime = lasteventpolltime
1036 return
1037 else:
1038 self.lasteventpolltime = now
1039
1040 wrdy = []
1041 if AsyncPendingWrite(self.writestream):
1042 wrdy.append(self.writestream)
1043
1044 if AsyncPendingWrite(self.errorstream):
1045 wrdy.append(self.errorstream)
1046
1047 # immediate return if nothing is ready.
1048 try:
1049 rrdy, wrdy, xrdy = select.select([self.readstream],wrdy,[],0)
1050 except (select.error, KeyboardInterrupt, socket.error):
1051 return
1052
1053 if self.readstream in rrdy:
1054 self.readReady(self.readstream.fileno())
1055
1056 if self.writestream in wrdy:
1057 self.writeReady(self.writestream.fileno())
1058
1059 if self.errorstream in wrdy:
1060 self.writeReady(self.errorstream.fileno())
1061
1062 def connectDebugger(self,port,remoteAddress=None,redirect=1):
1063 """
1064 Public method to establish a session with the debugger.
1065
1066 It opens a network connection to the debugger, connects it to stdin,
1067 stdout and stderr and saves these file objects in case the application
1068 being debugged redirects them itself.
1069
1070 @param port the port number to connect to (int)
1071 @param remoteAddress the network address of the debug server host (string)
1072 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean)
1073 """
1074 if remoteAddress is None: # default: 127.0.0.1
1075 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1076 sock.connect((DebugAddress, port))
1077 elif ":" in remoteAddress: # IPv6
1078 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
1079 sock.connect((remoteAddress, port))
1080 else: # IPv4
1081 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1082 sock.connect((remoteAddress, port))
1083
1084 self.readstream = AsyncFile(sock, sys.stdin.mode, sys.stdin.name)
1085 self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name)
1086 self.errorstream = AsyncFile(sock, sys.stderr.mode, sys.stderr.name)
1087
1088 if redirect:
1089 sys.stdin = self.readstream
1090 sys.stdout = self.writestream
1091 sys.stderr = self.errorstream
1092 self.redirect = redirect
1093
1094 # attach to the main thread here
1095 self.attachThread(mainThread = 1)
1096
1097 def __unhandled_exception(self, exctype, excval, exctb):
1098 """
1099 Private method called to report an uncaught exception.
1100
1101 @param exctype the type of the exception
1102 @param excval data about the exception
1103 @param exctb traceback for the exception
1104 """
1105 self.mainThread.user_exception(None, (exctype,excval,exctb), 1)
1106
1107 def absPath(self,fn):
1108 """
1109 Public method to convert a filename to an absolute name.
1110
1111 sys.path is used as a set of possible prefixes. The name stays
1112 relative if a file could not be found.
1113
1114 @param fn filename (string)
1115 @return the converted filename (string)
1116 """
1117 if os.path.isabs(fn):
1118 return fn
1119
1120 # Check the cache.
1121 if self.fncache.has_key(fn):
1122 return self.fncache[fn]
1123
1124 # Search sys.path.
1125 for p in sys.path:
1126 afn = os.path.abspath(os.path.join(p,fn))
1127 afn = os.path.normcase(afn)
1128
1129 if os.path.exists(afn):
1130 self.fncache[fn] = afn
1131 d = os.path.dirname(afn)
1132 if (d not in sys.path) and (d not in self.dircache):
1133 self.dircache.append(d)
1134 return afn
1135
1136 # Search the additional directory cache
1137 for p in self.dircache:
1138 afn = os.path.abspath(os.path.join(p,fn))
1139 afn = os.path.normcase(afn)
1140
1141 if os.path.exists(afn):
1142 self.fncache[fn] = afn
1143 return afn
1144
1145 # Nothing found.
1146 return fn
1147
1148 def shouldSkip(self, fn):
1149 """
1150 Public method to check if a file should be skipped.
1151
1152 @param fn filename to be checked
1153 @return non-zero if fn represents a file we are 'skipping', zero otherwise.
1154 """
1155 if self.mainThread.tracePython: # trace into Python library
1156 return 0
1157
1158 # Eliminate anything that is part of the Python installation.
1159 afn = self.absPath(fn)
1160 for d in self.skipdirs:
1161 if afn.startswith(d):
1162 return 1
1163
1164 return 0
1165
1166 def getRunning(self):
1167 """
1168 Public method to return the main script we are currently running.
1169 """
1170 return self.running
1171
1172 def progTerminated(self,status):
1173 """
1174 Public method to tell the debugger that the program has terminated.
1175
1176 @param status the return status
1177 """
1178 if status is None:
1179 status = 0
1180 else:
1181 try:
1182 int(status)
1183 except ValueError:
1184 status = 1
1185
1186 if self.running:
1187 self.set_quit()
1188 self.running = None
1189 self.write('%s%d\n' % (ResponseExit,status))
1190
1191 # reset coding
1192 self.__coding = self.defaultCoding
1193 try:
1194 sys.setappdefaultencoding(self.defaultCoding)
1195 except AttributeError:
1196 pass
1197
1198 def __dumpVariables(self, frmnr, scope, filter):
1199 """
1200 Private method to return the variables of a frame to the debug server.
1201
1202 @param frmnr distance of frame reported on. 0 is the current frame (int)
1203 @param scope 1 to report global variables, 0 for local variables (int)
1204 @param filter the indices of variable types to be filtered (list of int)
1205 """
1206 if scope == 0:
1207 self.framenr = frmnr
1208
1209 f = self.currentThread.getCurrentFrame()
1210
1211 while f is not None and frmnr > 0:
1212 f = f.f_back
1213 frmnr -= 1
1214
1215 if f is None:
1216 return
1217
1218 if scope:
1219 dict = f.f_globals
1220 else:
1221 dict = f.f_locals
1222
1223 if f.f_globals is f.f_locals:
1224 scope = -1
1225
1226 varlist = [scope]
1227
1228 if scope != -1:
1229 keylist = dict.keys()
1230
1231 vlist = self.__formatVariablesList(keylist, dict, scope, filter)
1232 varlist.extend(vlist)
1233
1234 self.write('%s%s\n' % (ResponseVariables, unicode(varlist)))
1235
1236 def __dumpVariable(self, var, frmnr, scope, filter):
1237 """
1238 Private method to return the variables of a frame to the debug server.
1239
1240 @param var list encoded name of the requested variable (list of strings)
1241 @param frmnr distance of frame reported on. 0 is the current frame (int)
1242 @param scope 1 to report global variables, 0 for local variables (int)
1243 @param filter the indices of variable types to be filtered (list of int)
1244 """
1245 f = self.currentThread.getCurrentFrame()
1246
1247 while f is not None and frmnr > 0:
1248 f = f.f_back
1249 frmnr -= 1
1250
1251 if f is None:
1252 return
1253
1254 if scope:
1255 dict = f.f_globals
1256 else:
1257 dict = f.f_locals
1258
1259 if f.f_globals is f.f_locals:
1260 scope = -1
1261
1262 varlist = [scope, var]
1263
1264 if scope != -1:
1265 # search the correct dictionary
1266 i = 0
1267 rvar = var[:]
1268 dictkeys = None
1269 obj = None
1270 isDict = 0
1271 formatSequences = 0
1272 access = ""
1273 oaccess = ""
1274 odict = dict
1275
1276 while i < len(var):
1277 if len(dict):
1278 udict = dict
1279 ndict = {}
1280 # this has to be in line with VariablesViewer.indicators
1281 if var[i][-2:] in ["[]", "()", "{}"]:
1282 if i+1 == len(var):
1283 if var[i][:-2] == '...':
1284 dictkeys = [var[i-1]]
1285 else:
1286 dictkeys = [var[i][:-2]]
1287 formatSequences = 1
1288 if not access and not oaccess:
1289 if var[i][:-2] == '...':
1290 access = '["%s"]' % var[i-1]
1291 dict = odict
1292 else:
1293 access = '["%s"]' % var[i][:-2]
1294 else:
1295 if var[i][:-2] == '...':
1296 if oaccess:
1297 access = oaccess
1298 else:
1299 access = '%s[%s]' % (access, var[i-1])
1300 dict = odict
1301 else:
1302 if oaccess:
1303 access = '%s[%s]' % (oaccess, var[i][:-2])
1304 oaccess = ''
1305 else:
1306 access = '%s[%s]' % (access, var[i][:-2])
1307 if var[i][-2:] == "{}":
1308 isDict = 1
1309 break
1310 else:
1311 if not access:
1312 if var[i][:-2] == '...':
1313 access = '["%s"]' % var[i-1]
1314 dict = odict
1315 else:
1316 access = '["%s"]' % var[i][:-2]
1317 else:
1318 if var[i][:-2] == '...':
1319 access = '%s[%s]' % (access, var[i-1])
1320 dict = odict
1321 else:
1322 if oaccess:
1323 access = '%s[%s]' % (oaccess, var[i][:-2])
1324 oaccess = ''
1325 else:
1326 access = '%s[%s]' % (access, var[i][:-2])
1327 else:
1328 if access:
1329 if oaccess:
1330 access = '%s[%s]' % (oaccess, var[i])
1331 else:
1332 access = '%s[%s]' % (access, var[i])
1333 if var[i-1][:-2] == '...':
1334 oaccess = access
1335 else:
1336 oaccess = ''
1337 try:
1338 exec 'mdict = dict%s.__dict__' % access
1339 ndict.update(mdict)
1340 exec 'obj = dict%s' % access
1341 if mdict and not "sipThis" in mdict.keys():
1342 del rvar[0:2]
1343 access = ""
1344 except:
1345 pass
1346 try:
1347 cdict = {}
1348 exec 'slv = dict%s.__slots__' % access
1349 for v in slv:
1350 try:
1351 exec 'cdict[v] = dict%s.%s' % (access, v)
1352 except:
1353 pass
1354 ndict.update(cdict)
1355 exec 'obj = dict%s' % access
1356 access = ""
1357 except:
1358 pass
1359 else:
1360 try:
1361 ndict.update(dict[var[i]].__dict__)
1362 del rvar[0]
1363 obj = dict[var[i]]
1364 except:
1365 pass
1366 try:
1367 cdict = {}
1368 slv = dict[var[i]].__slots__
1369 for v in slv:
1370 try:
1371 exec 'cdict[v] = dict[var[i]].%s' % v
1372 except:
1373 pass
1374 ndict.update(cdict)
1375 obj = dict[var[i]]
1376 except:
1377 pass
1378 odict = dict
1379 dict = ndict
1380 i += 1
1381
1382 if ("sipThis" in dict.keys() and len(dict) == 1) or \
1383 (len(dict) == 0 and len(udict) > 0):
1384 if access:
1385 exec 'qvar = udict%s' % access
1386 # this has to be in line with VariablesViewer.indicators
1387 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]:
1388 exec 'qvar = udict["%s"][%s]' % (rvar[0][:-2], rvar[1])
1389 else:
1390 qvar = udict[var[-1]]
1391 qvtype = ("%s" % type(qvar))[1:-1].split()[1][1:-1]
1392 if qvtype.startswith("PyQt4"):
1393 vlist = self.__formatQt4Variable(qvar, qvtype)
1394 else:
1395 vlist = []
1396 else:
1397 # format the dictionary found
1398 if dictkeys is None:
1399 dictkeys = dict.keys()
1400 else:
1401 # treatment for sequences and dictionaries
1402 if access:
1403 exec "dict = dict%s" % access
1404 else:
1405 dict = dict[dictkeys[0]]
1406 if isDict:
1407 dictkeys = dict.keys()
1408 else:
1409 dictkeys = range(len(dict))
1410 vlist = self.__formatVariablesList(dictkeys, dict, scope, filter,
1411 formatSequences)
1412 varlist.extend(vlist)
1413
1414 if obj is not None and not formatSequences:
1415 if unicode(repr(obj)).startswith('{'):
1416 varlist.append(('...', 'dict', "%d" % len(obj.keys())))
1417 elif unicode(repr(obj)).startswith('['):
1418 varlist.append(('...', 'list', "%d" % len(obj)))
1419 elif unicode(repr(obj)).startswith('('):
1420 varlist.append(('...', 'tuple', "%d" % len(obj)))
1421
1422 self.write('%s%s\n' % (ResponseVariable, unicode(varlist)))
1423
1424 def __formatQt4Variable(self, value, vtype):
1425 """
1426 Private method to produce a formated output of a simple Qt4 type.
1427
1428 @param value variable to be formated
1429 @param vtype type of the variable to be formatted (string)
1430 @return A tuple consisting of a list of formatted variables. Each
1431 variable entry is a tuple of three elements, the variable name,
1432 its type and value.
1433 """
1434 qttype = vtype.split('.')[-1]
1435 varlist = []
1436 if qttype == 'QString':
1437 varlist.append(("", "QString", "%s" % unicode(value)))
1438 elif qttype == 'QStringList':
1439 for s in value:
1440 varlist.append(("", "QString", "%s" % unicode(s)))
1441 elif qttype == 'QChar':
1442 varlist.append(("", "QChar", "%s" % unichr(value.unicode())))
1443 varlist.append(("", "int", "%d" % value.unicode()))
1444 elif qttype == 'QPoint':
1445 varlist.append(("x", "int", "%d" % value.x()))
1446 varlist.append(("y", "int", "%d" % value.y()))
1447 elif qttype == 'QPointF':
1448 varlist.append(("x", "float", "%g" % value.x()))
1449 varlist.append(("y", "float", "%g" % value.y()))
1450 elif qttype == 'QRect':
1451 varlist.append(("x", "int", "%d" % value.x()))
1452 varlist.append(("y", "int", "%d" % value.y()))
1453 varlist.append(("width", "int", "%d" % value.width()))
1454 varlist.append(("height", "int", "%d" % value.height()))
1455 elif qttype == 'QRectF':
1456 varlist.append(("x", "float", "%g" % value.x()))
1457 varlist.append(("y", "float", "%g" % value.y()))
1458 varlist.append(("width", "float", "%g" % value.width()))
1459 varlist.append(("height", "float", "%g" % value.height()))
1460 elif qttype == 'QSize':
1461 varlist.append(("width", "int", "%d" % value.width()))
1462 varlist.append(("height", "int", "%d" % value.height()))
1463 elif qttype == 'QSizeF':
1464 varlist.append(("width", "float", "%g" % value.width()))
1465 varlist.append(("height", "float", "%g" % value.height()))
1466 elif qttype == 'QColor':
1467 varlist.append(("name", "QString", "%s" % value.name()))
1468 r, g, b, a = value.getRgb()
1469 varlist.append(("rgb", "int", "%d, %d, %d, %d" % (r, g, b, a)))
1470 h, s, v, a = value.getHsv()
1471 varlist.append(("hsv", "int", "%d, %d, %d, %d" % (h, s, v, a)))
1472 c, m, y, k, a = value.getCmyk()
1473 varlist.append(("cmyk", "int", "%d, %d, %d, %d, %d" % (c, m, y, k, a)))
1474 elif qttype == 'QDate':
1475 varlist.append(("", "QDate", "%s" % unicode(value.toString())))
1476 elif qttype == 'QTime':
1477 varlist.append(("", "QTime", "%s" % unicode(value.toString())))
1478 elif qttype == 'QDateTime':
1479 varlist.append(("", "QDateTime", "%s" % unicode(value.toString())))
1480 elif qttype == 'QDir':
1481 varlist.append(("path", "QString", "%s" % unicode(value.path())))
1482 varlist.append(("absolutePath", "QString", "%s" % \
1483 unicode(value.absolutePath())))
1484 varlist.append(("canonicalPath", "QString", "%s" % \
1485 unicode(value.canonicalPath())))
1486 elif qttype == 'QFile':
1487 varlist.append(("fileName", "QString", "%s" % unicode(value.fileName())))
1488 elif qttype == 'QFont':
1489 varlist.append(("family", "QString", "%s" % unicode(value.family())))
1490 varlist.append(("pointSize", "int", "%d" % value.pointSize()))
1491 varlist.append(("weight", "int", "%d" % value.weight()))
1492 varlist.append(("bold", "bool", "%s" % value.bold()))
1493 varlist.append(("italic", "bool", "%s" % value.italic()))
1494 elif qttype == 'QUrl':
1495 varlist.append(("url", "QString", "%s" % unicode(value.toString())))
1496 varlist.append(("scheme", "QString", "%s" % unicode(value.scheme())))
1497 varlist.append(("user", "QString", "%s" % unicode(value.userName())))
1498 varlist.append(("password", "QString", "%s" % unicode(value.password())))
1499 varlist.append(("host", "QString", "%s" % unicode(value.host())))
1500 varlist.append(("port", "int", "%d" % value.port()))
1501 varlist.append(("path", "QString", "%s" % unicode(value.path())))
1502 elif qttype == 'QModelIndex':
1503 varlist.append(("valid", "bool", "%s" % value.isValid()))
1504 if value.isValid():
1505 varlist.append(("row", "int", "%s" % value.row()))
1506 varlist.append(("column", "int", "%s" % value.column()))
1507 varlist.append(("internalId", "int", "%s" % value.internalId()))
1508 varlist.append(("internalPointer", "void *", "%s" % \
1509 value.internalPointer()))
1510 elif qttype == 'QRegExp':
1511 varlist.append(("pattern", "QString", "%s" % unicode(value.pattern())))
1512
1513 # GUI stuff
1514 elif qttype == 'QAction':
1515 varlist.append(("name", "QString", "%s" % unicode(value.objectName())))
1516 varlist.append(("text", "QString", "%s" % unicode(value.text())))
1517 varlist.append(("icon text", "QString", "%s" % unicode(value.iconText())))
1518 varlist.append(("tooltip", "QString", "%s" % unicode(value.toolTip())))
1519 varlist.append(("whatsthis", "QString", "%s" % unicode(value.whatsThis())))
1520 varlist.append(("shortcut", "QString", "%s" % \
1521 unicode(value.shortcut().toString())))
1522 elif qttype == 'QKeySequence':
1523 varlist.append(("value", "", "%s" % unicode(value.toString())))
1524
1525 # XML stuff
1526 elif qttype == 'QDomAttr':
1527 varlist.append(("name", "QString", "%s" % unicode(value.name())))
1528 varlist.append(("value", "QString", "%s" % unicode(value.value())))
1529 elif qttype == 'QDomCharacterData':
1530 varlist.append(("data", "QString", "%s" % unicode(value.data())))
1531 elif qttype == 'QDomComment':
1532 varlist.append(("data", "QString", "%s" % unicode(value.data())))
1533 elif qttype == "QDomDocument":
1534 varlist.append(("text", "QString", "%s" % unicode(value.toString())))
1535 elif qttype == 'QDomElement':
1536 varlist.append(("tagName", "QString", "%s" % unicode(value.tagName())))
1537 varlist.append(("text", "QString", "%s" % unicode(value.text())))
1538 elif qttype == 'QDomText':
1539 varlist.append(("data", "QString", "%s" % unicode(value.data())))
1540
1541 # Networking stuff
1542 elif qttype == 'QHostAddress':
1543 varlist.append(("address", "QHostAddress", "%s" % unicode(value.toString())))
1544
1545 return varlist
1546
1547 def __formatVariablesList(self, keylist, dict, scope, filter = [],
1548 formatSequences = 0):
1549 """
1550 Private method to produce a formated variables list.
1551
1552 The dictionary passed in to it is scanned. Variables are
1553 only added to the list, if their type is not contained
1554 in the filter list and their name doesn't match any of the filter expressions.
1555 The formated variables list (a list of tuples of 3 values) is returned.
1556
1557 @param keylist keys of the dictionary
1558 @param dict the dictionary to be scanned
1559 @param scope 1 to filter using the globals filter, 0 using the locals
1560 filter (int).
1561 Variables are only added to the list, if their name do not match any of the
1562 filter expressions.
1563 @param filter the indices of variable types to be filtered. Variables are
1564 only added to the list, if their type is not contained in the filter
1565 list.
1566 @param formatSequences flag indicating, that sequence or dictionary variables
1567 should be formatted. If it is 0 (or false), just the number of items contained
1568 in these variables is returned. (boolean)
1569 @return A tuple consisting of a list of formatted variables. Each variable
1570 entry is a tuple of three elements, the variable name, its type and
1571 value.
1572 """
1573 varlist = []
1574 if scope:
1575 patternFilterObjects = self.globalsFilterObjects
1576 else:
1577 patternFilterObjects = self.localsFilterObjects
1578
1579 for key in keylist:
1580 # filter based on the filter pattern
1581 matched = 0
1582 for pat in patternFilterObjects:
1583 if pat.match(unicode(key)):
1584 matched = 1
1585 break
1586 if matched:
1587 continue
1588
1589 # filter hidden attributes (filter #0)
1590 if 0 in filter and unicode(key)[:2] == '__':
1591 continue
1592
1593 # special handling for '__builtins__' (it's way too big)
1594 if key == '__builtins__':
1595 rvalue = '<module __builtin__ (built-in)>'
1596 valtype = 'module'
1597 else:
1598 value = dict[key]
1599 valtypestr = ("%s" % type(value))[1:-1]
1600
1601 if valtypestr.split(' ',1)[0] == 'class':
1602 # handle new class type of python 2.2+
1603 if ConfigVarTypeStrings.index('instance') in filter:
1604 continue
1605 valtype = valtypestr
1606 else:
1607 valtype = valtypestr[6:-1]
1608 try:
1609 if ConfigVarTypeStrings.index(valtype) in filter:
1610 continue
1611 except ValueError:
1612 if valtype == "classobj":
1613 if ConfigVarTypeStrings.index('instance') in filter:
1614 continue
1615 elif ConfigVarTypeStrings.index('other') in filter:
1616 continue
1617
1618 try:
1619 if valtype not in ['list', 'tuple', 'dict']:
1620 rvalue = repr(value)
1621 if valtype.startswith('class') and \
1622 rvalue[0] in ['{', '(', '[']:
1623 rvalue = ""
1624 else:
1625 if valtype == 'dict':
1626 rvalue = "%d" % len(value.keys())
1627 else:
1628 rvalue = "%d" % len(value)
1629 except:
1630 rvalue = ''
1631
1632 if formatSequences:
1633 if unicode(key) == key:
1634 key = "'%s'" % key
1635 else:
1636 key = unicode(key)
1637 varlist.append((key, valtype, rvalue))
1638
1639 return varlist
1640
1641 def __generateFilterObjects(self, scope, filterString):
1642 """
1643 Private slot to convert a filter string to a list of filter objects.
1644
1645 @param scope 1 to generate filter for global variables, 0 for local
1646 variables (int)
1647 @param filterString string of filter patterns separated by ';'
1648 """
1649 patternFilterObjects = []
1650 for pattern in filterString.split(';'):
1651 patternFilterObjects.append(re.compile('^%s$' % pattern))
1652 if scope:
1653 self.globalsFilterObjects = patternFilterObjects[:]
1654 else:
1655 self.localsFilterObjects = patternFilterObjects[:]
1656
1657 def __completionList(self, text):
1658 """
1659 Private slot to handle the request for a commandline completion list.
1660
1661 @param text the text to be completed (string)
1662 """
1663 completerDelims = ' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?'
1664
1665 completions = []
1666 state = 0
1667 # find position of last delim character
1668 pos = -1
1669 while pos >= -len(text):
1670 if text[pos] in completerDelims:
1671 if pos == -1:
1672 text = ''
1673 else:
1674 text = text[pos+1:]
1675 break
1676 pos -= 1
1677
1678 try:
1679 comp = self.complete(text, state)
1680 except:
1681 comp = None
1682 while comp is not None:
1683 completions.append(comp)
1684 state += 1
1685 try:
1686 comp = self.complete(text, state)
1687 except:
1688 comp = None
1689
1690 self.write("%s%s||%s\n" % (ResponseCompletion, unicode(completions), text))
1691
1692 def startDebugger(self, filename = None, host = None, port = None,
1693 enableTrace = 1, exceptions = 1, tracePython = 0, redirect = 1):
1694 """
1695 Public method used to start the remote debugger.
1696
1697 @param filename the program to be debugged (string)
1698 @param host hostname of the debug server (string)
1699 @param port portnumber of the debug server (int)
1700 @param enableTrace flag to enable the tracing function (boolean)
1701 @param exceptions flag to enable exception reporting of the IDE (boolean)
1702 @param tracePython flag to enable tracing into the Python library (boolean)
1703 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean)
1704 """
1705 global debugClient
1706 if host is None:
1707 host = os.getenv('ERICHOST', 'localhost')
1708 if port is None:
1709 port = os.getenv('ERICPORT', 42424)
1710
1711 remoteAddress = self.__resolveHost(host)
1712 self.connectDebugger(port, remoteAddress, redirect)
1713 if filename is not None:
1714 self.running = os.path.abspath(filename)
1715 else:
1716 try:
1717 self.running = os.path.abspath(sys.argv[0])
1718 except IndexError:
1719 self.running = None
1720 if self.running:
1721 self.__setCoding(self.running)
1722 try:
1723 sys.setappdefaultencoding(self.defaultCoding)
1724 except AttributeError:
1725 pass
1726 self.passive = 1
1727 self.write("%s%s|%d\n" % (PassiveStartup, self.running, exceptions))
1728 self.__interact()
1729
1730 # setup the debugger variables
1731 self.fncache = {}
1732 self.dircache = []
1733 self.mainFrame = None
1734 self.inRawMode = 0
1735 self.debugging = 1
1736
1737 self.attachThread(mainThread = 1)
1738 self.mainThread.tracePython = tracePython
1739
1740 # set the system exception handling function to ensure, that
1741 # we report on all unhandled exceptions
1742 sys.excepthook = self.__unhandled_exception
1743
1744 # now start debugging
1745 if enableTrace:
1746 self.mainThread.set_trace()
1747
1748 def startProgInDebugger(self, progargs, wd = '', host = None,
1749 port = None, exceptions = 1, tracePython = 0, redirect = 1):
1750 """
1751 Public method used to start the remote debugger.
1752
1753 @param progargs commandline for the program to be debugged
1754 (list of strings)
1755 @param wd working directory for the program execution (string)
1756 @param host hostname of the debug server (string)
1757 @param port portnumber of the debug server (int)
1758 @param exceptions flag to enable exception reporting of the IDE (boolean)
1759 @param tracePython flag to enable tracing into the Python library (boolean)
1760 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean)
1761 """
1762 if host is None:
1763 host = os.getenv('ERICHOST', 'localhost')
1764 if port is None:
1765 port = os.getenv('ERICPORT', 42424)
1766
1767 remoteAddress = self.__resolveHost(host)
1768 self.connectDebugger(port, remoteAddress, redirect)
1769
1770 self.fncache = {}
1771 self.dircache = []
1772 sys.argv = progargs[:]
1773 sys.argv[0] = os.path.abspath(sys.argv[0])
1774 sys.path[0] = os.path.dirname(sys.argv[0])
1775 sys.path.insert(0, '')
1776 if wd == '':
1777 os.chdir(sys.path[1])
1778 else:
1779 os.chdir(wd)
1780 self.running = sys.argv[0]
1781 self.__setCoding(self.running)
1782 try:
1783 sys.setappdefaultencoding(self.__coding)
1784 except AttributeError:
1785 pass
1786 self.mainFrame = None
1787 self.inRawMode = 0
1788 self.debugging = 1
1789
1790 self.passive = 1
1791 self.write("%s%s|%d\n" % (PassiveStartup, self.running, exceptions))
1792 self.__interact()
1793
1794 self.attachThread(mainThread = 1)
1795 self.mainThread.tracePython = tracePython
1796
1797 # set the system exception handling function to ensure, that
1798 # we report on all unhandled exceptions
1799 sys.excepthook = self.__unhandled_exception
1800
1801 # This will eventually enter a local event loop.
1802 # Note the use of backquotes to cause a repr of self.running. The
1803 # need for this is on Windows os where backslash is the path separator.
1804 # They will get inadvertantly stripped away during the eval causing IOErrors
1805 # if self.running is passed as a normal str.
1806 self.debugMod.__dict__['__file__'] = self.running
1807 sys.modules['__main__'] = self.debugMod
1808 res = self.mainThread.run('execfile(' + `self.running` + ')',
1809 self.debugMod.__dict__)
1810 self.progTerminated(res)
1811
1812 def run_call(self, scriptname, func, *args):
1813 """
1814 Public method used to start the remote debugger and call a function.
1815
1816 @param scriptname name of the script to be debugged (string)
1817 @param func function to be called
1818 @param *args arguments being passed to func
1819 @return result of the function call
1820 """
1821 self.startDebugger(scriptname, enableTrace = 0)
1822 res = self.mainThread.runcall(func, *args)
1823 self.progTerminated(res)
1824 return res
1825
1826 def __resolveHost(self, host):
1827 """
1828 Private method to resolve a hostname to an IP address.
1829
1830 @param host hostname of the debug server (string)
1831 @return IP address (string)
1832 """
1833 try:
1834 host, version = host.split("@@")
1835 except ValueError:
1836 version = 'v4'
1837 if version == 'v4':
1838 family = socket.AF_INET
1839 else:
1840 family = socket.AF_INET6
1841 return socket.getaddrinfo(host, None, family, socket.SOCK_STREAM)[0][4][0]
1842
1843 def main(self):
1844 """
1845 Public method implementing the main method.
1846 """
1847 if '--' in sys.argv:
1848 args = sys.argv[1:]
1849 host = None
1850 port = None
1851 wd = ''
1852 tracePython = 0
1853 exceptions = 1
1854 redirect = 1
1855 while args[0]:
1856 if args[0] == '-h':
1857 host = args[1]
1858 del args[0]
1859 del args[0]
1860 elif args[0] == '-p':
1861 port = long(args[1])
1862 del args[0]
1863 del args[0]
1864 elif args[0] == '-w':
1865 wd = args[1]
1866 del args[0]
1867 del args[0]
1868 elif args[0] == '-t':
1869 tracePython = 1
1870 del args[0]
1871 elif args[0] == '-e':
1872 exceptions = 0
1873 del args[0]
1874 elif args[0] == '-n':
1875 redirect = 0
1876 del args[0]
1877 elif args[0] == '--no-encoding':
1878 self.noencoding = True
1879 del args[0]
1880 elif args[0] == '--':
1881 del args[0]
1882 break
1883 else: # unknown option
1884 del args[0]
1885 if not args:
1886 print "No program given. Aborting!"
1887 else:
1888 if not self.noencoding:
1889 self.__coding = self.defaultCoding
1890 try:
1891 sys.setappdefaultencoding(self.defaultCoding)
1892 except AttributeError:
1893 pass
1894 self.startProgInDebugger(args, wd, host, port,
1895 exceptions=exceptions, tracePython=tracePython,
1896 redirect=redirect)
1897 else:
1898 if sys.argv[1] == '--no-encoding':
1899 self.noencoding = True
1900 del sys.argv[1]
1901 if sys.argv[1] == '':
1902 del sys.argv[1]
1903 try:
1904 port = long(sys.argv[1])
1905 except (ValueError, IndexError):
1906 port = -1
1907 try:
1908 redirect = int(sys.argv[2])
1909 except (ValueError, IndexError):
1910 redirect = 1
1911 try:
1912 ipOrHost = sys.argv[3]
1913 if ':' in ipOrHost:
1914 remoteAddress = ipOrHost
1915 elif ipOrHost[0] in '0123456789':
1916 remoteAddress = ipOrHost
1917 else:
1918 remoteAddress = self.__resolveHost(ipOrHost)
1919 except:
1920 remoteAddress = None
1921 sys.argv = ['']
1922 if not '' in sys.path:
1923 sys.path.insert(0, '')
1924 if port >= 0:
1925 if not self.noencoding:
1926 self.__coding = self.defaultCoding
1927 try:
1928 sys.setappdefaultencoding(self.defaultCoding)
1929 except AttributeError:
1930 pass
1931 self.connectDebugger(port, remoteAddress, redirect)
1932 self.__interact()
1933 else:
1934 print "No network port given. Aborting..."
1935
1936 def fork(self):
1937 """
1938 Public method implementing a fork routine deciding which branch to follow.
1939 """
1940 if not self.fork_auto:
1941 self.write(RequestForkTo + '\n')
1942 self.eventLoop(True)
1943 pid = DebugClientOrigFork()
1944 if pid == 0:
1945 # child
1946 if not self.fork_child:
1947 sys.settrace(None)
1948 sys.setprofile(None)
1949 self.sessionClose(0)
1950 else:
1951 # parent
1952 if self.fork_child:
1953 sys.settrace(None)
1954 sys.setprofile(None)
1955 self.sessionClose(0)
1956 return pid
1957
1958 def close(self, fd):
1959 """
1960 Private method implementing a close method as a replacement for os.close().
1961
1962 It prevents the debugger connections from being closed.
1963
1964 @param fd file descriptor to be closed (integer)
1965 """
1966 if fd in [self.readstream.fileno(), self.writestream.fileno(),
1967 self.errorstream.fileno()]:
1968 return
1969
1970 DebugClientOrigClose(fd)

eric ide

mercurial