1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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:
46 """fallback colorize function"""
47 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
68 """colorize given source"""
69 parser = PyColorize.Parser()
70 output = StringIO()
71 parser.format(source, output)
72 return output.getvalue()
73
74
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
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 """
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
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
113 """starts the interactive mode"""
114 self.interaction(self._tcbk.tb_frame, self._tcbk)
115
116 - def setup(self, frame, tcbk):
120
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
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
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
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
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
203 """use our custom debugger"""
204 dbg = Debugger(sys.last_traceback)
205 dbg.start()
206
209