~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/python
# Copyright 2008 Canonical Ltd.  All rights reserved.
"""Perform pyflakes checks on doctests."""

import compiler
import doctest
import operator
import os
import sys

import pyflakes
# XXX sinzui 2008-04-03:
# pyflakes broke its API. We should be using pyflakes.checker.Checker,
# but while we are transitioning to Hardy, we will preserve the old
# behaviour.
try:
    from pyflakes.checker import Checker
except ImportError:
    Checker = pyflakes.Checker


# Names we define in the globals for our doctests
GLOBAL_NAMES = set([
    # for system documentation
    'ANONYMOUS',
    'ILaunchBag',
    'commit',
    'create_view',
    'flush_database_updates',
    'getUtility',
    'login',
    'logout',
    'transaction',
    # for page tests
    'admin_browser',
    'anon_browser',
    'browser',
    'extract_link_from_tag',
    'extract_text',
    'find_main_content',
    'find_portlet',
    'find_tag_by_id',
    'find_tags_by_class',
    'first_tag_by_class',
    'get_feedback_messages',
    'http',
    'mailinglist_api',
    'parse_relationship_section',
    'print_action_links',
    'print_batch_header',
    'print_comments',
    'print_portlet_links',
    'print_ppa_packages',
    'print_radio_button_field',
    'print_submit_buttons',
    'print_tab_links',
    'setupBrowser',
    'user_browser',
    'webservice',
    # For OpenID per-version tests
    'PROTOCOL_URI',
    ])


def extract_script(data):
    """Process a doctest into an equivalent Python script.

    This code is based on doctest.script_from_examples() but has been
    modified not to insert or remove lines of content.  This should
    make line numbers in the output script match those in the input.

        >>> text = '''
        ...
        ... Some text
        ...     >>> 2 + 2
        ...     5
        ...
        ... More text
        ...
        ...     >>> if False:
        ...     ...     whatever
        ...
        ... end.
        ... '''

        >>> print extract_script(text)
        #
        # Some text
        2 + 2
        ## 5
        #
        # More text
        #
        if False:
            whatever
        #
        # end.
        <BLANKLINE>
    """
    output = []
    for piece in doctest.DocTestParser().parse(data):
        if isinstance(piece, doctest.Example):
            # Add the example's source code (strip trailing NL)
            output.append(piece.source[:-1])
            # Add the expected output:
            want = piece.want
            if want:
                output += ['## '+l for l in want.split('\n')[:-1]]
        else:
            # Add non-example text.
            output += [doctest._comment_line(l)
                       for l in piece.split('\n')[:-1]]
    # Combine the output, and return it.
    # Add a courtesy newline to prevent exec from choking
    return '\n'.join(output) + '\n'


def suppress_warning(warning):
    """Returns True if a particular warning should be supressed."""
    if isinstance(warning, pyflakes.messages.UndefinedName):
        # Suppress warnings due to names that are defined as globals.
        if warning.message_args[0] in GLOBAL_NAMES:
            return True
    return False


def check_doctest(filename):
    """Create a PyFlakes object from a doctest."""
    data = open(filename, 'r').read()
    script = extract_script(data)

    try:
        tree = compiler.parse(script)
    except (SyntaxError, IndentationError), exc:
        (lineno, offset, line) = exc[1][1:]
        if line.endswith("\n"):
            line = line[:-1]
        print >> sys.stderr, 'could not compile %r:%d' % (filename, lineno)
        print >> sys.stderr, line
        print >> sys.stderr, " " * (offset-1), "^"
    else:
        w = Checker(tree, filename)
        for warning in sorted(w.messages, key=operator.attrgetter('lineno')):
            if suppress_warning(warning):
                continue
            print warning


def main(argv):
    """Check the files passed on the command line."""
    for arg in argv[1:]:
        if os.path.isdir(arg):
            for dirpath, dirnames, filenames in os.walk(arg):
                for filename in filenames:
                    if filename.endswith('.txt'):
                        check_doctest(os.path.join(dirpath, filename))
        else:
            check_doctest(arg)


if __name__ == '__main__':
    sys.exit(main(sys.argv))