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