| 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 |