summaryrefslogtreecommitdiff
path: root/flake8/__init__.py
blob: 2f5574e6e17661827de386ffa49ead5dab66496d (plain)
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

"""
Implementation of the command-line I{flake8} tool.
"""
import re
import sys
import os
import _ast
import pep8

checker = __import__('flake8.checker').checker


def check(codeString, filename):
    """
    Check the Python source given by C{codeString} for flakes.

    @param codeString: The Python source to check.
    @type codeString: C{str}

    @param filename: The name of the file the source came from, used to report
        errors.
    @type filename: C{str}

    @return: The number of warnings emitted.
    @rtype: C{int}
    """
    # First, compile into an AST and handle syntax errors.
    try:
        tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
    except SyntaxError, value:
        msg = value.args[0]

        (lineno, offset, text) = value.lineno, value.offset, value.text

        # If there's an encoding problem with the file, the text is None.
        if text is None:
            # Avoid using msg, since for the only known case, it contains a
            # bogus message that claims the encoding the file declared was
            # unknown.
            print >> sys.stderr, "%s: problem decoding source" % (filename, )
        else:
            line = text.splitlines()[-1]

            if offset is not None:
                offset = offset - (len(text) - len(line))

            print >> sys.stderr, '%s:%d: %s' % (filename, lineno, msg)
            print >> sys.stderr, line

            if offset is not None:
                print >> sys.stderr, " " * offset, "^"

        return 1
    else:
        # Okay, it's syntactically valid.  Now check it.
        w = checker.Checker(tree, filename)
        w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
        valid_warnings = 0

        for warning in w.messages:
            if _noqa(warning):
                continue
            print warning
            valid_warnings += 1

        return valid_warnings


def _noqa(warning):
    # XXX quick dirty hack, just need to keep the line in the warning
    line = open(warning.filename).readlines()[warning.lineno - 1]
    return line.strip().lower().endswith('# noqa')


_NOQA = re.compile(r'^# flake8: noqa', re.I | re.M)


def skip_file(path):
    """Returns True if this header is found in path

    # -*- flake8: noqa -*-
    """
    f = open(path)
    try:
        content = f.read()
    finally:
        f.close()
    return _NOQA.match(content) is not None


def checkPath(filename):
    """
    Check the given path, printing out any warnings detected.

    @return: the number of warnings printed
    """
    try:
        return check(file(filename, 'U').read() + '\n', filename)
    except IOError, msg:
        print >> sys.stderr, "%s: %s" % (filename, msg.args[1])
        return 1


def main():
    pep8.process_options()

    warnings = 0
    args = sys.argv[1:]
    if args:
        for arg in args:
            if os.path.isdir(arg):
                for dirpath, dirnames, filenames in os.walk(arg):
                    for filename in filenames:
                        if not filename.endswith('.py'):
                            continue
                        fullpath = os.path.join(dirpath, filename)
                        if skip_file(fullpath):
                            continue
                        warnings += checkPath(fullpath)
                        warnings += pep8.input_file(fullpath)
            else:
                if skip_file(filename):
                    continue
                warnings += checkPath(arg)
                warnings += pep8.input_file(arg)

    else:
        stdin = sys.stdin.read()
        warnings += check(stdin, '<stdin>')

    raise SystemExit(warnings > 0)


def hg_hook(ui, repo, **kwargs):
    pep8.process_options()
    warnings = 0
    files = []
    for rev in xrange(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            if not file_.endswith('.py'):
                continue
            if skip_file(file_):
                continue
            if file_ not in files:
                files.append(file_)

    for file_ in files:
        warnings += checkPath(file_)
        warnings += pep8.input_file(file_)

    strict = ui.config('flake8', 'strict')
    if strict is None:
        strict = True

    if strict.lower() in ('1', 'true'):
        return warnings

    return 0