Package logilab-common-0 ::
Package 39 ::
Package 0 ::
Module 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:
29 """fallback colorize function"""
30 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
51 """colorize given source"""
52 parser = PyColorize.Parser()
53 output = StringIO()
54 parser.format(source, output)
55 return output.getvalue()
56
57
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
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 """
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
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
95 """starts the interactive mode"""
96 self.interaction(self._tcbk.tb_frame, self._tcbk)
97
98 - def setup(self, frame, tcbk):
102
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
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
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
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
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
186 """use our custom debugger"""
187 dbg = Debugger(sys.last_traceback)
188 dbg.start()
189