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

Source Code for Module common.debugger

  1  # copyright 2003-2011 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 io 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 = os.path.expanduser("~/.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 # XXX try..except shouldn't be necessary 109 # read_history_file() can accept None 110 readline.read_history_file(self._histfile) 111 except IOError: 112 pass
113
114 - def start(self):
115 """starts the interactive mode""" 116 self.interaction(self._tcbk.tb_frame, self._tcbk)
117
118 - def setup(self, frame, tcbk):
119 """setup hook: set up history file""" 120 self.setup_history_file() 121 Pdb.setup(self, frame, tcbk)
122
123 - def set_quit(self):
124 """quit hook: save commands in the history file""" 125 if readline is not None: 126 readline.write_history_file(self._histfile) 127 Pdb.set_quit(self)
128
129 - def complete_p(self, text, line, begin_idx, end_idx):
130 """provide variable names completion for the ``p`` command""" 131 namespace = dict(self.curframe.f_globals) 132 namespace.update(self.curframe.f_locals) 133 if '.' in text: 134 return self.attr_matches(text, namespace) 135 return [varname for varname in namespace if varname.startswith(text)]
136 137
138 - def attr_matches(self, text, namespace):
139 """implementation coming from rlcompleter.Completer.attr_matches 140 Compute matches when text contains a dot. 141 142 Assuming the text is of the form NAME.NAME....[NAME], and is 143 evaluatable in self.namespace, it will be evaluated and its attributes 144 (as revealed by dir()) are used as possible completions. (For class 145 instances, class members are also considered.) 146 147 WARNING: this can still invoke arbitrary C code, if an object 148 with a __getattr__ hook is evaluated. 149 150 """ 151 import re 152 m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) 153 if not m: 154 return 155 expr, attr = m.group(1, 3) 156 object = eval(expr, namespace) 157 words = dir(object) 158 if hasattr(object, '__class__'): 159 words.append('__class__') 160 words = words + self.get_class_members(object.__class__) 161 matches = [] 162 n = len(attr) 163 for word in words: 164 if word[:n] == attr and word != "__builtins__": 165 matches.append("%s.%s" % (expr, word)) 166 return matches
167
168 - def get_class_members(self, klass):
169 """implementation coming from rlcompleter.get_class_members""" 170 ret = dir(klass) 171 if hasattr(klass, '__bases__'): 172 for base in klass.__bases__: 173 ret = ret + self.get_class_members(base) 174 return ret
175 176 ## specific / overridden commands
177 - def do_list(self, arg):
178 """overrides default list command to display the surrounding block 179 instead of 5 lines of context 180 """ 181 self.lastcmd = 'list' 182 if not arg: 183 try: 184 source, start_lineno = getsource(self.curframe) 185 print(colorize(''.join(source), start_lineno, 186 self.curframe.f_lineno)) 187 except KeyboardInterrupt: 188 pass 189 except IOError: 190 Pdb.do_list(self, arg) 191 else: 192 Pdb.do_list(self, arg)
193 do_l = do_list 194
195 - def do_open(self, arg):
196 """opens source file corresponding to the current stack level""" 197 filename = self.curframe.f_code.co_filename 198 lineno = self.curframe.f_lineno 199 cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename) 200 os.system(cmd)
201 202 do_o = do_open
203
204 -def pm():
205 """use our custom debugger""" 206 dbg = Debugger(sys.last_traceback) 207 dbg.start()
208
209 -def set_trace():
210 Debugger().set_trace(sys._getframe().f_back)
211