eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py

branch
eric7
changeset 8312
800c432b34c8
parent 8218
7c09585bd960
child 8682
04e80d1aaebf
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # pycodestyle.py - Check Python source code formatting, according to
5 # PEP 8
6 #
7 # Copyright (C) 2006-2009 Johann C. Rocholl <johann@rocholl.net>
8 # Copyright (C) 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
9 # Copyright (C) 2014-2016 Ian Lee <ianlee1521@gmail.com>
10 #
11 # Permission is hereby granted, free of charge, to any person
12 # obtaining a copy of this software and associated documentation files
13 # (the "Software"), to deal in the Software without restriction,
14 # including without limitation the rights to use, copy, modify, merge,
15 # publish, distribute, sublicense, and/or sell copies of the Software,
16 # and to permit persons to whom the Software is furnished to do so,
17 # subject to the following conditions:
18 #
19 # The above copyright notice and this permission notice shall be
20 # included in all copies or substantial portions of the Software.
21 #
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 # SOFTWARE.
30
31 r"""
32 Check Python source code formatting, according to PEP 8.
33
34 For usage and a list of options, try this:
35 $ python pycodestyle.py -h
36
37 This program and its regression test suite live here:
38 https://github.com/pycqa/pycodestyle
39
40 Groups of errors and warnings:
41 E errors
42 W warnings
43 100 indentation
44 200 whitespace
45 300 blank lines
46 400 imports
47 500 line length
48 600 deprecation
49 700 statements
50 900 syntax error
51 """
52
53 #
54 # This is a modified version to make the original pycodestyle.py better
55 # suitable for being called from within the eric IDE. The modifications
56 # are as follows:
57 #
58 # - made messages translatable via Qt
59 # - added code for eric integration
60 #
61 # Copyright (c) 2011 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
62 #
63
64 import inspect
65 import keyword
66 import os
67 import re
68 import sys
69 import time
70 import tokenize
71 import warnings
72 import bisect
73
74 try:
75 from functools import lru_cache
76 except ImportError:
77 def lru_cache(maxsize=128): # noqa as it's a fake implementation.
78 """Does not really need a real a lru_cache, it's just
79 optimization, so let's just do nothing here. Python 3.2+ will
80 just get better performances, time to upgrade?
81 """
82 return lambda function: function
83
84 from fnmatch import fnmatch
85 from optparse import OptionParser
86
87 try:
88 from configparser import RawConfigParser
89 from io import TextIOWrapper
90 except ImportError:
91 from ConfigParser import RawConfigParser # __IGNORE_WARNING__
92
93 __version__ = '2.7.0-eric'
94
95 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox'
96 DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504'
97 try:
98 if sys.platform == 'win32':
99 USER_CONFIG = os.path.expanduser(r'~\.pycodestyle')
100 else:
101 USER_CONFIG = os.path.join(
102 os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'),
103 'pycodestyle'
104 )
105 except ImportError:
106 USER_CONFIG = None
107
108 PROJECT_CONFIG = ('setup.cfg', 'tox.ini')
109 TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
110 MAX_LINE_LENGTH = 79
111 # Number of blank lines between various code parts.
112 BLANK_LINES_CONFIG = {
113 # Top level class and function.
114 'top_level': 2,
115 # Methods and nested class and function.
116 'method': 1,
117 }
118 MAX_DOC_LENGTH = 72
119 INDENT_SIZE = 4
120 REPORT_FORMAT = {
121 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
122 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
123 }
124
125 PyCF_ONLY_AST = 1024
126 SINGLETONS = frozenset(['False', 'None', 'True'])
127 KEYWORDS = frozenset(keyword.kwlist + ['print', 'async']) - SINGLETONS
128 UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
129 ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-', '@'])
130 WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%'])
131 # Warn for -> function annotation operator in py3.5+ (issue 803)
132 FUNCTION_RETURN_ANNOTATION_OP = ['->'] if sys.version_info >= (3, 5) else []
133 ASSIGNMENT_EXPRESSION_OP = [':='] if sys.version_info >= (3, 8) else []
134 WS_NEEDED_OPERATORS = frozenset([
135 '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>',
136 '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=',
137 'and', 'in', 'is', 'or'] +
138 FUNCTION_RETURN_ANNOTATION_OP +
139 ASSIGNMENT_EXPRESSION_OP)
140 WHITESPACE = frozenset(' \t')
141 NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
142 SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT])
143 # ERRORTOKEN is triggered by backticks in Python 3
144 SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN])
145 BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines']
146
147 INDENT_REGEX = re.compile(r'([ \t]*)')
148 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
149 RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
150 ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
151 DOCSTRING_REGEX = re.compile(r'u?r?["\']')
152 EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;]| :(?!=)')
153 WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
154 COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)'
155 r'\s*(?(1)|(None|False|True))\b')
156 COMPARE_NEGATIVE_REGEX = re.compile(r'\b(?<!is\s)(not)\s+[^][)(}{ ]+\s+'
157 r'(in|is)\s')
158 COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s+type(?:s.\w+Type'
159 r'|\s*\(\s*([^)]*[^ )])\s*\))')
160 KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS))
161 OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)')
162 LAMBDA_REGEX = re.compile(r'\blambda\b')
163 HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
164 STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\b')
165 STARTSWITH_TOP_LEVEL_REGEX = re.compile(r'^(async\s+def\s+|def\s+|class\s+|@)')
166 STARTSWITH_INDENT_STATEMENT_REGEX = re.compile(
167 r'^\s*({0})\b'.format('|'.join(s.replace(' ', r'\s+') for s in (
168 'def', 'async def',
169 'for', 'async for',
170 'if', 'elif', 'else',
171 'try', 'except', 'finally',
172 'with', 'async with',
173 'class',
174 'while',
175 )))
176 )
177 DUNDER_REGEX = re.compile(r'^__([^\s]+)__ = ')
178
179 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}}
180
181
182 def _get_parameters(function):
183 if sys.version_info >= (3, 3):
184 return [parameter.name
185 for parameter
186 in inspect.signature(function).parameters.values()
187 if parameter.kind == parameter.POSITIONAL_OR_KEYWORD]
188 else:
189 return inspect.getargspec(function)[0]
190
191
192 def register_check(check, codes=None):
193 """Register a new check object."""
194 def _add_check(check, kind, codes, args):
195 if check in _checks[kind]:
196 _checks[kind][check][0].extend(codes or [])
197 else:
198 _checks[kind][check] = (codes or [''], args)
199 if inspect.isfunction(check):
200 args = _get_parameters(check)
201 if args and args[0] in ('physical_line', 'logical_line'):
202 if codes is None:
203 codes = ERRORCODE_REGEX.findall(check.__doc__ or '')
204 _add_check(check, args[0], codes, args)
205 elif inspect.isclass(check):
206 if _get_parameters(check.__init__)[:2] == ['self', 'tree']:
207 _add_check(check, 'tree', codes, None)
208 return check
209
210
211 ########################################################################
212 # Plugins (check functions) for physical lines
213 ########################################################################
214
215 @register_check
216 def tabs_or_spaces(physical_line, indent_char):
217 r"""Never mix tabs and spaces.
218
219 The most popular way of indenting Python is with spaces only. The
220 second-most popular way is with tabs only. Code indented with a
221 mixture of tabs and spaces should be converted to using spaces
222 exclusively. When invoking the Python command line interpreter with
223 the -t option, it issues warnings about code that illegally mixes
224 tabs and spaces. When using -tt these warnings become errors.
225 These options are highly recommended!
226
227 Okay: if a == 0:\n a = 1\n b = 1
228 E101: if a == 0:\n a = 1\n\tb = 1
229 """
230 indent = INDENT_REGEX.match(physical_line).group(1)
231 for offset, char in enumerate(indent):
232 if char != indent_char:
233 return offset, "E101 indentation contains mixed spaces and tabs"
234
235
236 @register_check
237 def tabs_obsolete(physical_line):
238 r"""On new projects, spaces-only are strongly recommended over tabs.
239
240 Okay: if True:\n return
241 W191: if True:\n\treturn
242 """
243 indent = INDENT_REGEX.match(physical_line).group(1)
244 if '\t' in indent:
245 return indent.index('\t'), "W191 indentation contains tabs"
246
247
248 @register_check
249 def trailing_whitespace(physical_line):
250 r"""Trailing whitespace is superfluous.
251
252 The warning returned varies on whether the line itself is blank,
253 for easier filtering for those who want to indent their blank lines.
254
255 Okay: spam(1)\n#
256 W291: spam(1) \n#
257 W293: class Foo(object):\n \n bang = 12
258 """
259 physical_line = physical_line.rstrip('\n') # chr(10), newline
260 physical_line = physical_line.rstrip('\r') # chr(13), carriage return
261 physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L
262 stripped = physical_line.rstrip(' \t\v')
263 if physical_line != stripped:
264 if stripped:
265 return len(stripped), "W291 trailing whitespace"
266 else:
267 return 0, "W293 blank line contains whitespace"
268
269
270 @register_check
271 def trailing_blank_lines(physical_line, lines, line_number, total_lines):
272 r"""Trailing blank lines are superfluous.
273
274 Okay: spam(1)
275 W391: spam(1)\n
276
277 However the last line should end with a new line (warning W292).
278 """
279 if line_number == total_lines:
280 stripped_last_line = physical_line.rstrip('\r\n')
281 if physical_line and not stripped_last_line:
282 return 0, "W391 blank line at end of file"
283 if stripped_last_line == physical_line:
284 return len(lines[-1]), "W292 no newline at end of file"
285
286
287 @register_check
288 def maximum_line_length(physical_line, max_line_length, multiline,
289 line_number, noqa):
290 r"""Limit all lines to a maximum of 79 characters.
291
292 There are still many devices around that are limited to 80 character
293 lines; plus, limiting windows to 80 characters makes it possible to
294 have several windows side-by-side. The default wrapping on such
295 devices looks ugly. Therefore, please limit all lines to a maximum
296 of 79 characters. For flowing long blocks of text (docstrings or
297 comments), limiting the length to 72 characters is recommended.
298
299 Reports error E501.
300 """
301 line = physical_line.rstrip()
302 length = len(line)
303 if length > max_line_length and not noqa:
304 # Special case: ignore long shebang lines.
305 if line_number == 1 and line.startswith('#!'):
306 return
307 # Special case for long URLs in multi-line docstrings or
308 # comments, but still report the error when the 72 first chars
309 # are whitespaces.
310 chunks = line.split()
311 if ((len(chunks) == 1 and multiline) or
312 (len(chunks) == 2 and chunks[0] == '#')) and \
313 len(line) - len(chunks[-1]) < max_line_length - 7:
314 return
315 if hasattr(line, 'decode'): # Python 2
316 # The line could contain multi-byte characters
317 try:
318 length = len(line.decode('utf-8'))
319 except UnicodeError:
320 pass
321 if length > max_line_length:
322 return (max_line_length, "E501 line too long "
323 "(%d > %d characters)" % (length, max_line_length),
324 length, max_line_length)
325
326
327 ########################################################################
328 # Plugins (check functions) for logical lines
329 ########################################################################
330
331
332 def _is_one_liner(logical_line, indent_level, lines, line_number):
333 if not STARTSWITH_TOP_LEVEL_REGEX.match(logical_line):
334 return False
335
336 line_idx = line_number - 1
337
338 if line_idx < 1:
339 prev_indent = 0
340 else:
341 prev_indent = expand_indent(lines[line_idx - 1])
342
343 if prev_indent > indent_level:
344 return False
345
346 while line_idx < len(lines):
347 line = lines[line_idx].strip()
348 if not line.startswith('@') and STARTSWITH_TOP_LEVEL_REGEX.match(line):
349 break
350 else:
351 line_idx += 1
352 else:
353 return False # invalid syntax: EOF while searching for def/class
354
355 next_idx = line_idx + 1
356 while next_idx < len(lines):
357 if lines[next_idx].strip():
358 break
359 else:
360 next_idx += 1
361 else:
362 return True # line is last in the file
363
364 return expand_indent(lines[next_idx]) <= indent_level
365
366
367 @register_check
368 def blank_lines(logical_line, blank_lines, indent_level, line_number,
369 blank_before, previous_logical,
370 previous_unindented_logical_line, previous_indent_level,
371 lines):
372 r"""Separate top-level function and class definitions with two blank
373 lines.
374
375 Method definitions inside a class are separated by a single blank
376 line.
377
378 Extra blank lines may be used (sparingly) to separate groups of
379 related functions. Blank lines may be omitted between a bunch of
380 related one-liners (e.g. a set of dummy implementations).
381
382 Use blank lines in functions, sparingly, to indicate logical
383 sections.
384
385 Okay: def a():\n pass\n\n\ndef b():\n pass
386 Okay: def a():\n pass\n\n\nasync def b():\n pass
387 Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass
388 Okay: default = 1\nfoo = 1
389 Okay: classify = 1\nfoo = 1
390
391 E301: class Foo:\n b = 0\n def bar():\n pass
392 E302: def a():\n pass\n\ndef b(n):\n pass
393 E302: def a():\n pass\n\nasync def b(n):\n pass
394 E303: def a():\n pass\n\n\n\ndef b(n):\n pass
395 E303: def a():\n\n\n\n pass
396 E304: @decorator\n\ndef a():\n pass
397 E305: def a():\n pass\na()
398 E306: def a():\n def b():\n pass\n def c():\n pass
399 E307: def a():\n def b():\n pass\n\n\n def c():\n pass
400 E308: def a():\n\n\n\n pass
401 """ # noqa
402 top_level_lines = BLANK_LINES_CONFIG['top_level']
403 method_lines = BLANK_LINES_CONFIG['method']
404
405 if not previous_logical and blank_before < top_level_lines:
406 return # Don't expect blank lines before the first line
407 if previous_logical.startswith('@'):
408 if blank_lines:
409 yield 0, "E304 blank lines found after function decorator"
410 elif (blank_lines > top_level_lines or
411 (indent_level and blank_lines == method_lines + 1)
412 ):
413 if indent_level:
414 if previous_logical.strip().startswith(('def ', 'class ')):
415 yield (0, "E308 too many blank lines (%d)", blank_lines)
416 else:
417 yield (0, "E307 too many blank lines (%d) in a nested "
418 "scope, expected %d", blank_lines, method_lines)
419 else:
420 yield (0, "E303 too many blank lines (%d), expected %d",
421 blank_lines, top_level_lines)
422 elif STARTSWITH_TOP_LEVEL_REGEX.match(logical_line):
423 # allow a group of one-liners
424 if (
425 _is_one_liner(logical_line, indent_level, lines, line_number) and
426 blank_before == 0
427 ):
428 return
429 if indent_level:
430 if not (blank_before == method_lines or
431 previous_indent_level < indent_level or
432 DOCSTRING_REGEX.match(previous_logical)
433 ):
434 ancestor_level = indent_level
435 nested = False
436 # Search backwards for a def ancestor or tree root
437 # (top level).
438 for line in lines[line_number - top_level_lines::-1]:
439 if line.strip() and expand_indent(line) < ancestor_level:
440 ancestor_level = expand_indent(line)
441 nested = STARTSWITH_DEF_REGEX.match(line.lstrip())
442 if nested or ancestor_level == 0:
443 break
444 if nested:
445 yield (0, "E306 expected %s blank lines before a "
446 "nested definition, found %d", method_lines,
447 blank_before)
448 else:
449 yield (0, "E301 expected %s blank lines, found %d",
450 method_lines, blank_before)
451 elif blank_before != top_level_lines:
452 yield (0, "E302 expected %s blank lines, found %d",
453 top_level_lines, blank_before)
454 elif (logical_line and
455 not indent_level and
456 blank_before != top_level_lines and
457 previous_unindented_logical_line.startswith(('def ', 'class '))
458 ):
459 yield (0, "E305 expected %s blank lines after " \
460 "class or function definition, found %d",
461 top_level_lines, blank_before)
462
463
464 @register_check
465 def extraneous_whitespace(logical_line):
466 r"""Avoid extraneous whitespace.
467
468 Avoid extraneous whitespace in these situations:
469 - Immediately inside parentheses, brackets or braces.
470 - Immediately before a comma, semicolon, or colon.
471
472 Okay: spam(ham[1], {eggs: 2})
473 E201: spam( ham[1], {eggs: 2})
474 E201: spam(ham[ 1], {eggs: 2})
475 E201: spam(ham[1], { eggs: 2})
476 E202: spam(ham[1], {eggs: 2} )
477 E202: spam(ham[1 ], {eggs: 2})
478 E202: spam(ham[1], {eggs: 2 })
479
480 E203: if x == 4: print x, y; x, y = y , x
481 E203: if x == 4: print x, y ; x, y = y, x
482 E203: if x == 4 : print x, y; x, y = y, x
483 """
484 line = logical_line
485 for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
486 text = match.group()
487 char = text.strip()
488 found = match.start()
489 if text == char + ' ':
490 # assert char in '([{'
491 yield found + 1, "E201 whitespace after '%s'", char
492 elif line[found - 1] != ',':
493 code = ('E202' if char in '}])' else 'E203') # if char in ',;:'
494 yield found, "%s whitespace before '%s'" % (code, char), char
495
496
497 @register_check
498 def whitespace_around_keywords(logical_line):
499 r"""Avoid extraneous whitespace around keywords.
500
501 Okay: True and False
502 E271: True and False
503 E272: True and False
504 E273: True and\tFalse
505 E274: True\tand False
506 """
507 for match in KEYWORD_REGEX.finditer(logical_line):
508 before, after = match.groups()
509
510 if '\t' in before:
511 yield match.start(1), "E274 tab before keyword"
512 elif len(before) > 1:
513 yield match.start(1), "E272 multiple spaces before keyword"
514
515 if '\t' in after:
516 yield match.start(2), "E273 tab after keyword"
517 elif len(after) > 1:
518 yield match.start(2), "E271 multiple spaces after keyword"
519
520
521 @register_check
522 def missing_whitespace_after_import_keyword(logical_line):
523 r"""Multiple imports in form from x import (a, b, c) should have
524 space between import statement and parenthesised name list.
525
526 Okay: from foo import (bar, baz)
527 E275: from foo import(bar, baz)
528 E275: from importable.module import(bar, baz)
529 """
530 line = logical_line
531 indicator = ' import('
532 if line.startswith('from '):
533 found = line.find(indicator)
534 if -1 < found:
535 pos = found + len(indicator) - 1
536 yield pos, "E275 missing whitespace after keyword"
537
538
539 @register_check
540 def missing_whitespace(logical_line):
541 r"""Each comma, semicolon or colon should be followed by whitespace.
542
543 Okay: [a, b]
544 Okay: (3,)
545 Okay: a[1:4]
546 Okay: a[:4]
547 Okay: a[1:]
548 Okay: a[1:4:2]
549 E231: ['a','b']
550 E231: foo(bar,baz)
551 E231: [{'a':'b'}]
552 """
553 line = logical_line
554 for index in range(len(line) - 1):
555 char = line[index]
556 next_char = line[index + 1]
557 if char in ',;:' and next_char not in WHITESPACE:
558 before = line[:index]
559 if char == ':' and before.count('[') > before.count(']') and \
560 before.rfind('{') < before.rfind('['):
561 continue # Slice syntax, no space required
562 if char == ',' and next_char == ')':
563 continue # Allow tuple with only one element: (3,)
564 if char == ':' and next_char == '=' and sys.version_info >= (3, 8):
565 continue # Allow assignment expression
566 yield index, "E231 missing whitespace after '%s'", char
567
568
569 @register_check
570 def indentation(logical_line, previous_logical, indent_char,
571 indent_level, previous_indent_level,
572 indent_size, indent_size_str):
573 r"""Use indent_size (PEP8 says 4) spaces per indentation level.
574
575 For really old code that you don't want to mess up, you can continue
576 to use 8-space tabs.
577
578 Okay: a = 1
579 Okay: if a == 0:\n a = 1
580 E111: a = 1
581 E114: # a = 1
582
583 Okay: for item in items:\n pass
584 E112: for item in items:\npass
585 E115: for item in items:\n# Hi\n pass
586
587 Okay: a = 1\nb = 2
588 E113: a = 1\n b = 2
589 E116: a = 1\n # b = 2
590 """
591 c = 0 if logical_line else 3
592 tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)"
593 if indent_level % indent_size:
594 yield 0, tmpl % (
595 1 + c,
596 "indentation is not a multiple of " + indent_size_str,
597 )
598 indent_expect = previous_logical.endswith(':')
599 if indent_expect and indent_level <= previous_indent_level:
600 yield 0, tmpl % (2 + c, "expected an indented block")
601 elif not indent_expect and indent_level > previous_indent_level:
602 yield 0, tmpl % (3 + c, "unexpected indentation")
603
604 if indent_expect:
605 expected_indent_amount = 8 if indent_char == '\t' else 4
606 expected_indent_level = previous_indent_level + expected_indent_amount
607 if indent_level > expected_indent_level:
608 yield 0, tmpl % (7, 'over-indented')
609
610
611 @register_check
612 def continued_indentation(logical_line, tokens, indent_level, hang_closing,
613 indent_char, indent_size, indent_size_str, noqa,
614 verbose):
615 r"""Continuation lines indentation.
616
617 Continuation lines should align wrapped elements either vertically
618 using Python's implicit line joining inside parentheses, brackets
619 and braces, or using a hanging indent.
620
621 When using a hanging indent these considerations should be applied:
622 - there should be no arguments on the first line, and
623 - further indentation should be used to clearly distinguish itself
624 as a continuation line.
625
626 Okay: a = (\n)
627 E123: a = (\n )
628
629 Okay: a = (\n 42)
630 E121: a = (\n 42)
631 E122: a = (\n42)
632 E123: a = (\n 42\n )
633 E124: a = (24,\n 42\n)
634 E125: if (\n b):\n pass
635 E126: a = (\n 42)
636 E127: a = (24,\n 42)
637 E128: a = (24,\n 42)
638 E129: if (a or\n b):\n pass
639 E131: a = (\n 42\n 24)
640 """
641 first_row = tokens[0][2][0]
642 nrows = 1 + tokens[-1][2][0] - first_row
643 if noqa or nrows == 1:
644 return
645
646 # indent_next tells us whether the next block is indented; assuming
647 # that it is indented by 4 spaces, then we should not allow 4-space
648 # indents on the final continuation line; in turn, some other
649 # indents are allowed to have an extra 4 spaces.
650 indent_next = logical_line.endswith(':')
651
652 row = depth = 0
653 valid_hangs = (indent_size,) if indent_char != '\t' \
654 else (indent_size, indent_size * 2)
655 # remember how many brackets were opened on each line
656 parens = [0] * nrows
657 # relative indents of physical lines
658 rel_indent = [0] * nrows
659 # for each depth, collect a list of opening rows
660 open_rows = [[0]]
661 # for each depth, memorize the hanging indentation
662 hangs = [None]
663 # visual indents
664 indent_chances = {}
665 last_indent = tokens[0][2]
666 visual_indent = None
667 last_token_multiline = False
668 # for each depth, memorize the visual indent column
669 indent = [last_indent[1]]
670 if verbose >= 3:
671 print(">>> " + tokens[0][4].rstrip())
672
673 for token_type, text, start, end, line in tokens:
674
675 newline = row < start[0] - first_row
676 if newline:
677 row = start[0] - first_row
678 newline = not last_token_multiline and token_type not in NEWLINE
679
680 if newline:
681 # this is the beginning of a continuation line.
682 last_indent = start
683 if verbose >= 3:
684 print("... " + line.rstrip())
685
686 # record the initial indent.
687 rel_indent[row] = expand_indent(line) - indent_level
688
689 # identify closing bracket
690 close_bracket = (token_type == tokenize.OP and text in ']})')
691
692 # is the indent relative to an opening bracket line?
693 for open_row in reversed(open_rows[depth]):
694 hang = rel_indent[row] - rel_indent[open_row]
695 hanging_indent = hang in valid_hangs
696 if hanging_indent:
697 break
698 if hangs[depth]:
699 hanging_indent = (hang == hangs[depth])
700 # is there any chance of visual indent?
701 visual_indent = (not close_bracket and hang > 0 and
702 indent_chances.get(start[1]))
703
704 if close_bracket and indent[depth]:
705 # closing bracket for visual indent
706 if start[1] != indent[depth]:
707 yield (start, "E124 closing bracket does not match "
708 "visual indentation")
709 elif close_bracket and not hang:
710 # closing bracket matches indentation of opening
711 # bracket's line
712 if hang_closing:
713 yield start, "E133 closing bracket is missing indentation"
714 elif indent[depth] and start[1] < indent[depth]:
715 if visual_indent is not True:
716 # visual indent is broken
717 yield (start, "E128 continuation line "
718 "under-indented for visual indent")
719 elif hanging_indent or (indent_next and
720 rel_indent[row] == 2 * indent_size):
721 # hanging indent is verified
722 if close_bracket and not hang_closing:
723 yield (start, "E123 closing bracket does not match "
724 "indentation of opening bracket's line")
725 hangs[depth] = hang
726 elif visual_indent is True:
727 # visual indent is verified
728 indent[depth] = start[1]
729 elif visual_indent in (text, str):
730 # ignore token lined up with matching one from a
731 # previous line
732 pass
733 else:
734 # indent is broken
735 if hang <= 0:
736 error = "E122", "missing indentation or outdented"
737 elif indent[depth]:
738 error = "E127", "over-indented for visual indent"
739 elif not close_bracket and hangs[depth]:
740 error = "E131", "unaligned for hanging indent"
741 else:
742 hangs[depth] = hang
743 if hang > indent_size:
744 error = "E126", "over-indented for hanging indent"
745 else:
746 error = "E121", "under-indented for hanging indent"
747 yield start, "%s continuation line %s" % error
748
749 # look for visual indenting
750 if (parens[row] and
751 token_type not in (tokenize.NL, tokenize.COMMENT) and
752 not indent[depth]):
753 indent[depth] = start[1]
754 indent_chances[start[1]] = True
755 if verbose >= 4:
756 print("bracket depth %s indent to %s" % (depth, start[1]))
757 # deal with implicit string concatenation
758 elif (token_type in (tokenize.STRING, tokenize.COMMENT) or
759 text in ('u', 'ur', 'b', 'br')):
760 indent_chances[start[1]] = str
761 # visual indent after assert/raise/with
762 elif not row and not depth and text in ["assert", "raise", "with"]:
763 indent_chances[end[1] + 1] = True
764 # special case for the "if" statement because len("if (") == 4
765 elif not indent_chances and not row and not depth and text == 'if':
766 indent_chances[end[1] + 1] = True
767 elif text == ':' and line[end[1]:].isspace():
768 open_rows[depth].append(row)
769
770 # keep track of bracket depth
771 if token_type == tokenize.OP:
772 if text in '([{':
773 depth += 1
774 indent.append(0)
775 hangs.append(None)
776 if len(open_rows) == depth:
777 open_rows.append([])
778 open_rows[depth].append(row)
779 parens[row] += 1
780 if verbose >= 4:
781 print("bracket depth %s seen, col %s, visual min = %s" %
782 (depth, start[1], indent[depth]))
783 elif text in ')]}' and depth > 0:
784 # parent indents should not be more than this one
785 prev_indent = indent.pop() or last_indent[1]
786 hangs.pop()
787 for d in range(depth):
788 if indent[d] > prev_indent:
789 indent[d] = 0
790 for ind in list(indent_chances):
791 if ind >= prev_indent:
792 del indent_chances[ind]
793 del open_rows[depth + 1:]
794 depth -= 1
795 if depth:
796 indent_chances[indent[depth]] = True
797 for idx in range(row, -1, -1):
798 if parens[idx]:
799 parens[idx] -= 1
800 break
801 assert len(indent) == depth + 1
802 if start[1] not in indent_chances:
803 # allow lining up tokens
804 indent_chances[start[1]] = text
805
806 last_token_multiline = (start[0] != end[0])
807 if last_token_multiline:
808 rel_indent[end[0] - first_row] = rel_indent[row]
809
810 if indent_next and expand_indent(line) == indent_level + indent_size:
811 pos = (start[0], indent[0] + indent_size)
812 if visual_indent:
813 code = "E129 visually indented line"
814 else:
815 code = "E125 continuation line"
816 yield pos, "%s with same indent as next logical line" % code
817
818
819 @register_check
820 def whitespace_before_parameters(logical_line, tokens):
821 r"""Avoid extraneous whitespace.
822
823 Avoid extraneous whitespace in the following situations:
824 - before the open parenthesis that starts the argument list of a
825 function call.
826 - before the open parenthesis that starts an indexing or slicing.
827
828 Okay: spam(1)
829 E211: spam (1)
830
831 Okay: dict['key'] = list[index]
832 E211: dict ['key'] = list[index]
833 E211: dict['key'] = list [index]
834 """
835 prev_type, prev_text, __, prev_end, __ = tokens[0]
836 for index in range(1, len(tokens)):
837 token_type, text, start, end, __ = tokens[index]
838 if (token_type == tokenize.OP and
839 text in '([' and
840 start != prev_end and
841 (prev_type == tokenize.NAME or prev_text in '}])') and
842 # Syntax "class A (B):" is allowed, but avoid it
843 (index < 2 or tokens[index - 2][1] != 'class') and
844 # Allow "return (a.foo for a in range(5))"
845 not keyword.iskeyword(prev_text)):
846 yield prev_end, "E211 whitespace before '%s'", text
847 prev_type = token_type
848 prev_text = text
849 prev_end = end
850
851
852 @register_check
853 def whitespace_around_operator(logical_line):
854 r"""Avoid extraneous whitespace around an operator.
855
856 Okay: a = 12 + 3
857 E221: a = 4 + 5
858 E222: a = 4 + 5
859 E223: a = 4\t+ 5
860 E224: a = 4 +\t5
861 """
862 for match in OPERATOR_REGEX.finditer(logical_line):
863 before, after = match.groups()
864
865 if '\t' in before:
866 yield match.start(1), "E223 tab before operator"
867 elif len(before) > 1:
868 yield match.start(1), "E221 multiple spaces before operator"
869
870 if '\t' in after:
871 yield match.start(2), "E224 tab after operator"
872 elif len(after) > 1:
873 yield match.start(2), "E222 multiple spaces after operator"
874
875
876 @register_check
877 def missing_whitespace_around_operator(logical_line, tokens):
878 r"""Surround operators with a single space on either side.
879
880 - Always surround these binary operators with a single space on
881 either side: assignment (=), augmented assignment (+=, -= etc.),
882 comparisons (==, <, >, !=, <=, >=, in, not in, is, is not),
883 Booleans (and, or, not).
884
885 - If operators with different priorities are used, consider adding
886 whitespace around the operators with the lowest priorities.
887
888 Okay: i = i + 1
889 Okay: submitted += 1
890 Okay: x = x * 2 - 1
891 Okay: hypot2 = x * x + y * y
892 Okay: c = (a + b) * (a - b)
893 Okay: foo(bar, key='word', *args, **kwargs)
894 Okay: alpha[:-i]
895
896 E225: i=i+1
897 E225: submitted +=1
898 E225: x = x /2 - 1
899 E225: z = x **y
900 E225: z = 1and 1
901 E226: c = (a+b) * (a-b)
902 E226: hypot2 = x*x + y*y
903 E227: c = a|b
904 E228: msg = fmt%(errno, errmsg)
905 """
906 parens = 0
907 need_space = False
908 prev_type = tokenize.OP
909 prev_text = prev_end = None
910 operator_types = (tokenize.OP, tokenize.NAME)
911 for token_type, text, start, end, line in tokens:
912 if token_type in SKIP_COMMENTS:
913 continue
914 if text in ('(', 'lambda'):
915 parens += 1
916 elif text == ')':
917 parens -= 1
918 if need_space:
919 if start != prev_end:
920 # Found a (probably) needed space
921 if need_space is not True and not need_space[1]:
922 yield (need_space[0],
923 "E225 missing whitespace around operator")
924 need_space = False
925 elif text == '>' and prev_text in ('<', '-'):
926 # Tolerate the "<>" operator, even if running Python 3
927 # Deal with Python 3's annotated return value "->"
928 pass
929 elif (
930 # def f(a, /, b):
931 # ^
932 # def f(a, b, /):
933 # ^
934 prev_text == '/' and text in {',', ')'} or
935 # def f(a, b, /):
936 # ^
937 prev_text == ')' and text == ':'
938 ):
939 # Tolerate the "/" operator in function definition
940 # For more info see PEP570
941 pass
942 else:
943 if need_space is True or need_space[1]:
944 # A needed trailing space was not found
945 yield prev_end, "E225 missing whitespace around operator"
946 elif prev_text != '**':
947 code, optype = 'E226', 'arithmetic'
948 if prev_text == '%':
949 code, optype = 'E228', 'modulo'
950 elif prev_text not in ARITHMETIC_OP:
951 code, optype = 'E227', 'bitwise or shift'
952 yield (need_space[0], "%s missing whitespace "
953 "around %s operator" % (code, optype))
954 need_space = False
955 elif token_type in operator_types and prev_end is not None:
956 if text == '=' and parens:
957 # Allow keyword args or defaults: foo(bar=None).
958 pass
959 elif text in WS_NEEDED_OPERATORS:
960 need_space = True
961 elif text in UNARY_OPERATORS:
962 # Check if the operator is used as a binary operator
963 # Allow unary operators: -123, -x, +1.
964 # Allow argument unpacking: foo(*args, **kwargs).
965 if (prev_text in '}])' if prev_type == tokenize.OP
966 else prev_text not in KEYWORDS):
967 need_space = None
968 elif text in WS_OPTIONAL_OPERATORS:
969 need_space = None
970
971 if need_space is None:
972 # Surrounding space is optional, but ensure that
973 # trailing space matches opening space
974 need_space = (prev_end, start != prev_end)
975 elif need_space and start == prev_end:
976 # A needed opening space was not found
977 yield prev_end, "E225 missing whitespace around operator"
978 need_space = False
979 prev_type = token_type
980 prev_text = text
981 prev_end = end
982
983
984 @register_check
985 def whitespace_around_comma(logical_line):
986 r"""Avoid extraneous whitespace after a comma or a colon.
987
988 Note: these checks are disabled by default
989
990 Okay: a = (1, 2)
991 E241: a = (1, 2)
992 E242: a = (1,\t2)
993 """
994 line = logical_line
995 for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line):
996 found = m.start() + 1
997 if '\t' in m.group():
998 yield found, "E242 tab after '%s'", m.group()[0]
999 else:
1000 yield found, "E241 multiple spaces after '%s'", m.group()[0]
1001
1002
1003 @register_check
1004 def whitespace_around_named_parameter_equals(logical_line, tokens):
1005 r"""Don't use spaces around the '=' sign in function arguments.
1006
1007 Don't use spaces around the '=' sign when used to indicate a
1008 keyword argument or a default parameter value, except when
1009 using a type annotation.
1010
1011 Okay: def complex(real, imag=0.0):
1012 Okay: return magic(r=real, i=imag)
1013 Okay: boolean(a == b)
1014 Okay: boolean(a != b)
1015 Okay: boolean(a <= b)
1016 Okay: boolean(a >= b)
1017 Okay: def foo(arg: int = 42):
1018 Okay: async def foo(arg: int = 42):
1019
1020 E251: def complex(real, imag = 0.0):
1021 E251: return magic(r = real, i = imag)
1022 E252: def complex(real, image: float=0.0):
1023 """
1024 parens = 0
1025 no_space = False
1026 require_space = False
1027 prev_end = None
1028 annotated_func_arg = False
1029 in_def = bool(STARTSWITH_DEF_REGEX.match(logical_line))
1030
1031 message = "E251 unexpected spaces around keyword / parameter equals"
1032 missing_message = "E252 missing whitespace around parameter equals"
1033
1034 for token_type, text, start, end, line in tokens:
1035 if token_type == tokenize.NL:
1036 continue
1037 if no_space:
1038 no_space = False
1039 if start != prev_end:
1040 yield (prev_end, message)
1041 if require_space:
1042 require_space = False
1043 if start == prev_end:
1044 yield (prev_end, missing_message)
1045 if token_type == tokenize.OP:
1046 if text in '([':
1047 parens += 1
1048 elif text in ')]':
1049 parens -= 1
1050 elif in_def and text == ':' and parens == 1:
1051 annotated_func_arg = True
1052 elif parens == 1 and text == ',':
1053 annotated_func_arg = False
1054 elif parens and text == '=':
1055 if annotated_func_arg and parens == 1:
1056 require_space = True
1057 if start == prev_end:
1058 yield (prev_end, missing_message)
1059 else:
1060 no_space = True
1061 if start != prev_end:
1062 yield (prev_end, message)
1063 if not parens:
1064 annotated_func_arg = False
1065
1066 prev_end = end
1067
1068
1069 @register_check
1070 def whitespace_before_comment(logical_line, tokens):
1071 r"""Separate inline comments by at least two spaces.
1072
1073 An inline comment is a comment on the same line as a statement.
1074 Inline comments should be separated by at least two spaces from the
1075 statement. They should start with a # and a single space.
1076
1077 Each line of a block comment starts with a # and a single space
1078 (unless it is indented text inside the comment).
1079
1080 Okay: x = x + 1 # Increment x
1081 Okay: x = x + 1 # Increment x
1082 Okay: # Block comment
1083 E261: x = x + 1 # Increment x
1084 E262: x = x + 1 #Increment x
1085 E262: x = x + 1 # Increment x
1086 E265: #Block comment
1087 E266: ### Block comment
1088 """
1089 prev_end = (0, 0)
1090 for token_type, text, start, end, line in tokens:
1091 if token_type == tokenize.COMMENT:
1092 inline_comment = line[:start[1]].strip()
1093 if inline_comment:
1094 if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
1095 yield (prev_end,
1096 "E261 at least two spaces before inline comment")
1097 symbol, sp, comment = text.partition(' ')
1098 bad_prefix = symbol not in '#:' and (symbol.lstrip('#')[:1] or '#')
1099 if inline_comment:
1100 if bad_prefix or comment[:1] in WHITESPACE:
1101 yield start, "E262 inline comment should start with '# '"
1102 elif bad_prefix and (bad_prefix != '!' or start[0] > 1):
1103 if bad_prefix != '#':
1104 yield start, "E265 block comment should start with '# '"
1105 elif comment:
1106 yield start, "E266 too many leading '#' for block comment"
1107 elif token_type != tokenize.NL:
1108 prev_end = end
1109
1110
1111 @register_check
1112 def imports_on_separate_lines(logical_line):
1113 r"""Place imports on separate lines.
1114
1115 Okay: import os\nimport sys
1116 E401: import sys, os
1117
1118 Okay: from subprocess import Popen, PIPE
1119 Okay: from myclas import MyClass
1120 Okay: from foo.bar.yourclass import YourClass
1121 Okay: import myclass
1122 Okay: import foo.bar.yourclass
1123 """
1124 line = logical_line
1125 if line.startswith('import '):
1126 found = line.find(',')
1127 if -1 < found and ';' not in line[:found]:
1128 yield found, "E401 multiple imports on one line"
1129
1130
1131 @register_check
1132 def module_imports_on_top_of_file(
1133 logical_line, indent_level, checker_state, noqa):
1134 r"""Place imports at the top of the file.
1135
1136 Always put imports at the top of the file, just after any module
1137 comments and docstrings, and before module globals and constants.
1138
1139 Okay: import os
1140 Okay: # this is a comment\nimport os
1141 Okay: '''this is a module docstring'''\nimport os
1142 Okay: r'''this is a module docstring'''\nimport os
1143 Okay:
1144 try:\n\timport x\nexcept ImportError:\n\tpass\nelse:\n\tpass\nimport y
1145 Okay:
1146 try:\n\timport x\nexcept ImportError:\n\tpass\nfinally:\n\tpass\nimport y
1147 E402: a=1\nimport os
1148 E402: 'One string'\n"Two string"\nimport os
1149 E402: a=1\nfrom sys import x
1150
1151 Okay: if x:\n import os
1152 """ # noqa
1153 def is_string_literal(line):
1154 if line[0] in 'uUbB':
1155 line = line[1:]
1156 if line and line[0] in 'rR':
1157 line = line[1:]
1158 return line and (line[0] == '"' or line[0] == "'")
1159
1160 allowed_keywords = (
1161 'try', 'except', 'else', 'finally', 'with', 'if', 'elif')
1162
1163 if indent_level: # Allow imports in conditional statement/function
1164 return
1165 if not logical_line: # Allow empty lines or comments
1166 return
1167 if noqa:
1168 return
1169 line = logical_line
1170 if line.startswith('import ') or line.startswith('from '):
1171 if checker_state.get('seen_non_imports', False):
1172 yield 0, "E402 module level import not at top of file"
1173 elif re.match(DUNDER_REGEX, line):
1174 return
1175 elif any(line.startswith(kw) for kw in allowed_keywords):
1176 # Allow certain keywords intermixed with imports in order to
1177 # support conditional or filtered importing
1178 return
1179 elif is_string_literal(line):
1180 # The first literal is a docstring, allow it. Otherwise, report
1181 # error.
1182 if checker_state.get('seen_docstring', False):
1183 checker_state['seen_non_imports'] = True
1184 else:
1185 checker_state['seen_docstring'] = True
1186 else:
1187 checker_state['seen_non_imports'] = True
1188
1189
1190 @register_check
1191 def compound_statements(logical_line):
1192 r"""Compound statements (on the same line) are generally
1193 discouraged.
1194
1195 While sometimes it's okay to put an if/for/while with a small body
1196 on the same line, never do this for multi-clause statements.
1197 Also avoid folding such long lines!
1198
1199 Always use a def statement instead of an assignment statement that
1200 binds a lambda expression directly to a name.
1201
1202 Okay: if foo == 'blah':\n do_blah_thing()
1203 Okay: do_one()
1204 Okay: do_two()
1205 Okay: do_three()
1206
1207 E701: if foo == 'blah': do_blah_thing()
1208 E701: for x in lst: total += x
1209 E701: while t < 10: t = delay()
1210 E701: if foo == 'blah': do_blah_thing()
1211 E701: else: do_non_blah_thing()
1212 E701: try: something()
1213 E701: finally: cleanup()
1214 E701: if foo == 'blah': one(); two(); three()
1215 E702: do_one(); do_two(); do_three()
1216 E703: do_four(); # useless semicolon
1217 E704: def f(x): return 2*x
1218 E731: f = lambda x: 2*x
1219 """
1220 line = logical_line
1221 last_char = len(line) - 1
1222 found = line.find(':')
1223 prev_found = 0
1224 counts = {char: 0 for char in '{}[]()'}
1225 while -1 < found < last_char:
1226 update_counts(line[prev_found:found], counts)
1227 if ((counts['{'] <= counts['}'] and # {'a': 1} (dict)
1228 counts['['] <= counts[']'] and # [1:2] (slice)
1229 counts['('] <= counts[')']) and # (annotation)
1230 not (sys.version_info >= (3, 8) and
1231 line[found + 1] == '=')): # assignment expression
1232 lambda_kw = LAMBDA_REGEX.search(line, 0, found)
1233 if lambda_kw:
1234 before = line[:lambda_kw.start()].rstrip()
1235 if before[-1:] == '=' and isidentifier(before[:-1].strip()):
1236 yield 0, ("E731 do not assign a lambda expression, use a "
1237 "def")
1238 break
1239 if STARTSWITH_DEF_REGEX.match(line):
1240 yield 0, "E704 multiple statements on one line (def)"
1241 elif STARTSWITH_INDENT_STATEMENT_REGEX.match(line):
1242 yield found, "E701 multiple statements on one line (colon)"
1243 prev_found = found
1244 found = line.find(':', found + 1)
1245 found = line.find(';')
1246 while -1 < found:
1247 if found < last_char:
1248 yield found, "E702 multiple statements on one line (semicolon)"
1249 else:
1250 yield found, "E703 statement ends with a semicolon"
1251 found = line.find(';', found + 1)
1252
1253
1254 @register_check
1255 def explicit_line_join(logical_line, tokens):
1256 r"""Avoid explicit line join between brackets.
1257
1258 The preferred way of wrapping long lines is by using Python's
1259 implied line continuation inside parentheses, brackets and braces.
1260 Long lines can be broken over multiple lines by wrapping expressions
1261 in parentheses. These should be used in preference to using a
1262 backslash for line continuation.
1263
1264 E502: aaa = [123, \\n 123]
1265 E502: aaa = ("bbb " \\n "ccc")
1266
1267 Okay: aaa = [123,\n 123]
1268 Okay: aaa = ("bbb "\n "ccc")
1269 Okay: aaa = "bbb " \\n "ccc"
1270 Okay: aaa = 123 # \\
1271 """
1272 prev_start = prev_end = parens = 0
1273 comment = False
1274 backslash = None
1275 for token_type, text, start, end, line in tokens:
1276 if token_type == tokenize.COMMENT:
1277 comment = True
1278 if start[0] != prev_start and parens and backslash and not comment:
1279 yield backslash, "E502 the backslash is redundant between brackets"
1280 if end[0] != prev_end:
1281 if line.rstrip('\r\n').endswith('\\'):
1282 backslash = (end[0], len(line.splitlines()[-1]) - 1)
1283 else:
1284 backslash = None
1285 prev_start = prev_end = end[0]
1286 else:
1287 prev_start = start[0]
1288 if token_type == tokenize.OP:
1289 if text in '([{':
1290 parens += 1
1291 elif text in ')]}':
1292 parens -= 1
1293
1294
1295 _SYMBOLIC_OPS = frozenset("()[]{},:.;@=%~") | frozenset(("...",))
1296
1297
1298 def _is_binary_operator(token_type, text):
1299 is_op_token = token_type == tokenize.OP
1300 is_conjunction = text in ['and', 'or']
1301 # NOTE(sigmavirus24): Previously the not_a_symbol check was executed
1302 # conditionally. Since it is now *always* executed, text may be
1303 # None. In that case we get a TypeError for `text not in str`.
1304 not_a_symbol = text and text not in _SYMBOLIC_OPS
1305 # The % character is strictly speaking a binary operator, but the
1306 # common usage seems to be to put it next to the format parameters,
1307 # after a line break.
1308 return ((is_op_token or is_conjunction) and not_a_symbol)
1309
1310
1311 def _break_around_binary_operators(tokens):
1312 """Private function to reduce duplication.
1313
1314 This factors out the shared details between
1315 :func:`break_before_binary_operator` and
1316 :func:`break_after_binary_operator`.
1317 """
1318 line_break = False
1319 unary_context = True
1320 # Previous non-newline token types and text
1321 previous_token_type = None
1322 previous_text = None
1323 for token_type, text, start, end, line in tokens:
1324 if token_type == tokenize.COMMENT:
1325 continue
1326 if ('\n' in text or '\r' in text) and token_type != tokenize.STRING:
1327 line_break = True
1328 else:
1329 yield (token_type, text, previous_token_type, previous_text,
1330 line_break, unary_context, start)
1331 unary_context = text in '([{,;'
1332 line_break = False
1333 previous_token_type = token_type
1334 previous_text = text
1335
1336
1337 @register_check
1338 def break_before_binary_operator(logical_line, tokens):
1339 r"""
1340 Avoid breaks before binary operators.
1341
1342 The preferred place to break around a binary operator is after the
1343 operator, not before it.
1344
1345 W503: (width == 0\n + height == 0)
1346 W503: (width == 0\n and height == 0)
1347 W503: var = (1\n & ~2)
1348 W503: var = (1\n / -2)
1349 W503: var = (1\n + -1\n + -2)
1350
1351 Okay: foo(\n -x)
1352 Okay: foo(x\n [])
1353 Okay: x = '''\n''' + ''
1354 Okay: foo(x,\n -y)
1355 Okay: foo(x, # comment\n -y)
1356 """
1357 for context in _break_around_binary_operators(tokens):
1358 (token_type, text, previous_token_type, previous_text,
1359 line_break, unary_context, start) = context
1360 if (_is_binary_operator(token_type, text) and line_break and
1361 not unary_context and
1362 not _is_binary_operator(previous_token_type,
1363 previous_text)):
1364 yield start, "W503 line break before binary operator"
1365
1366
1367 @register_check
1368 def break_after_binary_operator(logical_line, tokens):
1369 r"""
1370 Avoid breaks after binary operators.
1371
1372 The preferred place to break around a binary operator is before the
1373 operator, not after it.
1374
1375 W504: (width == 0 +\n height == 0)
1376 W504: (width == 0 and\n height == 0)
1377 W504: var = (1 &\n ~2)
1378
1379 Okay: foo(\n -x)
1380 Okay: foo(x\n [])
1381 Okay: x = '''\n''' + ''
1382 Okay: x = '' + '''\n'''
1383 Okay: foo(x,\n -y)
1384 Okay: foo(x, # comment\n -y)
1385
1386 The following should be W504 but unary_context is tricky with these
1387 Okay: var = (1 /\n -2)
1388 Okay: var = (1 +\n -1 +\n -2)
1389 """
1390 prev_start = None
1391 for context in _break_around_binary_operators(tokens):
1392 (token_type, text, previous_token_type, previous_text,
1393 line_break, unary_context, start) = context
1394 if (_is_binary_operator(previous_token_type, previous_text) and
1395 line_break and
1396 not unary_context and
1397 not _is_binary_operator(token_type, text)):
1398 yield prev_start, "W504 line break after binary operator"
1399 prev_start = start
1400
1401
1402 @register_check
1403 def comparison_to_singleton(logical_line, noqa):
1404 r"""Comparison to singletons should use "is" or "is not".
1405
1406 Comparisons to singletons like None should always be done
1407 with "is" or "is not", never the equality operators.
1408
1409 Okay: if arg is not None:
1410 E711: if arg != None:
1411 E711: if None == arg:
1412 E712: if arg == True:
1413 E712: if False == arg:
1414
1415 Also, beware of writing if x when you really mean if x is not None
1416 -- e.g. when testing whether a variable or argument that defaults to
1417 None was set to some other value. The other value might have a type
1418 (such as a container) that could be false in a boolean context!
1419 """
1420 match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line)
1421 if match:
1422 singleton = match.group(1) or match.group(3)
1423 same = (match.group(2) == '==')
1424
1425 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton)
1426 if singleton in ('None',):
1427 code = 'E711'
1428 else:
1429 code = 'E712'
1430 nonzero = ((singleton == 'True' and same) or
1431 (singleton == 'False' and not same))
1432 msg += " or 'if %scond:'" % ('' if nonzero else 'not ')
1433 yield (match.start(2), ("%s comparison to %s should be %s" %
1434 (code, singleton, msg)), singleton, msg)
1435
1436
1437 @register_check
1438 def comparison_negative(logical_line):
1439 r"""Negative comparison should be done using "not in" and "is not".
1440
1441 Okay: if x not in y:\n pass
1442 Okay: assert (X in Y or X is Z)
1443 Okay: if not (X in Y):\n pass
1444 Okay: zz = x is not y
1445 E713: Z = not X in Y
1446 E713: if not X.B in Y:\n pass
1447 E714: if not X is Y:\n pass
1448 E714: Z = not X.B is Y
1449 """
1450 match = COMPARE_NEGATIVE_REGEX.search(logical_line)
1451 if match:
1452 pos = match.start(1)
1453 if match.group(2) == 'in':
1454 yield pos, "E713 test for membership should be 'not in'"
1455 else:
1456 yield pos, "E714 test for object identity should be 'is not'"
1457
1458
1459 @register_check
1460 def comparison_type(logical_line, noqa):
1461 r"""Object type comparisons should always use isinstance().
1462
1463 Do not compare types directly.
1464
1465 Okay: if isinstance(obj, int):
1466 E721: if type(obj) is type(1):
1467
1468 When checking if an object is a string, keep in mind that it might
1469 be a unicode string too! In Python 2.3, str and unicode have a
1470 common base class, basestring, so you can do:
1471
1472 Okay: if isinstance(obj, basestring):
1473 Okay: if type(a1) is type(b1):
1474 """
1475 match = COMPARE_TYPE_REGEX.search(logical_line)
1476 if match and not noqa:
1477 inst = match.group(1)
1478 if inst and isidentifier(inst) and inst not in SINGLETONS:
1479 return # Allow comparison for types which are not obvious
1480 yield match.start(), "E721 do not compare types, use 'isinstance()'"
1481
1482
1483 @register_check
1484 def bare_except(logical_line, noqa):
1485 r"""When catching exceptions, mention specific exceptions when
1486 possible.
1487
1488 Okay: except Exception:
1489 Okay: except BaseException:
1490 E722: except:
1491 """
1492 if noqa:
1493 return
1494
1495 regex = re.compile(r"except\s*:")
1496 match = regex.match(logical_line)
1497 if match:
1498 yield match.start(), "E722 do not use bare 'except'"
1499
1500
1501 @register_check
1502 def ambiguous_identifier(logical_line, tokens):
1503 r"""Never use the characters 'l', 'O', or 'I' as variable names.
1504
1505 In some fonts, these characters are indistinguishable from the
1506 numerals one and zero. When tempted to use 'l', use 'L' instead.
1507
1508 Okay: L = 0
1509 Okay: o = 123
1510 Okay: i = 42
1511 E741: l = 0
1512 E741: O = 123
1513 E741: I = 42
1514
1515 Variables can be bound in several other contexts, including class
1516 and function definitions, 'global' and 'nonlocal' statements,
1517 exception handlers, and 'with' and 'for' statements.
1518 In addition, we have a special handling for function parameters.
1519
1520 Okay: except AttributeError as o:
1521 Okay: with lock as L:
1522 Okay: foo(l=12)
1523 Okay: for a in foo(l=12):
1524 E741: except AttributeError as O:
1525 E741: with lock as l:
1526 E741: global I
1527 E741: nonlocal l
1528 E741: def foo(l):
1529 E741: def foo(l=12):
1530 E741: l = foo(l=12)
1531 E741: for l in range(10):
1532 E742: class I(object):
1533 E743: def l(x):
1534 """
1535 is_func_def = False # Set to true if 'def' is found
1536 parameter_parentheses_level = 0
1537 idents_to_avoid = ('l', 'O', 'I')
1538 prev_type, prev_text, prev_start, prev_end, __ = tokens[0]
1539 for token_type, text, start, end, line in tokens[1:]:
1540 ident = pos = None
1541 # find function definitions
1542 if prev_text == 'def':
1543 is_func_def = True
1544 # update parameter parentheses level
1545 if parameter_parentheses_level == 0 and \
1546 prev_type == tokenize.NAME and \
1547 token_type == tokenize.OP and text == '(':
1548 parameter_parentheses_level = 1
1549 elif parameter_parentheses_level > 0 and \
1550 token_type == tokenize.OP:
1551 if text == '(':
1552 parameter_parentheses_level += 1
1553 elif text == ')':
1554 parameter_parentheses_level -= 1
1555 # identifiers on the lhs of an assignment operator
1556 if token_type == tokenize.OP and '=' in text and \
1557 parameter_parentheses_level == 0:
1558 if prev_text in idents_to_avoid:
1559 ident = prev_text
1560 pos = prev_start
1561 # identifiers bound to values with 'as', 'for',
1562 # 'global', or 'nonlocal'
1563 if prev_text in ('as', 'for', 'global', 'nonlocal'):
1564 if text in idents_to_avoid:
1565 ident = text
1566 pos = start
1567 # function parameter definitions
1568 if is_func_def:
1569 if text in idents_to_avoid:
1570 ident = text
1571 pos = start
1572 if prev_text == 'class':
1573 if text in idents_to_avoid:
1574 yield start, "E742 ambiguous class definition '%s'", text
1575 if prev_text == 'def':
1576 if text in idents_to_avoid:
1577 yield start, "E743 ambiguous function definition '%s'", text
1578 if ident:
1579 yield pos, "E741 ambiguous variable name '%s'", ident
1580 prev_type = token_type
1581 prev_text = text
1582 prev_start = start
1583
1584
1585 @register_check
1586 def python_3000_has_key(logical_line, noqa):
1587 r"""The {}.has_key() method is removed in Python 3: use the 'in'
1588 operator.
1589
1590 Okay: if "alph" in d:\n print d["alph"]
1591 W601: assert d.has_key('alph')
1592 """
1593 pos = logical_line.find('.has_key(')
1594 if pos > -1 and not noqa:
1595 yield pos, "W601 .has_key() is deprecated, use 'in'"
1596
1597
1598 @register_check
1599 def python_3000_raise_comma(logical_line):
1600 r"""When raising an exception, use "raise ValueError('message')".
1601
1602 The older form is removed in Python 3.
1603
1604 Okay: raise DummyError("Message")
1605 W602: raise DummyError, "Message"
1606 """
1607 match = RAISE_COMMA_REGEX.match(logical_line)
1608 if match and not RERAISE_COMMA_REGEX.match(logical_line):
1609 yield match.end() - 1, "W602 deprecated form of raising exception"
1610
1611
1612 @register_check
1613 def python_3000_not_equal(logical_line):
1614 r"""New code should always use != instead of <>.
1615
1616 The older syntax is removed in Python 3.
1617
1618 Okay: if a != 'no':
1619 W603: if a <> 'no':
1620 """
1621 pos = logical_line.find('<>')
1622 if pos > -1:
1623 yield pos, "W603 '<>' is deprecated, use '!='"
1624
1625
1626 @register_check
1627 def python_3000_backticks(logical_line):
1628 r"""Use repr() instead of backticks in Python 3.
1629
1630 Okay: val = repr(1 + 2)
1631 W604: val = `1 + 2`
1632 """
1633 pos = logical_line.find('`')
1634 if pos > -1:
1635 yield pos, "W604 backticks are deprecated, use 'repr()'"
1636
1637
1638 @register_check
1639 def python_3000_invalid_escape_sequence(logical_line, tokens, noqa):
1640 r"""Invalid escape sequences are deprecated in Python 3.6.
1641
1642 Okay: regex = r'\.png$'
1643 W605: regex = '\.png$'
1644 """
1645 if noqa:
1646 return
1647
1648 # https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
1649 valid = [
1650 '\n',
1651 '\\',
1652 '\'',
1653 '"',
1654 'a',
1655 'b',
1656 'f',
1657 'n',
1658 'r',
1659 't',
1660 'v',
1661 '0', '1', '2', '3', '4', '5', '6', '7',
1662 'x',
1663
1664 # Escape sequences only recognized in string literals
1665 'N',
1666 'u',
1667 'U',
1668 ]
1669
1670 for token_type, text, start, end, line in tokens:
1671 if token_type == tokenize.STRING:
1672 start_line, start_col = start
1673 quote = text[-3:] if text[-3:] in ('"""', "'''") else text[-1]
1674 # Extract string modifiers (e.g. u or r)
1675 quote_pos = text.index(quote)
1676 prefix = text[:quote_pos].lower()
1677 start = quote_pos + len(quote)
1678 string = text[start:-len(quote)]
1679
1680 if 'r' not in prefix:
1681 pos = string.find('\\')
1682 while pos >= 0:
1683 pos += 1
1684 if string[pos] not in valid:
1685 line = start_line + string.count('\n', 0, pos)
1686 if line == start_line:
1687 col = start_col + len(prefix) + len(quote) + pos
1688 else:
1689 col = pos - string.rfind('\n', 0, pos) - 1
1690 yield (
1691 (line, col - 1),
1692 "W605 invalid escape sequence '\\%s'",
1693 string[pos],
1694 )
1695 pos = string.find('\\', pos + 1)
1696
1697
1698 @register_check
1699 def python_3000_async_await_keywords(logical_line, tokens):
1700 """'async' and 'await' are reserved keywords starting at Python 3.7.
1701
1702 W606: async = 42
1703 W606: await = 42
1704 Okay: async def read(db):\n data = await db.fetch('SELECT ...')
1705 """
1706 # The Python tokenize library before Python 3.5 recognizes
1707 # async/await as a NAME token. Therefore, use a state machine to
1708 # look for the possible async/await constructs as defined by the
1709 # Python grammar:
1710 # https://docs.python.org/3/reference/grammar.html
1711
1712 state = None
1713 for token_type, text, start, end, line in tokens:
1714 error = False
1715
1716 if token_type == tokenize.NL:
1717 continue
1718
1719 if state is None:
1720 if token_type == tokenize.NAME:
1721 if text == 'async':
1722 state = ('async_stmt', start)
1723 elif text == 'await':
1724 state = ('await', start)
1725 elif (token_type == tokenize.NAME and
1726 text in ('def', 'for')):
1727 state = ('define', start)
1728
1729 elif state[0] == 'async_stmt':
1730 if token_type == tokenize.NAME and text in ('def', 'with', 'for'):
1731 # One of funcdef, with_stmt, or for_stmt. Return to
1732 # looking for async/await names.
1733 state = None
1734 else:
1735 error = True
1736 elif state[0] == 'await':
1737 if token_type == tokenize.NAME:
1738 # An await expression. Return to looking for async/await
1739 # names.
1740 state = None
1741 elif token_type == tokenize.OP and text == '(':
1742 state = None
1743 else:
1744 error = True
1745 elif state[0] == 'define':
1746 if token_type == tokenize.NAME and text in ('async', 'await'):
1747 error = True
1748 else:
1749 state = None
1750
1751 if error:
1752 yield (
1753 state[1],
1754 "W606 'async' and 'await' are reserved keywords starting with "
1755 "Python 3.7",
1756 )
1757 state = None
1758
1759 # Last token
1760 if state is not None:
1761 yield (
1762 state[1],
1763 "W606 'async' and 'await' are reserved keywords starting with "
1764 "Python 3.7",
1765 )
1766
1767
1768 ########################################################################
1769 @register_check
1770 def maximum_doc_length(logical_line, max_doc_length, noqa, tokens):
1771 r"""Limit all doc lines to a maximum of 72 characters.
1772
1773 For flowing long blocks of text (docstrings or comments), limiting
1774 the length to 72 characters is recommended.
1775
1776 Reports warning W505
1777 """
1778 if max_doc_length is None or noqa:
1779 return
1780
1781 prev_token = None
1782 skip_lines = set()
1783 # Skip lines that
1784 for token_type, text, start, end, line in tokens:
1785 if token_type not in SKIP_COMMENTS.union([tokenize.STRING]):
1786 skip_lines.add(line)
1787
1788 for token_type, text, start, end, line in tokens:
1789 # Skip lines that aren't pure strings
1790 if token_type == tokenize.STRING and skip_lines:
1791 continue
1792 if token_type in (tokenize.STRING, tokenize.COMMENT):
1793 # Only check comment-only lines
1794 if prev_token is None or prev_token in SKIP_TOKENS:
1795 lines = line.splitlines()
1796 for line_num, physical_line in enumerate(lines):
1797 if hasattr(physical_line, 'decode'): # Python 2
1798 # The line could contain multi-byte characters
1799 try:
1800 physical_line = physical_line.decode('utf-8')
1801 except UnicodeError:
1802 pass
1803 if start[0] + line_num == 1 and line.startswith('#!'):
1804 return
1805 length = len(physical_line)
1806 chunks = physical_line.split()
1807 if token_type == tokenize.COMMENT:
1808 if (len(chunks) == 2 and
1809 length - len(chunks[-1]) < MAX_DOC_LENGTH):
1810 continue
1811 if len(chunks) == 1 and line_num + 1 < len(lines):
1812 if (len(chunks) == 1 and
1813 length - len(chunks[-1]) < MAX_DOC_LENGTH):
1814 continue
1815 if length > max_doc_length:
1816 doc_error = (start[0] + line_num, max_doc_length)
1817 yield (doc_error, "W505 doc line too long "
1818 "(%d > %d characters)",
1819 length, max_doc_length)
1820 prev_token = token_type
1821
1822
1823 ########################################################################
1824 # Helper functions
1825 ########################################################################
1826
1827
1828 if sys.version_info < (3,):
1829 # Python 2: implicit encoding.
1830 def readlines(filename):
1831 """Read the source code."""
1832 with open(filename, 'rU') as f:
1833 return f.readlines()
1834 isidentifier = re.compile(r'[a-zA-Z_]\w*$').match
1835 stdin_get_value = sys.stdin.read
1836 else:
1837 # Python 3
1838 def readlines(filename):
1839 """Read the source code."""
1840 try:
1841 with tokenize.open(filename) as f:
1842 return f.readlines()
1843 except (LookupError, SyntaxError, UnicodeError):
1844 # Fall back if file encoding is improperly declared
1845 with open(filename, encoding='latin-1') as f:
1846 return f.readlines()
1847 isidentifier = str.isidentifier
1848
1849 def stdin_get_value():
1850 """Read the value from stdin."""
1851 return TextIOWrapper(sys.stdin.buffer, errors='ignore').read()
1852
1853 noqa = lru_cache(512)(re.compile(r'# no(?:qa|pep8)\b', re.I).search)
1854
1855
1856 def expand_indent(line):
1857 r"""Return the amount of indentation.
1858
1859 Tabs are expanded to the next multiple of 8.
1860
1861 >>> expand_indent(' ')
1862 4
1863 >>> expand_indent('\t')
1864 8
1865 >>> expand_indent(' \t')
1866 8
1867 >>> expand_indent(' \t')
1868 16
1869 """
1870 line = line.rstrip('\n\r')
1871 if '\t' not in line:
1872 return len(line) - len(line.lstrip())
1873 result = 0
1874 for char in line:
1875 if char == '\t':
1876 result = result // 8 * 8 + 8
1877 elif char == ' ':
1878 result += 1
1879 else:
1880 break
1881 return result
1882
1883
1884 def mute_string(text):
1885 """Replace contents with 'xxx' to prevent syntax matching.
1886
1887 >>> mute_string('"abc"')
1888 '"xxx"'
1889 >>> mute_string("'''abc'''")
1890 "'''xxx'''"
1891 >>> mute_string("r'abc'")
1892 "r'xxx'"
1893 """
1894 # String modifiers (e.g. u or r)
1895 start = text.index(text[-1]) + 1
1896 end = len(text) - 1
1897 # Triple quotes
1898 if text[-3:] in ('"""', "'''"):
1899 start += 2
1900 end -= 2
1901 return text[:start] + 'x' * (end - start) + text[end:]
1902
1903
1904 def parse_udiff(diff, patterns=None, parent='.'):
1905 """Return a dictionary of matching lines."""
1906 # For each file of the diff, the entry key is the filename,
1907 # and the value is a set of row numbers to consider.
1908 rv = {}
1909 path = nrows = None
1910 for line in diff.splitlines():
1911 if nrows:
1912 if line[:1] != '-':
1913 nrows -= 1
1914 continue
1915 if line[:3] == '@@ ':
1916 hunk_match = HUNK_REGEX.match(line)
1917 (row, nrows) = [int(g or '1') for g in hunk_match.groups()]
1918 rv[path].update(range(row, row + nrows))
1919 elif line[:3] == '+++':
1920 path = line[4:].split('\t', 1)[0]
1921 # Git diff will use (i)ndex, (w)ork tree, (c)ommit and
1922 # (o)bject instead of a/b/c/d as prefixes for patches
1923 if path[:2] in ('b/', 'w/', 'i/'):
1924 path = path[2:]
1925 rv[path] = set()
1926 return {
1927 os.path.join(parent, filepath): rows
1928 for (filepath, rows) in rv.items()
1929 if rows and filename_match(filepath, patterns)
1930 }
1931
1932
1933 def normalize_paths(value, parent=os.curdir):
1934 """Parse a comma-separated list of paths.
1935
1936 Return a list of absolute paths.
1937 """
1938 if not value:
1939 return []
1940 if isinstance(value, list):
1941 return value
1942 paths = []
1943 for path in value.split(','):
1944 path = path.strip()
1945 if '/' in path:
1946 path = os.path.abspath(os.path.join(parent, path))
1947 paths.append(path.rstrip('/'))
1948 return paths
1949
1950
1951 def filename_match(filename, patterns, default=True):
1952 """Check if patterns contains a pattern that matches filename.
1953
1954 If patterns is unspecified, this always returns True.
1955 """
1956 if not patterns:
1957 return default
1958 return any(fnmatch(filename, pattern) for pattern in patterns)
1959
1960
1961 def update_counts(s, counts):
1962 r"""Adds one to the counts of each appearance of characters in s,
1963 for characters in counts"""
1964 for char in s:
1965 if char in counts:
1966 counts[char] += 1
1967
1968
1969 def _is_eol_token(token):
1970 return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == '\\\n'
1971
1972
1973 ########################################################################
1974 # Framework to run all checks
1975 ########################################################################
1976
1977
1978 class Checker(object):
1979 """Load a Python source file, tokenize it, check coding style."""
1980
1981 def __init__(self, filename=None, lines=None,
1982 options=None, report=None, **kwargs):
1983 if options is None:
1984 options = StyleGuide(kwargs).options
1985 else:
1986 assert not kwargs
1987 self._io_error = None
1988 self._physical_checks = options.physical_checks
1989 self._logical_checks = options.logical_checks
1990 self._ast_checks = options.ast_checks
1991 self.max_line_length = options.max_line_length
1992 self.max_doc_length = options.max_doc_length
1993 self.indent_size = options.indent_size
1994 self.multiline = False # in a multiline string?
1995 self.hang_closing = options.hang_closing
1996 self.indent_size = options.indent_size
1997 self.indent_size_str = ({2: 'two', 4: 'four', 8: 'eight'}
1998 .get(self.indent_size, str(self.indent_size)))
1999 self.verbose = options.verbose
2000 self.filename = filename
2001 # Dictionary where a checker can store its custom state.
2002 self._checker_states = {}
2003 if filename is None:
2004 self.filename = 'stdin'
2005 self.lines = lines or []
2006 elif filename == '-':
2007 self.filename = 'stdin'
2008 self.lines = stdin_get_value().splitlines(True)
2009 elif lines is None:
2010 try:
2011 self.lines = readlines(filename)
2012 except OSError:
2013 (exc_type, exc) = sys.exc_info()[:2]
2014 self._io_error = '%s: %s' % (exc_type.__name__, exc)
2015 self.lines = []
2016 else:
2017 self.lines = lines
2018 if self.lines:
2019 ord0 = ord(self.lines[0][0])
2020 if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM
2021 if ord0 == 0xfeff:
2022 self.lines[0] = self.lines[0][1:]
2023 elif self.lines[0][:3] == '\xef\xbb\xbf':
2024 self.lines[0] = self.lines[0][3:]
2025 self.report = report or options.report
2026 self.report_error = self.report.error
2027 self.report_error_args = self.report.error_args
2028 self.noqa = False
2029
2030 # added for eric integration
2031 self.options = options
2032
2033 def report_invalid_syntax(self):
2034 """Check if the syntax is valid."""
2035 (exc_type, exc) = sys.exc_info()[:2]
2036 if len(exc.args) > 1:
2037 offset = exc.args[1]
2038 if len(offset) > 2:
2039 offset = offset[1:3]
2040 else:
2041 offset = (1, 0)
2042 self.report_error_args(offset[0], offset[1] or 0,
2043 'E901', self.report_invalid_syntax,
2044 exc_type.__name__, exc.args[0])
2045
2046 def readline(self):
2047 """Get the next line from the input buffer."""
2048 if self.line_number >= self.total_lines:
2049 return ''
2050 line = self.lines[self.line_number]
2051 self.line_number += 1
2052 if self.indent_char is None and line[:1] in WHITESPACE:
2053 self.indent_char = line[0]
2054 return line
2055
2056 def run_check(self, check, argument_names):
2057 """Run a check plugin."""
2058 arguments = []
2059 for name in argument_names:
2060 arguments.append(getattr(self, name))
2061 return check(*arguments)
2062
2063 def init_checker_state(self, name, argument_names):
2064 """Prepare custom state for the specific checker plugin."""
2065 if 'checker_state' in argument_names:
2066 self.checker_state = self._checker_states.setdefault(name, {})
2067
2068 def check_physical(self, line):
2069 """Run all physical checks on a raw input line."""
2070 self.physical_line = line
2071 for name, check, argument_names in self._physical_checks:
2072 self.init_checker_state(name, argument_names)
2073 result = self.run_check(check, argument_names)
2074 if result is not None:
2075 (offset, text) = result[:2]
2076 args = result[2:]
2077 self.report_error_args(
2078 self.line_number, offset, text, check, *args)
2079 if text[:4] == 'E101':
2080 self.indent_char = line[0]
2081
2082 def build_tokens_line(self):
2083 """Build a logical line from tokens."""
2084 logical = []
2085 comments = []
2086 length = 0
2087 prev_row = prev_col = mapping = None
2088 for token_type, text, start, end, line in self.tokens:
2089 if token_type in SKIP_TOKENS:
2090 continue
2091 if not mapping:
2092 mapping = [(0, start)]
2093 if token_type == tokenize.COMMENT:
2094 comments.append(text)
2095 continue
2096 if token_type == tokenize.STRING:
2097 text = mute_string(text)
2098 if prev_row:
2099 (start_row, start_col) = start
2100 if prev_row != start_row: # different row
2101 prev_text = self.lines[prev_row - 1][prev_col - 1]
2102 if prev_text == ',' or (prev_text not in '{[(' and
2103 text not in '}])'):
2104 text = ' ' + text
2105 elif prev_col != start_col: # different column
2106 text = line[prev_col:start_col] + text
2107 logical.append(text)
2108 length += len(text)
2109 mapping.append((length, end))
2110 (prev_row, prev_col) = end
2111 self.logical_line = ''.join(logical)
2112 self.noqa = comments and noqa(''.join(comments))
2113 return mapping
2114
2115 def check_logical(self):
2116 """Build a line from tokens and run all logical checks on it."""
2117 self.report.increment_logical_line()
2118 mapping = self.build_tokens_line()
2119 if not mapping:
2120 return
2121
2122 mapping_offsets = [offset for offset, _ in mapping]
2123 (start_row, start_col) = mapping[0][1]
2124 start_line = self.lines[start_row - 1]
2125 self.indent_level = expand_indent(start_line[:start_col])
2126 if self.blank_before < self.blank_lines:
2127 self.blank_before = self.blank_lines
2128 if self.verbose >= 2:
2129 print(self.logical_line[:80].rstrip())
2130 for name, check, argument_names in self._logical_checks:
2131 if self.verbose >= 4:
2132 print(' ' + name)
2133 self.init_checker_state(name, argument_names)
2134 for result in self.run_check(check, argument_names) or ():
2135 offset, text = result[:2]
2136 args = result[2:]
2137 if not isinstance(offset, tuple):
2138 # As mappings are ordered, bisecting is a fast way
2139 # to find a given offset in them.
2140 token_offset, pos = mapping[bisect.bisect_left(
2141 mapping_offsets, offset)]
2142 offset = (pos[0], pos[1] + offset - token_offset)
2143 self.report_error_args(
2144 offset[0], offset[1], text, check, *args)
2145 if self.logical_line:
2146 self.previous_indent_level = self.indent_level
2147 self.previous_logical = self.logical_line
2148 if not self.indent_level:
2149 self.previous_unindented_logical_line = self.logical_line
2150 self.blank_lines = 0
2151 self.tokens = []
2152
2153 def check_ast(self):
2154 """Build the file's AST and run all AST checks."""
2155 try:
2156 tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
2157 except (ValueError, SyntaxError, TypeError):
2158 return self.report_invalid_syntax()
2159 for name, cls, __ in self._ast_checks:
2160 # extended API for eric integration
2161 checker = cls(tree, self.filename, self.options)
2162 for args in checker.run():
2163 lineno = args[0]
2164 if not self.lines or not noqa(self.lines[lineno - 1]):
2165 self.report_error_args(lineno, *args[1:])
2166
2167 def generate_tokens(self):
2168 """Tokenize file, run physical line checks and yield tokens."""
2169 if self._io_error:
2170 self.report_error_args(1, 0, 'E902', self._io_error, readlines)
2171 tokengen = tokenize.generate_tokens(self.readline)
2172 try:
2173 prev_physical = ''
2174 for token in tokengen:
2175 if token[2][0] > self.total_lines:
2176 return
2177 self.noqa = token[4] and noqa(token[4])
2178 self.maybe_check_physical(token, prev_physical)
2179 yield token
2180 prev_physical = token[4]
2181 except (SyntaxError, tokenize.TokenError):
2182 self.report_invalid_syntax()
2183
2184 def maybe_check_physical(self, token, prev_physical):
2185 """If appropriate for token, check current physical line(s)."""
2186 # Called after every token, but act only on end of line.
2187
2188 # a newline token ends a single physical line.
2189 if _is_eol_token(token):
2190 # if the file does not end with a newline, the NEWLINE
2191 # token is inserted by the parser, but it does not contain
2192 # the previous physical line in `token[4]`
2193 if token[4] == '':
2194 self.check_physical(prev_physical)
2195 else:
2196 self.check_physical(token[4])
2197 elif token[0] == tokenize.STRING and '\n' in token[1]:
2198 # Less obviously, a string that contains newlines is a
2199 # multiline string, either triple-quoted or with internal
2200 # newlines backslash-escaped. Check every physical line in
2201 # the string *except* for the last one: its newline is
2202 # outside of the multiline string, so we consider it a
2203 # regular physical line, and will check it like any other
2204 # physical line.
2205 #
2206 # Subtleties:
2207 # - we don't *completely* ignore the last line; if it
2208 # contains the magical "# noqa" comment, we disable all
2209 # physical checks for the entire multiline string
2210 # - have to wind self.line_number back because initially it
2211 # points to the last line of the string, and we want
2212 # check_physical() to give accurate feedback
2213 if noqa(token[4]):
2214 return
2215 self.multiline = True
2216 self.line_number = token[2][0]
2217 _, src, (_, offset), _, _ = token
2218 src = self.lines[self.line_number - 1][:offset] + src
2219 for line in src.split('\n')[:-1]:
2220 self.check_physical(line + '\n')
2221 self.line_number += 1
2222 self.multiline = False
2223
2224 def check_all(self, expected=None, line_offset=0):
2225 """Run all checks on the input file."""
2226 self.report.init_file(self.filename, self.lines, expected, line_offset)
2227 self.total_lines = len(self.lines)
2228 if self._ast_checks:
2229 self.check_ast()
2230 self.line_number = 0
2231 self.indent_char = None
2232 self.indent_level = self.previous_indent_level = 0
2233 self.previous_logical = ''
2234 self.previous_unindented_logical_line = ''
2235 self.tokens = []
2236 self.blank_lines = self.blank_before = 0
2237 parens = 0
2238 for token in self.generate_tokens():
2239 self.tokens.append(token)
2240 token_type, text = token[0:2]
2241 if self.verbose >= 3:
2242 if token[2][0] == token[3][0]:
2243 pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
2244 else:
2245 pos = 'l.%s' % token[3][0]
2246 print('l.%s\t%s\t%s\t%r' %
2247 (token[2][0], pos, tokenize.tok_name[token[0]], text))
2248 if token_type == tokenize.OP:
2249 if text in '([{':
2250 parens += 1
2251 elif text in '}])':
2252 parens -= 1
2253 elif not parens:
2254 if token_type in NEWLINE:
2255 if token_type == tokenize.NEWLINE:
2256 self.check_logical()
2257 self.blank_before = 0
2258 elif len(self.tokens) == 1:
2259 # The physical line contains only this token.
2260 self.blank_lines += 1
2261 del self.tokens[0]
2262 else:
2263 self.check_logical()
2264 if self.tokens:
2265 self.check_physical(self.lines[-1])
2266 self.check_logical()
2267 return self.report.get_file_results()
2268
2269
2270 class BaseReport(object):
2271 """Collect the results of the checks."""
2272
2273 print_filename = False
2274
2275 def __init__(self, options):
2276 self._benchmark_keys = options.benchmark_keys
2277 self._ignore_code = options.ignore_code
2278 # Results
2279 self.elapsed = 0
2280 self.total_errors = 0
2281 self.counters = dict.fromkeys(self._benchmark_keys, 0)
2282 self.messages = {}
2283
2284 def start(self):
2285 """Start the timer."""
2286 self._start_time = time.time()
2287
2288 def stop(self):
2289 """Stop the timer."""
2290 self.elapsed = time.time() - self._start_time
2291
2292 def init_file(self, filename, lines, expected, line_offset):
2293 """Signal a new file."""
2294 self.filename = filename
2295 self.lines = lines
2296 self.expected = expected or ()
2297 self.line_offset = line_offset
2298 self.file_errors = 0
2299 self.counters['files'] += 1
2300 self.counters['physical lines'] += len(lines)
2301
2302 def increment_logical_line(self):
2303 """Signal a new logical line."""
2304 self.counters['logical lines'] += 1
2305
2306 def error(self, line_number, offset, text, check):
2307 """Report an error, according to options."""
2308 code = text[:4]
2309 if self._ignore_code(code):
2310 return
2311 if code in self.counters:
2312 self.counters[code] += 1
2313 else:
2314 self.counters[code] = 1
2315 self.messages[code] = []
2316 # Don't care about expected errors or warnings
2317 if code in self.expected:
2318 return
2319 if self.print_filename and not self.file_errors:
2320 print(self.filename)
2321 self.file_errors += 1
2322 self.total_errors += 1
2323 return code
2324
2325 def error_args(self, line_number, offset, text, check, *args):
2326 """Report an error, according to options."""
2327 code = text[:4]
2328 if self._ignore_code(code):
2329 return
2330 if code in self.counters:
2331 self.counters[code] += 1
2332 else:
2333 self.counters[code] = 1
2334 self.messages[code] = args
2335 # Don't care about expected errors or warnings
2336 if code in self.expected:
2337 return
2338 if self.print_filename and not self.file_errors:
2339 print(self.filename)
2340 self.file_errors += 1
2341 self.total_errors += 1
2342 return code
2343
2344 def get_file_results(self):
2345 """Return the count of errors and warnings for this file."""
2346 return self.file_errors
2347
2348 def get_count(self, prefix=''):
2349 """Return the total count of errors and warnings."""
2350 return sum(self.counters[key]
2351 for key in self.messages if key.startswith(prefix))
2352
2353 def get_statistics(self, prefix=''):
2354 """Get statistics for message codes that start with the prefix.
2355
2356 prefix='' matches all errors and warnings
2357 prefix='E' matches all errors
2358 prefix='W' matches all warnings
2359 prefix='E4' matches all errors that have to do with imports
2360 """
2361 return ['%-7s %s %s' % (self.counters[key], key, self.messages[key])
2362 for key in sorted(self.messages) if key.startswith(prefix)]
2363
2364 def print_statistics(self, prefix=''):
2365 """Print overall statistics (number of errors and warnings)."""
2366 for line in self.get_statistics(prefix):
2367 print(line)
2368
2369 def print_benchmark(self):
2370 """Print benchmark numbers."""
2371 print('%-7.2f %s' % (self.elapsed, 'seconds elapsed'))
2372 if self.elapsed:
2373 for key in self._benchmark_keys:
2374 print('%-7d %s per second (%d total)' %
2375 (self.counters[key] / self.elapsed, key,
2376 self.counters[key]))
2377
2378
2379 class FileReport(BaseReport):
2380 """Collect the results of the checks and print the filenames."""
2381
2382 print_filename = True
2383
2384
2385 class StandardReport(BaseReport):
2386 """Collect and print the results of the checks."""
2387
2388 def __init__(self, options):
2389 super().__init__(options)
2390 self._fmt = REPORT_FORMAT.get(options.format.lower(),
2391 options.format)
2392 self._repeat = options.repeat
2393 self._show_source = options.show_source
2394 self._show_pep8 = options.show_pep8
2395
2396 def init_file(self, filename, lines, expected, line_offset):
2397 """Signal a new file."""
2398 self._deferred_print = []
2399 return super().init_file(
2400 filename, lines, expected, line_offset)
2401
2402 def error(self, line_number, offset, text, check):
2403 """Report an error, according to options."""
2404 code = super().error(line_number, offset,
2405 text, check)
2406 if code and (self.counters[code] == 1 or self._repeat):
2407 self._deferred_print.append(
2408 (line_number, offset, code, text[5:], check.__doc__))
2409 return code
2410
2411 def error_args(self, line_number, offset, code, check, *args):
2412 """Report an error, according to options."""
2413 code = super().error_args(line_number, offset,
2414 code, check, *args)
2415 if code and (self.counters[code] == 1 or self._repeat):
2416 self._deferred_print.append(
2417 (line_number, offset, code, args, check.__doc__))
2418 return code
2419
2420 def get_file_results(self):
2421 """Print results and return the overall count for this file."""
2422 self._deferred_print.sort()
2423 for line_number, offset, code, text, doc in self._deferred_print:
2424 print(self._fmt % {
2425 'path': self.filename,
2426 'row': self.line_offset + line_number, 'col': offset + 1,
2427 'code': code, 'text': text,
2428 })
2429 if self._show_source:
2430 if line_number > len(self.lines):
2431 line = ''
2432 else:
2433 line = self.lines[line_number - 1]
2434 print(line.rstrip())
2435 print(re.sub(r'\S', ' ', line[:offset]) + '^')
2436 if self._show_pep8 and doc:
2437 print(' ' + doc.strip())
2438
2439 # stdout is block buffered when not stdout.isatty().
2440 # line can be broken where buffer boundary since other
2441 # processes write to same file.
2442 # flush() after print() to avoid buffer boundary.
2443 # Typical buffer size is 8192. line written safely when
2444 # len(line) < 8192.
2445 sys.stdout.flush()
2446 return self.file_errors
2447
2448
2449 class DiffReport(StandardReport):
2450 """Collect and print the results for the changed lines only."""
2451
2452 def __init__(self, options):
2453 super().__init__(options)
2454 self._selected = options.selected_lines
2455
2456 def error(self, line_number, offset, text, check):
2457 if line_number not in self._selected[self.filename]:
2458 return
2459 return super().error(line_number, offset, text, check)
2460
2461
2462 class StyleGuide(object):
2463 """Initialize a PEP-8 instance with few options."""
2464
2465 def __init__(self, *args, **kwargs):
2466 # build options from the command line
2467 self.checker_class = kwargs.pop('checker_class', Checker)
2468 parse_argv = kwargs.pop('parse_argv', False)
2469 config_file = kwargs.pop('config_file', False)
2470 parser = kwargs.pop('parser', None)
2471 # build options from dict
2472 options_dict = dict(*args, **kwargs)
2473 arglist = None if parse_argv else options_dict.get('paths', None)
2474 verbose = options_dict.get('verbose', None)
2475 options, self.paths = process_options(
2476 arglist, parse_argv, config_file, parser, verbose)
2477 if options_dict:
2478 options.__dict__.update(options_dict)
2479 if 'paths' in options_dict:
2480 self.paths = options_dict['paths']
2481
2482 self.runner = self.input_file
2483 self.options = options
2484
2485 if not options.reporter:
2486 options.reporter = BaseReport if options.quiet else StandardReport
2487
2488 options.select = tuple(options.select or ())
2489 # if not (options.select or options.ignore or
2490 # options.testsuite or options.doctest) and DEFAULT_IGNORE:
2491 # # The default choice: ignore controversial checks
2492 # options.ignore = tuple(DEFAULT_IGNORE.split(','))
2493 # else:
2494 # # Ignore all checks which are not explicitly selected or all if no
2495 # options.ignore = ('',) if options.select else tuple(options.ignore)
2496
2497 # check is ignored or explicitly selected
2498 options.ignore = ('',) if options.select else tuple(options.ignore)
2499 options.benchmark_keys = BENCHMARK_KEYS[:]
2500 options.ignore_code = self.ignore_code
2501 options.physical_checks = self.get_checks('physical_line')
2502 options.logical_checks = self.get_checks('logical_line')
2503 options.ast_checks = self.get_checks('tree')
2504 self.init_report()
2505
2506 def init_report(self, reporter=None):
2507 """Initialize the report instance."""
2508 self.options.report = (reporter or self.options.reporter)(self.options)
2509 return self.options.report
2510
2511 def check_files(self, paths=None):
2512 """Run all checks on the paths."""
2513 if paths is None:
2514 paths = self.paths
2515 report = self.options.report
2516 runner = self.runner
2517 report.start()
2518 try:
2519 for path in paths:
2520 if os.path.isdir(path):
2521 self.input_dir(path)
2522 elif not self.excluded(path):
2523 runner(path)
2524 except KeyboardInterrupt:
2525 print('... stopped')
2526 report.stop()
2527 return report
2528
2529 def input_file(self, filename, lines=None, expected=None, line_offset=0):
2530 """Run all checks on a Python source file."""
2531 if self.options.verbose:
2532 print('checking %s' % filename)
2533 fchecker = self.checker_class(
2534 filename, lines=lines, options=self.options)
2535 return fchecker.check_all(expected=expected, line_offset=line_offset)
2536
2537 def input_dir(self, dirname):
2538 """Check all files in this directory and all subdirectories."""
2539 dirname = dirname.rstrip('/')
2540 if self.excluded(dirname):
2541 return 0
2542 counters = self.options.report.counters
2543 verbose = self.options.verbose
2544 filepatterns = self.options.filename
2545 runner = self.runner
2546 for root, dirs, files in os.walk(dirname):
2547 if verbose:
2548 print('directory ' + root)
2549 counters['directories'] += 1
2550 for subdir in sorted(dirs):
2551 if self.excluded(subdir, root):
2552 dirs.remove(subdir)
2553 for filename in sorted(files):
2554 # contain a pattern that matches?
2555 if ((filename_match(filename, filepatterns) and
2556 not self.excluded(filename, root))):
2557 runner(os.path.join(root, filename))
2558
2559 def excluded(self, filename, parent=None):
2560 """Check if the file should be excluded.
2561
2562 Check if 'options.exclude' contains a pattern matching filename.
2563 """
2564 if not self.options.exclude:
2565 return False
2566 basename = os.path.basename(filename)
2567 if filename_match(basename, self.options.exclude):
2568 return True
2569 if parent:
2570 filename = os.path.join(parent, filename)
2571 filename = os.path.abspath(filename)
2572 return filename_match(filename, self.options.exclude)
2573
2574 def ignore_code(self, code):
2575 """Check if the error code should be ignored.
2576
2577 If 'options.select' contains a prefix of the error code,
2578 return False. Else, if 'options.ignore' contains a prefix of
2579 the error code, return True.
2580 """
2581 if len(code) < 4 and any(s.startswith(code)
2582 for s in self.options.select):
2583 return False
2584 return (code.startswith(self.options.ignore) and
2585 not code.startswith(self.options.select))
2586
2587 def get_checks(self, argument_name):
2588 """Get all the checks for this category.
2589
2590 Find all globally visible functions where the first argument
2591 name starts with argument_name and which contain selected tests.
2592 """
2593 checks = []
2594 for check, attrs in _checks[argument_name].items():
2595 (codes, args) = attrs
2596 if any(not (code and self.ignore_code(code)) for code in codes):
2597 checks.append((check.__name__, check, args))
2598 return sorted(checks)
2599
2600
2601 def get_parser(prog='pycodestyle', version=__version__):
2602 """Create the parser for the program."""
2603 parser = OptionParser(prog=prog, version=version,
2604 usage="%prog [options] input ...")
2605 parser.config_options = [
2606 'exclude', 'filename', 'select', 'ignore', 'max-line-length',
2607 'max-doc-length', 'indent-size', 'hang-closing', 'count', 'format',
2608 'quiet', 'show-pep8', 'show-source', 'statistics', 'verbose']
2609 parser.add_option('-v', '--verbose', default=0, action='count',
2610 help="print status messages, or debug with -vv")
2611 parser.add_option('-q', '--quiet', default=0, action='count',
2612 help="report only file names, or nothing with -qq")
2613 parser.add_option('-r', '--repeat', default=True, action='store_true',
2614 help="(obsolete) show all occurrences of the same error")
2615 parser.add_option('--first', action='store_false', dest='repeat',
2616 help="show first occurrence of each error")
2617 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE,
2618 help="exclude files or directories which match these "
2619 "comma separated patterns (default: %default)")
2620 parser.add_option('--filename', metavar='patterns', default='*.py',
2621 help="when parsing directories, only check filenames "
2622 "matching these comma separated patterns "
2623 "(default: %default)")
2624 parser.add_option('--select', metavar='errors', default='',
2625 help="select errors and warnings (e.g. E,W6)")
2626 parser.add_option('--ignore', metavar='errors', default='',
2627 help="skip errors and warnings (e.g. E4,W) "
2628 "(default: %s)" % DEFAULT_IGNORE)
2629 parser.add_option('--show-source', action='store_true',
2630 help="show source code for each error")
2631 parser.add_option('--show-pep8', action='store_true',
2632 help="show text of PEP 8 for each error "
2633 "(implies --first)")
2634 parser.add_option('--statistics', action='store_true',
2635 help="count errors and warnings")
2636 parser.add_option('--count', action='store_true',
2637 help="print total number of errors and warnings "
2638 "to standard error and set exit code to 1 if "
2639 "total is not null")
2640 parser.add_option('--max-line-length', type='int', metavar='n',
2641 default=MAX_LINE_LENGTH,
2642 help="set maximum allowed line length "
2643 "(default: %default)")
2644 parser.add_option('--max-doc-length', type='int', metavar='n',
2645 default=None,
2646 help="set maximum allowed doc line length and perform "
2647 "these checks (unchecked if not set)")
2648 parser.add_option('--indent-size', type='int', metavar='n',
2649 default=INDENT_SIZE,
2650 help="set how many spaces make up an indent "
2651 "(default: %default)")
2652 parser.add_option('--hang-closing', action='store_true',
2653 help="hang closing bracket instead of matching "
2654 "indentation of opening bracket's line")
2655 parser.add_option('--format', metavar='format', default='default',
2656 help="set the error format [default|pylint|<custom>]")
2657 parser.add_option('--diff', action='store_true',
2658 help="report changes only within line number ranges in "
2659 "the unified diff received on STDIN")
2660 group = parser.add_option_group("Testing Options")
2661 if os.path.exists(TESTSUITE_PATH):
2662 group.add_option('--testsuite', metavar='dir',
2663 help="run regression tests from dir")
2664 group.add_option('--doctest', action='store_true',
2665 help="run doctest on myself")
2666 group.add_option('--benchmark', action='store_true',
2667 help="measure processing speed")
2668 return parser
2669
2670
2671 def read_config(options, args, arglist, parser):
2672 """Read and parse configurations.
2673
2674 If a config file is specified on the command line with the
2675 "--config" option, then only it is used for configuration.
2676
2677 Otherwise, the user configuration (~/.config/pycodestyle) and any
2678 local configurations in the current directory or above will be
2679 merged together (in that order) using the read method of
2680 ConfigParser.
2681 """
2682 config = RawConfigParser()
2683
2684 cli_conf = options.config
2685
2686 local_dir = os.curdir
2687
2688 if USER_CONFIG and os.path.isfile(USER_CONFIG):
2689 if options.verbose:
2690 print('user configuration: %s' % USER_CONFIG)
2691 config.read(USER_CONFIG)
2692
2693 parent = tail = args and os.path.abspath(os.path.commonprefix(args))
2694 while tail:
2695 if config.read(os.path.join(parent, fn) for fn in PROJECT_CONFIG):
2696 local_dir = parent
2697 if options.verbose:
2698 print('local configuration: in %s' % parent)
2699 break
2700 (parent, tail) = os.path.split(parent)
2701
2702 if cli_conf and os.path.isfile(cli_conf):
2703 if options.verbose:
2704 print('cli configuration: %s' % cli_conf)
2705 config.read(cli_conf)
2706
2707 pycodestyle_section = None
2708 if config.has_section(parser.prog):
2709 pycodestyle_section = parser.prog
2710 elif config.has_section('pep8'):
2711 pycodestyle_section = 'pep8' # Deprecated
2712 warnings.warn('[pep8] section is deprecated. Use [pycodestyle].')
2713
2714 if pycodestyle_section:
2715 option_list = {o.dest: o.type or o.action for o in parser.option_list}
2716
2717 # First, read the default values
2718 (new_options, __) = parser.parse_args([])
2719
2720 # Second, parse the configuration
2721 for opt in config.options(pycodestyle_section):
2722 if opt.replace('_', '-') not in parser.config_options:
2723 print(" unknown option '%s' ignored" % opt)
2724 continue
2725 if options.verbose > 1:
2726 print(" %s = %s" % (opt,
2727 config.get(pycodestyle_section, opt)))
2728 normalized_opt = opt.replace('-', '_')
2729 opt_type = option_list[normalized_opt]
2730 if opt_type in ('int', 'count'):
2731 value = config.getint(pycodestyle_section, opt)
2732 elif opt_type in ('store_true', 'store_false'):
2733 value = config.getboolean(pycodestyle_section, opt)
2734 else:
2735 value = config.get(pycodestyle_section, opt)
2736 if normalized_opt == 'exclude':
2737 value = normalize_paths(value, local_dir)
2738 setattr(new_options, normalized_opt, value)
2739
2740 # Third, overwrite with the command-line options
2741 (options, __) = parser.parse_args(arglist, values=new_options)
2742 options.doctest = options.testsuite = False
2743 return options
2744
2745
2746 def process_options(arglist=None, parse_argv=False, config_file=None,
2747 parser=None, verbose=None):
2748 """Process options passed either via arglist or command line args.
2749
2750 Passing in the ``config_file`` parameter allows other tools, such as
2751 flake8 to specify their own options to be processed in pycodestyle.
2752 """
2753 if not parser:
2754 parser = get_parser()
2755 if not parser.has_option('--config'):
2756 group = parser.add_option_group("Configuration", description=(
2757 "The project options are read from the [%s] section of the "
2758 "tox.ini file or the setup.cfg file located in any parent folder "
2759 "of the path(s) being processed. Allowed options are: %s." %
2760 (parser.prog, ', '.join(parser.config_options))))
2761 group.add_option('--config', metavar='path', default=config_file,
2762 help="user config file location")
2763 # Don't read the command line if the module is used as a library.
2764 if not arglist and not parse_argv:
2765 arglist = []
2766 # If parse_argv is True and arglist is None, arguments are
2767 # parsed from the command line (sys.argv)
2768 (options, args) = parser.parse_args(arglist)
2769 options.reporter = None
2770
2771 # If explicitly specified verbosity, override any `-v` CLI flag
2772 if verbose is not None:
2773 options.verbose = verbose
2774
2775 if options.ensure_value('testsuite', False):
2776 args.append(options.testsuite)
2777 elif not options.ensure_value('doctest', False):
2778 if parse_argv and not args:
2779 if options.diff or any(os.path.exists(name)
2780 for name in PROJECT_CONFIG):
2781 args = ['.']
2782 else:
2783 parser.error('input not specified')
2784 options = read_config(options, args, arglist, parser)
2785 options.reporter = parse_argv and options.quiet == 1 and FileReport
2786
2787 options.filename = _parse_multi_options(options.filename)
2788 options.exclude = normalize_paths(options.exclude)
2789 options.select = _parse_multi_options(options.select)
2790 options.ignore = _parse_multi_options(options.ignore)
2791
2792 if options.diff:
2793 options.reporter = DiffReport
2794 stdin = stdin_get_value()
2795 options.selected_lines = parse_udiff(stdin, options.filename, args[0])
2796 args = sorted(options.selected_lines)
2797
2798 return options, args
2799
2800
2801 def _parse_multi_options(options, split_token=','):
2802 r"""Split and strip and discard empties.
2803
2804 Turns the following:
2805
2806 A,
2807 B,
2808
2809 into ["A", "B"]
2810 """
2811 if options:
2812 return [o.strip() for o in options.split(split_token) if o.strip()]
2813 else:
2814 return options
2815
2816
2817 def _main():
2818 """Parse options and run checks on Python source."""
2819 import signal
2820
2821 # Handle "Broken pipe" gracefully
2822 try:
2823 signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1))
2824 except AttributeError:
2825 pass # not supported on Windows
2826
2827 style_guide = StyleGuide(parse_argv=True)
2828 options = style_guide.options
2829
2830 if options.doctest or options.testsuite:
2831 from testsuite.support import run_tests
2832 report = run_tests(style_guide)
2833 else:
2834 report = style_guide.check_files()
2835
2836 if options.statistics:
2837 report.print_statistics()
2838
2839 if options.benchmark:
2840 report.print_benchmark()
2841
2842 if options.testsuite and not options.quiet:
2843 report.print_results()
2844
2845 if report.total_errors:
2846 if options.count:
2847 sys.stderr.write(str(report.total_errors) + '\n')
2848 sys.exit(1)
2849
2850
2851 if __name__ == '__main__':
2852 _main()
2853 #
2854 # eflag: noqa = D2, M601, M801

eric ide

mercurial