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

Source Code for Module logilab.common.debugger

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