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 io 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 = os.path.expanduser("~/.pdbhist")
102
104 """if readline is available, read pdb history file
105 """
106 if readline is not None:
107 try:
108
109
110 readline.read_history_file(self._histfile)
111 except IOError:
112 pass
113
115 """starts the interactive mode"""
116 self.interaction(self._tcbk.tb_frame, self._tcbk)
117
118 - def setup(self, frame, tcbk):
122
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
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
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
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
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
205 """use our custom debugger"""
206 dbg = Debugger(sys.last_traceback)
207 dbg.start()
208
211