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

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