root / logilab.pylintinstaller / logilab / common / debugger.py

Revision 202:d67e86292521, 5.5 kB (checked in by tziade@…, 9 months ago)

added logilab.pylintinstaller

Line 
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
9try:
10    import readline
11except ImportError:
12    readline = None
13import os
14import os.path as osp
15import sys
16from pdb import Pdb
17from cStringIO import StringIO
18import inspect
19
20try:
21    from IPython import PyColorize
22except ImportError:
23    def colorize(source, *args):
24        """fallback colorize function"""
25        return source
26else:
27    def colorize(source, start_lineno, curlineno):
28        """"""
29        parser = PyColorize.Parser()
30        output = StringIO()
31        parser.format(source, output)
32        annotated = []
33        for index, line in enumerate(output.getvalue().splitlines()):
34            lineno = index + start_lineno
35            if lineno == curlineno:
36                annotated.append('%4s\t->\t%s' % (lineno, line))
37            else:
38                annotated.append('%4s\t\t%s' % (lineno, line))               
39        return '\n'.join(annotated)
40
41def getsource(obj):
42    """Return the text of the source code for an object.
43
44    The argument may be a module, class, method, function, traceback, frame,
45    or code object.  The source code is returned as a single string.  An
46    IOError is raised if the source code cannot be retrieved."""
47    lines, lnum = inspect.getsourcelines(obj)
48    return ''.join(lines), lnum
49
50
51################################################################
52class Debugger(Pdb):
53    """custom debugger
54   
55    - sets up a history file
56    - uses ipython if available to colorize lines of code
57    - overrides list command to search for current block instead
58      of using 5 lines of context
59    """
60    def __init__(self, tcbk):
61        Pdb.__init__(self)
62        self.reset()
63        while tcbk.tb_next is not None:
64            tcbk = tcbk.tb_next
65        self._tcbk = tcbk
66        self._histfile = osp.join(os.environ["HOME"], ".pdbhist")
67       
68    def setup_history_file(self):
69        """if readline is available, read pdb history file
70        """
71        if readline is not None:
72            try:
73                readline.read_history_file(self._histfile)
74            except IOError:
75                pass
76
77    def start(self):
78        """starts the interactive mode"""
79        self.interaction(self._tcbk.tb_frame, self._tcbk)
80
81    def setup(self, frame, tcbk):
82        """setup hook: set up history file"""
83        self.setup_history_file()
84        Pdb.setup(self, frame, tcbk)
85
86    def set_quit(self):
87        """quit hook: save commands in the history file"""
88        if readline is not None:
89            readline.write_history_file(self._histfile)
90        Pdb.set_quit(self)
91
92    def complete_p(self, text, line, begin_idx, end_idx):
93        """provide variable names completion for the ``p`` command"""
94        namespace = dict(self.curframe.f_globals)
95        namespace.update(self.curframe.f_locals)
96        if '.' in text:
97            return self.attr_matches(text, namespace)
98        return [varname for varname in namespace if varname.startswith(text)]
99
100
101    def attr_matches(self, text, namespace):
102        """implementation coming from rlcompleter.Completer.attr_matches
103        Compute matches when text contains a dot.
104
105        Assuming the text is of the form NAME.NAME....[NAME], and is
106        evaluatable in self.namespace, it will be evaluated and its attributes
107        (as revealed by dir()) are used as possible completions.  (For class
108        instances, class members are also considered.)
109
110        WARNING: this can still invoke arbitrary C code, if an object
111        with a __getattr__ hook is evaluated.
112
113        """
114        import re
115        m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
116        if not m:
117            return
118        expr, attr = m.group(1, 3)
119        object = eval(expr, namespace)
120        words = dir(object)
121        if hasattr(object,'__class__'):
122            words.append('__class__')
123            words = words + self.get_class_members(object.__class__)
124        matches = []
125        n = len(attr)
126        for word in words:
127            if word[:n] == attr and word != "__builtins__":
128                matches.append("%s.%s" % (expr, word))
129        return matches
130   
131    def get_class_members(self, klass):
132        """implementation coming from rlcompleter.get_class_members"""
133        ret = dir(klass)
134        if hasattr(klass,'__bases__'):
135            for base in klass.__bases__:
136                ret = ret + self.get_class_members(base)
137        return ret
138       
139    ## specific / overidden commands
140    def do_list(self, arg):
141        """overrides default list command to display the surrounding block
142        instead of 5 lines of context
143        """
144        self.lastcmd = 'list'
145        if not arg:
146            try:
147                source, start_lineno = getsource(self.curframe)
148                print colorize(''.join(source), start_lineno,
149                               self.curframe.f_lineno)
150            except KeyboardInterrupt:
151                pass
152            except IOError:
153                Pdb.do_list(self, arg)
154        else:
155            Pdb.do_list(self, arg)
156    do_l = do_list
157
158    def do_open(self, arg):
159        """opens source file corresponding to the current stack level"""
160        filename = self.curframe.f_code.co_filename
161        lineno = self.curframe.f_lineno
162        cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename)
163        os.system(cmd)
164       
165    do_o = do_open
166
167
168def pm():
169    """use our custom debugger"""
170    dbg = Debugger(sys.last_traceback)
171    dbg.start()
Note: See TracBrowser for help on using the browser.