Vega strike Python Modules doc  0.5.1
Documentation of the " Modules " folder of Vega strike
 All Data Structures Namespaces Files Functions Variables
bdb.py
Go to the documentation of this file.
1 """Debugger basics"""
2 
3 import sys
4 import os
5 import types
6 
7 __all__ = ["BdbQuit","Bdb","Breakpoint"]
8 
9 BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
10 
11 
12 class Bdb:
13 
14  """Generic Python debugger base class.
15 
16  This class takes care of details of the trace facility;
17  a derived class should implement user interaction.
18  The standard debugger class (pdb.Pdb) is an example.
19  """
20 
21  def __init__(self):
22  self.breaks = {}
23  self.fncache = {}
24 
25  def canonic(self, filename):
26  if filename == "<" + filename[1:-1] + ">":
27  return filename
28  canonic = self.fncache.get(filename)
29  if not canonic:
30  canonic = os.path.abspath(filename)
31  canonic = os.path.normcase(canonic)
32  self.fncache[filename] = canonic
33  return canonic
34 
35  def reset(self):
36  import linecache
38  self.botframe = None
39  self.stopframe = None
40  self.returnframe = None
41  self.quitting = 0
42 
43  def trace_dispatch(self, frame, event, arg):
44  if self.quitting:
45  return # None
46  if event == 'line':
47  return self.dispatch_line(frame)
48  if event == 'call':
49  return self.dispatch_call(frame, arg)
50  if event == 'return':
51  return self.dispatch_return(frame, arg)
52  if event == 'exception':
53  return self.dispatch_exception(frame, arg)
54  print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
55  return self.trace_dispatch
56 
57  def dispatch_line(self, frame):
58  if self.stop_here(frame) or self.break_here(frame):
59  self.user_line(frame)
60  if self.quitting: raise BdbQuit
61  return self.trace_dispatch
62 
63  def dispatch_call(self, frame, arg):
64  # XXX 'arg' is no longer used
65  if self.botframe is None:
66  # First call of dispatch since reset()
67  self.botframe = frame
68  return self.trace_dispatch
69  if not (self.stop_here(frame) or self.break_anywhere(frame)):
70  # No need to trace this function
71  return # None
72  self.user_call(frame, arg)
73  if self.quitting: raise BdbQuit
74  return self.trace_dispatch
75 
76  def dispatch_return(self, frame, arg):
77  if self.stop_here(frame) or frame == self.returnframe:
78  self.user_return(frame, arg)
79  if self.quitting: raise BdbQuit
80  return self.trace_dispatch
81 
82  def dispatch_exception(self, frame, arg):
83  if self.stop_here(frame):
84  self.user_exception(frame, arg)
85  if self.quitting: raise BdbQuit
86  return self.trace_dispatch
87 
88  # Normally derived classes don't override the following
89  # methods, but they may if they want to redefine the
90  # definition of stopping and breakpoints.
91 
92  def stop_here(self, frame):
93  if self.stopframe is None:
94  return 1
95  if frame is self.stopframe:
96  return 1
97  while frame is not None and frame is not self.stopframe:
98  if frame is self.botframe:
99  return 1
100  frame = frame.f_back
101  return 0
102 
103  def break_here(self, frame):
104  filename = self.canonic(frame.f_code.co_filename)
105  if not self.breaks.has_key(filename):
106  return 0
107  lineno = frame.f_lineno
108  if not lineno in self.breaks[filename]:
109  return 0
110  # flag says ok to delete temp. bp
111  (bp, flag) = effective(filename, lineno, frame)
112  if bp:
113  self.currentbp = bp.number
114  if (flag and bp.temporary):
115  self.do_clear(str(bp.number))
116  return 1
117  else:
118  return 0
119 
120  def do_clear(self, arg):
121  raise NotImplementedError, "subclass of bdb must implement do_clear()"
122 
123  def break_anywhere(self, frame):
124  return self.breaks.has_key(
125  self.canonic(frame.f_code.co_filename))
126 
127  # Derived classes should override the user_* methods
128  # to gain control.
129 
130  def user_call(self, frame, argument_list):
131  """This method is called when there is the remote possibility
132  that we ever need to stop in this function."""
133  pass
134 
135  def user_line(self, frame):
136  """This method is called when we stop or break at this line."""
137  pass
138 
139  def user_return(self, frame, return_value):
140  """This method is called when a return trap is set here."""
141  pass
142 
143  def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
144  """This method is called if an exception occurs,
145  but only if we are to stop at or just below this level."""
146  pass
147 
148  # Derived classes and clients can call the following methods
149  # to affect the stepping state.
150 
151  def set_step(self):
152  """Stop after one line of code."""
153  self.stopframe = None
154  self.returnframe = None
155  self.quitting = 0
156 
157  def set_next(self, frame):
158  """Stop on the next line in or below the given frame."""
159  self.stopframe = frame
160  self.returnframe = None
161  self.quitting = 0
162 
163  def set_return(self, frame):
164  """Stop when returning from the given frame."""
165  self.stopframe = frame.f_back
166  self.returnframe = frame
167  self.quitting = 0
168 
169  def set_trace(self):
170  """Start debugging from here."""
171  try:
172  1 + ''
173  except:
174  frame = sys.exc_info()[2].tb_frame.f_back
175  self.reset()
176  while frame:
177  frame.f_trace = self.trace_dispatch
178  self.botframe = frame
179  frame = frame.f_back
180  self.set_step()
181  sys.settrace(self.trace_dispatch)
182 
183  def set_continue(self):
184  # Don't stop except at breakpoints or when finished
185  self.stopframe = self.botframe
186  self.returnframe = None
187  self.quitting = 0
188  if not self.breaks:
189  # no breakpoints; run without debugger overhead
190  sys.settrace(None)
191  try:
192  1 + '' # raise an exception
193  except:
194  frame = sys.exc_info()[2].tb_frame.f_back
195  while frame and frame is not self.botframe:
196  del frame.f_trace
197  frame = frame.f_back
198 
199  def set_quit(self):
200  self.stopframe = self.botframe
201  self.returnframe = None
202  self.quitting = 1
203  sys.settrace(None)
204 
205  # Derived classes and clients can call the following methods
206  # to manipulate breakpoints. These methods return an
207  # error message is something went wrong, None if all is well.
208  # Set_break prints out the breakpoint line and file:lineno.
209  # Call self.get_*break*() to see the breakpoints or better
210  # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
211 
212  def set_break(self, filename, lineno, temporary=0, cond = None):
213  filename = self.canonic(filename)
214  import linecache # Import as late as possible
215  line = linecache.getline(filename, lineno)
216  if not line:
217  return 'Line %s:%d does not exist' % (filename,
218  lineno)
219  if not self.breaks.has_key(filename):
220  self.breaks[filename] = []
221  list = self.breaks[filename]
222  if not lineno in list:
223  list.append(lineno)
224  bp = Breakpoint(filename, lineno, temporary, cond)
225 
226  def clear_break(self, filename, lineno):
227  filename = self.canonic(filename)
228  if not self.breaks.has_key(filename):
229  return 'There are no breakpoints in %s' % filename
230  if lineno not in self.breaks[filename]:
231  return 'There is no breakpoint at %s:%d' % (filename,
232  lineno)
233  # If there's only one bp in the list for that file,line
234  # pair, then remove the breaks entry
235  for bp in Breakpoint.bplist[filename, lineno][:]:
236  bp.deleteMe()
237  if not Breakpoint.bplist.has_key((filename, lineno)):
238  self.breaks[filename].remove(lineno)
239  if not self.breaks[filename]:
240  del self.breaks[filename]
241 
242  def clear_bpbynumber(self, arg):
243  try:
244  number = int(arg)
245  except:
246  return 'Non-numeric breakpoint number (%s)' % arg
247  try:
248  bp = Breakpoint.bpbynumber[number]
249  except IndexError:
250  return 'Breakpoint number (%d) out of range' % number
251  if not bp:
252  return 'Breakpoint (%d) already deleted' % number
253  self.clear_break(bp.file, bp.line)
254 
255  def clear_all_file_breaks(self, filename):
256  filename = self.canonic(filename)
257  if not self.breaks.has_key(filename):
258  return 'There are no breakpoints in %s' % filename
259  for line in self.breaks[filename]:
260  blist = Breakpoint.bplist[filename, line]
261  for bp in blist:
262  bp.deleteMe()
263  del self.breaks[filename]
264 
265  def clear_all_breaks(self):
266  if not self.breaks:
267  return 'There are no breakpoints'
268  for bp in Breakpoint.bpbynumber:
269  if bp:
270  bp.deleteMe()
271  self.breaks = {}
272 
273  def get_break(self, filename, lineno):
274  filename = self.canonic(filename)
275  return self.breaks.has_key(filename) and \
276  lineno in self.breaks[filename]
277 
278  def get_breaks(self, filename, lineno):
279  filename = self.canonic(filename)
280  return self.breaks.has_key(filename) and \
281  lineno in self.breaks[filename] and \
282  Breakpoint.bplist[filename, lineno] or []
283 
284  def get_file_breaks(self, filename):
285  filename = self.canonic(filename)
286  if self.breaks.has_key(filename):
287  return self.breaks[filename]
288  else:
289  return []
290 
291  def get_all_breaks(self):
292  return self.breaks
293 
294  # Derived classes and clients can call the following method
295  # to get a data structure representing a stack trace.
296 
297  def get_stack(self, f, t):
298  stack = []
299  if t and t.tb_frame is f:
300  t = t.tb_next
301  while f is not None:
302  stack.append((f, f.f_lineno))
303  if f is self.botframe:
304  break
305  f = f.f_back
306  stack.reverse()
307  i = max(0, len(stack) - 1)
308  while t is not None:
309  stack.append((t.tb_frame, t.tb_lineno))
310  t = t.tb_next
311  return stack, i
312 
313  #
314 
315  def format_stack_entry(self, frame_lineno, lprefix=': '):
316  import linecache, repr
317  frame, lineno = frame_lineno
318  filename = self.canonic(frame.f_code.co_filename)
319  s = filename + '(' + `lineno` + ')'
320  if frame.f_code.co_name:
321  s = s + frame.f_code.co_name
322  else:
323  s = s + "<lambda>"
324  if frame.f_locals.has_key('__args__'):
325  args = frame.f_locals['__args__']
326  else:
327  args = None
328  if args:
329  s = s + repr.repr(args)
330  else:
331  s = s + '()'
332  if frame.f_locals.has_key('__return__'):
333  rv = frame.f_locals['__return__']
334  s = s + '->'
335  s = s + repr.repr(rv)
336  line = linecache.getline(filename, lineno)
337  if line: s = s + lprefix + line.strip()
338  return s
339 
340  # The following two methods can be called by clients to use
341  # a debugger to debug a statement, given as a string.
342 
343  def run(self, cmd, globals=None, locals=None):
344  if globals is None:
345  import __main__
346  globals = __main__.__dict__
347  if locals is None:
348  locals = globals
349  self.reset()
350  sys.settrace(self.trace_dispatch)
351  if not isinstance(cmd, types.CodeType):
352  cmd = cmd+'\n'
353  try:
354  try:
355  exec cmd in globals, locals
356  except BdbQuit:
357  pass
358  finally:
359  self.quitting = 1
360  sys.settrace(None)
361 
362  def runeval(self, expr, globals=None, locals=None):
363  if globals is None:
364  import __main__
365  globals = __main__.__dict__
366  if locals is None:
367  locals = globals
368  self.reset()
369  sys.settrace(self.trace_dispatch)
370  if not isinstance(expr, types.CodeType):
371  expr = expr+'\n'
372  try:
373  try:
374  return eval(expr, globals, locals)
375  except BdbQuit:
376  pass
377  finally:
378  self.quitting = 1
379  sys.settrace(None)
380 
381  def runctx(self, cmd, globals, locals):
382  # B/W compatibility
383  self.run(cmd, globals, locals)
384 
385  # This method is more useful to debug a single function call.
386 
387  def runcall(self, func, *args):
388  self.reset()
389  sys.settrace(self.trace_dispatch)
390  res = None
391  try:
392  try:
393  res = apply(func, args)
394  except BdbQuit:
395  pass
396  finally:
397  self.quitting = 1
398  sys.settrace(None)
399  return res
400 
401 
402 def set_trace():
403  Bdb().set_trace()
404 
405 
407 
408  """Breakpoint class
409 
410  Implements temporary breakpoints, ignore counts, disabling and
411  (re)-enabling, and conditionals.
412 
413  Breakpoints are indexed by number through bpbynumber and by
414  the file,line tuple using bplist. The former points to a
415  single instance of class Breakpoint. The latter points to a
416  list of such instances since there may be more than one
417  breakpoint per line.
418 
419  """
420 
421  # XXX Keeping state in the class is a mistake -- this means
422  # you cannot have more than one active Bdb instance.
423 
424  next = 1 # Next bp to be assigned
425  bplist = {} # indexed by (file, lineno) tuple
426  bpbynumber = [None] # Each entry is None or an instance of Bpt
427  # index 0 is unused, except for marking an
428  # effective break .... see effective()
429 
430  def __init__(self, file, line, temporary=0, cond = None):
431  self.file = file # This better be in canonical form!
432  self.line = line
433  self.temporary = temporary
434  self.cond = cond
435  self.enabled = 1
436  self.ignore = 0
437  self.hits = 0
438  self.number = Breakpoint.next
439  Breakpoint.next = Breakpoint.next + 1
440  # Build the two lists
441  self.bpbynumber.append(self)
442  if self.bplist.has_key((file, line)):
443  self.bplist[file, line].append(self)
444  else:
445  self.bplist[file, line] = [self]
446 
447 
448  def deleteMe(self):
449  index = (self.file, self.line)
450  self.bpbynumber[self.number] = None # No longer in list
451  self.bplist[index].remove(self)
452  if not self.bplist[index]:
453  # No more bp for this f:l combo
454  del self.bplist[index]
455 
456  def enable(self):
457  self.enabled = 1
458 
459  def disable(self):
460  self.enabled = 0
461 
462  def bpprint(self):
463  if self.temporary:
464  disp = 'del '
465  else:
466  disp = 'keep '
467  if self.enabled:
468  disp = disp + 'yes'
469  else:
470  disp = disp + 'no '
471  print '%-4dbreakpoint %s at %s:%d' % (self.number, disp,
472  self.file, self.line)
473  if self.cond:
474  print '\tstop only if %s' % (self.cond,)
475  if self.ignore:
476  print '\tignore next %d hits' % (self.ignore)
477  if (self.hits):
478  if (self.hits > 1): ss = 's'
479  else: ss = ''
480  print ('\tbreakpoint already hit %d time%s' %
481  (self.hits, ss))
482 
483 # -----------end of Breakpoint class----------
484 
485 # Determines if there is an effective (active) breakpoint at this
486 # line of code. Returns breakpoint number or 0 if none
487 def effective(file, line, frame):
488  """Determine which breakpoint for this file:line is to be acted upon.
489 
490  Called only if we know there is a bpt at this
491  location. Returns breakpoint that was triggered and a flag
492  that indicates if it is ok to delete a temporary bp.
493 
494  """
495  possibles = Breakpoint.bplist[file,line]
496  for i in range(0, len(possibles)):
497  b = possibles[i]
498  if b.enabled == 0:
499  continue
500  # Count every hit when bp is enabled
501  b.hits = b.hits + 1
502  if not b.cond:
503  # If unconditional, and ignoring,
504  # go on to next, else break
505  if b.ignore > 0:
506  b.ignore = b.ignore -1
507  continue
508  else:
509  # breakpoint and marker that's ok
510  # to delete if temporary
511  return (b,1)
512  else:
513  # Conditional bp.
514  # Ignore count applies only to those bpt hits where the
515  # condition evaluates to true.
516  try:
517  val = eval(b.cond, frame.f_globals,
518  frame.f_locals)
519  if val:
520  if b.ignore > 0:
521  b.ignore = b.ignore -1
522  # continue
523  else:
524  return (b,1)
525  # else:
526  # continue
527  except:
528  # if eval fails, most conservative
529  # thing is to stop on breakpoint
530  # regardless of ignore count.
531  # Don't delete temporary,
532  # as another hint to user.
533  return (b,0)
534  return (None, None)
535 
536 # -------------------- testing --------------------
537 
538 class Tdb(Bdb):
539  def user_call(self, frame, args):
540  name = frame.f_code.co_name
541  if not name: name = '???'
542  print '+++ call', name, args
543  def user_line(self, frame):
544  import linecache
545  name = frame.f_code.co_name
546  if not name: name = '???'
547  fn = self.canonic(frame.f_code.co_filename)
548  line = linecache.getline(fn, frame.f_lineno)
549  print '+++', fn, frame.f_lineno, name, ':', line.strip()
550  def user_return(self, frame, retval):
551  print '+++ return', retval
552  def user_exception(self, frame, exc_stuff):
553  print '+++ exception', exc_stuff
554  self.set_continue()
555 
556 def foo(n):
557  print 'foo(', n, ')'
558  x = bar(n*10)
559  print 'bar returned', x
560 
561 def bar(a):
562  print 'bar(', a, ')'
563  return a/2
564 
565 def test():
566  t = Tdb()
567  t.run('import bdb; bdb.foo(10)')
568 
569 # end