OLD | NEW |
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() |
OLD | NEW |