Package logilab-common-0 :: Package 39 :: Package 0 :: Module debugger
[frames] | no frames]

Source Code for Module logilab-common-0.39.0.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):
78 Pdb.__init__(self) 79 self.reset() 80 while tcbk.tb_next is not None: 81 tcbk = tcbk.tb_next 82 self._tcbk = tcbk 83 self._histfile = osp.join(os.environ["HOME"], ".pdbhist")
84
85 - def setup_history_file(self):
86 """if readline is available, read pdb history file 87 """ 88 if readline is not None: 89 try: 90 readline.read_history_file(self._histfile) 91 except IOError: 92 pass
93
94 - def start(self):
95 """starts the interactive mode""" 96 self.interaction(self._tcbk.tb_frame, self._tcbk)
97
98 - def setup(self, frame, tcbk):
99 """setup hook: set up history file""" 100 self.setup_history_file() 101 Pdb.setup(self, frame, tcbk)
102
103 - def set_quit(self):
104 """quit hook: save commands in the history file""" 105 if readline is not None: 106 readline.write_history_file(self._histfile) 107 Pdb.set_quit(self)
108
109 - def complete_p(self, text, line, begin_idx, end_idx):
110 """provide variable names completion for the ``p`` command""" 111 namespace = dict(self.curframe.f_globals) 112 namespace.update(self.curframe.f_locals) 113 if '.' in text: 114 return self.attr_matches(text, namespace) 115 return [varname for varname in namespace if varname.startswith(text)]
116 117
118 - def attr_matches(self, text, namespace):
119 """implementation coming from rlcompleter.Completer.attr_matches 120 Compute matches when text contains a dot. 121 122 Assuming the text is of the form NAME.NAME....[NAME], and is 123 evaluatable in self.namespace, it will be evaluated and its attributes 124 (as revealed by dir()) are used as possible completions. (For class 125 instances, class members are also considered.) 126 127 WARNING: this can still invoke arbitrary C code, if an object 128 with a __getattr__ hook is evaluated. 129 130 """ 131 import re 132 m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) 133 if not m: 134 return 135 expr, attr = m.group(1, 3) 136 object = eval(expr, namespace) 137 words = dir(object) 138 if hasattr(object,'__class__'): 139 words.append('__class__') 140 words = words + self.get_class_members(object.__class__) 141 matches = [] 142 n = len(attr) 143 for word in words: 144 if word[:n] == attr and word != "__builtins__": 145 matches.append("%s.%s" % (expr, word)) 146 return matches
147
148 - def get_class_members(self, klass):
149 """implementation coming from rlcompleter.get_class_members""" 150 ret = dir(klass) 151 if hasattr(klass,'__bases__'): 152 for base in klass.__bases__: 153 ret = ret + self.get_class_members(base) 154 return ret
155 156 ## specific / overidden commands
157 - def do_list(self, arg):
158 """overrides default list command to display the surrounding block 159 instead of 5 lines of context 160 """ 161 self.lastcmd = 'list' 162 if not arg: 163 try: 164 source, start_lineno = getsource(self.curframe) 165 print colorize(''.join(source), start_lineno, 166 self.curframe.f_lineno) 167 except KeyboardInterrupt: 168 pass 169 except IOError: 170 Pdb.do_list(self, arg) 171 else: 172 Pdb.do_list(self, arg)
173 do_l = do_list 174
175 - def do_open(self, arg):
176 """opens source file corresponding to the current stack level""" 177 filename = self.curframe.f_code.co_filename 178 lineno = self.curframe.f_lineno 179 cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename) 180 os.system(cmd)
181 182 do_o = do_open
183 184
185 -def pm():
186 """use our custom debugger""" 187 dbg = Debugger(sys.last_traceback) 188 dbg.start()
189