Package logilab-common-0 ::
Package 36 ::
Package 1 ::
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
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
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
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 """
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
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
83 """starts the interactive mode"""
84 self.interaction(self._tcbk.tb_frame, self._tcbk)
85
86 - def setup(self, frame, tcbk):
90
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
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
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
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
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
174 """use our custom debugger"""
175 dbg = Debugger(sys.last_traceback)
176 dbg.start()
177