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