Package logilab :: Package common :: Module debugger
[frames] | no frames]

Source Code for Module logilab.common.debugger

  1  """Customized version of pdb's default debugger. 
  2   
  3  - sets up a history file 
  4  - uses ipython if available to colorize lines of code 
  5  - overrides list command to search for current block instead 
  6    of using 5 lines of context 
  7   
  8  :copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  9  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr 
 10  :license: General Public License version 2 - http://www.gnu.org/licenses 
 11  """ 
 12  __docformat__ = "restructuredtext en" 
 13   
 14  try: 
 15      import readline 
 16  except ImportError: 
 17      readline = None 
 18  import os 
 19  import os.path as osp 
 20  import sys 
 21  from pdb import Pdb 
 22  from cStringIO import StringIO 
 23  import inspect 
 24   
 25  try: 
 26      from IPython import PyColorize 
 27  except ImportError: 
28 - def colorize(source, *args):
29 """fallback colorize function""" 30 return source
31 - def colorize_source(source, *args):
32 return source
33 else:
34 - def colorize(source, start_lineno, curlineno):
35 """colorize and annotate source with linenos 36 (as in pdb's list command) 37 """ 38 parser = PyColorize.Parser() 39 output = StringIO() 40 parser.format(source, output) 41 annotated = [] 42 for index, line in enumerate(output.getvalue().splitlines()): 43 lineno = index + start_lineno 44 if lineno == curlineno: 45 annotated.append('%4s\t->\t%s' % (lineno, line)) 46 else: 47 annotated.append('%4s\t\t%s' % (lineno, line)) 48 return '\n'.join(annotated)
49
50 - def colorize_source(source):
51 """colorize given source""" 52 parser = PyColorize.Parser() 53 output = StringIO() 54 parser.format(source, output) 55 return output.getvalue()
56 57
58 -def getsource(obj):
59 """Return the text of the source code for an object. 60 61 The argument may be a module, class, method, function, traceback, frame, 62 or code object. The source code is returned as a single string. An 63 IOError is raised if the source code cannot be retrieved.""" 64 lines, lnum = inspect.getsourcelines(obj) 65 return ''.join(lines), lnum
66 67 68 ################################################################
69 -class Debugger(Pdb):
70 """custom debugger 71 72 - sets up a history file 73 - uses ipython if available to colorize lines of code 74 - overrides list command to search for current block instead 75 of using 5 lines of context 76 """
77 - def __init__(self, tcbk=None):
78 Pdb.__init__(self) 79 self.reset() 80 if tcbk: 81 while tcbk.tb_next is not None: 82 tcbk = tcbk.tb_next 83 self._tcbk = tcbk 84 self._histfile = osp.join(os.environ["HOME"], ".pdbhist")
85
86 - def setup_history_file(self):
87 """if readline is available, read pdb history file 88 """ 89 if readline is not None: 90 try: 91 readline.read_history_file(self._histfile) 92 except IOError: 93 pass
94
95 - def start(self):
96 """starts the interactive mode""" 97 self.interaction(self._tcbk.tb_frame, self._tcbk)
98
99 - def setup(self, frame, tcbk):
100 """setup hook: set up history file""" 101 self.setup_history_file() 102 Pdb.setup(self, frame, tcbk)
103
104 - def set_quit(self):
105 """quit hook: save commands in the history file""" 106 if readline is not None: 107 readline.write_history_file(self._histfile) 108 Pdb.set_quit(self)
109
110 - def complete_p(self, text, line, begin_idx, end_idx):
111 """provide variable names completion for the ``p`` command""" 112 namespace = dict(self.curframe.f_globals) 113 namespace.update(self.curframe.f_locals) 114 if '.' in text: 115 return self.attr_matches(text, namespace) 116 return [varname for varname in namespace if varname.startswith(text)]
117 118
119 - def attr_matches(self, text, namespace):
120 """implementation coming from rlcompleter.Completer.attr_matches 121 Compute matches when text contains a dot. 122 123 Assuming the text is of the form NAME.NAME....[NAME], and is 124 evaluatable in self.namespace, it will be evaluated and its attributes 125 (as revealed by dir()) are used as possible completions. (For class 126 instances, class members are also considered.) 127 128 WARNING: this can still invoke arbitrary C code, if an object 129 with a __getattr__ hook is evaluated. 130 131 """ 132 import re 133 m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) 134 if not m: 135 return 136 expr, attr = m.group(1, 3) 137 object = eval(expr, namespace) 138 words = dir(object) 139 if hasattr(object,'__class__'): 140 words.append('__class__') 141 words = words + self.get_class_members(object.__class__) 142 matches = [] 143 n = len(attr) 144 for word in words: 145 if word[:n] == attr and word != "__builtins__": 146 matches.append("%s.%s" % (expr, word)) 147 return matches
148
149 - def get_class_members(self, klass):
150 """implementation coming from rlcompleter.get_class_members""" 151 ret = dir(klass) 152 if hasattr(klass,'__bases__'): 153 for base in klass.__bases__: 154 ret = ret + self.get_class_members(base) 155 return ret
156 157 ## specific / overridden commands
158 - def do_list(self, arg):
159 """overrides default list command to display the surrounding block 160 instead of 5 lines of context 161 """ 162 self.lastcmd = 'list' 163 if not arg: 164 try: 165 source, start_lineno = getsource(self.curframe) 166 print colorize(''.join(source), start_lineno, 167 self.curframe.f_lineno) 168 except KeyboardInterrupt: 169 pass 170 except IOError: 171 Pdb.do_list(self, arg) 172 else: 173 Pdb.do_list(self, arg)
174 do_l = do_list 175
176 - def do_open(self, arg):
177 """opens source file corresponding to the current stack level""" 178 filename = self.curframe.f_code.co_filename 179 lineno = self.curframe.f_lineno 180 cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename) 181 os.system(cmd)
182 183 do_o = do_open
184 185
186 -def pm():
187 """use our custom debugger""" 188 dbg = Debugger(sys.last_traceback) 189 dbg.start()
190
191 -def set_trace():
192 Debugger().set_trace(sys._getframe().f_back)
193