DebugClients/Python3/DebugClientBase.py

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

eric ide

mercurial