Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2049)

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/pep8.py

Issue 546613003: Add a new 'format-webkitpy' command that will reformat code to the style guide. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: remove hack from test/main.py Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Tools/Scripts/webkitpy/thirdparty/autopep8.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/env python
2 # pep8.py - Check Python source code formatting, according to PEP 8 2 # pep8.py - Check Python source code formatting, according to PEP 8
3 # Copyright (C) 2006 Johann C. Rocholl <johann@rocholl.net> 3 # Copyright (C) 2006-2009 Johann C. Rocholl <johann@rocholl.net>
4 # Copyright (C) 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
4 # 5 #
5 # Permission is hereby granted, free of charge, to any person 6 # Permission is hereby granted, free of charge, to any person
6 # obtaining a copy of this software and associated documentation files 7 # obtaining a copy of this software and associated documentation files
7 # (the "Software"), to deal in the Software without restriction, 8 # (the "Software"), to deal in the Software without restriction,
8 # including without limitation the rights to use, copy, modify, merge, 9 # including without limitation the rights to use, copy, modify, merge,
9 # publish, distribute, sublicense, and/or sell copies of the Software, 10 # publish, distribute, sublicense, and/or sell copies of the Software,
10 # and to permit persons to whom the Software is furnished to do so, 11 # and to permit persons to whom the Software is furnished to do so,
11 # subject to the following conditions: 12 # subject to the following conditions:
12 # 13 #
13 # The above copyright notice and this permission notice shall be 14 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software. 15 # included in all copies or substantial portions of the Software.
15 # 16 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 # SOFTWARE. 24 # SOFTWARE.
24 25
25 """ 26 r"""
26 Check Python source code formatting, according to PEP 8: 27 Check Python source code formatting, according to PEP 8.
27 http://www.python.org/dev/peps/pep-0008/
28 28
29 For usage and a list of options, try this: 29 For usage and a list of options, try this:
30 $ python pep8.py -h 30 $ python pep8.py -h
31 31
32 This program and its regression test suite live here: 32 This program and its regression test suite live here:
33 http://github.com/jcrocholl/pep8 33 http://github.com/jcrocholl/pep8
34 34
35 Groups of errors and warnings: 35 Groups of errors and warnings:
36 E errors 36 E errors
37 W warnings 37 W warnings
38 100 indentation 38 100 indentation
39 200 whitespace 39 200 whitespace
40 300 blank lines 40 300 blank lines
41 400 imports 41 400 imports
42 500 line length 42 500 line length
43 600 deprecation 43 600 deprecation
44 700 statements 44 700 statements
45 900 syntax error
46 """
47 from __future__ import with_statement
45 48
46 You can add checks to this program by writing plugins. Each plugin is 49 __version__ = '1.5.7'
47 a simple function that is called for each line of source code, either
48 physical or logical.
49
50 Physical line:
51 - Raw line of text from the input file.
52
53 Logical line:
54 - Multi-line statements converted to a single line.
55 - Stripped left and right.
56 - Contents of strings replaced with 'xxx' of same length.
57 - Comments removed.
58
59 The check function requests physical or logical lines by the name of
60 the first argument:
61
62 def maximum_line_length(physical_line)
63 def extraneous_whitespace(logical_line)
64 def blank_lines(logical_line, blank_lines, indent_level, line_number)
65
66 The last example above demonstrates how check plugins can request
67 additional information with extra arguments. All attributes of the
68 Checker object are available. Some examples:
69
70 lines: a list of the raw lines from the input file
71 tokens: the tokens that contribute to this logical line
72 line_number: line number in the input file
73 blank_lines: blank lines before this one
74 indent_char: first indentation character in this file (' ' or '\t')
75 indent_level: indentation (with tabs expanded to multiples of 8)
76 previous_indent_level: indentation on previous line
77 previous_logical: previous logical line
78
79 The docstring of each check function shall be the relevant part of
80 text from PEP 8. It is printed if the user enables --show-pep8.
81 Several docstrings contain examples directly from the PEP 8 document.
82
83 Okay: spam(ham[1], {eggs: 2})
84 E201: spam( ham[1], {eggs: 2})
85
86 These examples are verified automatically when pep8.py is run with the
87 --doctest option. You can add examples for your own check functions.
88 The format is simple: "Okay" or error/warning code followed by colon
89 and space, the rest of the line is example source code. If you put 'r'
90 before the docstring, you can use \n for newline, \t for tab and \s
91 for space.
92
93 """
94
95 __version__ = '0.5.0'
96 50
97 import os 51 import os
98 import sys 52 import sys
99 import re 53 import re
100 import time 54 import time
101 import inspect 55 import inspect
56 import keyword
102 import tokenize 57 import tokenize
103 from optparse import OptionParser 58 from optparse import OptionParser
104 from keyword import iskeyword
105 from fnmatch import fnmatch 59 from fnmatch import fnmatch
60 try:
61 from configparser import RawConfigParser
62 from io import TextIOWrapper
63 except ImportError:
64 from ConfigParser import RawConfigParser
106 65
107 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' 66 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__'
108 DEFAULT_IGNORE = ['E24'] 67 DEFAULT_IGNORE = 'E123,E226,E24'
68 if sys.platform == 'win32':
69 DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8')
70 else:
71 DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or
72 os.path.expanduser('~/.config'), 'pep8')
73 PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8')
74 TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
75 MAX_LINE_LENGTH = 79
76 REPORT_FORMAT = {
77 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
78 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
79 }
80
81 PyCF_ONLY_AST = 1024
82 SINGLETONS = frozenset(['False', 'None', 'True'])
83 KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS
84 UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
85 ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-'])
86 WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%'])
87 WS_NEEDED_OPERATORS = frozenset([
88 '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>',
89 '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '='])
90 WHITESPACE = frozenset(' \t')
91 NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
92 SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT])
93 # ERRORTOKEN is triggered by backticks in Python 3
94 SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN])
95 BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines']
109 96
110 INDENT_REGEX = re.compile(r'([ \t]*)') 97 INDENT_REGEX = re.compile(r'([ \t]*)')
111 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)') 98 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
112 SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)') 99 RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
113 ERRORCODE_REGEX = re.compile(r'[EW]\d{3}') 100 ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
114 E301NOT_REGEX = re.compile(r'class |def |u?r?["\']') 101 DOCSTRING_REGEX = re.compile(r'u?r?["\']')
102 EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
103 WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
104 COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)')
105 COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^[({ ]+\s+(in|is)\s')
106 COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type'
107 r'|\s*\(\s*([^)]*[^ )])\s*\))')
108 KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS))
109 OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)')
110 LAMBDA_REGEX = re.compile(r'\blambda\b')
111 HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
115 112
116 WHITESPACE = ' \t' 113 # Work around Python < 2.6 behaviour, which does not generate NL after
117 114 # a comment which is on a line by itself.
118 BINARY_OPERATORS = ['**=', '*=', '+=', '-=', '!=', '<>', 115 COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n'
119 '%=', '^=', '&=', '|=', '==', '/=', '//=', '>=', '<=', '>>=', '<<=',
120 '%', '^', '&', '|', '=', '/', '//', '>', '<', '>>', '<<']
121 UNARY_OPERATORS = ['**', '*', '+', '-']
122 OPERATORS = BINARY_OPERATORS + UNARY_OPERATORS
123
124 options = None
125 args = None
126 116
127 117
128 ############################################################################## 118 ##############################################################################
129 # Plugins (check functions) for physical lines 119 # Plugins (check functions) for physical lines
130 ############################################################################## 120 ##############################################################################
131 121
132 122
133 def tabs_or_spaces(physical_line, indent_char): 123 def tabs_or_spaces(physical_line, indent_char):
134 r""" 124 r"""Never mix tabs and spaces.
135 Never mix tabs and spaces.
136 125
137 The most popular way of indenting Python is with spaces only. The 126 The most popular way of indenting Python is with spaces only. The
138 second-most popular way is with tabs only. Code indented with a mixture 127 second-most popular way is with tabs only. Code indented with a mixture
139 of tabs and spaces should be converted to using spaces exclusively. When 128 of tabs and spaces should be converted to using spaces exclusively. When
140 invoking the Python command line interpreter with the -t option, it issues 129 invoking the Python command line interpreter with the -t option, it issues
141 warnings about code that illegally mixes tabs and spaces. When using -tt 130 warnings about code that illegally mixes tabs and spaces. When using -tt
142 these warnings become errors. These options are highly recommended! 131 these warnings become errors. These options are highly recommended!
143 132
144 Okay: if a == 0:\n a = 1\n b = 1 133 Okay: if a == 0:\n a = 1\n b = 1
145 E101: if a == 0:\n a = 1\n\tb = 1 134 E101: if a == 0:\n a = 1\n\tb = 1
146 """ 135 """
147 indent = INDENT_REGEX.match(physical_line).group(1) 136 indent = INDENT_REGEX.match(physical_line).group(1)
148 for offset, char in enumerate(indent): 137 for offset, char in enumerate(indent):
149 if char != indent_char: 138 if char != indent_char:
150 return offset, "E101 indentation contains mixed spaces and tabs" 139 return offset, "E101 indentation contains mixed spaces and tabs"
151 140
152 141
153 def tabs_obsolete(physical_line): 142 def tabs_obsolete(physical_line):
154 r""" 143 r"""For new projects, spaces-only are strongly recommended over tabs.
155 For new projects, spaces-only are strongly recommended over tabs. Most
156 editors have features that make this easy to do.
157 144
158 Okay: if True:\n return 145 Okay: if True:\n return
159 W191: if True:\n\treturn 146 W191: if True:\n\treturn
160 """ 147 """
161 indent = INDENT_REGEX.match(physical_line).group(1) 148 indent = INDENT_REGEX.match(physical_line).group(1)
162 if indent.count('\t'): 149 if '\t' in indent:
163 return indent.index('\t'), "W191 indentation contains tabs" 150 return indent.index('\t'), "W191 indentation contains tabs"
164 151
165 152
166 def trailing_whitespace(physical_line): 153 def trailing_whitespace(physical_line):
167 """ 154 r"""Trailing whitespace is superfluous.
168 JCR: Trailing whitespace is superfluous.
169 155
170 Okay: spam(1) 156 The warning returned varies on whether the line itself is blank, for easier
171 W291: spam(1)\s 157 filtering for those who want to indent their blank lines.
158
159 Okay: spam(1)\n#
160 W291: spam(1) \n#
161 W293: class Foo(object):\n \n bang = 12
172 """ 162 """
173 physical_line = physical_line.rstrip('\n') # chr(10), newline 163 physical_line = physical_line.rstrip('\n') # chr(10), newline
174 physical_line = physical_line.rstrip('\r') # chr(13), carriage return 164 physical_line = physical_line.rstrip('\r') # chr(13), carriage return
175 physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L 165 physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L
176 stripped = physical_line.rstrip() 166 stripped = physical_line.rstrip(' \t\v')
177 if physical_line != stripped: 167 if physical_line != stripped:
178 return len(stripped), "W291 trailing whitespace" 168 if stripped:
169 return len(stripped), "W291 trailing whitespace"
170 else:
171 return 0, "W293 blank line contains whitespace"
179 172
180 173
181 def trailing_blank_lines(physical_line, lines, line_number): 174 def trailing_blank_lines(physical_line, lines, line_number, total_lines):
182 r""" 175 r"""Trailing blank lines are superfluous.
183 JCR: Trailing blank lines are superfluous.
184 176
185 Okay: spam(1) 177 Okay: spam(1)
186 W391: spam(1)\n 178 W391: spam(1)\n
179
180 However the last line should end with a new line (warning W292).
187 """ 181 """
188 if physical_line.strip() == '' and line_number == len(lines): 182 if line_number == total_lines:
189 return 0, "W391 blank line at end of file" 183 stripped_last_line = physical_line.rstrip()
184 if not stripped_last_line:
185 return 0, "W391 blank line at end of file"
186 if stripped_last_line == physical_line:
187 return len(physical_line), "W292 no newline at end of file"
190 188
191 189
192 def missing_newline(physical_line): 190 def maximum_line_length(physical_line, max_line_length, multiline):
193 """ 191 r"""Limit all lines to a maximum of 79 characters.
194 JCR: The last line should have a newline.
195 """
196 if physical_line.rstrip() == physical_line:
197 return len(physical_line), "W292 no newline at end of file"
198
199
200 def maximum_line_length(physical_line):
201 """
202 Limit all lines to a maximum of 79 characters.
203 192
204 There are still many devices around that are limited to 80 character 193 There are still many devices around that are limited to 80 character
205 lines; plus, limiting windows to 80 characters makes it possible to have 194 lines; plus, limiting windows to 80 characters makes it possible to have
206 several windows side-by-side. The default wrapping on such devices looks 195 several windows side-by-side. The default wrapping on such devices looks
207 ugly. Therefore, please limit all lines to a maximum of 79 characters. 196 ugly. Therefore, please limit all lines to a maximum of 79 characters.
208 For flowing long blocks of text (docstrings or comments), limiting the 197 For flowing long blocks of text (docstrings or comments), limiting the
209 length to 72 characters is recommended. 198 length to 72 characters is recommended.
199
200 Reports error E501.
210 """ 201 """
211 length = len(physical_line.rstrip()) 202 line = physical_line.rstrip()
212 if length > 79: 203 length = len(line)
213 return 79, "E501 line too long (%d characters)" % length 204 if length > max_line_length and not noqa(line):
205 # Special case for long URLs in multi-line docstrings or comments,
206 # but still report the error when the 72 first chars are whitespaces.
207 chunks = line.split()
208 if ((len(chunks) == 1 and multiline) or
209 (len(chunks) == 2 and chunks[0] == '#')) and \
210 len(line) - len(chunks[-1]) < max_line_length - 7:
211 return
212 if hasattr(line, 'decode'): # Python 2
213 # The line could contain multi-byte characters
214 try:
215 length = len(line.decode('utf-8'))
216 except UnicodeError:
217 pass
218 if length > max_line_length:
219 return (max_line_length, "E501 line too long "
220 "(%d > %d characters)" % (length, max_line_length))
214 221
215 222
216 ############################################################################## 223 ##############################################################################
217 # Plugins (check functions) for logical lines 224 # Plugins (check functions) for logical lines
218 ############################################################################## 225 ##############################################################################
219 226
220 227
221 def blank_lines(logical_line, blank_lines, indent_level, line_number, 228 def blank_lines(logical_line, blank_lines, indent_level, line_number,
222 previous_logical, blank_lines_before_comment): 229 blank_before, previous_logical, previous_indent_level):
223 r""" 230 r"""Separate top-level function and class definitions with two blank lines.
224 Separate top-level function and class definitions with two blank lines.
225 231
226 Method definitions inside a class are separated by a single blank line. 232 Method definitions inside a class are separated by a single blank line.
227 233
228 Extra blank lines may be used (sparingly) to separate groups of related 234 Extra blank lines may be used (sparingly) to separate groups of related
229 functions. Blank lines may be omitted between a bunch of related 235 functions. Blank lines may be omitted between a bunch of related
230 one-liners (e.g. a set of dummy implementations). 236 one-liners (e.g. a set of dummy implementations).
231 237
232 Use blank lines in functions, sparingly, to indicate logical sections. 238 Use blank lines in functions, sparingly, to indicate logical sections.
233 239
234 Okay: def a():\n pass\n\n\ndef b():\n pass 240 Okay: def a():\n pass\n\n\ndef b():\n pass
235 Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass 241 Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass
236 242
237 E301: class Foo:\n b = 0\n def bar():\n pass 243 E301: class Foo:\n b = 0\n def bar():\n pass
238 E302: def a():\n pass\n\ndef b(n):\n pass 244 E302: def a():\n pass\n\ndef b(n):\n pass
239 E303: def a():\n pass\n\n\n\ndef b(n):\n pass 245 E303: def a():\n pass\n\n\n\ndef b(n):\n pass
240 E303: def a():\n\n\n\n pass 246 E303: def a():\n\n\n\n pass
241 E304: @decorator\n\ndef a():\n pass 247 E304: @decorator\n\ndef a():\n pass
242 """ 248 """
243 if line_number == 1: 249 if line_number < 3 and not previous_logical:
244 return # Don't expect blank lines before the first line 250 return # Don't expect blank lines before the first line
245 max_blank_lines = max(blank_lines, blank_lines_before_comment)
246 if previous_logical.startswith('@'): 251 if previous_logical.startswith('@'):
247 if max_blank_lines: 252 if blank_lines:
248 return 0, "E304 blank lines found after function decorator" 253 yield 0, "E304 blank lines found after function decorator"
249 elif max_blank_lines > 2 or (indent_level and max_blank_lines == 2): 254 elif blank_lines > 2 or (indent_level and blank_lines == 2):
250 return 0, "E303 too many blank lines (%d)" % max_blank_lines 255 yield 0, "E303 too many blank lines (%d)" % blank_lines
251 elif (logical_line.startswith('def ') or 256 elif logical_line.startswith(('def ', 'class ', '@')):
252 logical_line.startswith('class ') or
253 logical_line.startswith('@')):
254 if indent_level: 257 if indent_level:
255 if not (max_blank_lines or E301NOT_REGEX.match(previous_logical)): 258 if not (blank_before or previous_indent_level < indent_level or
256 return 0, "E301 expected 1 blank line, found 0" 259 DOCSTRING_REGEX.match(previous_logical)):
257 elif max_blank_lines != 2: 260 yield 0, "E301 expected 1 blank line, found 0"
258 return 0, "E302 expected 2 blank lines, found %d" % max_blank_lines 261 elif blank_before != 2:
262 yield 0, "E302 expected 2 blank lines, found %d" % blank_before
259 263
260 264
261 def extraneous_whitespace(logical_line): 265 def extraneous_whitespace(logical_line):
262 """ 266 r"""Avoid extraneous whitespace.
263 Avoid extraneous whitespace in the following situations:
264 267
268 Avoid extraneous whitespace in these situations:
265 - Immediately inside parentheses, brackets or braces. 269 - Immediately inside parentheses, brackets or braces.
266
267 - Immediately before a comma, semicolon, or colon. 270 - Immediately before a comma, semicolon, or colon.
268 271
269 Okay: spam(ham[1], {eggs: 2}) 272 Okay: spam(ham[1], {eggs: 2})
270 E201: spam( ham[1], {eggs: 2}) 273 E201: spam( ham[1], {eggs: 2})
271 E201: spam(ham[ 1], {eggs: 2}) 274 E201: spam(ham[ 1], {eggs: 2})
272 E201: spam(ham[1], { eggs: 2}) 275 E201: spam(ham[1], { eggs: 2})
273 E202: spam(ham[1], {eggs: 2} ) 276 E202: spam(ham[1], {eggs: 2} )
274 E202: spam(ham[1 ], {eggs: 2}) 277 E202: spam(ham[1 ], {eggs: 2})
275 E202: spam(ham[1], {eggs: 2 }) 278 E202: spam(ham[1], {eggs: 2 })
276 279
277 E203: if x == 4: print x, y; x, y = y , x 280 E203: if x == 4: print x, y; x, y = y , x
278 E203: if x == 4: print x, y ; x, y = y, x 281 E203: if x == 4: print x, y ; x, y = y, x
279 E203: if x == 4 : print x, y; x, y = y, x 282 E203: if x == 4 : print x, y; x, y = y, x
280 """ 283 """
281 line = logical_line 284 line = logical_line
282 for char in '([{': 285 for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
283 found = line.find(char + ' ') 286 text = match.group()
284 if found > -1: 287 char = text.strip()
285 return found + 1, "E201 whitespace after '%s'" % char 288 found = match.start()
286 for char in '}])': 289 if text == char + ' ':
287 found = line.find(' ' + char) 290 # assert char in '([{'
288 if found > -1 and line[found - 1] != ',': 291 yield found + 1, "E201 whitespace after '%s'" % char
289 return found, "E202 whitespace before '%s'" % char 292 elif line[found - 1] != ',':
290 for char in ',;:': 293 code = ('E202' if char in '}])' else 'E203') # if char in ',;:'
291 found = line.find(' ' + char) 294 yield found, "%s whitespace before '%s'" % (code, char)
292 if found > -1: 295
293 return found, "E203 whitespace before '%s'" % char 296
297 def whitespace_around_keywords(logical_line):
298 r"""Avoid extraneous whitespace around keywords.
299
300 Okay: True and False
301 E271: True and False
302 E272: True and False
303 E273: True and\tFalse
304 E274: True\tand False
305 """
306 for match in KEYWORD_REGEX.finditer(logical_line):
307 before, after = match.groups()
308
309 if '\t' in before:
310 yield match.start(1), "E274 tab before keyword"
311 elif len(before) > 1:
312 yield match.start(1), "E272 multiple spaces before keyword"
313
314 if '\t' in after:
315 yield match.start(2), "E273 tab after keyword"
316 elif len(after) > 1:
317 yield match.start(2), "E271 multiple spaces after keyword"
294 318
295 319
296 def missing_whitespace(logical_line): 320 def missing_whitespace(logical_line):
297 """ 321 r"""Each comma, semicolon or colon should be followed by whitespace.
298 JCR: Each comma, semicolon or colon should be followed by whitespace.
299 322
300 Okay: [a, b] 323 Okay: [a, b]
301 Okay: (3,) 324 Okay: (3,)
302 Okay: a[1:4] 325 Okay: a[1:4]
303 Okay: a[:4] 326 Okay: a[:4]
304 Okay: a[1:] 327 Okay: a[1:]
305 Okay: a[1:4:2] 328 Okay: a[1:4:2]
306 E231: ['a','b'] 329 E231: ['a','b']
307 E231: foo(bar,baz) 330 E231: foo(bar,baz)
331 E231: [{'a':'b'}]
308 """ 332 """
309 line = logical_line 333 line = logical_line
310 for index in range(len(line) - 1): 334 for index in range(len(line) - 1):
311 char = line[index] 335 char = line[index]
312 if char in ',;:' and line[index + 1] not in WHITESPACE: 336 if char in ',;:' and line[index + 1] not in WHITESPACE:
313 before = line[:index] 337 before = line[:index]
314 if char == ':' and before.count('[') > before.count(']'): 338 if char == ':' and before.count('[') > before.count(']') and \
339 before.rfind('{') < before.rfind('['):
315 continue # Slice syntax, no space required 340 continue # Slice syntax, no space required
316 if char == ',' and line[index + 1] == ')': 341 if char == ',' and line[index + 1] == ')':
317 continue # Allow tuple with only one element: (3,) 342 continue # Allow tuple with only one element: (3,)
318 return index, "E231 missing whitespace after '%s'" % char 343 yield index, "E231 missing whitespace after '%s'" % char
319 344
320 345
321 def indentation(logical_line, previous_logical, indent_char, 346 def indentation(logical_line, previous_logical, indent_char,
322 indent_level, previous_indent_level): 347 indent_level, previous_indent_level):
323 r""" 348 r"""Use 4 spaces per indentation level.
324 Use 4 spaces per indentation level.
325 349
326 For really old code that you don't want to mess up, you can continue to 350 For really old code that you don't want to mess up, you can continue to
327 use 8-space tabs. 351 use 8-space tabs.
328 352
329 Okay: a = 1 353 Okay: a = 1
330 Okay: if a == 0:\n a = 1 354 Okay: if a == 0:\n a = 1
331 E111: a = 1 355 E111: a = 1
332 356
333 Okay: for item in items:\n pass 357 Okay: for item in items:\n pass
334 E112: for item in items:\npass 358 E112: for item in items:\npass
335 359
336 Okay: a = 1\nb = 2 360 Okay: a = 1\nb = 2
337 E113: a = 1\n b = 2 361 E113: a = 1\n b = 2
338 """ 362 """
339 if indent_char == ' ' and indent_level % 4: 363 if indent_char == ' ' and indent_level % 4:
340 return 0, "E111 indentation is not a multiple of four" 364 yield 0, "E111 indentation is not a multiple of four"
341 indent_expect = previous_logical.endswith(':') 365 indent_expect = previous_logical.endswith(':')
342 if indent_expect and indent_level <= previous_indent_level: 366 if indent_expect and indent_level <= previous_indent_level:
343 return 0, "E112 expected an indented block" 367 yield 0, "E112 expected an indented block"
344 if indent_level > previous_indent_level and not indent_expect: 368 if indent_level > previous_indent_level and not indent_expect:
345 return 0, "E113 unexpected indentation" 369 yield 0, "E113 unexpected indentation"
370
371
372 def continued_indentation(logical_line, tokens, indent_level, hang_closing,
373 indent_char, noqa, verbose):
374 r"""Continuation lines indentation.
375
376 Continuation lines should align wrapped elements either vertically
377 using Python's implicit line joining inside parentheses, brackets
378 and braces, or using a hanging indent.
379
380 When using a hanging indent these considerations should be applied:
381 - there should be no arguments on the first line, and
382 - further indentation should be used to clearly distinguish itself as a
383 continuation line.
384
385 Okay: a = (\n)
386 E123: a = (\n )
387
388 Okay: a = (\n 42)
389 E121: a = (\n 42)
390 E122: a = (\n42)
391 E123: a = (\n 42\n )
392 E124: a = (24,\n 42\n)
393 E125: if (\n b):\n pass
394 E126: a = (\n 42)
395 E127: a = (24,\n 42)
396 E128: a = (24,\n 42)
397 E129: if (a or\n b):\n pass
398 E131: a = (\n 42\n 24)
399 """
400 first_row = tokens[0][2][0]
401 nrows = 1 + tokens[-1][2][0] - first_row
402 if noqa or nrows == 1:
403 return
404
405 # indent_next tells us whether the next block is indented; assuming
406 # that it is indented by 4 spaces, then we should not allow 4-space
407 # indents on the final continuation line; in turn, some other
408 # indents are allowed to have an extra 4 spaces.
409 indent_next = logical_line.endswith(':')
410
411 row = depth = 0
412 valid_hangs = (4,) if indent_char != '\t' else (4, 8)
413 # remember how many brackets were opened on each line
414 parens = [0] * nrows
415 # relative indents of physical lines
416 rel_indent = [0] * nrows
417 # for each depth, collect a list of opening rows
418 open_rows = [[0]]
419 # for each depth, memorize the hanging indentation
420 hangs = [None]
421 # visual indents
422 indent_chances = {}
423 last_indent = tokens[0][2]
424 visual_indent = None
425 # for each depth, memorize the visual indent column
426 indent = [last_indent[1]]
427 if verbose >= 3:
428 print(">>> " + tokens[0][4].rstrip())
429
430 for token_type, text, start, end, line in tokens:
431
432 newline = row < start[0] - first_row
433 if newline:
434 row = start[0] - first_row
435 newline = not last_token_multiline and token_type not in NEWLINE
436
437 if newline:
438 # this is the beginning of a continuation line.
439 last_indent = start
440 if verbose >= 3:
441 print("... " + line.rstrip())
442
443 # record the initial indent.
444 rel_indent[row] = expand_indent(line) - indent_level
445
446 # identify closing bracket
447 close_bracket = (token_type == tokenize.OP and text in ']})')
448
449 # is the indent relative to an opening bracket line?
450 for open_row in reversed(open_rows[depth]):
451 hang = rel_indent[row] - rel_indent[open_row]
452 hanging_indent = hang in valid_hangs
453 if hanging_indent:
454 break
455 if hangs[depth]:
456 hanging_indent = (hang == hangs[depth])
457 # is there any chance of visual indent?
458 visual_indent = (not close_bracket and hang > 0 and
459 indent_chances.get(start[1]))
460
461 if close_bracket and indent[depth]:
462 # closing bracket for visual indent
463 if start[1] != indent[depth]:
464 yield (start, "E124 closing bracket does not match "
465 "visual indentation")
466 elif close_bracket and not hang:
467 # closing bracket matches indentation of opening bracket's line
468 if hang_closing:
469 yield start, "E133 closing bracket is missing indentation"
470 elif indent[depth] and start[1] < indent[depth]:
471 if visual_indent is not True:
472 # visual indent is broken
473 yield (start, "E128 continuation line "
474 "under-indented for visual indent")
475 elif hanging_indent or (indent_next and rel_indent[row] == 8):
476 # hanging indent is verified
477 if close_bracket and not hang_closing:
478 yield (start, "E123 closing bracket does not match "
479 "indentation of opening bracket's line")
480 hangs[depth] = hang
481 elif visual_indent is True:
482 # visual indent is verified
483 indent[depth] = start[1]
484 elif visual_indent in (text, str):
485 # ignore token lined up with matching one from a previous line
486 pass
487 else:
488 # indent is broken
489 if hang <= 0:
490 error = "E122", "missing indentation or outdented"
491 elif indent[depth]:
492 error = "E127", "over-indented for visual indent"
493 elif not close_bracket and hangs[depth]:
494 error = "E131", "unaligned for hanging indent"
495 else:
496 hangs[depth] = hang
497 if hang > 4:
498 error = "E126", "over-indented for hanging indent"
499 else:
500 error = "E121", "under-indented for hanging indent"
501 yield start, "%s continuation line %s" % error
502
503 # look for visual indenting
504 if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT)
505 and not indent[depth]):
506 indent[depth] = start[1]
507 indent_chances[start[1]] = True
508 if verbose >= 4:
509 print("bracket depth %s indent to %s" % (depth, start[1]))
510 # deal with implicit string concatenation
511 elif (token_type in (tokenize.STRING, tokenize.COMMENT) or
512 text in ('u', 'ur', 'b', 'br')):
513 indent_chances[start[1]] = str
514 # special case for the "if" statement because len("if (") == 4
515 elif not indent_chances and not row and not depth and text == 'if':
516 indent_chances[end[1] + 1] = True
517 elif text == ':' and line[end[1]:].isspace():
518 open_rows[depth].append(row)
519
520 # keep track of bracket depth
521 if token_type == tokenize.OP:
522 if text in '([{':
523 depth += 1
524 indent.append(0)
525 hangs.append(None)
526 if len(open_rows) == depth:
527 open_rows.append([])
528 open_rows[depth].append(row)
529 parens[row] += 1
530 if verbose >= 4:
531 print("bracket depth %s seen, col %s, visual min = %s" %
532 (depth, start[1], indent[depth]))
533 elif text in ')]}' and depth > 0:
534 # parent indents should not be more than this one
535 prev_indent = indent.pop() or last_indent[1]
536 hangs.pop()
537 for d in range(depth):
538 if indent[d] > prev_indent:
539 indent[d] = 0
540 for ind in list(indent_chances):
541 if ind >= prev_indent:
542 del indent_chances[ind]
543 del open_rows[depth + 1:]
544 depth -= 1
545 if depth:
546 indent_chances[indent[depth]] = True
547 for idx in range(row, -1, -1):
548 if parens[idx]:
549 parens[idx] -= 1
550 break
551 assert len(indent) == depth + 1
552 if start[1] not in indent_chances:
553 # allow to line up tokens
554 indent_chances[start[1]] = text
555
556 last_token_multiline = (start[0] != end[0])
557 if last_token_multiline:
558 rel_indent[end[0] - first_row] = rel_indent[row]
559
560 if indent_next and expand_indent(line) == indent_level + 4:
561 pos = (start[0], indent[0] + 4)
562 if visual_indent:
563 code = "E129 visually indented line"
564 else:
565 code = "E125 continuation line"
566 yield pos, "%s with same indent as next logical line" % code
346 567
347 568
348 def whitespace_before_parameters(logical_line, tokens): 569 def whitespace_before_parameters(logical_line, tokens):
349 """ 570 r"""Avoid extraneous whitespace.
571
350 Avoid extraneous whitespace in the following situations: 572 Avoid extraneous whitespace in the following situations:
351 573 - before the open parenthesis that starts the argument list of a
352 - Immediately before the open parenthesis that starts the argument 574 function call.
353 list of a function call. 575 - before the open parenthesis that starts an indexing or slicing.
354
355 - Immediately before the open parenthesis that starts an indexing or
356 slicing.
357 576
358 Okay: spam(1) 577 Okay: spam(1)
359 E211: spam (1) 578 E211: spam (1)
360 579
361 Okay: dict['key'] = list[index] 580 Okay: dict['key'] = list[index]
362 E211: dict ['key'] = list[index] 581 E211: dict ['key'] = list[index]
363 E211: dict['key'] = list [index] 582 E211: dict['key'] = list [index]
364 """ 583 """
365 prev_type = tokens[0][0] 584 prev_type, prev_text, __, prev_end, __ = tokens[0]
366 prev_text = tokens[0][1]
367 prev_end = tokens[0][3]
368 for index in range(1, len(tokens)): 585 for index in range(1, len(tokens)):
369 token_type, text, start, end, line = tokens[index] 586 token_type, text, start, end, __ = tokens[index]
370 if (token_type == tokenize.OP and 587 if (token_type == tokenize.OP and
371 text in '([' and 588 text in '([' and
372 start != prev_end and 589 start != prev_end and
373 prev_type == tokenize.NAME and 590 (prev_type == tokenize.NAME or prev_text in '}])') and
591 # Syntax "class A (B):" is allowed, but avoid it
374 (index < 2 or tokens[index - 2][1] != 'class') and 592 (index < 2 or tokens[index - 2][1] != 'class') and
375 (not iskeyword(prev_text))): 593 # Allow "return (a.foo for a in range(5))"
376 return prev_end, "E211 whitespace before '%s'" % text 594 not keyword.iskeyword(prev_text)):
595 yield prev_end, "E211 whitespace before '%s'" % text
377 prev_type = token_type 596 prev_type = token_type
378 prev_text = text 597 prev_text = text
379 prev_end = end 598 prev_end = end
380 599
381 600
382 def whitespace_around_operator(logical_line): 601 def whitespace_around_operator(logical_line):
383 """ 602 r"""Avoid extraneous whitespace around an operator.
384 Avoid extraneous whitespace in the following situations:
385
386 - More than one space around an assignment (or other) operator to
387 align it with another.
388 603
389 Okay: a = 12 + 3 604 Okay: a = 12 + 3
390 E221: a = 4 + 5 605 E221: a = 4 + 5
391 E222: a = 4 + 5 606 E222: a = 4 + 5
392 E223: a = 4\t+ 5 607 E223: a = 4\t+ 5
393 E224: a = 4 +\t5 608 E224: a = 4 +\t5
394 """ 609 """
395 line = logical_line 610 for match in OPERATOR_REGEX.finditer(logical_line):
396 for operator in OPERATORS: 611 before, after = match.groups()
397 found = line.find(' ' + operator) 612
398 if found > -1: 613 if '\t' in before:
399 return found, "E221 multiple spaces before operator" 614 yield match.start(1), "E223 tab before operator"
400 found = line.find(operator + ' ') 615 elif len(before) > 1:
401 if found > -1: 616 yield match.start(1), "E221 multiple spaces before operator"
402 return found, "E222 multiple spaces after operator" 617
403 found = line.find('\t' + operator) 618 if '\t' in after:
404 if found > -1: 619 yield match.start(2), "E224 tab after operator"
405 return found, "E223 tab before operator" 620 elif len(after) > 1:
406 found = line.find(operator + '\t') 621 yield match.start(2), "E222 multiple spaces after operator"
407 if found > -1:
408 return found, "E224 tab after operator"
409 622
410 623
411 def missing_whitespace_around_operator(logical_line, tokens): 624 def missing_whitespace_around_operator(logical_line, tokens):
412 r""" 625 r"""Surround operators with a single space on either side.
626
413 - Always surround these binary operators with a single space on 627 - Always surround these binary operators with a single space on
414 either side: assignment (=), augmented assignment (+=, -= etc.), 628 either side: assignment (=), augmented assignment (+=, -= etc.),
415 comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), 629 comparisons (==, <, >, !=, <=, >=, in, not in, is, is not),
416 Booleans (and, or, not). 630 Booleans (and, or, not).
417 631
418 - Use spaces around arithmetic operators. 632 - If operators with different priorities are used, consider adding
633 whitespace around the operators with the lowest priorities.
419 634
420 Okay: i = i + 1 635 Okay: i = i + 1
421 Okay: submitted += 1 636 Okay: submitted += 1
422 Okay: x = x * 2 - 1 637 Okay: x = x * 2 - 1
423 Okay: hypot2 = x * x + y * y 638 Okay: hypot2 = x * x + y * y
424 Okay: c = (a + b) * (a - b) 639 Okay: c = (a + b) * (a - b)
425 Okay: foo(bar, key='word', *args, **kwargs) 640 Okay: foo(bar, key='word', *args, **kwargs)
426 Okay: baz(**kwargs)
427 Okay: negative = -1
428 Okay: spam(-1)
429 Okay: alpha[:-i] 641 Okay: alpha[:-i]
430 Okay: if not -5 < x < +5:\n pass
431 Okay: lambda *args, **kw: (args, kw)
432 642
433 E225: i=i+1 643 E225: i=i+1
434 E225: submitted +=1 644 E225: submitted +=1
435 E225: x = x*2 - 1 645 E225: x = x /2 - 1
436 E225: hypot2 = x*x + y*y
437 E225: c = (a+b) * (a-b)
438 E225: c = alpha -4
439 E225: z = x **y 646 E225: z = x **y
647 E226: c = (a+b) * (a-b)
648 E226: hypot2 = x*x + y*y
649 E227: c = a|b
650 E228: msg = fmt%(errno, errmsg)
440 """ 651 """
441 parens = 0 652 parens = 0
442 need_space = False 653 need_space = False
443 prev_type = tokenize.OP 654 prev_type = tokenize.OP
444 prev_text = prev_end = None 655 prev_text = prev_end = None
445 for token_type, text, start, end, line in tokens: 656 for token_type, text, start, end, line in tokens:
446 if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): 657 if token_type in SKIP_COMMENTS:
447 # ERRORTOKEN is triggered by backticks in Python 3000
448 continue 658 continue
449 if text in ('(', 'lambda'): 659 if text in ('(', 'lambda'):
450 parens += 1 660 parens += 1
451 elif text == ')': 661 elif text == ')':
452 parens -= 1 662 parens -= 1
453 if need_space: 663 if need_space:
454 if start == prev_end: 664 if start != prev_end:
455 return prev_end, "E225 missing whitespace around operator" 665 # Found a (probably) needed space
456 need_space = False 666 if need_space is not True and not need_space[1]:
457 elif token_type == tokenize.OP: 667 yield (need_space[0],
668 "E225 missing whitespace around operator")
669 need_space = False
670 elif text == '>' and prev_text in ('<', '-'):
671 # Tolerate the "<>" operator, even if running Python 3
672 # Deal with Python 3's annotated return value "->"
673 pass
674 else:
675 if need_space is True or need_space[1]:
676 # A needed trailing space was not found
677 yield prev_end, "E225 missing whitespace around operator"
678 else:
679 code, optype = 'E226', 'arithmetic'
680 if prev_text == '%':
681 code, optype = 'E228', 'modulo'
682 elif prev_text not in ARITHMETIC_OP:
683 code, optype = 'E227', 'bitwise or shift'
684 yield (need_space[0], "%s missing whitespace "
685 "around %s operator" % (code, optype))
686 need_space = False
687 elif token_type == tokenize.OP and prev_end is not None:
458 if text == '=' and parens: 688 if text == '=' and parens:
459 # Allow keyword args or defaults: foo(bar=None). 689 # Allow keyword args or defaults: foo(bar=None).
460 pass 690 pass
461 elif text in BINARY_OPERATORS: 691 elif text in WS_NEEDED_OPERATORS:
462 need_space = True 692 need_space = True
463 elif text in UNARY_OPERATORS: 693 elif text in UNARY_OPERATORS:
464 if ((prev_type != tokenize.OP or prev_text in '}])') and not 694 # Check if the operator is being used as a binary operator
465 (prev_type == tokenize.NAME and iskeyword(prev_text))): 695 # Allow unary operators: -123, -x, +1.
466 # Allow unary operators: -123, -x, +1. 696 # Allow argument unpacking: foo(*args, **kwargs).
467 # Allow argument unpacking: foo(*args, **kwargs). 697 if (prev_text in '}])' if prev_type == tokenize.OP
468 need_space = True 698 else prev_text not in KEYWORDS):
469 if need_space and start == prev_end: 699 need_space = None
470 return prev_end, "E225 missing whitespace around operator" 700 elif text in WS_OPTIONAL_OPERATORS:
701 need_space = None
702
703 if need_space is None:
704 # Surrounding space is optional, but ensure that
705 # trailing space matches opening space
706 need_space = (prev_end, start != prev_end)
707 elif need_space and start == prev_end:
708 # A needed opening space was not found
709 yield prev_end, "E225 missing whitespace around operator"
710 need_space = False
471 prev_type = token_type 711 prev_type = token_type
472 prev_text = text 712 prev_text = text
473 prev_end = end 713 prev_end = end
474 714
475 715
476 def whitespace_around_comma(logical_line): 716 def whitespace_around_comma(logical_line):
477 """ 717 r"""Avoid extraneous whitespace after a comma or a colon.
478 Avoid extraneous whitespace in the following situations:
479 718
480 - More than one space around an assignment (or other) operator to
481 align it with another.
482
483 JCR: This should also be applied around comma etc.
484 Note: these checks are disabled by default 719 Note: these checks are disabled by default
485 720
486 Okay: a = (1, 2) 721 Okay: a = (1, 2)
487 E241: a = (1, 2) 722 E241: a = (1, 2)
488 E242: a = (1,\t2) 723 E242: a = (1,\t2)
489 """ 724 """
490 line = logical_line 725 line = logical_line
491 for separator in ',;:': 726 for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line):
492 found = line.find(separator + ' ') 727 found = m.start() + 1
493 if found > -1: 728 if '\t' in m.group():
494 return found + 1, "E241 multiple spaces after '%s'" % separator 729 yield found, "E242 tab after '%s'" % m.group()[0]
495 found = line.find(separator + '\t') 730 else:
496 if found > -1: 731 yield found, "E241 multiple spaces after '%s'" % m.group()[0]
497 return found + 1, "E242 tab after '%s'" % separator
498 732
499 733
500 def whitespace_around_named_parameter_equals(logical_line): 734 def whitespace_around_named_parameter_equals(logical_line, tokens):
501 """ 735 r"""Don't use spaces around the '=' sign in function arguments.
736
502 Don't use spaces around the '=' sign when used to indicate a 737 Don't use spaces around the '=' sign when used to indicate a
503 keyword argument or a default parameter value. 738 keyword argument or a default parameter value.
504 739
505 Okay: def complex(real, imag=0.0): 740 Okay: def complex(real, imag=0.0):
506 Okay: return magic(r=real, i=imag) 741 Okay: return magic(r=real, i=imag)
507 Okay: boolean(a == b) 742 Okay: boolean(a == b)
508 Okay: boolean(a != b) 743 Okay: boolean(a != b)
509 Okay: boolean(a <= b) 744 Okay: boolean(a <= b)
510 Okay: boolean(a >= b) 745 Okay: boolean(a >= b)
511 746
512 E251: def complex(real, imag = 0.0): 747 E251: def complex(real, imag = 0.0):
513 E251: return magic(r = real, i = imag) 748 E251: return magic(r = real, i = imag)
514 """ 749 """
515 parens = 0 750 parens = 0
516 window = ' ' 751 no_space = False
517 equal_ok = ['==', '!=', '<=', '>='] 752 prev_end = None
518 753 message = "E251 unexpected spaces around keyword / parameter equals"
519 for pos, c in enumerate(logical_line): 754 for token_type, text, start, end, line in tokens:
520 window = window[1:] + c 755 if token_type == tokenize.NL:
521 if parens: 756 continue
522 if window[0] in WHITESPACE and window[1] == '=': 757 if no_space:
523 if window[1:] not in equal_ok: 758 no_space = False
524 issue = "E251 no spaces around keyword / parameter equals" 759 if start != prev_end:
525 return pos, issue 760 yield (prev_end, message)
526 if window[2] in WHITESPACE and window[1] == '=': 761 elif token_type == tokenize.OP:
527 if window[:2] not in equal_ok: 762 if text == '(':
528 issue = "E251 no spaces around keyword / parameter equals" 763 parens += 1
529 return pos, issue 764 elif text == ')':
530 if c == '(': 765 parens -= 1
531 parens += 1 766 elif parens and text == '=':
532 elif c == ')': 767 no_space = True
533 parens -= 1 768 if start != prev_end:
769 yield (prev_end, message)
770 prev_end = end
534 771
535 772
536 def whitespace_before_inline_comment(logical_line, tokens): 773 def whitespace_before_comment(logical_line, tokens):
537 """ 774 r"""Separate inline comments by at least two spaces.
538 Separate inline comments by at least two spaces.
539 775
540 An inline comment is a comment on the same line as a statement. Inline 776 An inline comment is a comment on the same line as a statement. Inline
541 comments should be separated by at least two spaces from the statement. 777 comments should be separated by at least two spaces from the statement.
542 They should start with a # and a single space. 778 They should start with a # and a single space.
543 779
780 Each line of a block comment starts with a # and a single space
781 (unless it is indented text inside the comment).
782
544 Okay: x = x + 1 # Increment x 783 Okay: x = x + 1 # Increment x
545 Okay: x = x + 1 # Increment x 784 Okay: x = x + 1 # Increment x
785 Okay: # Block comment
546 E261: x = x + 1 # Increment x 786 E261: x = x + 1 # Increment x
547 E262: x = x + 1 #Increment x 787 E262: x = x + 1 #Increment x
548 E262: x = x + 1 # Increment x 788 E262: x = x + 1 # Increment x
789 E265: #Block comment
549 """ 790 """
550 prev_end = (0, 0) 791 prev_end = (0, 0)
551 for token_type, text, start, end, line in tokens: 792 for token_type, text, start, end, line in tokens:
552 if token_type == tokenize.NL:
553 continue
554 if token_type == tokenize.COMMENT: 793 if token_type == tokenize.COMMENT:
555 if not line[:start[1]].strip(): 794 inline_comment = line[:start[1]].strip()
556 continue 795 if inline_comment:
557 if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: 796 if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
558 return (prev_end, 797 yield (prev_end,
559 "E261 at least two spaces before inline comment") 798 "E261 at least two spaces before inline comment")
560 if (len(text) > 1 and text.startswith('# ') 799 symbol, sp, comment = text.partition(' ')
561 or not text.startswith('# ')): 800 bad_prefix = symbol not in ('#', '#:')
562 return start, "E262 inline comment should start with '# '" 801 if inline_comment:
563 else: 802 if bad_prefix or comment[:1].isspace():
803 yield start, "E262 inline comment should start with '# '"
804 elif bad_prefix:
805 if text.rstrip('#') and (start[0] > 1 or symbol[1] != '!'):
806 yield start, "E265 block comment should start with '# '"
807 elif token_type != tokenize.NL:
564 prev_end = end 808 prev_end = end
565 809
566 810
567 def imports_on_separate_lines(logical_line): 811 def imports_on_separate_lines(logical_line):
568 r""" 812 r"""Imports should usually be on separate lines.
569 Imports should usually be on separate lines.
570 813
571 Okay: import os\nimport sys 814 Okay: import os\nimport sys
572 E401: import sys, os 815 E401: import sys, os
573 816
574 Okay: from subprocess import Popen, PIPE 817 Okay: from subprocess import Popen, PIPE
575 Okay: from myclas import MyClass 818 Okay: from myclas import MyClass
576 Okay: from foo.bar.yourclass import YourClass 819 Okay: from foo.bar.yourclass import YourClass
577 Okay: import myclass 820 Okay: import myclass
578 Okay: import foo.bar.yourclass 821 Okay: import foo.bar.yourclass
579 """ 822 """
580 line = logical_line 823 line = logical_line
581 if line.startswith('import '): 824 if line.startswith('import '):
582 found = line.find(',') 825 found = line.find(',')
583 if found > -1: 826 if -1 < found and ';' not in line[:found]:
584 return found, "E401 multiple imports on one line" 827 yield found, "E401 multiple imports on one line"
585 828
586 829
587 def compound_statements(logical_line): 830 def compound_statements(logical_line):
588 r""" 831 r"""Compound statements (on the same line) are generally discouraged.
589 Compound statements (multiple statements on the same line) are
590 generally discouraged.
591 832
592 While sometimes it's okay to put an if/for/while with a small body 833 While sometimes it's okay to put an if/for/while with a small body
593 on the same line, never do this for multi-clause statements. Also 834 on the same line, never do this for multi-clause statements.
594 avoid folding such long lines! 835 Also avoid folding such long lines!
595 836
596 Okay: if foo == 'blah':\n do_blah_thing() 837 Okay: if foo == 'blah':\n do_blah_thing()
597 Okay: do_one() 838 Okay: do_one()
598 Okay: do_two() 839 Okay: do_two()
599 Okay: do_three() 840 Okay: do_three()
600 841
601 E701: if foo == 'blah': do_blah_thing() 842 E701: if foo == 'blah': do_blah_thing()
602 E701: for x in lst: total += x 843 E701: for x in lst: total += x
603 E701: while t < 10: t = delay() 844 E701: while t < 10: t = delay()
604 E701: if foo == 'blah': do_blah_thing() 845 E701: if foo == 'blah': do_blah_thing()
605 E701: else: do_non_blah_thing() 846 E701: else: do_non_blah_thing()
606 E701: try: something() 847 E701: try: something()
607 E701: finally: cleanup() 848 E701: finally: cleanup()
608 E701: if foo == 'blah': one(); two(); three() 849 E701: if foo == 'blah': one(); two(); three()
609 850
610 E702: do_one(); do_two(); do_three() 851 E702: do_one(); do_two(); do_three()
852 E703: do_four(); # useless semicolon
611 """ 853 """
612 line = logical_line 854 line = logical_line
855 last_char = len(line) - 1
613 found = line.find(':') 856 found = line.find(':')
614 if -1 < found < len(line) - 1: 857 while -1 < found < last_char:
615 before = line[:found] 858 before = line[:found]
616 if (before.count('{') <= before.count('}') and # {'a': 1} (dict) 859 if (before.count('{') <= before.count('}') and # {'a': 1} (dict)
617 before.count('[') <= before.count(']') and # [1:2] (slice) 860 before.count('[') <= before.count(']') and # [1:2] (slice)
618 not re.search(r'\blambda\b', before)): # lambda x: x 861 before.count('(') <= before.count(')') and # (Python 3 annotation)
619 return found, "E701 multiple statements on one line (colon)" 862 not LAMBDA_REGEX.search(before)): # lambda x: x
863 yield found, "E701 multiple statements on one line (colon)"
864 found = line.find(':', found + 1)
620 found = line.find(';') 865 found = line.find(';')
621 if -1 < found: 866 while -1 < found:
622 return found, "E702 multiple statements on one line (semicolon)" 867 if found < last_char:
868 yield found, "E702 multiple statements on one line (semicolon)"
869 else:
870 yield found, "E703 statement ends with a semicolon"
871 found = line.find(';', found + 1)
623 872
624 873
625 def python_3000_has_key(logical_line): 874 def explicit_line_join(logical_line, tokens):
875 r"""Avoid explicit line join between brackets.
876
877 The preferred way of wrapping long lines is by using Python's implied line
878 continuation inside parentheses, brackets and braces. Long lines can be
879 broken over multiple lines by wrapping expressions in parentheses. These
880 should be used in preference to using a backslash for line continuation.
881
882 E502: aaa = [123, \\n 123]
883 E502: aaa = ("bbb " \\n "ccc")
884
885 Okay: aaa = [123,\n 123]
886 Okay: aaa = ("bbb "\n "ccc")
887 Okay: aaa = "bbb " \\n "ccc"
626 """ 888 """
627 The {}.has_key() method will be removed in the future version of 889 prev_start = prev_end = parens = 0
628 Python. Use the 'in' operation instead, like: 890 for token_type, text, start, end, line in tokens:
629 d = {"a": 1, "b": 2} 891 if start[0] != prev_start and parens and backslash:
630 if "b" in d: 892 yield backslash, "E502 the backslash is redundant between brackets"
631 print d["b"] 893 if end[0] != prev_end:
894 if line.rstrip('\r\n').endswith('\\'):
895 backslash = (end[0], len(line.splitlines()[-1]) - 1)
896 else:
897 backslash = None
898 prev_start = prev_end = end[0]
899 else:
900 prev_start = start[0]
901 if token_type == tokenize.OP:
902 if text in '([{':
903 parens += 1
904 elif text in ')]}':
905 parens -= 1
906
907
908 def comparison_to_singleton(logical_line, noqa):
909 r"""Comparison to singletons should use "is" or "is not".
910
911 Comparisons to singletons like None should always be done
912 with "is" or "is not", never the equality operators.
913
914 Okay: if arg is not None:
915 E711: if arg != None:
916 E712: if arg == True:
917
918 Also, beware of writing if x when you really mean if x is not None --
919 e.g. when testing whether a variable or argument that defaults to None was
920 set to some other value. The other value might have a type (such as a
921 container) that could be false in a boolean context!
922 """
923 match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line)
924 if match:
925 same = (match.group(1) == '==')
926 singleton = match.group(2)
927 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton)
928 if singleton in ('None',):
929 code = 'E711'
930 else:
931 code = 'E712'
932 nonzero = ((singleton == 'True' and same) or
933 (singleton == 'False' and not same))
934 msg += " or 'if %scond:'" % ('' if nonzero else 'not ')
935 yield match.start(1), ("%s comparison to %s should be %s" %
936 (code, singleton, msg))
937
938
939 def comparison_negative(logical_line):
940 r"""Negative comparison should be done using "not in" and "is not".
941
942 Okay: if x not in y:\n pass
943 Okay: assert (X in Y or X is Z)
944 Okay: if not (X in Y):\n pass
945 Okay: zz = x is not y
946 E713: Z = not X in Y
947 E713: if not X.B in Y:\n pass
948 E714: if not X is Y:\n pass
949 E714: Z = not X.B is Y
950 """
951 match = COMPARE_NEGATIVE_REGEX.search(logical_line)
952 if match:
953 pos = match.start(1)
954 if match.group(2) == 'in':
955 yield pos, "E713 test for membership should be 'not in'"
956 else:
957 yield pos, "E714 test for object identity should be 'is not'"
958
959
960 def comparison_type(logical_line):
961 r"""Object type comparisons should always use isinstance().
962
963 Do not compare types directly.
964
965 Okay: if isinstance(obj, int):
966 E721: if type(obj) is type(1):
967
968 When checking if an object is a string, keep in mind that it might be a
969 unicode string too! In Python 2.3, str and unicode have a common base
970 class, basestring, so you can do:
971
972 Okay: if isinstance(obj, basestring):
973 Okay: if type(a1) is type(b1):
974 """
975 match = COMPARE_TYPE_REGEX.search(logical_line)
976 if match:
977 inst = match.group(1)
978 if inst and isidentifier(inst) and inst not in SINGLETONS:
979 return # Allow comparison for types which are not obvious
980 yield match.start(), "E721 do not compare types, use 'isinstance()'"
981
982
983 def python_3000_has_key(logical_line, noqa):
984 r"""The {}.has_key() method is removed in Python 3: use the 'in' operator.
985
986 Okay: if "alph" in d:\n print d["alph"]
987 W601: assert d.has_key('alph')
632 """ 988 """
633 pos = logical_line.find('.has_key(') 989 pos = logical_line.find('.has_key(')
634 if pos > -1: 990 if pos > -1 and not noqa:
635 return pos, "W601 .has_key() is deprecated, use 'in'" 991 yield pos, "W601 .has_key() is deprecated, use 'in'"
636 992
637 993
638 def python_3000_raise_comma(logical_line): 994 def python_3000_raise_comma(logical_line):
639 """ 995 r"""When raising an exception, use "raise ValueError('message')".
640 When raising an exception, use "raise ValueError('message')"
641 instead of the older form "raise ValueError, 'message'".
642 996
643 The paren-using form is preferred because when the exception arguments 997 The older form is removed in Python 3.
644 are long or include string formatting, you don't need to use line 998
645 continuation characters thanks to the containing parentheses. The older 999 Okay: raise DummyError("Message")
646 form will be removed in Python 3000. 1000 W602: raise DummyError, "Message"
647 """ 1001 """
648 match = RAISE_COMMA_REGEX.match(logical_line) 1002 match = RAISE_COMMA_REGEX.match(logical_line)
649 if match: 1003 if match and not RERAISE_COMMA_REGEX.match(logical_line):
650 return match.start(1), "W602 deprecated form of raising exception" 1004 yield match.end() - 1, "W602 deprecated form of raising exception"
651 1005
652 1006
653 def python_3000_not_equal(logical_line): 1007 def python_3000_not_equal(logical_line):
654 """ 1008 r"""New code should always use != instead of <>.
655 != can also be written <>, but this is an obsolete usage kept for 1009
656 backwards compatibility only. New code should always use !=. 1010 The older syntax is removed in Python 3.
657 The older syntax is removed in Python 3000. 1011
1012 Okay: if a != 'no':
1013 W603: if a <> 'no':
658 """ 1014 """
659 pos = logical_line.find('<>') 1015 pos = logical_line.find('<>')
660 if pos > -1: 1016 if pos > -1:
661 return pos, "W603 '<>' is deprecated, use '!='" 1017 yield pos, "W603 '<>' is deprecated, use '!='"
662 1018
663 1019
664 def python_3000_backticks(logical_line): 1020 def python_3000_backticks(logical_line):
665 """ 1021 r"""Backticks are removed in Python 3: use repr() instead.
666 Backticks are removed in Python 3000. 1022
667 Use repr() instead. 1023 Okay: val = repr(1 + 2)
1024 W604: val = `1 + 2`
668 """ 1025 """
669 pos = logical_line.find('`') 1026 pos = logical_line.find('`')
670 if pos > -1: 1027 if pos > -1:
671 return pos, "W604 backticks are deprecated, use 'repr()'" 1028 yield pos, "W604 backticks are deprecated, use 'repr()'"
672 1029
673 1030
674 ############################################################################## 1031 ##############################################################################
675 # Helper functions 1032 # Helper functions
676 ############################################################################## 1033 ##############################################################################
677 1034
678 1035
1036 if '' == ''.encode():
1037 # Python 2: implicit encoding.
1038 def readlines(filename):
1039 """Read the source code."""
1040 with open(filename, 'rU') as f:
1041 return f.readlines()
1042 isidentifier = re.compile(r'[a-zA-Z_]\w*').match
1043 stdin_get_value = sys.stdin.read
1044 else:
1045 # Python 3
1046 def readlines(filename):
1047 """Read the source code."""
1048 try:
1049 with open(filename, 'rb') as f:
1050 (coding, lines) = tokenize.detect_encoding(f.readline)
1051 f = TextIOWrapper(f, coding, line_buffering=True)
1052 return [l.decode(coding) for l in lines] + f.readlines()
1053 except (LookupError, SyntaxError, UnicodeError):
1054 # Fall back if file encoding is improperly declared
1055 with open(filename, encoding='latin-1') as f:
1056 return f.readlines()
1057 isidentifier = str.isidentifier
1058
1059 def stdin_get_value():
1060 return TextIOWrapper(sys.stdin.buffer, errors='ignore').read()
1061 noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search
1062
1063
679 def expand_indent(line): 1064 def expand_indent(line):
680 """ 1065 r"""Return the amount of indentation.
681 Return the amount of indentation. 1066
682 Tabs are expanded to the next multiple of 8. 1067 Tabs are expanded to the next multiple of 8.
683 1068
684 >>> expand_indent(' ') 1069 >>> expand_indent(' ')
685 4 1070 4
686 >>> expand_indent('\\t') 1071 >>> expand_indent('\t')
687 8 1072 8
688 >>> expand_indent(' \\t') 1073 >>> expand_indent(' \t')
689 8 1074 8
690 >>> expand_indent(' \\t') 1075 >>> expand_indent(' \t')
691 8
692 >>> expand_indent(' \\t')
693 16 1076 16
694 """ 1077 """
1078 if '\t' not in line:
1079 return len(line) - len(line.lstrip())
695 result = 0 1080 result = 0
696 for char in line: 1081 for char in line:
697 if char == '\t': 1082 if char == '\t':
698 result = result // 8 * 8 + 8 1083 result = result // 8 * 8 + 8
699 elif char == ' ': 1084 elif char == ' ':
700 result += 1 1085 result += 1
701 else: 1086 else:
702 break 1087 break
703 return result 1088 return result
704 1089
705 1090
706 def mute_string(text): 1091 def mute_string(text):
707 """ 1092 """Replace contents with 'xxx' to prevent syntax matching.
708 Replace contents with 'xxx' to prevent syntax matching.
709 1093
710 >>> mute_string('"abc"') 1094 >>> mute_string('"abc"')
711 '"xxx"' 1095 '"xxx"'
712 >>> mute_string("'''abc'''") 1096 >>> mute_string("'''abc'''")
713 "'''xxx'''" 1097 "'''xxx'''"
714 >>> mute_string("r'abc'") 1098 >>> mute_string("r'abc'")
715 "r'xxx'" 1099 "r'xxx'"
716 """ 1100 """
717 start = 1 1101 # String modifiers (e.g. u or r)
1102 start = text.index(text[-1]) + 1
718 end = len(text) - 1 1103 end = len(text) - 1
719 # String modifiers (e.g. u or r)
720 if text.endswith('"'):
721 start += text.index('"')
722 elif text.endswith("'"):
723 start += text.index("'")
724 # Triple quotes 1104 # Triple quotes
725 if text.endswith('"""') or text.endswith("'''"): 1105 if text[-3:] in ('"""', "'''"):
726 start += 2 1106 start += 2
727 end -= 2 1107 end -= 2
728 return text[:start] + 'x' * (end - start) + text[end:] 1108 return text[:start] + 'x' * (end - start) + text[end:]
729 1109
730 1110
731 def message(text): 1111 def parse_udiff(diff, patterns=None, parent='.'):
732 """Print a message.""" 1112 """Return a dictionary of matching lines."""
733 # print >> sys.stderr, options.prog + ': ' + text 1113 # For each file of the diff, the entry key is the filename,
734 # print >> sys.stderr, text 1114 # and the value is a set of row numbers to consider.
735 print(text) 1115 rv = {}
1116 path = nrows = None
1117 for line in diff.splitlines():
1118 if nrows:
1119 if line[:1] != '-':
1120 nrows -= 1
1121 continue
1122 if line[:3] == '@@ ':
1123 hunk_match = HUNK_REGEX.match(line)
1124 (row, nrows) = [int(g or '1') for g in hunk_match.groups()]
1125 rv[path].update(range(row, row + nrows))
1126 elif line[:3] == '+++':
1127 path = line[4:].split('\t', 1)[0]
1128 if path[:2] == 'b/':
1129 path = path[2:]
1130 rv[path] = set()
1131 return dict([(os.path.join(parent, path), rows)
1132 for (path, rows) in rv.items()
1133 if rows and filename_match(path, patterns)])
1134
1135
1136 def normalize_paths(value, parent=os.curdir):
1137 """Parse a comma-separated list of paths.
1138
1139 Return a list of absolute paths.
1140 """
1141 if not value or isinstance(value, list):
1142 return value
1143 paths = []
1144 for path in value.split(','):
1145 if '/' in path:
1146 path = os.path.abspath(os.path.join(parent, path))
1147 paths.append(path.rstrip('/'))
1148 return paths
1149
1150
1151 def filename_match(filename, patterns, default=True):
1152 """Check if patterns contains a pattern that matches filename.
1153
1154 If patterns is unspecified, this always returns True.
1155 """
1156 if not patterns:
1157 return default
1158 return any(fnmatch(filename, pattern) for pattern in patterns)
1159
1160
1161 if COMMENT_WITH_NL:
1162 def _is_eol_token(token):
1163 return (token[0] in NEWLINE or
1164 (token[0] == tokenize.COMMENT and token[1] == token[4]))
1165 else:
1166 def _is_eol_token(token):
1167 return token[0] in NEWLINE
736 1168
737 1169
738 ############################################################################## 1170 ##############################################################################
739 # Framework to run all checks 1171 # Framework to run all checks
740 ############################################################################## 1172 ##############################################################################
741 1173
742 1174
743 def find_checks(argument_name): 1175 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}}
1176
1177
1178 def register_check(check, codes=None):
1179 """Register a new check object."""
1180 def _add_check(check, kind, codes, args):
1181 if check in _checks[kind]:
1182 _checks[kind][check][0].extend(codes or [])
1183 else:
1184 _checks[kind][check] = (codes or [''], args)
1185 if inspect.isfunction(check):
1186 args = inspect.getargspec(check)[0]
1187 if args and args[0] in ('physical_line', 'logical_line'):
1188 if codes is None:
1189 codes = ERRORCODE_REGEX.findall(check.__doc__ or '')
1190 _add_check(check, args[0], codes, args)
1191 elif inspect.isclass(check):
1192 if inspect.getargspec(check.__init__)[0][:2] == ['self', 'tree']:
1193 _add_check(check, 'tree', codes, None)
1194
1195
1196 def init_checks_registry():
1197 """Register all globally visible functions.
1198
1199 The first argument name is either 'physical_line' or 'logical_line'.
744 """ 1200 """
745 Find all globally visible functions where the first argument name 1201 mod = inspect.getmodule(register_check)
746 starts with argument_name. 1202 for (name, function) in inspect.getmembers(mod, inspect.isfunction):
747 """ 1203 register_check(function)
748 checks = [] 1204 init_checks_registry()
749 for name, function in globals().items():
750 if not inspect.isfunction(function):
751 continue
752 args = inspect.getargspec(function)[0]
753 if args and args[0].startswith(argument_name):
754 codes = ERRORCODE_REGEX.findall(inspect.getdoc(function) or '')
755 for code in codes or ['']:
756 if not code or not ignore_code(code):
757 checks.append((name, function, args))
758 break
759 checks.sort()
760 return checks
761 1205
762 1206
763 class Checker(object): 1207 class Checker(object):
764 """ 1208 """Load a Python source file, tokenize it, check coding style."""
765 Load a Python source file, tokenize it, check coding style. 1209
766 """ 1210 def __init__(self, filename=None, lines=None,
767 1211 options=None, report=None, **kwargs):
768 def __init__(self, filename): 1212 if options is None:
769 if filename: 1213 options = StyleGuide(kwargs).options
770 self.filename = filename 1214 else:
1215 assert not kwargs
1216 self._io_error = None
1217 self._physical_checks = options.physical_checks
1218 self._logical_checks = options.logical_checks
1219 self._ast_checks = options.ast_checks
1220 self.max_line_length = options.max_line_length
1221 self.multiline = False # in a multiline string?
1222 self.hang_closing = options.hang_closing
1223 self.verbose = options.verbose
1224 self.filename = filename
1225 if filename is None:
1226 self.filename = 'stdin'
1227 self.lines = lines or []
1228 elif filename == '-':
1229 self.filename = 'stdin'
1230 self.lines = stdin_get_value().splitlines(True)
1231 elif lines is None:
771 try: 1232 try:
772 self.lines = open(filename).readlines() 1233 self.lines = readlines(filename)
773 except UnicodeDecodeError: 1234 except IOError:
774 # Errors may occur with non-UTF8 files in Python 3000 1235 (exc_type, exc) = sys.exc_info()[:2]
775 self.lines = open(filename, errors='replace').readlines() 1236 self._io_error = '%s: %s' % (exc_type.__name__, exc)
1237 self.lines = []
776 else: 1238 else:
777 self.filename = 'stdin' 1239 self.lines = lines
778 self.lines = [] 1240 if self.lines:
779 options.counters['physical lines'] = \ 1241 ord0 = ord(self.lines[0][0])
780 options.counters.get('physical lines', 0) + len(self.lines) 1242 if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM
1243 if ord0 == 0xfeff:
1244 self.lines[0] = self.lines[0][1:]
1245 elif self.lines[0][:3] == '\xef\xbb\xbf':
1246 self.lines[0] = self.lines[0][3:]
1247 self.report = report or options.report
1248 self.report_error = self.report.error
1249
1250 def report_invalid_syntax(self):
1251 """Check if the syntax is valid."""
1252 (exc_type, exc) = sys.exc_info()[:2]
1253 if len(exc.args) > 1:
1254 offset = exc.args[1]
1255 if len(offset) > 2:
1256 offset = offset[1:3]
1257 else:
1258 offset = (1, 0)
1259 self.report_error(offset[0], offset[1] or 0,
1260 'E901 %s: %s' % (exc_type.__name__, exc.args[0]),
1261 self.report_invalid_syntax)
781 1262
782 def readline(self): 1263 def readline(self):
783 """ 1264 """Get the next line from the input buffer."""
784 Get the next line from the input buffer. 1265 if self.line_number >= self.total_lines:
785 """ 1266 return ''
1267 line = self.lines[self.line_number]
786 self.line_number += 1 1268 self.line_number += 1
787 if self.line_number > len(self.lines): 1269 if self.indent_char is None and line[:1] in WHITESPACE:
788 return '' 1270 self.indent_char = line[0]
789 return self.lines[self.line_number - 1]
790
791 def readline_check_physical(self):
792 """
793 Check and return the next physical line. This method can be
794 used to feed tokenize.generate_tokens.
795 """
796 line = self.readline()
797 if line:
798 self.check_physical(line)
799 return line 1271 return line
800 1272
801 def run_check(self, check, argument_names): 1273 def run_check(self, check, argument_names):
802 """ 1274 """Run a check plugin."""
803 Run a check plugin.
804 """
805 arguments = [] 1275 arguments = []
806 for name in argument_names: 1276 for name in argument_names:
807 arguments.append(getattr(self, name)) 1277 arguments.append(getattr(self, name))
808 return check(*arguments) 1278 return check(*arguments)
809 1279
810 def check_physical(self, line): 1280 def check_physical(self, line):
811 """ 1281 """Run all physical checks on a raw input line."""
812 Run all physical checks on a raw input line.
813 """
814 self.physical_line = line 1282 self.physical_line = line
815 if self.indent_char is None and len(line) and line[0] in ' \t': 1283 for name, check, argument_names in self._physical_checks:
816 self.indent_char = line[0]
817 for name, check, argument_names in options.physical_checks:
818 result = self.run_check(check, argument_names) 1284 result = self.run_check(check, argument_names)
819 if result is not None: 1285 if result is not None:
820 offset, text = result 1286 (offset, text) = result
821 self.report_error(self.line_number, offset, text, check) 1287 self.report_error(self.line_number, offset, text, check)
1288 if text[:4] == 'E101':
1289 self.indent_char = line[0]
822 1290
823 def build_tokens_line(self): 1291 def build_tokens_line(self):
824 """ 1292 """Build a logical line from tokens."""
825 Build a logical line from tokens.
826 """
827 self.mapping = []
828 logical = [] 1293 logical = []
1294 comments = []
829 length = 0 1295 length = 0
830 previous = None 1296 prev_row = prev_col = mapping = None
831 for token in self.tokens: 1297 for token_type, text, start, end, line in self.tokens:
832 token_type, text = token[0:2] 1298 if token_type in SKIP_TOKENS:
833 if token_type in (tokenize.COMMENT, tokenize.NL, 1299 continue
834 tokenize.INDENT, tokenize.DEDENT, 1300 if not mapping:
835 tokenize.NEWLINE): 1301 mapping = [(0, start)]
1302 if token_type == tokenize.COMMENT:
1303 comments.append(text)
836 continue 1304 continue
837 if token_type == tokenize.STRING: 1305 if token_type == tokenize.STRING:
838 text = mute_string(text) 1306 text = mute_string(text)
839 if previous: 1307 if prev_row:
840 end_line, end = previous[3] 1308 (start_row, start_col) = start
841 start_line, start = token[2] 1309 if prev_row != start_row: # different row
842 if end_line != start_line: # different row 1310 prev_text = self.lines[prev_row - 1][prev_col - 1]
843 if self.lines[end_line - 1][end - 1] not in '{[(': 1311 if prev_text == ',' or (prev_text not in '{[('
844 logical.append(' ') 1312 and text not in '}])'):
845 length += 1 1313 text = ' ' + text
846 elif end != start: # different column 1314 elif prev_col != start_col: # different column
847 fill = self.lines[end_line - 1][end:start] 1315 text = line[prev_col:start_col] + text
848 logical.append(fill)
849 length += len(fill)
850 self.mapping.append((length, token))
851 logical.append(text) 1316 logical.append(text)
852 length += len(text) 1317 length += len(text)
853 previous = token 1318 mapping.append((length, end))
1319 (prev_row, prev_col) = end
854 self.logical_line = ''.join(logical) 1320 self.logical_line = ''.join(logical)
855 assert self.logical_line.lstrip() == self.logical_line 1321 self.noqa = comments and noqa(''.join(comments))
856 assert self.logical_line.rstrip() == self.logical_line 1322 return mapping
857 1323
858 def check_logical(self): 1324 def check_logical(self):
859 """ 1325 """Build a line from tokens and run all logical checks on it."""
860 Build a line from tokens and run all logical checks on it. 1326 self.report.increment_logical_line()
861 """ 1327 mapping = self.build_tokens_line()
862 options.counters['logical lines'] = \ 1328 (start_row, start_col) = mapping[0][1]
863 options.counters.get('logical lines', 0) + 1 1329 start_line = self.lines[start_row - 1]
864 self.build_tokens_line() 1330 self.indent_level = expand_indent(start_line[:start_col])
865 first_line = self.lines[self.mapping[0][1][2][0] - 1] 1331 if self.blank_before < self.blank_lines:
866 indent = first_line[:self.mapping[0][1][2][1]] 1332 self.blank_before = self.blank_lines
867 self.previous_indent_level = self.indent_level 1333 if self.verbose >= 2:
868 self.indent_level = expand_indent(indent)
869 if options.verbose >= 2:
870 print(self.logical_line[:80].rstrip()) 1334 print(self.logical_line[:80].rstrip())
871 for name, check, argument_names in options.logical_checks: 1335 for name, check, argument_names in self._logical_checks:
872 if options.verbose >= 3: 1336 if self.verbose >= 4:
873 print(' ', name) 1337 print(' ' + name)
874 result = self.run_check(check, argument_names) 1338 for offset, text in self.run_check(check, argument_names) or ():
875 if result is not None: 1339 if not isinstance(offset, tuple):
876 offset, text = result 1340 for token_offset, pos in mapping:
877 if isinstance(offset, tuple): 1341 if offset <= token_offset:
878 original_number, original_offset = offset 1342 break
879 else: 1343 offset = (pos[0], pos[1] + offset - token_offset)
880 for token_offset, token in self.mapping: 1344 self.report_error(offset[0], offset[1], text, check)
881 if offset >= token_offset: 1345 if self.logical_line:
882 original_number = token[2][0] 1346 self.previous_indent_level = self.indent_level
883 original_offset = (token[2][1] 1347 self.previous_logical = self.logical_line
884 + offset - token_offset) 1348 self.blank_lines = 0
885 self.report_error(original_number, original_offset, 1349 self.tokens = []
886 text, check) 1350
887 self.previous_logical = self.logical_line 1351 def check_ast(self):
888 1352 """Build the file's AST and run all AST checks."""
889 def check_all(self): 1353 try:
890 """ 1354 tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
891 Run all checks on the input file. 1355 except (SyntaxError, TypeError):
892 """ 1356 return self.report_invalid_syntax()
893 self.file_errors = 0 1357 for name, cls, __ in self._ast_checks:
1358 checker = cls(tree, self.filename)
1359 for lineno, offset, text, check in checker.run():
1360 if not self.lines or not noqa(self.lines[lineno - 1]):
1361 self.report_error(lineno, offset, text, check)
1362
1363 def generate_tokens(self):
1364 """Tokenize the file, run physical line checks and yield tokens."""
1365 if self._io_error:
1366 self.report_error(1, 0, 'E902 %s' % self._io_error, readlines)
1367 tokengen = tokenize.generate_tokens(self.readline)
1368 try:
1369 for token in tokengen:
1370 if token[2][0] > self.total_lines:
1371 return
1372 self.maybe_check_physical(token)
1373 yield token
1374 except (SyntaxError, tokenize.TokenError):
1375 self.report_invalid_syntax()
1376
1377 def maybe_check_physical(self, token):
1378 """If appropriate (based on token), check current physical line(s)."""
1379 # Called after every token, but act only on end of line.
1380 if _is_eol_token(token):
1381 # Obviously, a newline token ends a single physical line.
1382 self.check_physical(token[4])
1383 elif token[0] == tokenize.STRING and '\n' in token[1]:
1384 # Less obviously, a string that contains newlines is a
1385 # multiline string, either triple-quoted or with internal
1386 # newlines backslash-escaped. Check every physical line in the
1387 # string *except* for the last one: its newline is outside of
1388 # the multiline string, so we consider it a regular physical
1389 # line, and will check it like any other physical line.
1390 #
1391 # Subtleties:
1392 # - we don't *completely* ignore the last line; if it contains
1393 # the magical "# noqa" comment, we disable all physical
1394 # checks for the entire multiline string
1395 # - have to wind self.line_number back because initially it
1396 # points to the last line of the string, and we want
1397 # check_physical() to give accurate feedback
1398 if noqa(token[4]):
1399 return
1400 self.multiline = True
1401 self.line_number = token[2][0]
1402 for line in token[1].split('\n')[:-1]:
1403 self.check_physical(line + '\n')
1404 self.line_number += 1
1405 self.multiline = False
1406
1407 def check_all(self, expected=None, line_offset=0):
1408 """Run all checks on the input file."""
1409 self.report.init_file(self.filename, self.lines, expected, line_offset)
1410 self.total_lines = len(self.lines)
1411 if self._ast_checks:
1412 self.check_ast()
894 self.line_number = 0 1413 self.line_number = 0
895 self.indent_char = None 1414 self.indent_char = None
896 self.indent_level = 0 1415 self.indent_level = self.previous_indent_level = 0
897 self.previous_logical = '' 1416 self.previous_logical = ''
898 self.blank_lines = 0
899 self.blank_lines_before_comment = 0
900 self.tokens = [] 1417 self.tokens = []
1418 self.blank_lines = self.blank_before = 0
901 parens = 0 1419 parens = 0
902 for token in tokenize.generate_tokens(self.readline_check_physical): 1420 for token in self.generate_tokens():
903 # print(tokenize.tok_name[token[0]], repr(token))
904 self.tokens.append(token) 1421 self.tokens.append(token)
905 token_type, text = token[0:2] 1422 token_type, text = token[0:2]
906 if token_type == tokenize.OP and text in '([{': 1423 if self.verbose >= 3:
907 parens += 1 1424 if token[2][0] == token[3][0]:
908 if token_type == tokenize.OP and text in '}])': 1425 pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
909 parens -= 1 1426 else:
910 if token_type == tokenize.NEWLINE and not parens: 1427 pos = 'l.%s' % token[3][0]
911 self.check_logical() 1428 print('l.%s\t%s\t%s\t%r' %
912 self.blank_lines = 0 1429 (token[2][0], pos, tokenize.tok_name[token[0]], text))
913 self.blank_lines_before_comment = 0 1430 if token_type == tokenize.OP:
914 self.tokens = [] 1431 if text in '([{':
915 if token_type == tokenize.NL and not parens: 1432 parens += 1
916 if len(self.tokens) <= 1: 1433 elif text in '}])':
917 # The physical line contains only this token. 1434 parens -= 1
918 self.blank_lines += 1 1435 elif not parens:
919 self.tokens = [] 1436 if token_type in NEWLINE:
920 if token_type == tokenize.COMMENT: 1437 if token_type == tokenize.NEWLINE:
921 source_line = token[4] 1438 self.check_logical()
922 token_start = token[2][1] 1439 self.blank_before = 0
923 if source_line[:token_start].strip() == '': 1440 elif len(self.tokens) == 1:
924 self.blank_lines_before_comment = max(self.blank_lines, 1441 # The physical line contains only this token.
925 self.blank_lines_before_comment) 1442 self.blank_lines += 1
926 self.blank_lines = 0 1443 del self.tokens[0]
927 if text.endswith('\n') and not parens: 1444 else:
928 # The comment also ends a physical line. This works around 1445 self.check_logical()
929 # Python < 2.6 behaviour, which does not generate NL after 1446 elif COMMENT_WITH_NL and token_type == tokenize.COMMENT:
930 # a comment which is on a line by itself. 1447 if len(self.tokens) == 1:
931 self.tokens = [] 1448 # The comment also ends a physical line
1449 token = list(token)
1450 token[1] = text.rstrip('\r\n')
1451 token[3] = (token[2][0], token[2][1] + len(token[1]))
1452 self.tokens = [tuple(token)]
1453 self.check_logical()
1454 if self.tokens:
1455 self.check_physical(self.lines[-1])
1456 self.check_logical()
1457 return self.report.get_file_results()
1458
1459
1460 class BaseReport(object):
1461 """Collect the results of the checks."""
1462
1463 print_filename = False
1464
1465 def __init__(self, options):
1466 self._benchmark_keys = options.benchmark_keys
1467 self._ignore_code = options.ignore_code
1468 # Results
1469 self.elapsed = 0
1470 self.total_errors = 0
1471 self.counters = dict.fromkeys(self._benchmark_keys, 0)
1472 self.messages = {}
1473
1474 def start(self):
1475 """Start the timer."""
1476 self._start_time = time.time()
1477
1478 def stop(self):
1479 """Stop the timer."""
1480 self.elapsed = time.time() - self._start_time
1481
1482 def init_file(self, filename, lines, expected, line_offset):
1483 """Signal a new file."""
1484 self.filename = filename
1485 self.lines = lines
1486 self.expected = expected or ()
1487 self.line_offset = line_offset
1488 self.file_errors = 0
1489 self.counters['files'] += 1
1490 self.counters['physical lines'] += len(lines)
1491
1492 def increment_logical_line(self):
1493 """Signal a new logical line."""
1494 self.counters['logical lines'] += 1
1495
1496 def error(self, line_number, offset, text, check):
1497 """Report an error, according to options."""
1498 code = text[:4]
1499 if self._ignore_code(code):
1500 return
1501 if code in self.counters:
1502 self.counters[code] += 1
1503 else:
1504 self.counters[code] = 1
1505 self.messages[code] = text[5:]
1506 # Don't care about expected errors or warnings
1507 if code in self.expected:
1508 return
1509 if self.print_filename and not self.file_errors:
1510 print(self.filename)
1511 self.file_errors += 1
1512 self.total_errors += 1
1513 return code
1514
1515 def get_file_results(self):
1516 """Return the count of errors and warnings for this file."""
932 return self.file_errors 1517 return self.file_errors
933 1518
934 def report_error(self, line_number, offset, text, check): 1519 def get_count(self, prefix=''):
1520 """Return the total count of errors and warnings."""
1521 return sum([self.counters[key]
1522 for key in self.messages if key.startswith(prefix)])
1523
1524 def get_statistics(self, prefix=''):
1525 """Get statistics for message codes that start with the prefix.
1526
1527 prefix='' matches all errors and warnings
1528 prefix='E' matches all errors
1529 prefix='W' matches all warnings
1530 prefix='E4' matches all errors that have to do with imports
935 """ 1531 """
936 Report an error, according to options. 1532 return ['%-7s %s %s' % (self.counters[key], key, self.messages[key])
1533 for key in sorted(self.messages) if key.startswith(prefix)]
1534
1535 def print_statistics(self, prefix=''):
1536 """Print overall statistics (number of errors and warnings)."""
1537 for line in self.get_statistics(prefix):
1538 print(line)
1539
1540 def print_benchmark(self):
1541 """Print benchmark numbers."""
1542 print('%-7.2f %s' % (self.elapsed, 'seconds elapsed'))
1543 if self.elapsed:
1544 for key in self._benchmark_keys:
1545 print('%-7d %s per second (%d total)' %
1546 (self.counters[key] / self.elapsed, key,
1547 self.counters[key]))
1548
1549
1550 class FileReport(BaseReport):
1551 """Collect the results of the checks and print only the filenames."""
1552 print_filename = True
1553
1554
1555 class StandardReport(BaseReport):
1556 """Collect and print the results of the checks."""
1557
1558 def __init__(self, options):
1559 super(StandardReport, self).__init__(options)
1560 self._fmt = REPORT_FORMAT.get(options.format.lower(),
1561 options.format)
1562 self._repeat = options.repeat
1563 self._show_source = options.show_source
1564 self._show_pep8 = options.show_pep8
1565
1566 def init_file(self, filename, lines, expected, line_offset):
1567 """Signal a new file."""
1568 self._deferred_print = []
1569 return super(StandardReport, self).init_file(
1570 filename, lines, expected, line_offset)
1571
1572 def error(self, line_number, offset, text, check):
1573 """Report an error, according to options."""
1574 code = super(StandardReport, self).error(line_number, offset,
1575 text, check)
1576 if code and (self.counters[code] == 1 or self._repeat):
1577 self._deferred_print.append(
1578 (line_number, offset, code, text[5:], check.__doc__))
1579 return code
1580
1581 def get_file_results(self):
1582 """Print the result and return the overall count for this file."""
1583 self._deferred_print.sort()
1584 for line_number, offset, code, text, doc in self._deferred_print:
1585 print(self._fmt % {
1586 'path': self.filename,
1587 'row': self.line_offset + line_number, 'col': offset + 1,
1588 'code': code, 'text': text,
1589 })
1590 if self._show_source:
1591 if line_number > len(self.lines):
1592 line = ''
1593 else:
1594 line = self.lines[line_number - 1]
1595 print(line.rstrip())
1596 print(re.sub(r'\S', ' ', line[:offset]) + '^')
1597 if self._show_pep8 and doc:
1598 print(' ' + doc.strip())
1599 return self.file_errors
1600
1601
1602 class DiffReport(StandardReport):
1603 """Collect and print the results for the changed lines only."""
1604
1605 def __init__(self, options):
1606 super(DiffReport, self).__init__(options)
1607 self._selected = options.selected_lines
1608
1609 def error(self, line_number, offset, text, check):
1610 if line_number not in self._selected[self.filename]:
1611 return
1612 return super(DiffReport, self).error(line_number, offset, text, check)
1613
1614
1615 class StyleGuide(object):
1616 """Initialize a PEP-8 instance with few options."""
1617
1618 def __init__(self, *args, **kwargs):
1619 # build options from the command line
1620 self.checker_class = kwargs.pop('checker_class', Checker)
1621 parse_argv = kwargs.pop('parse_argv', False)
1622 config_file = kwargs.pop('config_file', None)
1623 parser = kwargs.pop('parser', None)
1624 # build options from dict
1625 options_dict = dict(*args, **kwargs)
1626 arglist = None if parse_argv else options_dict.get('paths', None)
1627 options, self.paths = process_options(
1628 arglist, parse_argv, config_file, parser)
1629 if options_dict:
1630 options.__dict__.update(options_dict)
1631 if 'paths' in options_dict:
1632 self.paths = options_dict['paths']
1633
1634 self.runner = self.input_file
1635 self.options = options
1636
1637 if not options.reporter:
1638 options.reporter = BaseReport if options.quiet else StandardReport
1639
1640 options.select = tuple(options.select or ())
1641 if not (options.select or options.ignore or
1642 options.testsuite or options.doctest) and DEFAULT_IGNORE:
1643 # The default choice: ignore controversial checks
1644 options.ignore = tuple(DEFAULT_IGNORE.split(','))
1645 else:
1646 # Ignore all checks which are not explicitly selected
1647 options.ignore = ('',) if options.select else tuple(options.ignore)
1648 options.benchmark_keys = BENCHMARK_KEYS[:]
1649 options.ignore_code = self.ignore_code
1650 options.physical_checks = self.get_checks('physical_line')
1651 options.logical_checks = self.get_checks('logical_line')
1652 options.ast_checks = self.get_checks('tree')
1653 self.init_report()
1654
1655 def init_report(self, reporter=None):
1656 """Initialize the report instance."""
1657 self.options.report = (reporter or self.options.reporter)(self.options)
1658 return self.options.report
1659
1660 def check_files(self, paths=None):
1661 """Run all checks on the paths."""
1662 if paths is None:
1663 paths = self.paths
1664 report = self.options.report
1665 runner = self.runner
1666 report.start()
1667 try:
1668 for path in paths:
1669 if os.path.isdir(path):
1670 self.input_dir(path)
1671 elif not self.excluded(path):
1672 runner(path)
1673 except KeyboardInterrupt:
1674 print('... stopped')
1675 report.stop()
1676 return report
1677
1678 def input_file(self, filename, lines=None, expected=None, line_offset=0):
1679 """Run all checks on a Python source file."""
1680 if self.options.verbose:
1681 print('checking %s' % filename)
1682 fchecker = self.checker_class(
1683 filename, lines=lines, options=self.options)
1684 return fchecker.check_all(expected=expected, line_offset=line_offset)
1685
1686 def input_dir(self, dirname):
1687 """Check all files in this directory and all subdirectories."""
1688 dirname = dirname.rstrip('/')
1689 if self.excluded(dirname):
1690 return 0
1691 counters = self.options.report.counters
1692 verbose = self.options.verbose
1693 filepatterns = self.options.filename
1694 runner = self.runner
1695 for root, dirs, files in os.walk(dirname):
1696 if verbose:
1697 print('directory ' + root)
1698 counters['directories'] += 1
1699 for subdir in sorted(dirs):
1700 if self.excluded(subdir, root):
1701 dirs.remove(subdir)
1702 for filename in sorted(files):
1703 # contain a pattern that matches?
1704 if ((filename_match(filename, filepatterns) and
1705 not self.excluded(filename, root))):
1706 runner(os.path.join(root, filename))
1707
1708 def excluded(self, filename, parent=None):
1709 """Check if the file should be excluded.
1710
1711 Check if 'options.exclude' contains a pattern that matches filename.
937 """ 1712 """
938 if options.quiet == 1 and not self.file_errors: 1713 if not self.options.exclude:
939 message(self.filename) 1714 return False
940 self.file_errors += 1
941 code = text[:4]
942 options.counters[code] = options.counters.get(code, 0) + 1
943 options.messages[code] = text[5:]
944 if options.quiet:
945 return
946 if options.testsuite:
947 basename = os.path.basename(self.filename)
948 if basename[:4] != code:
949 return # Don't care about other errors or warnings
950 if 'not' not in basename:
951 return # Don't print the expected error message
952 if ignore_code(code):
953 return
954 if options.counters[code] == 1 or options.repeat:
955 message("%s:%s:%d: %s" %
956 (self.filename, line_number, offset + 1, text))
957 if options.show_source:
958 line = self.lines[line_number - 1]
959 message(line.rstrip())
960 message(' ' * offset + '^')
961 if options.show_pep8:
962 message(check.__doc__.lstrip('\n').rstrip())
963
964
965 def input_file(filename):
966 """
967 Run all checks on a Python source file.
968 """
969 if excluded(filename):
970 return {}
971 if options.verbose:
972 message('checking ' + filename)
973 files_counter_before = options.counters.get('files', 0)
974 if options.testsuite: # Keep showing errors for multiple tests
975 options.counters = {}
976 options.counters['files'] = files_counter_before + 1
977 errors = Checker(filename).check_all()
978 if options.testsuite: # Check if the expected error was found
979 basename = os.path.basename(filename) 1715 basename = os.path.basename(filename)
980 code = basename[:4] 1716 if filename_match(basename, self.options.exclude):
981 count = options.counters.get(code, 0)
982 if count == 0 and 'not' not in basename:
983 message("%s: error %s not found" % (filename, code))
984
985
986 def input_dir(dirname):
987 """
988 Check all Python source files in this directory and all subdirectories.
989 """
990 dirname = dirname.rstrip('/')
991 if excluded(dirname):
992 return
993 for root, dirs, files in os.walk(dirname):
994 if options.verbose:
995 message('directory ' + root)
996 options.counters['directories'] = \
997 options.counters.get('directories', 0) + 1
998 dirs.sort()
999 for subdir in dirs:
1000 if excluded(subdir):
1001 dirs.remove(subdir)
1002 files.sort()
1003 for filename in files:
1004 if filename_match(filename):
1005 input_file(os.path.join(root, filename))
1006
1007
1008 def excluded(filename):
1009 """
1010 Check if options.exclude contains a pattern that matches filename.
1011 """
1012 basename = os.path.basename(filename)
1013 for pattern in options.exclude:
1014 if fnmatch(basename, pattern):
1015 # print basename, 'excluded because it matches', pattern
1016 return True 1717 return True
1017 1718 if parent:
1018 1719 filename = os.path.join(parent, filename)
1019 def filename_match(filename): 1720 filename = os.path.abspath(filename)
1020 """ 1721 return filename_match(filename, self.options.exclude)
1021 Check if options.filename contains a pattern that matches filename. 1722
1022 If options.filename is unspecified, this always returns True. 1723 def ignore_code(self, code):
1023 """ 1724 """Check if the error code should be ignored.
1024 if not options.filename: 1725
1025 return True 1726 If 'options.select' contains a prefix of the error code,
1026 for pattern in options.filename: 1727 return False. Else, if 'options.ignore' contains a prefix of
1027 if fnmatch(filename, pattern): 1728 the error code, return True.
1028 return True 1729 """
1029 1730 if len(code) < 4 and any(s.startswith(code)
1030 1731 for s in self.options.select):
1031 def ignore_code(code):
1032 """
1033 Check if options.ignore contains a prefix of the error code.
1034 If options.select contains a prefix of the error code, do not ignore it.
1035 """
1036 for select in options.select:
1037 if code.startswith(select):
1038 return False 1732 return False
1039 for ignore in options.ignore: 1733 return (code.startswith(self.options.ignore) and
1040 if code.startswith(ignore): 1734 not code.startswith(self.options.select))
1041 return True 1735
1042 1736 def get_checks(self, argument_name):
1043 1737 """Get all the checks for this category.
1044 def get_error_statistics(): 1738
1045 """Get error statistics.""" 1739 Find all globally visible functions where the first argument name
1046 return get_statistics("E") 1740 starts with argument_name and which contain selected tests.
1047 1741 """
1048 1742 checks = []
1049 def get_warning_statistics(): 1743 for check, attrs in _checks[argument_name].items():
1050 """Get warning statistics.""" 1744 (codes, args) = attrs
1051 return get_statistics("W") 1745 if any(not (code and self.ignore_code(code)) for code in codes):
1052 1746 checks.append((check.__name__, check, args))
1053 1747 return sorted(checks)
1054 def get_statistics(prefix=''): 1748
1055 """ 1749
1056 Get statistics for message codes that start with the prefix. 1750 def get_parser(prog='pep8', version=__version__):
1057 1751 parser = OptionParser(prog=prog, version=version,
1058 prefix='' matches all errors and warnings
1059 prefix='E' matches all errors
1060 prefix='W' matches all warnings
1061 prefix='E4' matches all errors that have to do with imports
1062 """
1063 stats = []
1064 keys = list(options.messages.keys())
1065 keys.sort()
1066 for key in keys:
1067 if key.startswith(prefix):
1068 stats.append('%-7s %s %s' %
1069 (options.counters[key], key, options.messages[key]))
1070 return stats
1071
1072
1073 def get_count(prefix=''):
1074 """Return the total count of errors and warnings."""
1075 keys = list(options.messages.keys())
1076 count = 0
1077 for key in keys:
1078 if key.startswith(prefix):
1079 count += options.counters[key]
1080 return count
1081
1082
1083 def print_statistics(prefix=''):
1084 """Print overall statistics (number of errors and warnings)."""
1085 for line in get_statistics(prefix):
1086 print(line)
1087
1088
1089 def print_benchmark(elapsed):
1090 """
1091 Print benchmark numbers.
1092 """
1093 print('%-7.2f %s' % (elapsed, 'seconds elapsed'))
1094 keys = ['directories', 'files',
1095 'logical lines', 'physical lines']
1096 for key in keys:
1097 if key in options.counters:
1098 print('%-7d %s per second (%d total)' % (
1099 options.counters[key] / elapsed, key,
1100 options.counters[key]))
1101
1102
1103 def selftest():
1104 """
1105 Test all check functions with test cases in docstrings.
1106 """
1107 count_passed = 0
1108 count_failed = 0
1109 checks = options.physical_checks + options.logical_checks
1110 for name, check, argument_names in checks:
1111 for line in check.__doc__.splitlines():
1112 line = line.lstrip()
1113 match = SELFTEST_REGEX.match(line)
1114 if match is None:
1115 continue
1116 code, source = match.groups()
1117 checker = Checker(None)
1118 for part in source.split(r'\n'):
1119 part = part.replace(r'\t', '\t')
1120 part = part.replace(r'\s', ' ')
1121 checker.lines.append(part + '\n')
1122 options.quiet = 2
1123 options.counters = {}
1124 checker.check_all()
1125 error = None
1126 if code == 'Okay':
1127 if len(options.counters) > 1:
1128 codes = [key for key in options.counters.keys()
1129 if key != 'logical lines']
1130 error = "incorrectly found %s" % ', '.join(codes)
1131 elif options.counters.get(code, 0) == 0:
1132 error = "failed to find %s" % code
1133 if not error:
1134 count_passed += 1
1135 else:
1136 count_failed += 1
1137 if len(checker.lines) == 1:
1138 print("pep8.py: %s: %s" %
1139 (error, checker.lines[0].rstrip()))
1140 else:
1141 print("pep8.py: %s:" % error)
1142 for line in checker.lines:
1143 print(line.rstrip())
1144 if options.verbose:
1145 print("%d passed and %d failed." % (count_passed, count_failed))
1146 if count_failed:
1147 print("Test failed.")
1148 else:
1149 print("Test passed.")
1150
1151
1152 def process_options(arglist=None):
1153 """
1154 Process options passed either via arglist or via command line args.
1155 """
1156 global options, args
1157 parser = OptionParser(version=__version__,
1158 usage="%prog [options] input ...") 1752 usage="%prog [options] input ...")
1753 parser.config_options = [
1754 'exclude', 'filename', 'select', 'ignore', 'max-line-length',
1755 'hang-closing', 'count', 'format', 'quiet', 'show-pep8',
1756 'show-source', 'statistics', 'verbose']
1159 parser.add_option('-v', '--verbose', default=0, action='count', 1757 parser.add_option('-v', '--verbose', default=0, action='count',
1160 help="print status messages, or debug with -vv") 1758 help="print status messages, or debug with -vv")
1161 parser.add_option('-q', '--quiet', default=0, action='count', 1759 parser.add_option('-q', '--quiet', default=0, action='count',
1162 help="report only file names, or nothing with -qq") 1760 help="report only file names, or nothing with -qq")
1163 parser.add_option('-r', '--repeat', action='store_true', 1761 parser.add_option('-r', '--repeat', default=True, action='store_true',
1164 help="show all occurrences of the same error") 1762 help="(obsolete) show all occurrences of the same error")
1763 parser.add_option('--first', action='store_false', dest='repeat',
1764 help="show first occurrence of each error")
1165 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, 1765 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE,
1166 help="exclude files or directories which match these " 1766 help="exclude files or directories which match these "
1167 "comma separated patterns (default: %s)" % 1767 "comma separated patterns (default: %default)")
1168 DEFAULT_EXCLUDE)
1169 parser.add_option('--filename', metavar='patterns', default='*.py', 1768 parser.add_option('--filename', metavar='patterns', default='*.py',
1170 help="when parsing directories, only check filenames " 1769 help="when parsing directories, only check filenames "
1171 "matching these comma separated patterns (default: " 1770 "matching these comma separated patterns "
1172 "*.py)") 1771 "(default: %default)")
1173 parser.add_option('--select', metavar='errors', default='', 1772 parser.add_option('--select', metavar='errors', default='',
1174 help="select errors and warnings (e.g. E,W6)") 1773 help="select errors and warnings (e.g. E,W6)")
1175 parser.add_option('--ignore', metavar='errors', default='', 1774 parser.add_option('--ignore', metavar='errors', default='',
1176 help="skip errors and warnings (e.g. E4,W)") 1775 help="skip errors and warnings (e.g. E4,W)")
1177 parser.add_option('--show-source', action='store_true', 1776 parser.add_option('--show-source', action='store_true',
1178 help="show source code for each error") 1777 help="show source code for each error")
1179 parser.add_option('--show-pep8', action='store_true', 1778 parser.add_option('--show-pep8', action='store_true',
1180 help="show text of PEP 8 for each error") 1779 help="show text of PEP 8 for each error "
1780 "(implies --first)")
1181 parser.add_option('--statistics', action='store_true', 1781 parser.add_option('--statistics', action='store_true',
1182 help="count errors and warnings") 1782 help="count errors and warnings")
1183 parser.add_option('--count', action='store_true', 1783 parser.add_option('--count', action='store_true',
1184 help="print total number of errors and warnings " 1784 help="print total number of errors and warnings "
1185 "to standard error and set exit code to 1 if " 1785 "to standard error and set exit code to 1 if "
1186 "total is not null") 1786 "total is not null")
1187 parser.add_option('--benchmark', action='store_true', 1787 parser.add_option('--max-line-length', type='int', metavar='n',
1188 help="measure processing speed") 1788 default=MAX_LINE_LENGTH,
1189 parser.add_option('--testsuite', metavar='dir', 1789 help="set maximum allowed line length "
1190 help="run regression tests from dir") 1790 "(default: %default)")
1191 parser.add_option('--doctest', action='store_true', 1791 parser.add_option('--hang-closing', action='store_true',
1192 help="run doctest on myself") 1792 help="hang closing bracket instead of matching "
1193 options, args = parser.parse_args(arglist) 1793 "indentation of opening bracket's line")
1194 if options.testsuite: 1794 parser.add_option('--format', metavar='format', default='default',
1795 help="set the error format [default|pylint|<custom>]")
1796 parser.add_option('--diff', action='store_true',
1797 help="report only lines changed according to the "
1798 "unified diff received on STDIN")
1799 group = parser.add_option_group("Testing Options")
1800 if os.path.exists(TESTSUITE_PATH):
1801 group.add_option('--testsuite', metavar='dir',
1802 help="run regression tests from dir")
1803 group.add_option('--doctest', action='store_true',
1804 help="run doctest on myself")
1805 group.add_option('--benchmark', action='store_true',
1806 help="measure processing speed")
1807 return parser
1808
1809
1810 def read_config(options, args, arglist, parser):
1811 """Read both user configuration and local configuration."""
1812 config = RawConfigParser()
1813
1814 user_conf = options.config
1815 if user_conf and os.path.isfile(user_conf):
1816 if options.verbose:
1817 print('user configuration: %s' % user_conf)
1818 config.read(user_conf)
1819
1820 local_dir = os.curdir
1821 parent = tail = args and os.path.abspath(os.path.commonprefix(args))
1822 while tail:
1823 if config.read([os.path.join(parent, fn) for fn in PROJECT_CONFIG]):
1824 local_dir = parent
1825 if options.verbose:
1826 print('local configuration: in %s' % parent)
1827 break
1828 (parent, tail) = os.path.split(parent)
1829
1830 pep8_section = parser.prog
1831 if config.has_section(pep8_section):
1832 option_list = dict([(o.dest, o.type or o.action)
1833 for o in parser.option_list])
1834
1835 # First, read the default values
1836 (new_options, __) = parser.parse_args([])
1837
1838 # Second, parse the configuration
1839 for opt in config.options(pep8_section):
1840 if opt.replace('_', '-') not in parser.config_options:
1841 print(" unknown option '%s' ignored" % opt)
1842 continue
1843 if options.verbose > 1:
1844 print(" %s = %s" % (opt, config.get(pep8_section, opt)))
1845 normalized_opt = opt.replace('-', '_')
1846 opt_type = option_list[normalized_opt]
1847 if opt_type in ('int', 'count'):
1848 value = config.getint(pep8_section, opt)
1849 elif opt_type == 'string':
1850 value = config.get(pep8_section, opt)
1851 if normalized_opt == 'exclude':
1852 value = normalize_paths(value, local_dir)
1853 else:
1854 assert opt_type in ('store_true', 'store_false')
1855 value = config.getboolean(pep8_section, opt)
1856 setattr(new_options, normalized_opt, value)
1857
1858 # Third, overwrite with the command-line options
1859 (options, __) = parser.parse_args(arglist, values=new_options)
1860 options.doctest = options.testsuite = False
1861 return options
1862
1863
1864 def process_options(arglist=None, parse_argv=False, config_file=None,
1865 parser=None):
1866 """Process options passed either via arglist or via command line args."""
1867 if not parser:
1868 parser = get_parser()
1869 if not parser.has_option('--config'):
1870 if config_file is True:
1871 config_file = DEFAULT_CONFIG
1872 group = parser.add_option_group("Configuration", description=(
1873 "The project options are read from the [%s] section of the "
1874 "tox.ini file or the setup.cfg file located in any parent folder "
1875 "of the path(s) being processed. Allowed options are: %s." %
1876 (parser.prog, ', '.join(parser.config_options))))
1877 group.add_option('--config', metavar='path', default=config_file,
1878 help="user config file location (default: %default)")
1879 # Don't read the command line if the module is used as a library.
1880 if not arglist and not parse_argv:
1881 arglist = []
1882 # If parse_argv is True and arglist is None, arguments are
1883 # parsed from the command line (sys.argv)
1884 (options, args) = parser.parse_args(arglist)
1885 options.reporter = None
1886
1887 if options.ensure_value('testsuite', False):
1195 args.append(options.testsuite) 1888 args.append(options.testsuite)
1196 if len(args) == 0 and not options.doctest: 1889 elif not options.ensure_value('doctest', False):
1197 parser.error('input not specified') 1890 if parse_argv and not args:
1198 options.prog = os.path.basename(sys.argv[0]) 1891 if options.diff or any(os.path.exists(name)
1199 options.exclude = options.exclude.split(',') 1892 for name in PROJECT_CONFIG):
1200 for index in range(len(options.exclude)): 1893 args = ['.']
1201 options.exclude[index] = options.exclude[index].rstrip('/') 1894 else:
1202 if options.filename: 1895 parser.error('input not specified')
1203 options.filename = options.filename.split(',') 1896 options = read_config(options, args, arglist, parser)
1204 if options.select: 1897 options.reporter = parse_argv and options.quiet == 1 and FileReport
1205 options.select = options.select.split(',') 1898
1206 else: 1899 options.filename = options.filename and options.filename.split(',')
1207 options.select = [] 1900 options.exclude = normalize_paths(options.exclude)
1208 if options.ignore: 1901 options.select = options.select and options.select.split(',')
1209 options.ignore = options.ignore.split(',') 1902 options.ignore = options.ignore and options.ignore.split(',')
1210 elif options.select: 1903
1211 # Ignore all checks which are not explicitly selected 1904 if options.diff:
1212 options.ignore = [''] 1905 options.reporter = DiffReport
1213 elif options.testsuite or options.doctest: 1906 stdin = stdin_get_value()
1214 # For doctest and testsuite, all checks are required 1907 options.selected_lines = parse_udiff(stdin, options.filename, args[0])
1215 options.ignore = [] 1908 args = sorted(options.selected_lines)
1216 else: 1909
1217 # The default choice: ignore controversial checks
1218 options.ignore = DEFAULT_IGNORE
1219 options.physical_checks = find_checks('physical_line')
1220 options.logical_checks = find_checks('logical_line')
1221 options.counters = {}
1222 options.messages = {}
1223 return options, args 1910 return options, args
1224 1911
1225 1912
1226 def _main(): 1913 def _main():
1227 """ 1914 """Parse options and run checks on Python source."""
1228 Parse options and run checks on Python source. 1915 import signal
1229 """ 1916
1230 options, args = process_options() 1917 # Handle "Broken pipe" gracefully
1231 if options.doctest: 1918 try:
1232 import doctest 1919 signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1))
1233 doctest.testmod(verbose=options.verbose) 1920 except AttributeError:
1234 selftest() 1921 pass # not supported on Windows
1235 start_time = time.time() 1922
1236 for path in args: 1923 pep8style = StyleGuide(parse_argv=True, config_file=True)
1237 if os.path.isdir(path): 1924 options = pep8style.options
1238 input_dir(path) 1925 if options.doctest or options.testsuite:
1239 else: 1926 from testsuite.support import run_tests
1240 input_file(path) 1927 report = run_tests(pep8style)
1241 elapsed = time.time() - start_time 1928 else:
1929 report = pep8style.check_files()
1242 if options.statistics: 1930 if options.statistics:
1243 print_statistics() 1931 report.print_statistics()
1244 if options.benchmark: 1932 if options.benchmark:
1245 print_benchmark(elapsed) 1933 report.print_benchmark()
1246 if options.count: 1934 if options.testsuite and not options.quiet:
1247 count = get_count() 1935 report.print_results()
1248 if count: 1936 if report.total_errors:
1249 sys.stderr.write(str(count) + '\n') 1937 if options.count:
1250 sys.exit(1) 1938 sys.stderr.write(str(report.total_errors) + '\n')
1251 1939 sys.exit(1)
1252 1940
1253 if __name__ == '__main__': 1941 if __name__ == '__main__':
1254 _main() 1942 _main()
OLDNEW
« no previous file with comments | « Tools/Scripts/webkitpy/thirdparty/autopep8.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698