root/galaxy-central/eggs/nose-0.11.1-py2.6.egg/nose/inspector.py

リビジョン 3, 6.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""Simple traceback introspection. Used to add additional information to
2AssertionErrors in tests, so that failure messages may be more informative.
3"""
4import inspect
5import logging
6import re
7import sys
8import textwrap
9import tokenize
10
11try:
12    from cStringIO import StringIO
13except ImportError:
14    from StringIO import StringIO
15
16log = logging.getLogger(__name__)
17
18def inspect_traceback(tb):
19    """Inspect a traceback and its frame, returning source for the expression
20    where the exception was raised, with simple variable replacement performed
21    and the line on which the exception was raised marked with '>>'
22    """
23    log.debug('inspect traceback %s', tb)
24
25    # we only want the innermost frame, where the exception was raised
26    while tb.tb_next:
27        tb = tb.tb_next
28       
29    frame = tb.tb_frame
30    lines, exc_line = tbsource(tb)
31       
32    # figure out the set of lines to grab.
33    inspect_lines, mark_line = find_inspectable_lines(lines, exc_line)
34    src = StringIO(textwrap.dedent(''.join(inspect_lines)))
35    exp = Expander(frame.f_locals, frame.f_globals)
36
37    while inspect_lines:
38        try:
39            tokenize.tokenize(src.readline, exp)
40        except tokenize.TokenError, e:
41            # this can happen if our inspectable region happens to butt up
42            # against the end of a construct like a docstring with the closing
43            # """ on separate line
44            log.debug("Tokenizer error: %s", e)
45            inspect_lines.pop(0)
46            mark_line -= 1
47            src = StringIO(textwrap.dedent(''.join(inspect_lines)))
48            exp = Expander(frame.f_locals, frame.f_globals)
49            continue
50        break
51    padded = []
52    if exp.expanded_source:
53        exp_lines = exp.expanded_source.split('\n')
54        ep = 0
55        for line in exp_lines:
56            if ep == mark_line:
57                padded.append('>>  ' + line)
58            else:
59                padded.append('    ' + line)
60            ep += 1
61    return '\n'.join(padded)
62
63
64def tbsource(tb, context=6):
65    """Get source from  a traceback object.
66
67    A tuple of two things is returned: a list of lines of context from
68    the source code, and the index of the current line within that list.
69    The optional second argument specifies the number of lines of context
70    to return, which are centered around the current line.
71
72    .. Note ::
73       This is adapted from inspect.py in the python 2.4 standard library,
74       since a bug in the 2.3 version of inspect prevents it from correctly
75       locating source lines in a traceback frame.
76    """
77   
78    lineno = tb.tb_lineno
79    frame = tb.tb_frame
80
81    if context > 0:
82        start = lineno - 1 - context//2
83        log.debug("lineno: %s start: %s", lineno, start)
84       
85        try:
86            lines, dummy = inspect.findsource(frame)
87        except IOError:
88            lines, index = [''], 0
89        else:
90            all_lines = lines
91            start = max(start, 1)
92            start = max(0, min(start, len(lines) - context))
93            lines = lines[start:start+context]
94            index = lineno - 1 - start
95           
96            # python 2.5 compat: if previous line ends in a continuation,
97            # decrement start by 1 to match 2.4 behavior               
98            if sys.version_info >= (2, 5) and index > 0:
99                while lines[index-1].strip().endswith('\\'):
100                    start -= 1
101                    lines = all_lines[start:start+context]
102    else:
103        lines, index = [''], 0
104    log.debug("tbsource lines '''%s''' around index %s", lines, index)
105    return (lines, index)   
106
107   
108def find_inspectable_lines(lines, pos):
109    """Find lines in home that are inspectable.
110   
111    Walk back from the err line up to 3 lines, but don't walk back over
112    changes in indent level.
113
114    Walk forward up to 3 lines, counting \ separated lines as 1. Don't walk
115    over changes in indent level (unless part of an extended line)
116    """
117    cnt = re.compile(r'\\[\s\n]*$')
118    df = re.compile(r':[\s\n]*$')
119    ind = re.compile(r'^(\s*)')
120    toinspect = []
121    home = lines[pos]
122    home_indent = ind.match(home).groups()[0]
123   
124    before = lines[max(pos-3, 0):pos]
125    before.reverse()
126    after = lines[pos+1:min(pos+4, len(lines))]
127
128    for line in before:
129        if ind.match(line).groups()[0] == home_indent:
130            toinspect.append(line)
131        else:
132            break
133    toinspect.reverse()
134    toinspect.append(home)
135    home_pos = len(toinspect)-1
136    continued = cnt.search(home)
137    for line in after:
138        if ((continued or ind.match(line).groups()[0] == home_indent)
139            and not df.search(line)):
140            toinspect.append(line)
141            continued = cnt.search(line)
142        else:
143            break
144    log.debug("Inspecting lines '''%s''' around %s", toinspect, home_pos)
145    return toinspect, home_pos
146
147
148class Expander:
149    """Simple expression expander. Uses tokenize to find the names and
150    expands any that can be looked up in the frame.
151    """
152    def __init__(self, locals, globals):
153        self.locals = locals
154        self.globals = globals
155        self.lpos = None
156        self.expanded_source = ''
157         
158    def __call__(self, ttype, tok, start, end, line):
159        # TODO
160        # deal with unicode properly
161       
162        # TODO
163        # Dealing with instance members
164        #   always keep the last thing seen 
165        #   if the current token is a dot,
166        #      get ready to getattr(lastthing, this thing) on the
167        #      next call.
168       
169        if self.lpos is not None and start[1] >= self.lpos:
170            self.expanded_source += ' ' * (start[1]-self.lpos)
171        elif start[1] < self.lpos:
172            # newline, indent correctly
173            self.expanded_source += ' ' * start[1]
174        self.lpos = end[1]
175     
176        if ttype == tokenize.INDENT:
177            pass
178        elif ttype == tokenize.NAME:
179            # Clean this junk up
180            try:
181                val = self.locals[tok]
182                if callable(val):
183                    val = tok
184                else:
185                    val = repr(val)
186            except KeyError:
187                try:
188                    val = self.globals[tok]
189                    if callable(val):
190                        val = tok
191                    else:
192                        val = repr(val)
193
194                except KeyError:
195                    val = tok
196            # FIXME... not sure how to handle things like funcs, classes
197            # FIXME this is broken for some unicode strings
198            self.expanded_source += val
199        else:
200            self.expanded_source += tok
201        # if this is the end of the line and the line ends with
202        # \, then tack a \ and newline onto the output
203        # print line[end[1]:]
204        if re.match(r'\s+\\\n', line[end[1]:]):
205            self.expanded_source += ' \\\n'
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。