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