DebugClients/Python/DebugBase.py

branch
debugger speed
changeset 5178
878ce843ca9f
parent 5174
8c48f5e0cd92
child 5179
5f56410e7624
equal deleted inserted replaced
5174:8c48f5e0cd92 5178:878ce843ca9f
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the debug base class which based originally on bdb.
8 """
9
10 import sys
11 import os
12 import atexit
13 import inspect
14 import ctypes
15 import _thread
16 import time
17 from inspect import CO_GENERATOR
18
19 from DebugUtilities import getargvalues, formatargvalues
20 from BreakpointWatch import Breakpoint, Watch
21
22 gRecursionLimit = 64
23
24
25 def printerr(s):
26 """
27 Module function used for debugging the debug client.
28
29 @param s data to be printed
30 """
31 sys.__stderr__.write('{0!s}\n'.format(s))
32 sys.__stderr__.flush()
33
34
35 def setRecursionLimit(limit):
36 """
37 Module function to set the recursion limit.
38
39 @param limit recursion limit (integer)
40 """
41 global gRecursionLimit
42 gRecursionLimit = limit
43
44
45 class DebugBase(object):
46 """
47 Class implementing base class of the debugger.
48
49 Provides methods for the 'owning' client to call to step etc.
50 """
51 # Don't thrust distutils.sysconfig.get_python_lib: possible case mismatch
52 # on Windows
53 lib = os.path.dirname(inspect.__file__)
54 # tuple required because it's accessed a lot of times by startswith method
55 pathsToSkip = ('<', os.path.dirname(__file__), inspect.__file__[:-1])
56 filesToSkip = {}
57
58 # cache for fixed file names
59 _fnCache = {}
60
61 def __init__(self, dbgClient):
62 """
63 Constructor
64
65 @param dbgClient the owning client
66 """
67 self._dbgClient = dbgClient
68 self._mainThread = True
69 self.quitting = 0
70
71 self.tracePythonLibs(0)
72
73 # Special handling of a recursion error
74 self.skipFrames = 0
75
76 self.__isBroken = False
77 self.cFrame = None
78
79 # current frame we are at
80 self.currentFrame = None
81
82 # frame that we are stepping in, can be different than currentFrame
83 self.stepFrame = None
84
85 self.botframe = None
86 self.stopframe = None
87 self.returnframe = None
88 self.stop_everywhere = False
89
90 # provide a hook to perform a hard breakpoint
91 # Use it like this:
92 # if hasattr(sys, 'breakpoint): sys.breakpoint()
93 sys.breakpoint = self.set_trace
94
95 self.__recursionDepth = -1
96 self.setRecursionDepth(inspect.currentframe())
97
98 # background task to periodicaly check for client interactions
99 self.eventPollFlag = False
100 self.timer = _thread.start_new_thread(self.__eventPollTimer, ())
101
102 def __eventPollTimer(self):
103 """
104 Private method to set a flag every 0.5 s to check for new messages.
105 """
106 while True:
107 time.sleep(0.5)
108 self.eventPollFlag = True
109
110 def getCurrentFrame(self):
111 """
112 Public method to return the current frame.
113
114 @return the current frame
115 """
116 return self.currentFrame
117
118 def getFrameLocals(self, frmnr=0):
119 """
120 Public method to return the locals dictionary of the current frame
121 or a frame below.
122
123 @keyparam frmnr distance of frame to get locals dictionary of. 0 is
124 the current frame (int)
125 @return locals dictionary of the frame
126 """
127 f = self.currentFrame
128 while f is not None and frmnr > 0:
129 f = f.f_back
130 frmnr -= 1
131 return f.f_locals
132
133 def storeFrameLocals(self, frmnr=0):
134 """
135 Public method to store the locals into the frame, so an access to
136 frame.f_locals returns the last data.
137
138 @keyparam frmnr distance of frame to store locals dictionary to. 0 is
139 the current frame (int)
140 """
141 cf = self.currentFrame
142 while cf is not None and frmnr > 0:
143 cf = cf.f_back
144 frmnr -= 1
145
146 try:
147 if "__pypy__" in sys.builtin_module_names:
148 import __pypy__
149 __pypy__.locals_to_fast(cf)
150 return
151 except Exception:
152 pass
153
154 ctypes.pythonapi.PyFrame_LocalsToFast(
155 ctypes.py_object(cf),
156 ctypes.c_int(0))
157
158 def step(self, traceMode):
159 """
160 Public method to perform a step operation in this thread.
161
162 @param traceMode If it is True, then the step is a step into,
163 otherwise it is a step over.
164 """
165 self.stepFrame = self.currentFrame
166
167 if traceMode:
168 self.currentFrame = None
169 self.set_step()
170 else:
171 self.set_next(self.currentFrame)
172
173 def stepOut(self):
174 """
175 Public method to perform a step out of the current call.
176 """
177 self.stepFrame = self.currentFrame
178 self.set_return(self.currentFrame)
179
180 def go(self, special):
181 """
182 Public method to resume the thread.
183
184 It resumes the thread stopping only at breakpoints or exceptions.
185
186 @param special flag indicating a special continue operation
187 """
188 self.currentFrame = None
189 self.set_continue(special)
190
191 def setRecursionDepth(self, frame):
192 """
193 Public method to determine the current recursion depth.
194
195 @param frame The current stack frame.
196 """
197 self.__recursionDepth = 0
198 while frame is not None:
199 self.__recursionDepth += 1
200 frame = frame.f_back
201
202 def profileWithRecursion(self, frame, event, arg):
203 """
204 Public method used to trace some stuff independent of the debugger
205 trace function.
206
207 @param frame current stack frame
208 @type frame object
209 @param event trace event
210 @type str
211 @param arg arguments
212 @type depends on the previous event parameter
213 @exception RuntimeError raised to indicate too many recursions
214 """
215 if event == 'return':
216 self.cFrame = frame.f_back
217 self.__recursionDepth -= 1
218 if self._dbgClient.callTraceEnabled:
219 self.__sendCallTrace(event, frame, self.cFrame)
220 elif event == 'call':
221 if self._dbgClient.callTraceEnabled:
222 self.__sendCallTrace(event, self.cFrame, frame)
223 self.cFrame = frame
224 self.__recursionDepth += 1
225 if self.__recursionDepth > gRecursionLimit:
226 raise RuntimeError(
227 'maximum recursion depth exceeded\n'
228 '(offending frame is two down the stack)')
229
230 def profile(self, frame, event, arg):
231 """
232 Public method used to trace some stuff independent of the debugger
233 trace function.
234
235 @param frame current stack frame
236 @type frame object
237 @param event trace event
238 @type str
239 @param arg arguments
240 @type depends on the previous event parameter
241 """
242 if event == 'return':
243 self.__sendCallTrace(event, frame, frame.f_back)
244 elif event == 'call':
245 self.__sendCallTrace(event, frame.f_back, frame)
246
247 def __sendCallTrace(self, event, fromFrame, toFrame):
248 """
249 Private method to send a call/return trace.
250
251 @param event trace event
252 @type str
253 @param fromFrame originating frame
254 @type frame object
255 @param toFrame destination frame
256 @type frame object
257 """
258 if not self.__skipFrame(fromFrame) and not self.__skipFrame(toFrame):
259 fromInfo = {
260 "filename": self._dbgClient.absPath(
261 self.fix_frame_filename(fromFrame)),
262 "linenumber": fromFrame.f_lineno,
263 "codename": fromFrame.f_code.co_name,
264 }
265 toInfo = {
266 "filename": self._dbgClient.absPath(
267 self.fix_frame_filename(toFrame)),
268 "linenumber": toFrame.f_lineno,
269 "codename": toFrame.f_code.co_name,
270 }
271 self._dbgClient.sendCallTrace(event, fromInfo, toInfo)
272
273 def trace_dispatch(self, frame, event, arg):
274 """
275 Public method reimplemented from bdb.py to do some special things.
276
277 This specialty is to check the connection to the debug server
278 for new events (i.e. new breakpoints) while we are going through
279 the code.
280
281 @param frame The current stack frame
282 @type frame object
283 @param event The trace event
284 @type str
285 @param arg The arguments
286 @type depends on the previous event parameter
287 @return local trace function
288 @rtype trace function or None
289 @exception SystemExit
290 """
291 # give the client a chance to push through new break points.
292 if self.eventPollFlag:
293 self._dbgClient.eventPoll()
294 self.eventPollFlag = False
295
296 if self.quitting:
297 raise SystemExit
298
299 if event == 'line':
300 if self.stop_here(frame) or self.break_here(frame):
301 self.user_line(frame)
302 return
303
304 if event == 'call':
305 if self.botframe is None and frame.f_lineno > 1:
306 self.botframe = frame.f_back
307 frame.f_trace = self.trace_dispatch
308 self._dbgClient.mainFrame = frame
309
310 self.user_line(frame)
311 return self.trace_dispatch
312
313 if not (self.stop_here(frame) or
314 self.__checkBreakInFrame(frame) or
315 Watch.watches != []):
316 # No need to trace this function
317 return
318 return self.trace_dispatch
319
320 if event == 'return':
321 if self.stop_here(frame):
322 # Ignore return events in generator except when stepping.
323 if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
324 return
325 # The program has finished if we have just left the first frame
326 if (frame == self._dbgClient.mainFrame and
327 self._mainThread):
328 atexit._run_exitfuncs()
329 self._dbgClient.progTerminated(arg)
330
331 if self.quitting and not self._dbgClient.passive:
332 raise SystemExit
333 return
334
335 if event == 'exception':
336 if not self.__skipFrame(frame):
337 # When stepping with next/until/return in a generator frame,
338 # skip the internal StopIteration exception (with no traceback)
339 # triggered by a subiterator run with the 'yield from'
340 # statement.
341 if not (frame.f_code.co_flags & CO_GENERATOR and
342 arg[0] is StopIteration and arg[2] is None):
343 self.user_exception(frame, arg)
344 # Stop at the StopIteration or GeneratorExit exception when the
345 # user has set stopframe in a generator by issuing a return
346 # command, or a next/until command at the last statement in the
347 # generator before the exception.
348 elif (self.stopframe and frame is not self.stopframe and
349 self.stopframe.f_code.co_flags & CO_GENERATOR and
350 arg[0] in (StopIteration, GeneratorExit)):
351 self.user_exception(frame, arg)
352 return
353
354 if event == 'c_call':
355 return
356 if event == 'c_exception':
357 return
358 if event == 'c_return':
359 return
360
361 print('DebugBase.trace_dispatch:' # __IGNORE_WARNING__
362 ' unknown debugging event: ',
363 repr(event))
364 return self.trace_dispatch
365
366 def set_trace(self, frame=None):
367 """
368 Public method to start debugging from 'frame'.
369
370 If frame is not specified, debugging starts from caller's frame.
371 Because of jump optimizations it's not possible to use sys.breakpoint()
372 as last instruction in a function or method.
373
374 @keyparam frame frame to start debugging from
375 @type frame object
376 """
377 if frame is None:
378 frame = sys._getframe().f_back # Skip set_trace method
379
380 frame.f_trace = self.trace_dispatch
381 while frame is not None:
382 # stop at erics debugger frame
383 if frame.f_back.f_code == self._dbgClient.handleLine.__code__:
384 frame.f_trace = self.trace_dispatch
385 self.botframe = frame
386 self._dbgClient.mainFrame = frame
387 break
388
389 frame = frame.f_back
390
391 self.stop_everywhere = True
392 sys.settrace(self.trace_dispatch)
393 sys.setprofile(self._dbgClient.callTraceEnabled)
394
395 def run(self, cmd, globals=None, locals=None):
396 """
397 Public method to start a given command under debugger control.
398
399 @param cmd command / code to execute under debugger control
400 @type str or CodeType
401 @keyparam globals dictionary of global variables for cmd
402 @type dict
403 @keyparam locals dictionary of local variables for cmd
404 @type dict
405 """
406 if globals is None:
407 import __main__
408 globals = __main__.__dict__
409
410 if locals is None:
411 locals = globals
412
413 sys.settrace(self.trace_dispatch)
414 if isinstance(cmd, str):
415 cmd = compile(cmd, "<string>", "exec")
416
417 try:
418 exec(cmd, globals, locals)
419 except SystemExit:
420 pass
421 finally:
422 self.quitting = 1
423 sys.settrace(None)
424
425 def _set_stopinfo(self, stopframe, returnframe):
426 """
427 Protected method to update the frame pointers.
428
429 @param stopframe the frame object where to stop
430 @type frame object
431 @param returnframe the frame object where to stop on a function return
432 @type frame object
433 """
434 self.stopframe = stopframe
435 self.returnframe = returnframe
436 self.stop_everywhere = False
437
438 def set_continue(self, special):
439 """
440 Public method to stop only on next breakpoint.
441
442 @param special flag indicating a special continue operation
443 @type bool
444 """
445 # Here we only set a new stop frame if it is a normal continue.
446 if not special:
447 self._set_stopinfo(self.botframe, None)
448
449 # Disable tracing if not started in debug mode
450 if not self._dbgClient.debugging:
451 sys.settrace(None)
452 sys.setprofile(None)
453
454 def set_step(self):
455 """
456 Public method to stop after one line of code.
457 """
458 self._set_stopinfo(None, None)
459 self.stop_everywhere = True
460
461 def set_next(self, frame):
462 """
463 Public method to stop on the next line in or below the given frame.
464
465 @param frame the frame object
466 @type frame object
467 """
468 self._set_stopinfo(frame, frame.f_back)
469 frame.f_back.f_trace = self.trace_dispatch
470 frame.f_trace = self.trace_dispatch
471
472 def set_return(self, frame):
473 """
474 Public method to stop when returning from the given frame.
475
476 @param frame the frame object
477 @type frame object
478 """
479 self._set_stopinfo(None, frame.f_back)
480
481 def set_quit(self):
482 """
483 Public method to quit.
484
485 Disables the trace functions and resets all frame pointer.
486 """
487 self.currentFrame = None
488 sys.setprofile(None)
489 sys.settrace(None)
490 self.stopframe = None
491 self.returnframe = None
492 self.quitting = 1
493
494 def fix_frame_filename(self, frame):
495 """
496 Public method used to fixup the filename for a given frame.
497
498 The logic employed here is that if a module was loaded
499 from a .pyc file, then the correct .py to operate with
500 should be in the same path as the .pyc. The reason this
501 logic is needed is that when a .pyc file is generated, the
502 filename embedded and thus what is readable in the code object
503 of the frame object is the fully qualified filepath when the
504 pyc is generated. If files are moved from machine to machine
505 this can break debugging as the .pyc will refer to the .py
506 on the original machine. Another case might be sharing
507 code over a network... This logic deals with that.
508
509 @param frame the frame object
510 @type frame object
511 @return fixed up file name
512 @rtype str
513 """
514 # get module name from __file__
515 fn = frame.f_globals.get('__file__')
516 try:
517 return self._fnCache[fn]
518 except KeyError:
519 if fn and fn != frame.f_code.co_filename:
520 absFilename = os.path.abspath(fn)
521 if absFilename.endswith(('.pyc', '.pyo')):
522 fixedName = absFilename[:-1]
523 if not os.path.exists(fixedName):
524 fixedName = absFilename
525 else:
526 fixedName = absFilename
527 else:
528 fixedName = frame.f_code.co_filename
529 # update cache
530 self._fnCache[fn] = fixedName
531 return fixedName
532
533 def __checkBreakInFrame(self, frame):
534 """
535 Private method to check if the function / method has a line number
536 which is a breakpoint.
537
538 @param frame the frame object
539 @type frame object
540 @return Flag indicating a function / method with breakpoint
541 @rtype bool
542 """
543 try:
544 return Breakpoint.breakInFrameCache[
545 frame.f_globals.get('__file__'),
546 frame.f_code.co_firstlineno]
547 except KeyError:
548 filename = self.fix_frame_filename(frame)
549 if filename not in Breakpoint.breakInFile:
550 Breakpoint.breakInFrameCache[
551 frame.f_globals.get('__file__'),
552 frame.f_code.co_firstlineno] = False
553 return False
554 lineNo = frame.f_code.co_firstlineno
555 lineNumbers = [lineNo]
556 # No need to handle special case if a lot of lines between
557 # (e.g. closure), because the additional lines won't cause a bp
558 for co_lno in frame.f_code.co_lnotab[1::2]:
559 lineNo += co_lno
560 lineNumbers.append(lineNo)
561
562 for bp in Breakpoint.breakInFile[filename]:
563 if bp in lineNumbers:
564 Breakpoint.breakInFrameCache[
565 frame.f_globals.get('__file__'),
566 frame.f_code.co_firstlineno] = True
567 return True
568 Breakpoint.breakInFrameCache[
569 frame.f_globals.get('__file__'),
570 frame.f_code.co_firstlineno] = False
571 return False
572
573 def break_here(self, frame):
574 """
575 Public method reimplemented from bdb.py to fix the filename from the
576 frame.
577
578 See fix_frame_filename for more info.
579
580 @param frame the frame object
581 @type frame object
582 @return flag indicating the break status
583 @rtype bool
584 """
585 filename = self.fix_frame_filename(frame)
586 if (filename, frame.f_lineno) in Breakpoint.breaks:
587 bp, flag = Breakpoint.effectiveBreak(
588 filename, frame.f_lineno, frame)
589 if bp:
590 # flag says ok to delete temp. bp
591 if flag and bp.temporary:
592 self.__do_clearBreak(filename, frame.f_lineno)
593 return True
594
595 if Watch.watches != []:
596 bp, flag = Watch.effectiveWatch(frame)
597 if bp:
598 # flag says ok to delete temp. watch
599 if flag and bp.temporary:
600 self.__do_clearWatch(bp.cond)
601 return True
602
603 return False
604
605 def __do_clearBreak(self, filename, lineno):
606 """
607 Private method called to clear a temporary breakpoint.
608
609 @param filename name of the file the bp belongs to
610 @type str
611 @param lineno linenumber of the bp
612 @type int
613 """
614 Breakpoint.clear_break(filename, lineno)
615 self._dbgClient.sendClearTemporaryBreakpoint(filename, lineno)
616
617 def __do_clearWatch(self, cond):
618 """
619 Private method called to clear a temporary watch expression.
620
621 @param cond expression of the watch expression to be cleared
622 @type str
623 """
624 Watch.clear_watch(cond)
625 self._dbgClient.sendClearTemporaryWatch(cond)
626
627 def getStack(self):
628 """
629 Public method to get the stack.
630
631 @return list of lists with file name (string), line number (integer)
632 and function name (string)
633 """
634 fr = self.cFrame
635 stack = []
636 while fr is not None:
637 fname = self._dbgClient.absPath(self.fix_frame_filename(fr))
638 if not fname.startswith("<"):
639 fline = fr.f_lineno
640 ffunc = fr.f_code.co_name
641
642 if ffunc == '?':
643 ffunc = ''
644
645 if ffunc and not ffunc.startswith("<"):
646 argInfo = getargvalues(fr)
647 try:
648 fargs = formatargvalues(
649 argInfo.args, argInfo.varargs,
650 argInfo.keywords, argInfo.locals)
651 except Exception:
652 fargs = ""
653 else:
654 fargs = ""
655
656 stack.append([fname, fline, ffunc, fargs])
657
658 if fr == self._dbgClient.mainFrame:
659 fr = None
660 else:
661 fr = fr.f_back
662
663 return stack
664
665 def user_line(self, frame):
666 """
667 Public method reimplemented to handle the program about to execute a
668 particular line.
669
670 @param frame the frame object
671 """
672 # We never stop on line 0.
673 if frame.f_lineno == 0:
674 return
675
676 self.currentFrame = frame
677
678 fr = frame
679 stack = []
680 while fr is not None:
681 # Reset the trace function so we can be sure
682 # to trace all functions up the stack... This gets around
683 # problems where an exception/breakpoint has occurred
684 # but we had disabled tracing along the way via a None
685 # return from dispatch_call
686 fr.f_trace = self.trace_dispatch
687 fname = self._dbgClient.absPath(self.fix_frame_filename(fr))
688 if not fname.startswith("<"):
689 fline = fr.f_lineno
690 ffunc = fr.f_code.co_name
691
692 if ffunc == '?':
693 ffunc = ''
694
695 if ffunc and not ffunc.startswith("<"):
696 argInfo = getargvalues(fr)
697 try:
698 fargs = formatargvalues(
699 argInfo.args, argInfo.varargs,
700 argInfo.keywords, argInfo.locals)
701 except Exception:
702 fargs = ""
703 else:
704 fargs = ""
705
706 stack.append([fname, fline, ffunc, fargs])
707
708 if fr == self._dbgClient.mainFrame:
709 fr = None
710 else:
711 fr = fr.f_back
712
713 self.__isBroken = True
714
715 self._dbgClient.sendResponseLine(stack)
716 self._dbgClient.eventLoop()
717
718 self.__isBroken = False
719
720 def user_exception(self, frame, excinfo, unhandled=False):
721 """
722 Public method reimplemented to report an exception to the debug server.
723
724 @param frame the frame object
725 @type frame object
726 @param excinfo details about the exception
727 @type tuple(Exception, excval object, traceback frame object)
728 @keyparam unhandled flag indicating an uncaught exception
729 @type bool
730 """
731 exctype, excval, exctb = excinfo
732
733 if exctype in [GeneratorExit, StopIteration]:
734 # ignore these
735 return
736
737 if exctype == SystemExit:
738 atexit._run_exitfuncs()
739 if excval is None:
740 exitcode = 0
741 message = ""
742 elif isinstance(excval, str):
743 exitcode = 1
744 message = excval
745 elif isinstance(excval, bytes):
746 exitcode = 1
747 message = excval.decode()
748 elif isinstance(excval, int):
749 exitcode = excval
750 message = ""
751 elif isinstance(excval, SystemExit):
752 code = excval.code
753 if isinstance(code, str):
754 exitcode = 1
755 message = code
756 elif isinstance(code, bytes):
757 exitcode = 1
758 message = code.decode()
759 elif isinstance(code, int):
760 exitcode = code
761 message = ""
762 else:
763 exitcode = 1
764 message = str(code)
765 else:
766 exitcode = 1
767 message = str(excval)
768 self._dbgClient.progTerminated(exitcode, message)
769 return
770
771 if exctype in [SyntaxError, IndentationError]:
772 try:
773 message = str(excval)
774 filename = excval.filename
775 lineno = excval.lineno
776 charno = excval.offset
777 realSyntaxError = os.path.exists(filename)
778 except (AttributeError, ValueError):
779 message = ""
780 filename = ""
781 lineno = 0
782 charno = 0
783 realSyntaxError = True
784
785 if realSyntaxError:
786 self._dbgClient.sendSyntaxError(
787 message, filename, lineno, charno)
788 self._dbgClient.eventLoop()
789 return
790
791 self.skipFrames = 0
792 if (exctype == RuntimeError and
793 str(excval).startswith('maximum recursion depth exceeded') or
794 sys.version_info >= (3, 5) and exctype == RecursionError):
795 excval = 'maximum recursion depth exceeded'
796 depth = 0
797 tb = exctb
798 while tb:
799 tb = tb.tb_next
800
801 if (tb and tb.tb_frame.f_code.co_name == 'trace_dispatch' and
802 __file__.startswith(tb.tb_frame.f_code.co_filename)):
803 depth = 1
804 self.skipFrames += depth
805
806 # always 1 if running without debugger
807 self.skipFrames = max(1, self.skipFrames)
808
809 exctype = self.__extractExceptionName(exctype)
810
811 if excval is None:
812 excval = ''
813
814 if unhandled:
815 exctypetxt = "unhandled {0!s}".format(str(exctype))
816 else:
817 exctypetxt = str(exctype)
818
819 stack = []
820 if exctb:
821 frlist = self.__extract_stack(exctb)
822 frlist.reverse()
823
824 self.currentFrame = frlist[0]
825
826 for fr in frlist[self.skipFrames:]:
827 filename = self._dbgClient.absPath(self.fix_frame_filename(fr))
828
829 if os.path.basename(filename).startswith("DebugClientBase"):
830 break
831
832 linenr = fr.f_lineno
833 ffunc = fr.f_code.co_name
834
835 if ffunc == '?':
836 ffunc = ''
837
838 if ffunc and not ffunc.startswith("<"):
839 argInfo = getargvalues(fr)
840 try:
841 fargs = formatargvalues(
842 argInfo.args, argInfo.varargs,
843 argInfo.keywords, argInfo.locals)
844 except Exception:
845 fargs = ""
846 else:
847 fargs = ""
848
849 stack.append([filename, linenr, ffunc, fargs])
850
851 self._dbgClient.sendException(exctypetxt, str(excval), stack)
852
853 if exctb is None:
854 return
855
856 self._dbgClient.eventLoop()
857 self.skipFrames = 0
858
859 def __extractExceptionName(self, exctype):
860 """
861 Private method to extract the exception name given the exception
862 type object.
863
864 @param exctype type of the exception
865 @return exception name (string)
866 """
867 return str(exctype).replace("<class '", "").replace("'>", "")
868
869 def __extract_stack(self, exctb):
870 """
871 Private member to return a list of stack frames.
872
873 @param exctb exception traceback
874 @return list of stack frames
875 """
876 tb = exctb
877 stack = []
878 while tb is not None:
879 stack.append(tb.tb_frame)
880 tb = tb.tb_next
881 tb = None
882 return stack
883
884 def stop_here(self, frame):
885 """
886 Public method reimplemented to filter out debugger files.
887
888 Tracing is turned off for files that are part of the
889 debugger that are called from the application being debugged.
890
891 @param frame the frame object
892 @type frame object
893 @return flag indicating whether the debugger should stop here
894 @rtype bool
895 """
896 if self.__skipFrame(frame):
897 return False
898
899 return (self.stop_everywhere or
900 frame is self.stopframe or
901 frame is self.returnframe or
902 frame is self.botframe)
903
904 def tracePythonLibs(self, enable):
905 """
906 Public method to update the settings to trace into Python libraries.
907
908 @param enable flag to debug into Python libraries
909 @type bool
910 """
911 pathsToSkip = list(self.pathsToSkip)
912 # don't trace into Python library?
913 if enable:
914 pathsToSkip = [x for x in pathsToSkip if not x.endswith(
915 ("site-packages", "dist-packages", self.lib))]
916 else:
917 pathsToSkip.append(self.lib)
918 localLib = [x for x in sys.path if x.endswith(("site-packages",
919 "dist-packages")) and not x.startswith(self.lib)]
920 pathsToSkip.extend(localLib)
921
922 self.pathsToSkip = tuple(pathsToSkip)
923
924 def __skipFrame(self, frame):
925 """
926 Private method to filter out debugger files.
927
928 Tracing is turned off for files that are part of the
929 debugger that are called from the application being debugged.
930
931 @param frame the frame object
932 @type frame object
933 @return flag indicating whether the debugger should skip this frame
934 @rtype bool
935 """
936 try:
937 return self.filesToSkip[frame.f_code.co_filename]
938 except KeyError:
939 ret = frame.f_code.co_filename.startswith(self.pathsToSkip)
940 self.filesToSkip[frame.f_code.co_filename] = ret
941 return ret
942 except AttributeError:
943 # if frame is None
944 return True
945
946 def isBroken(self):
947 """
948 Public method to return the broken state of the debugger.
949
950 @return flag indicating the broken state
951 @rtype bool
952 """
953 return self.__isBroken
954
955 #
956 # eflag: noqa = M702

eric ide

mercurial