DebugClients/Python2/DebugClientBase.py

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

eric ide

mercurial