UtilitiesPython2/pep8.py

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

eric ide

mercurial