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