DebugClients/Python3/DebugClientBase.py

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

eric ide

mercurial