DebugClients/Python/DebugClientBase.py

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

eric ide

mercurial