blob: 18491da897357e152dfe92527b361bf0765002db [file] [log] [blame]
xf.li86118912025-03-19 20:07:27 -07001"""Debugger basics"""
2
3import fnmatch
4import sys
5import os
6from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
7
8__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
9
10GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR
11
12
13class BdbQuit(Exception):
14 """Exception to give up completely."""
15
16
17class Bdb:
18 """Generic Python debugger base class.
19
20 This class takes care of details of the trace facility;
21 a derived class should implement user interaction.
22 The standard debugger class (pdb.Pdb) is an example.
23
24 The optional skip argument must be an iterable of glob-style
25 module name patterns. The debugger will not step into frames
26 that originate in a module that matches one of these patterns.
27 Whether a frame is considered to originate in a certain module
28 is determined by the __name__ in the frame globals.
29 """
30
31 def __init__(self, skip=None):
32 self.skip = set(skip) if skip else None
33 self.breaks = {}
34 self.fncache = {}
35 self.frame_returning = None
36
37 def canonic(self, filename):
38 """Return canonical form of filename.
39
40 For real filenames, the canonical form is a case-normalized (on
41 case insensitive filesystems) absolute path. 'Filenames' with
42 angle brackets, such as "<stdin>", generated in interactive
43 mode, are returned unchanged.
44 """
45 if filename == "<" + filename[1:-1] + ">":
46 return filename
47 canonic = self.fncache.get(filename)
48 if not canonic:
49 canonic = os.path.abspath(filename)
50 canonic = os.path.normcase(canonic)
51 self.fncache[filename] = canonic
52 return canonic
53
54 def reset(self):
55 """Set values of attributes as ready to start debugging."""
56 import linecache
57 linecache.checkcache()
58 self.botframe = None
59 self._set_stopinfo(None, None)
60
61 def trace_dispatch(self, frame, event, arg):
62 """Dispatch a trace function for debugged frames based on the event.
63
64 This function is installed as the trace function for debugged
65 frames. Its return value is the new trace function, which is
66 usually itself. The default implementation decides how to
67 dispatch a frame, depending on the type of event (passed in as a
68 string) that is about to be executed.
69
70 The event can be one of the following:
71 line: A new line of code is going to be executed.
72 call: A function is about to be called or another code block
73 is entered.
74 return: A function or other code block is about to return.
75 exception: An exception has occurred.
76 c_call: A C function is about to be called.
77 c_return: A C function has returned.
78 c_exception: A C function has raised an exception.
79
80 For the Python events, specialized functions (see the dispatch_*()
81 methods) are called. For the C events, no action is taken.
82
83 The arg parameter depends on the previous event.
84 """
85 if self.quitting:
86 return # None
87 if event == 'line':
88 return self.dispatch_line(frame)
89 if event == 'call':
90 return self.dispatch_call(frame, arg)
91 if event == 'return':
92 return self.dispatch_return(frame, arg)
93 if event == 'exception':
94 return self.dispatch_exception(frame, arg)
95 if event == 'c_call':
96 return self.trace_dispatch
97 if event == 'c_exception':
98 return self.trace_dispatch
99 if event == 'c_return':
100 return self.trace_dispatch
101 print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
102 return self.trace_dispatch
103
104 def dispatch_line(self, frame):
105 """Invoke user function and return trace function for line event.
106
107 If the debugger stops on the current line, invoke
108 self.user_line(). Raise BdbQuit if self.quitting is set.
109 Return self.trace_dispatch to continue tracing in this scope.
110 """
111 if self.stop_here(frame) or self.break_here(frame):
112 self.user_line(frame)
113 if self.quitting: raise BdbQuit
114 return self.trace_dispatch
115
116 def dispatch_call(self, frame, arg):
117 """Invoke user function and return trace function for call event.
118
119 If the debugger stops on this function call, invoke
120 self.user_call(). Raise BbdQuit if self.quitting is set.
121 Return self.trace_dispatch to continue tracing in this scope.
122 """
123 # XXX 'arg' is no longer used
124 if self.botframe is None:
125 # First call of dispatch since reset()
126 self.botframe = frame.f_back # (CT) Note that this may also be None!
127 return self.trace_dispatch
128 if not (self.stop_here(frame) or self.break_anywhere(frame)):
129 # No need to trace this function
130 return # None
131 # Ignore call events in generator except when stepping.
132 if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
133 return self.trace_dispatch
134 self.user_call(frame, arg)
135 if self.quitting: raise BdbQuit
136 return self.trace_dispatch
137
138 def dispatch_return(self, frame, arg):
139 """Invoke user function and return trace function for return event.
140
141 If the debugger stops on this function return, invoke
142 self.user_return(). Raise BdbQuit if self.quitting is set.
143 Return self.trace_dispatch to continue tracing in this scope.
144 """
145 if self.stop_here(frame) or frame == self.returnframe:
146 # Ignore return events in generator except when stepping.
147 if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
148 return self.trace_dispatch
149 try:
150 self.frame_returning = frame
151 self.user_return(frame, arg)
152 finally:
153 self.frame_returning = None
154 if self.quitting: raise BdbQuit
155 # The user issued a 'next' or 'until' command.
156 if self.stopframe is frame and self.stoplineno != -1:
157 self._set_stopinfo(None, None)
158 return self.trace_dispatch
159
160 def dispatch_exception(self, frame, arg):
161 """Invoke user function and return trace function for exception event.
162
163 If the debugger stops on this exception, invoke
164 self.user_exception(). Raise BdbQuit if self.quitting is set.
165 Return self.trace_dispatch to continue tracing in this scope.
166 """
167 if self.stop_here(frame):
168 # When stepping with next/until/return in a generator frame, skip
169 # the internal StopIteration exception (with no traceback)
170 # triggered by a subiterator run with the 'yield from' statement.
171 if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
172 and arg[0] is StopIteration and arg[2] is None):
173 self.user_exception(frame, arg)
174 if self.quitting: raise BdbQuit
175 # Stop at the StopIteration or GeneratorExit exception when the user
176 # has set stopframe in a generator by issuing a return command, or a
177 # next/until command at the last statement in the generator before the
178 # exception.
179 elif (self.stopframe and frame is not self.stopframe
180 and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
181 and arg[0] in (StopIteration, GeneratorExit)):
182 self.user_exception(frame, arg)
183 if self.quitting: raise BdbQuit
184
185 return self.trace_dispatch
186
187 # Normally derived classes don't override the following
188 # methods, but they may if they want to redefine the
189 # definition of stopping and breakpoints.
190
191 def is_skipped_module(self, module_name):
192 "Return True if module_name matches any skip pattern."
193 if module_name is None: # some modules do not have names
194 return False
195 for pattern in self.skip:
196 if fnmatch.fnmatch(module_name, pattern):
197 return True
198 return False
199
200 def stop_here(self, frame):
201 "Return True if frame is below the starting frame in the stack."
202 # (CT) stopframe may now also be None, see dispatch_call.
203 # (CT) the former test for None is therefore removed from here.
204 if self.skip and \
205 self.is_skipped_module(frame.f_globals.get('__name__')):
206 return False
207 if frame is self.stopframe:
208 if self.stoplineno == -1:
209 return False
210 return frame.f_lineno >= self.stoplineno
211 if not self.stopframe:
212 return True
213 return False
214
215 def break_here(self, frame):
216 """Return True if there is an effective breakpoint for this line.
217
218 Check for line or function breakpoint and if in effect.
219 Delete temporary breakpoints if effective() says to.
220 """
221 filename = self.canonic(frame.f_code.co_filename)
222 if filename not in self.breaks:
223 return False
224 lineno = frame.f_lineno
225 if lineno not in self.breaks[filename]:
226 # The line itself has no breakpoint, but maybe the line is the
227 # first line of a function with breakpoint set by function name.
228 lineno = frame.f_code.co_firstlineno
229 if lineno not in self.breaks[filename]:
230 return False
231
232 # flag says ok to delete temp. bp
233 (bp, flag) = effective(filename, lineno, frame)
234 if bp:
235 self.currentbp = bp.number
236 if (flag and bp.temporary):
237 self.do_clear(str(bp.number))
238 return True
239 else:
240 return False
241
242 def do_clear(self, arg):
243 """Remove temporary breakpoint.
244
245 Must implement in derived classes or get NotImplementedError.
246 """
247 raise NotImplementedError("subclass of bdb must implement do_clear()")
248
249 def break_anywhere(self, frame):
250 """Return True if there is any breakpoint for frame's filename.
251 """
252 return self.canonic(frame.f_code.co_filename) in self.breaks
253
254 # Derived classes should override the user_* methods
255 # to gain control.
256
257 def user_call(self, frame, argument_list):
258 """Called if we might stop in a function."""
259 pass
260
261 def user_line(self, frame):
262 """Called when we stop or break at a line."""
263 pass
264
265 def user_return(self, frame, return_value):
266 """Called when a return trap is set here."""
267 pass
268
269 def user_exception(self, frame, exc_info):
270 """Called when we stop on an exception."""
271 pass
272
273 def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
274 """Set the attributes for stopping.
275
276 If stoplineno is greater than or equal to 0, then stop at line
277 greater than or equal to the stopline. If stoplineno is -1, then
278 don't stop at all.
279 """
280 self.stopframe = stopframe
281 self.returnframe = returnframe
282 self.quitting = False
283 # stoplineno >= 0 means: stop at line >= the stoplineno
284 # stoplineno -1 means: don't stop at all
285 self.stoplineno = stoplineno
286
287 # Derived classes and clients can call the following methods
288 # to affect the stepping state.
289
290 def set_until(self, frame, lineno=None):
291 """Stop when the line with the lineno greater than the current one is
292 reached or when returning from current frame."""
293 # the name "until" is borrowed from gdb
294 if lineno is None:
295 lineno = frame.f_lineno + 1
296 self._set_stopinfo(frame, frame, lineno)
297
298 def set_step(self):
299 """Stop after one line of code."""
300 # Issue #13183: pdb skips frames after hitting a breakpoint and running
301 # step commands.
302 # Restore the trace function in the caller (that may not have been set
303 # for performance reasons) when returning from the current frame.
304 if self.frame_returning:
305 caller_frame = self.frame_returning.f_back
306 if caller_frame and not caller_frame.f_trace:
307 caller_frame.f_trace = self.trace_dispatch
308 self._set_stopinfo(None, None)
309
310 def set_next(self, frame):
311 """Stop on the next line in or below the given frame."""
312 self._set_stopinfo(frame, None)
313
314 def set_return(self, frame):
315 """Stop when returning from the given frame."""
316 if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
317 self._set_stopinfo(frame, None, -1)
318 else:
319 self._set_stopinfo(frame.f_back, frame)
320
321 def set_trace(self, frame=None):
322 """Start debugging from frame.
323
324 If frame is not specified, debugging starts from caller's frame.
325 """
326 if frame is None:
327 frame = sys._getframe().f_back
328 self.reset()
329 while frame:
330 frame.f_trace = self.trace_dispatch
331 self.botframe = frame
332 frame = frame.f_back
333 self.set_step()
334 sys.settrace(self.trace_dispatch)
335
336 def set_continue(self):
337 """Stop only at breakpoints or when finished.
338
339 If there are no breakpoints, set the system trace function to None.
340 """
341 # Don't stop except at breakpoints or when finished
342 self._set_stopinfo(self.botframe, None, -1)
343 if not self.breaks:
344 # no breakpoints; run without debugger overhead
345 sys.settrace(None)
346 frame = sys._getframe().f_back
347 while frame and frame is not self.botframe:
348 del frame.f_trace
349 frame = frame.f_back
350
351 def set_quit(self):
352 """Set quitting attribute to True.
353
354 Raises BdbQuit exception in the next call to a dispatch_*() method.
355 """
356 self.stopframe = self.botframe
357 self.returnframe = None
358 self.quitting = True
359 sys.settrace(None)
360
361 # Derived classes and clients can call the following methods
362 # to manipulate breakpoints. These methods return an
363 # error message if something went wrong, None if all is well.
364 # Set_break prints out the breakpoint line and file:lineno.
365 # Call self.get_*break*() to see the breakpoints or better
366 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
367
368 def set_break(self, filename, lineno, temporary=False, cond=None,
369 funcname=None):
370 """Set a new breakpoint for filename:lineno.
371
372 If lineno doesn't exist for the filename, return an error message.
373 The filename should be in canonical form.
374 """
375 filename = self.canonic(filename)
376 import linecache # Import as late as possible
377 line = linecache.getline(filename, lineno)
378 if not line:
379 return 'Line %s:%d does not exist' % (filename, lineno)
380 list = self.breaks.setdefault(filename, [])
381 if lineno not in list:
382 list.append(lineno)
383 bp = Breakpoint(filename, lineno, temporary, cond, funcname)
384 return None
385
386 def _prune_breaks(self, filename, lineno):
387 """Prune breakpoints for filename:lineno.
388
389 A list of breakpoints is maintained in the Bdb instance and in
390 the Breakpoint class. If a breakpoint in the Bdb instance no
391 longer exists in the Breakpoint class, then it's removed from the
392 Bdb instance.
393 """
394 if (filename, lineno) not in Breakpoint.bplist:
395 self.breaks[filename].remove(lineno)
396 if not self.breaks[filename]:
397 del self.breaks[filename]
398
399 def clear_break(self, filename, lineno):
400 """Delete breakpoints for filename:lineno.
401
402 If no breakpoints were set, return an error message.
403 """
404 filename = self.canonic(filename)
405 if filename not in self.breaks:
406 return 'There are no breakpoints in %s' % filename
407 if lineno not in self.breaks[filename]:
408 return 'There is no breakpoint at %s:%d' % (filename, lineno)
409 # If there's only one bp in the list for that file,line
410 # pair, then remove the breaks entry
411 for bp in Breakpoint.bplist[filename, lineno][:]:
412 bp.deleteMe()
413 self._prune_breaks(filename, lineno)
414 return None
415
416 def clear_bpbynumber(self, arg):
417 """Delete a breakpoint by its index in Breakpoint.bpbynumber.
418
419 If arg is invalid, return an error message.
420 """
421 try:
422 bp = self.get_bpbynumber(arg)
423 except ValueError as err:
424 return str(err)
425 bp.deleteMe()
426 self._prune_breaks(bp.file, bp.line)
427 return None
428
429 def clear_all_file_breaks(self, filename):
430 """Delete all breakpoints in filename.
431
432 If none were set, return an error message.
433 """
434 filename = self.canonic(filename)
435 if filename not in self.breaks:
436 return 'There are no breakpoints in %s' % filename
437 for line in self.breaks[filename]:
438 blist = Breakpoint.bplist[filename, line]
439 for bp in blist:
440 bp.deleteMe()
441 del self.breaks[filename]
442 return None
443
444 def clear_all_breaks(self):
445 """Delete all existing breakpoints.
446
447 If none were set, return an error message.
448 """
449 if not self.breaks:
450 return 'There are no breakpoints'
451 for bp in Breakpoint.bpbynumber:
452 if bp:
453 bp.deleteMe()
454 self.breaks = {}
455 return None
456
457 def get_bpbynumber(self, arg):
458 """Return a breakpoint by its index in Breakpoint.bybpnumber.
459
460 For invalid arg values or if the breakpoint doesn't exist,
461 raise a ValueError.
462 """
463 if not arg:
464 raise ValueError('Breakpoint number expected')
465 try:
466 number = int(arg)
467 except ValueError:
468 raise ValueError('Non-numeric breakpoint number %s' % arg) from None
469 try:
470 bp = Breakpoint.bpbynumber[number]
471 except IndexError:
472 raise ValueError('Breakpoint number %d out of range' % number) from None
473 if bp is None:
474 raise ValueError('Breakpoint %d already deleted' % number)
475 return bp
476
477 def get_break(self, filename, lineno):
478 """Return True if there is a breakpoint for filename:lineno."""
479 filename = self.canonic(filename)
480 return filename in self.breaks and \
481 lineno in self.breaks[filename]
482
483 def get_breaks(self, filename, lineno):
484 """Return all breakpoints for filename:lineno.
485
486 If no breakpoints are set, return an empty list.
487 """
488 filename = self.canonic(filename)
489 return filename in self.breaks and \
490 lineno in self.breaks[filename] and \
491 Breakpoint.bplist[filename, lineno] or []
492
493 def get_file_breaks(self, filename):
494 """Return all lines with breakpoints for filename.
495
496 If no breakpoints are set, return an empty list.
497 """
498 filename = self.canonic(filename)
499 if filename in self.breaks:
500 return self.breaks[filename]
501 else:
502 return []
503
504 def get_all_breaks(self):
505 """Return all breakpoints that are set."""
506 return self.breaks
507
508 # Derived classes and clients can call the following method
509 # to get a data structure representing a stack trace.
510
511 def get_stack(self, f, t):
512 """Return a list of (frame, lineno) in a stack trace and a size.
513
514 List starts with original calling frame, if there is one.
515 Size may be number of frames above or below f.
516 """
517 stack = []
518 if t and t.tb_frame is f:
519 t = t.tb_next
520 while f is not None:
521 stack.append((f, f.f_lineno))
522 if f is self.botframe:
523 break
524 f = f.f_back
525 stack.reverse()
526 i = max(0, len(stack) - 1)
527 while t is not None:
528 stack.append((t.tb_frame, t.tb_lineno))
529 t = t.tb_next
530 if f is None:
531 i = max(0, len(stack) - 1)
532 return stack, i
533
534 def format_stack_entry(self, frame_lineno, lprefix=': '):
535 """Return a string with information about a stack entry.
536
537 The stack entry frame_lineno is a (frame, lineno) tuple. The
538 return string contains the canonical filename, the function name
539 or '<lambda>', the input arguments, the return value, and the
540 line of code (if it exists).
541
542 """
543 import linecache, reprlib
544 frame, lineno = frame_lineno
545 filename = self.canonic(frame.f_code.co_filename)
546 s = '%s(%r)' % (filename, lineno)
547 if frame.f_code.co_name:
548 s += frame.f_code.co_name
549 else:
550 s += "<lambda>"
551 s += '()'
552 if '__return__' in frame.f_locals:
553 rv = frame.f_locals['__return__']
554 s += '->'
555 s += reprlib.repr(rv)
556 line = linecache.getline(filename, lineno, frame.f_globals)
557 if line:
558 s += lprefix + line.strip()
559 return s
560
561 # The following methods can be called by clients to use
562 # a debugger to debug a statement or an expression.
563 # Both can be given as a string, or a code object.
564
565 def run(self, cmd, globals=None, locals=None):
566 """Debug a statement executed via the exec() function.
567
568 globals defaults to __main__.dict; locals defaults to globals.
569 """
570 if globals is None:
571 import __main__
572 globals = __main__.__dict__
573 if locals is None:
574 locals = globals
575 self.reset()
576 if isinstance(cmd, str):
577 cmd = compile(cmd, "<string>", "exec")
578 sys.settrace(self.trace_dispatch)
579 try:
580 exec(cmd, globals, locals)
581 except BdbQuit:
582 pass
583 finally:
584 self.quitting = True
585 sys.settrace(None)
586
587 def runeval(self, expr, globals=None, locals=None):
588 """Debug an expression executed via the eval() function.
589
590 globals defaults to __main__.dict; locals defaults to globals.
591 """
592 if globals is None:
593 import __main__
594 globals = __main__.__dict__
595 if locals is None:
596 locals = globals
597 self.reset()
598 sys.settrace(self.trace_dispatch)
599 try:
600 return eval(expr, globals, locals)
601 except BdbQuit:
602 pass
603 finally:
604 self.quitting = True
605 sys.settrace(None)
606
607 def runctx(self, cmd, globals, locals):
608 """For backwards-compatibility. Defers to run()."""
609 # B/W compatibility
610 self.run(cmd, globals, locals)
611
612 # This method is more useful to debug a single function call.
613
614 def runcall(*args, **kwds):
615 """Debug a single function call.
616
617 Return the result of the function call.
618 """
619 if len(args) >= 2:
620 self, func, *args = args
621 elif not args:
622 raise TypeError("descriptor 'runcall' of 'Bdb' object "
623 "needs an argument")
624 elif 'func' in kwds:
625 func = kwds.pop('func')
626 self, *args = args
627 import warnings
628 warnings.warn("Passing 'func' as keyword argument is deprecated",
629 DeprecationWarning, stacklevel=2)
630 else:
631 raise TypeError('runcall expected at least 1 positional argument, '
632 'got %d' % (len(args)-1))
633
634 self.reset()
635 sys.settrace(self.trace_dispatch)
636 res = None
637 try:
638 res = func(*args, **kwds)
639 except BdbQuit:
640 pass
641 finally:
642 self.quitting = True
643 sys.settrace(None)
644 return res
645 runcall.__text_signature__ = '($self, func, /, *args, **kwds)'
646
647
648def set_trace():
649 """Start debugging with a Bdb instance from the caller's frame."""
650 Bdb().set_trace()
651
652
653class Breakpoint:
654 """Breakpoint class.
655
656 Implements temporary breakpoints, ignore counts, disabling and
657 (re)-enabling, and conditionals.
658
659 Breakpoints are indexed by number through bpbynumber and by
660 the (file, line) tuple using bplist. The former points to a
661 single instance of class Breakpoint. The latter points to a
662 list of such instances since there may be more than one
663 breakpoint per line.
664
665 When creating a breakpoint, its associated filename should be
666 in canonical form. If funcname is defined, a breakpoint hit will be
667 counted when the first line of that function is executed. A
668 conditional breakpoint always counts a hit.
669 """
670
671 # XXX Keeping state in the class is a mistake -- this means
672 # you cannot have more than one active Bdb instance.
673
674 next = 1 # Next bp to be assigned
675 bplist = {} # indexed by (file, lineno) tuple
676 bpbynumber = [None] # Each entry is None or an instance of Bpt
677 # index 0 is unused, except for marking an
678 # effective break .... see effective()
679
680 def __init__(self, file, line, temporary=False, cond=None, funcname=None):
681 self.funcname = funcname
682 # Needed if funcname is not None.
683 self.func_first_executable_line = None
684 self.file = file # This better be in canonical form!
685 self.line = line
686 self.temporary = temporary
687 self.cond = cond
688 self.enabled = True
689 self.ignore = 0
690 self.hits = 0
691 self.number = Breakpoint.next
692 Breakpoint.next += 1
693 # Build the two lists
694 self.bpbynumber.append(self)
695 if (file, line) in self.bplist:
696 self.bplist[file, line].append(self)
697 else:
698 self.bplist[file, line] = [self]
699
700 def deleteMe(self):
701 """Delete the breakpoint from the list associated to a file:line.
702
703 If it is the last breakpoint in that position, it also deletes
704 the entry for the file:line.
705 """
706
707 index = (self.file, self.line)
708 self.bpbynumber[self.number] = None # No longer in list
709 self.bplist[index].remove(self)
710 if not self.bplist[index]:
711 # No more bp for this f:l combo
712 del self.bplist[index]
713
714 def enable(self):
715 """Mark the breakpoint as enabled."""
716 self.enabled = True
717
718 def disable(self):
719 """Mark the breakpoint as disabled."""
720 self.enabled = False
721
722 def bpprint(self, out=None):
723 """Print the output of bpformat().
724
725 The optional out argument directs where the output is sent
726 and defaults to standard output.
727 """
728 if out is None:
729 out = sys.stdout
730 print(self.bpformat(), file=out)
731
732 def bpformat(self):
733 """Return a string with information about the breakpoint.
734
735 The information includes the breakpoint number, temporary
736 status, file:line position, break condition, number of times to
737 ignore, and number of times hit.
738
739 """
740 if self.temporary:
741 disp = 'del '
742 else:
743 disp = 'keep '
744 if self.enabled:
745 disp = disp + 'yes '
746 else:
747 disp = disp + 'no '
748 ret = '%-4dbreakpoint %s at %s:%d' % (self.number, disp,
749 self.file, self.line)
750 if self.cond:
751 ret += '\n\tstop only if %s' % (self.cond,)
752 if self.ignore:
753 ret += '\n\tignore next %d hits' % (self.ignore,)
754 if self.hits:
755 if self.hits > 1:
756 ss = 's'
757 else:
758 ss = ''
759 ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)
760 return ret
761
762 def __str__(self):
763 "Return a condensed description of the breakpoint."
764 return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
765
766# -----------end of Breakpoint class----------
767
768
769def checkfuncname(b, frame):
770 """Return True if break should happen here.
771
772 Whether a break should happen depends on the way that b (the breakpoint)
773 was set. If it was set via line number, check if b.line is the same as
774 the one in the frame. If it was set via function name, check if this is
775 the right function and if it is on the first executable line.
776 """
777 if not b.funcname:
778 # Breakpoint was set via line number.
779 if b.line != frame.f_lineno:
780 # Breakpoint was set at a line with a def statement and the function
781 # defined is called: don't break.
782 return False
783 return True
784
785 # Breakpoint set via function name.
786 if frame.f_code.co_name != b.funcname:
787 # It's not a function call, but rather execution of def statement.
788 return False
789
790 # We are in the right frame.
791 if not b.func_first_executable_line:
792 # The function is entered for the 1st time.
793 b.func_first_executable_line = frame.f_lineno
794
795 if b.func_first_executable_line != frame.f_lineno:
796 # But we are not at the first line number: don't break.
797 return False
798 return True
799
800
801# Determines if there is an effective (active) breakpoint at this
802# line of code. Returns breakpoint number or 0 if none
803def effective(file, line, frame):
804 """Determine which breakpoint for this file:line is to be acted upon.
805
806 Called only if we know there is a breakpoint at this location. Return
807 the breakpoint that was triggered and a boolean that indicates if it is
808 ok to delete a temporary breakpoint. Return (None, None) if there is no
809 matching breakpoint.
810 """
811 possibles = Breakpoint.bplist[file, line]
812 for b in possibles:
813 if not b.enabled:
814 continue
815 if not checkfuncname(b, frame):
816 continue
817 # Count every hit when bp is enabled
818 b.hits += 1
819 if not b.cond:
820 # If unconditional, and ignoring go on to next, else break
821 if b.ignore > 0:
822 b.ignore -= 1
823 continue
824 else:
825 # breakpoint and marker that it's ok to delete if temporary
826 return (b, True)
827 else:
828 # Conditional bp.
829 # Ignore count applies only to those bpt hits where the
830 # condition evaluates to true.
831 try:
832 val = eval(b.cond, frame.f_globals, frame.f_locals)
833 if val:
834 if b.ignore > 0:
835 b.ignore -= 1
836 # continue
837 else:
838 return (b, True)
839 # else:
840 # continue
841 except:
842 # if eval fails, most conservative thing is to stop on
843 # breakpoint regardless of ignore count. Don't delete
844 # temporary, as another hint to user.
845 return (b, False)
846 return (None, None)
847
848
849# -------------------- testing --------------------
850
851class Tdb(Bdb):
852 def user_call(self, frame, args):
853 name = frame.f_code.co_name
854 if not name: name = '???'
855 print('+++ call', name, args)
856 def user_line(self, frame):
857 import linecache
858 name = frame.f_code.co_name
859 if not name: name = '???'
860 fn = self.canonic(frame.f_code.co_filename)
861 line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
862 print('+++', fn, frame.f_lineno, name, ':', line.strip())
863 def user_return(self, frame, retval):
864 print('+++ return', retval)
865 def user_exception(self, frame, exc_stuff):
866 print('+++ exception', exc_stuff)
867 self.set_continue()
868
869def foo(n):
870 print('foo(', n, ')')
871 x = bar(n*10)
872 print('bar returned', x)
873
874def bar(a):
875 print('bar(', a, ')')
876 return a/2
877
878def test():
879 t = Tdb()
880 t.run('import bdb; bdb.foo(10)')