OLD | NEW |
1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
2 # | 2 # |
3 # This program is free software; you can redistribute it and/or modify it under | 3 # This program is free software; you can redistribute it and/or modify it under |
4 # the terms of the GNU General Public License as published by the Free Software | 4 # the terms of the GNU General Public License as published by the Free Software |
5 # Foundation; either version 2 of the License, or (at your option) any later | 5 # Foundation; either version 2 of the License, or (at your option) any later |
6 # version. | 6 # version. |
7 # | 7 # |
8 # This program is distributed in the hope that it will be useful, but WITHOUT | 8 # This program is distributed in the hope that it will be useful, but WITHOUT |
9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | 10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
11 # | 11 # |
12 # You should have received a copy of the GNU General Public License along with | 12 # You should have received a copy of the GNU General Public License along with |
13 # this program; if not, write to the Free Software Foundation, Inc., | 13 # this program; if not, write to the Free Software Foundation, Inc., |
14 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 14 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
15 """Python code format's checker. | 15 """Python code format's checker. |
16 | 16 |
17 By default try to follow Guido's style guide : | 17 By default try to follow Guido's style guide : |
18 | 18 |
19 http://www.python.org/doc/essays/styleguide.html | 19 http://www.python.org/doc/essays/styleguide.html |
20 | 20 |
21 Some parts of the process_token method is based from The Tab Nanny std module. | 21 Some parts of the process_token method is based from The Tab Nanny std module. |
22 """ | 22 """ |
23 | 23 |
24 import keyword | 24 import keyword |
25 import sys | 25 import sys |
26 import tokenize | 26 import tokenize |
| 27 from functools import reduce # pylint: disable=redefined-builtin |
27 | 28 |
28 if not hasattr(tokenize, 'NL'): | 29 import six |
29 raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") | 30 from six.moves import zip, map, filter # pylint: disable=redefined-builtin |
30 | 31 |
31 from astroid import nodes | 32 from astroid import nodes |
32 | 33 |
33 from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker | 34 from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker |
34 from pylint.checkers import BaseTokenChecker | 35 from pylint.checkers import BaseTokenChecker |
35 from pylint.checkers.utils import check_messages | 36 from pylint.checkers.utils import check_messages |
36 from pylint.utils import WarningScope, OPTION_RGX | 37 from pylint.utils import WarningScope, OPTION_RGX |
37 | 38 |
38 _CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'c
lass'] | 39 _CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'c
lass'] |
39 _KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not', | 40 _KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not', |
(...skipping 18 matching lines...) Expand all Loading... |
58 | 59 |
59 # Whitespace checking config constants | 60 # Whitespace checking config constants |
60 _DICT_SEPARATOR = 'dict-separator' | 61 _DICT_SEPARATOR = 'dict-separator' |
61 _TRAILING_COMMA = 'trailing-comma' | 62 _TRAILING_COMMA = 'trailing-comma' |
62 _NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR] | 63 _NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR] |
63 | 64 |
64 MSGS = { | 65 MSGS = { |
65 'C0301': ('Line too long (%s/%s)', | 66 'C0301': ('Line too long (%s/%s)', |
66 'line-too-long', | 67 'line-too-long', |
67 'Used when a line is longer than a given number of characters.'), | 68 'Used when a line is longer than a given number of characters.'), |
68 'C0302': ('Too many lines in module (%s)', # was W0302 | 69 'C0302': ('Too many lines in module (%s/%s)', # was W0302 |
69 'too-many-lines', | 70 'too-many-lines', |
70 'Used when a module has too much lines, reducing its readability.' | 71 'Used when a module has too much lines, reducing its readability.' |
71 ), | 72 ), |
72 'C0303': ('Trailing whitespace', | 73 'C0303': ('Trailing whitespace', |
73 'trailing-whitespace', | 74 'trailing-whitespace', |
74 'Used when there is whitespace between the end of a line and the ' | 75 'Used when there is whitespace between the end of a line and the ' |
75 'newline.'), | 76 'newline.'), |
76 'C0304': ('Final newline missing', | 77 'C0304': ('Final newline missing', |
77 'missing-final-newline', | 78 'missing-final-newline', |
78 'Used when the last line in a file is missing a newline.'), | 79 'Used when the last line in a file is missing a newline.'), |
(...skipping 19 matching lines...) Expand all Loading... |
98 'superfluous-parens', | 99 'superfluous-parens', |
99 'Used when a single item in parentheses follows an if, for, or ' | 100 'Used when a single item in parentheses follows an if, for, or ' |
100 'other keyword.'), | 101 'other keyword.'), |
101 'C0326': ('%s space %s %s %s\n%s', | 102 'C0326': ('%s space %s %s %s\n%s', |
102 'bad-whitespace', | 103 'bad-whitespace', |
103 ('Used when a wrong number of spaces is used around an operator, ' | 104 ('Used when a wrong number of spaces is used around an operator, ' |
104 'bracket or block opener.'), | 105 'bracket or block opener.'), |
105 {'old_names': [('C0323', 'no-space-after-operator'), | 106 {'old_names': [('C0323', 'no-space-after-operator'), |
106 ('C0324', 'no-space-after-comma'), | 107 ('C0324', 'no-space-after-comma'), |
107 ('C0322', 'no-space-before-operator')]}), | 108 ('C0322', 'no-space-before-operator')]}), |
108 'W0331': ('Use of the <> operator', | |
109 'old-ne-operator', | |
110 'Used when the deprecated "<>" operator is used instead ' | |
111 'of "!=".', | |
112 {'maxversion': (3, 0)}), | |
113 'W0332': ('Use of "l" as long integer identifier', | 109 'W0332': ('Use of "l" as long integer identifier', |
114 'lowercase-l-suffix', | 110 'lowercase-l-suffix', |
115 'Used when a lower case "l" is used to mark a long integer. You ' | 111 'Used when a lower case "l" is used to mark a long integer. You ' |
116 'should use a upper case "L" since the letter "l" looks too much ' | 112 'should use a upper case "L" since the letter "l" looks too much ' |
117 'like the digit "1"', | 113 'like the digit "1"', |
118 {'maxversion': (3, 0)}), | 114 {'maxversion': (3, 0)}), |
119 'W0333': ('Use of the `` operator', | 115 'C0327': ('Mixed line endings LF and CRLF', |
120 'backtick', | 116 'mixed-line-endings', |
121 'Used when the deprecated "``" (backtick) operator is used ' | 117 'Used when there are mixed (LF and CRLF) newline signs in a file.'
), |
122 'instead of the str() function.', | 118 'C0328': ('Unexpected line ending format. There is \'%s\' while it should be
\'%s\'.', |
123 {'scope': WarningScope.NODE, 'maxversion': (3, 0)}), | 119 'unexpected-line-ending-format', |
| 120 'Used when there is different newline than expected.'), |
124 } | 121 } |
125 | 122 |
126 | 123 |
127 def _underline_token(token): | 124 def _underline_token(token): |
128 length = token[3][1] - token[2][1] | 125 length = token[3][1] - token[2][1] |
129 offset = token[2][1] | 126 offset = token[2][1] |
130 return token[4] + (' ' * offset) + ('^' * length) | 127 return token[4] + (' ' * offset) + ('^' * length) |
131 | 128 |
132 | 129 |
133 def _column_distance(token1, token2): | 130 def _column_distance(token1, token2): |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 position, | 326 position, |
330 _Offsets(indentation + self._continuation_size, indentation), | 327 _Offsets(indentation + self._continuation_size, indentation), |
331 _BeforeBlockOffsets(indentation + self._continuation_size, | 328 _BeforeBlockOffsets(indentation + self._continuation_size, |
332 indentation + self._continuation_size * 2)) | 329 indentation + self._continuation_size * 2)) |
333 elif bracket == ':': | 330 elif bracket == ':': |
334 # If the dict key was on the same line as the open brace, the new | 331 # If the dict key was on the same line as the open brace, the new |
335 # correct indent should be relative to the key instead of the | 332 # correct indent should be relative to the key instead of the |
336 # current indent level | 333 # current indent level |
337 paren_align = self._cont_stack[-1].valid_outdent_offsets | 334 paren_align = self._cont_stack[-1].valid_outdent_offsets |
338 next_align = self._cont_stack[-1].valid_continuation_offsets.copy() | 335 next_align = self._cont_stack[-1].valid_continuation_offsets.copy() |
339 next_align[next_align.keys()[0] + self._continuation_size] = True | 336 next_align_keys = list(next_align.keys()) |
| 337 next_align[next_align_keys[0] + self._continuation_size] = True |
340 # Note that the continuation of | 338 # Note that the continuation of |
341 # d = { | 339 # d = { |
342 # 'a': 'b' | 340 # 'a': 'b' |
343 # 'c' | 341 # 'c' |
344 # } | 342 # } |
345 # is handled by the special-casing for hanging continued string inde
nts. | 343 # is handled by the special-casing for hanging continued string inde
nts. |
346 return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren
_align, next_align) | 344 return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren
_align, next_align) |
347 else: | 345 else: |
348 return _ContinuedIndent( | 346 return _ContinuedIndent( |
349 HANGING, | 347 HANGING, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 else: | 392 else: |
395 self._cont_stack.append( | 393 self._cont_stack.append( |
396 self._continuation_inside_bracket(token, position)) | 394 self._continuation_inside_bracket(token, position)) |
397 | 395 |
398 | 396 |
399 class FormatChecker(BaseTokenChecker): | 397 class FormatChecker(BaseTokenChecker): |
400 """checks for : | 398 """checks for : |
401 * unauthorized constructions | 399 * unauthorized constructions |
402 * strict indentation | 400 * strict indentation |
403 * line length | 401 * line length |
404 * use of <> instead of != | |
405 """ | 402 """ |
406 | 403 |
407 __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) | 404 __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) |
408 | 405 |
409 # configuration section name | 406 # configuration section name |
410 name = 'format' | 407 name = 'format' |
411 # messages | 408 # messages |
412 msgs = MSGS | 409 msgs = MSGS |
413 # configuration options | 410 # configuration options |
414 # for available dict keys/values see the optik parser 'add_option' method | 411 # for available dict keys/values see the optik parser 'add_option' method |
415 options = (('max-line-length', | 412 options = (('max-line-length', |
416 {'default' : 80, 'type' : "int", 'metavar' : '<int>', | 413 {'default' : 100, 'type' : "int", 'metavar' : '<int>', |
417 'help' : 'Maximum number of characters on a single line.'}), | 414 'help' : 'Maximum number of characters on a single line.'}), |
418 ('ignore-long-lines', | 415 ('ignore-long-lines', |
419 {'type': 'regexp', 'metavar': '<regexp>', | 416 {'type': 'regexp', 'metavar': '<regexp>', |
420 'default': r'^\s*(# )?<?https?://\S+>?$', | 417 'default': r'^\s*(# )?<?https?://\S+>?$', |
421 'help': ('Regexp for a line that is allowed to be longer than ' | 418 'help': ('Regexp for a line that is allowed to be longer than ' |
422 'the limit.')}), | 419 'the limit.')}), |
423 ('single-line-if-stmt', | 420 ('single-line-if-stmt', |
424 {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>', | 421 {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>', |
425 'help' : ('Allow the body of an if to be on the same ' | 422 'help' : ('Allow the body of an if to be on the same ' |
426 'line as the test if there is no else.')}), | 423 'line as the test if there is no else.')}), |
427 ('no-space-check', | 424 ('no-space-check', |
428 {'default': ','.join(_NO_SPACE_CHECK_CHOICES), | 425 {'default': ','.join(_NO_SPACE_CHECK_CHOICES), |
429 'type': 'multiple_choice', | 426 'type': 'multiple_choice', |
430 'choices': _NO_SPACE_CHECK_CHOICES, | 427 'choices': _NO_SPACE_CHECK_CHOICES, |
431 'help': ('List of optional constructs for which whitespace ' | 428 'help': ('List of optional constructs for which whitespace ' |
432 'checking is disabled')}), | 429 'checking is disabled')}), |
433 ('max-module-lines', | 430 ('max-module-lines', |
434 {'default' : 1000, 'type' : 'int', 'metavar' : '<int>', | 431 {'default' : 1000, 'type' : 'int', 'metavar' : '<int>', |
435 'help': 'Maximum number of lines in a module'} | 432 'help': 'Maximum number of lines in a module'} |
436 ), | 433 ), |
437 ('indent-string', | 434 ('indent-string', |
438 {'default' : ' ', 'type' : "string", 'metavar' : '<string>', | 435 {'default' : ' ', 'type' : "string", 'metavar' : '<string>', |
439 'help' : 'String used as indentation unit. This is usually ' | 436 'help' : 'String used as indentation unit. This is usually ' |
440 '" " (4 spaces) or "\\t" (1 tab).'}), | 437 '" " (4 spaces) or "\\t" (1 tab).'}), |
441 ('indent-after-paren', | 438 ('indent-after-paren', |
442 {'type': 'int', 'metavar': '<int>', 'default': 4, | 439 {'type': 'int', 'metavar': '<int>', 'default': 4, |
443 'help': 'Number of spaces of indent required inside a hanging ' | 440 'help': 'Number of spaces of indent required inside a hanging ' |
444 ' or continued line.'}), | 441 ' or continued line.'}), |
| 442 ('expected-line-ending-format', |
| 443 {'type': 'choice', 'metavar': '<empty or LF or CRLF>', 'default'
: '', |
| 444 'choices': ['', 'LF', 'CRLF'], |
| 445 'help': 'Expected format of line ending, e.g. empty (any line e
nding), LF or CRLF.'}), |
445 ) | 446 ) |
446 | 447 |
447 def __init__(self, linter=None): | 448 def __init__(self, linter=None): |
448 BaseTokenChecker.__init__(self, linter) | 449 BaseTokenChecker.__init__(self, linter) |
449 self._lines = None | 450 self._lines = None |
450 self._visited_lines = None | 451 self._visited_lines = None |
451 self._bracket_stack = [None] | 452 self._bracket_stack = [None] |
452 | 453 |
453 def _pop_token(self): | 454 def _pop_token(self): |
454 self._bracket_stack.pop() | 455 self._bracket_stack.pop() |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 if self._inside_brackets(':') and tokens[start][1] == 'for': | 490 if self._inside_brackets(':') and tokens[start][1] == 'for': |
490 self._pop_token() | 491 self._pop_token() |
491 if tokens[start+1][1] != '(': | 492 if tokens[start+1][1] != '(': |
492 return | 493 return |
493 | 494 |
494 found_and_or = False | 495 found_and_or = False |
495 depth = 0 | 496 depth = 0 |
496 keyword_token = tokens[start][1] | 497 keyword_token = tokens[start][1] |
497 line_num = tokens[start][2][0] | 498 line_num = tokens[start][2][0] |
498 | 499 |
499 for i in xrange(start, len(tokens) - 1): | 500 for i in range(start, len(tokens) - 1): |
500 token = tokens[i] | 501 token = tokens[i] |
501 | 502 |
502 # If we hit a newline, then assume any parens were for continuation. | 503 # If we hit a newline, then assume any parens were for continuation. |
503 if token[0] == tokenize.NL: | 504 if token[0] == tokenize.NL: |
504 return | 505 return |
505 | 506 |
506 if token[1] == '(': | 507 if token[1] == '(': |
507 depth += 1 | 508 depth += 1 |
508 elif token[1] == ')': | 509 elif token[1] == ')': |
509 depth -= 1 | 510 depth -= 1 |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
615 self._check_space(tokens, i, (_MUST, _MUST)) | 616 self._check_space(tokens, i, (_MUST, _MUST)) |
616 | 617 |
617 def _check_space(self, tokens, i, policies): | 618 def _check_space(self, tokens, i, policies): |
618 def _policy_string(policy): | 619 def _policy_string(policy): |
619 if policy == _MUST: | 620 if policy == _MUST: |
620 return 'Exactly one', 'required' | 621 return 'Exactly one', 'required' |
621 else: | 622 else: |
622 return 'No', 'allowed' | 623 return 'No', 'allowed' |
623 | 624 |
624 def _name_construct(token): | 625 def _name_construct(token): |
625 if tokens[i][1] == ',': | 626 if token[1] == ',': |
626 return 'comma' | 627 return 'comma' |
627 elif tokens[i][1] == ':': | 628 elif token[1] == ':': |
628 return ':' | 629 return ':' |
629 elif tokens[i][1] in '()[]{}': | 630 elif token[1] in '()[]{}': |
630 return 'bracket' | 631 return 'bracket' |
631 elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='): | 632 elif token[1] in ('<', '>', '<=', '>=', '!=', '=='): |
632 return 'comparison' | 633 return 'comparison' |
633 else: | 634 else: |
634 if self._inside_brackets('('): | 635 if self._inside_brackets('('): |
635 return 'keyword argument assignment' | 636 return 'keyword argument assignment' |
636 else: | 637 else: |
637 return 'assignment' | 638 return 'assignment' |
638 | 639 |
639 good_space = [True, True] | 640 good_space = [True, True] |
640 pairs = [(tokens[i-1], tokens[i]), (tokens[i], tokens[i+1])] | 641 token = tokens[i] |
| 642 pairs = [(tokens[i-1], token), (token, tokens[i+1])] |
641 | 643 |
642 for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): | 644 for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): |
643 if token_pair[other_idx][0] in _EOL or policy == _IGNORE: | 645 if token_pair[other_idx][0] in _EOL or policy == _IGNORE: |
644 continue | 646 continue |
645 | 647 |
646 distance = _column_distance(*token_pair) | 648 distance = _column_distance(*token_pair) |
647 if distance is None: | 649 if distance is None: |
648 continue | 650 continue |
649 good_space[other_idx] = ( | 651 good_space[other_idx] = ( |
650 (policy == _MUST and distance == 1) or | 652 (policy == _MUST and distance == 1) or |
651 (policy == _MUST_NOT and distance == 0)) | 653 (policy == _MUST_NOT and distance == 0)) |
652 | 654 |
653 warnings = [] | 655 warnings = [] |
654 if not any(good_space) and policies[0] == policies[1]: | 656 if not any(good_space) and policies[0] == policies[1]: |
655 warnings.append((policies[0], 'around')) | 657 warnings.append((policies[0], 'around')) |
656 else: | 658 else: |
657 for ok, policy, position in zip(good_space, policies, ('before', 'af
ter')): | 659 for ok, policy, position in zip(good_space, policies, ('before', 'af
ter')): |
658 if not ok: | 660 if not ok: |
659 warnings.append((policy, position)) | 661 warnings.append((policy, position)) |
660 for policy, position in warnings: | 662 for policy, position in warnings: |
661 construct = _name_construct(tokens[i]) | 663 construct = _name_construct(token) |
662 count, state = _policy_string(policy) | 664 count, state = _policy_string(policy) |
663 self.add_message('bad-whitespace', line=tokens[i][2][0], | 665 self.add_message('bad-whitespace', line=token[2][0], |
664 args=(count, state, position, construct, | 666 args=(count, state, position, construct, |
665 _underline_token(tokens[i]))) | 667 _underline_token(token))) |
666 | 668 |
667 def _inside_brackets(self, left): | 669 def _inside_brackets(self, left): |
668 return self._bracket_stack[-1] == left | 670 return self._bracket_stack[-1] == left |
669 | 671 |
670 def _handle_old_ne_operator(self, tokens, i): | |
671 if tokens[i][1] == '<>': | |
672 self.add_message('old-ne-operator', line=tokens[i][2][0]) | |
673 | |
674 def _prepare_token_dispatcher(self): | 672 def _prepare_token_dispatcher(self): |
675 raw = [ | 673 raw = [ |
676 (_KEYWORD_TOKENS, | 674 (_KEYWORD_TOKENS, |
677 self._check_keyword_parentheses), | 675 self._check_keyword_parentheses), |
678 | 676 |
679 (_OPENING_BRACKETS, self._opening_bracket), | 677 (_OPENING_BRACKETS, self._opening_bracket), |
680 | 678 |
681 (_CLOSING_BRACKETS, self._closing_bracket), | 679 (_CLOSING_BRACKETS, self._closing_bracket), |
682 | 680 |
683 (['='], self._check_equals_spacing), | 681 (['='], self._check_equals_spacing), |
684 | 682 |
685 (_SPACED_OPERATORS, self._check_surrounded_by_space), | 683 (_SPACED_OPERATORS, self._check_surrounded_by_space), |
686 | 684 |
687 ([','], self._handle_comma), | 685 ([','], self._handle_comma), |
688 | 686 |
689 ([':'], self._handle_colon), | 687 ([':'], self._handle_colon), |
690 | 688 |
691 (['lambda'], self._open_lambda), | 689 (['lambda'], self._open_lambda), |
692 | 690 |
693 (['<>'], self._handle_old_ne_operator), | |
694 ] | 691 ] |
695 | 692 |
696 dispatch = {} | 693 dispatch = {} |
697 for tokens, handler in raw: | 694 for tokens, handler in raw: |
698 for token in tokens: | 695 for token in tokens: |
699 dispatch[token] = handler | 696 dispatch[token] = handler |
700 return dispatch | 697 return dispatch |
701 | 698 |
702 def process_tokens(self, tokens): | 699 def process_tokens(self, tokens): |
703 """process tokens and search for : | 700 """process tokens and search for : |
704 | 701 |
705 _ non strict indentation (i.e. not always using the <indent> parameter
as | 702 _ non strict indentation (i.e. not always using the <indent> parameter
as |
706 indent unit) | 703 indent unit) |
707 _ too long lines (i.e. longer than <max_chars>) | 704 _ too long lines (i.e. longer than <max_chars>) |
708 _ optionally bad construct (if given, bad_construct must be a compiled | 705 _ optionally bad construct (if given, bad_construct must be a compiled |
709 regular expression). | 706 regular expression). |
710 """ | 707 """ |
711 self._bracket_stack = [None] | 708 self._bracket_stack = [None] |
712 indents = [0] | 709 indents = [0] |
713 check_equal = False | 710 check_equal = False |
714 line_num = 0 | 711 line_num = 0 |
715 self._lines = {} | 712 self._lines = {} |
716 self._visited_lines = {} | 713 self._visited_lines = {} |
717 token_handlers = self._prepare_token_dispatcher() | 714 token_handlers = self._prepare_token_dispatcher() |
| 715 self._last_line_ending = None |
718 | 716 |
719 self._current_line = ContinuedLineState(tokens, self.config) | 717 self._current_line = ContinuedLineState(tokens, self.config) |
720 for idx, (tok_type, token, start, _, line) in enumerate(tokens): | 718 for idx, (tok_type, token, start, _, line) in enumerate(tokens): |
721 if start[0] != line_num: | 719 if start[0] != line_num: |
722 line_num = start[0] | 720 line_num = start[0] |
723 # A tokenizer oddity: if an indented line contains a multi-line | 721 # A tokenizer oddity: if an indented line contains a multi-line |
724 # docstring, the line member of the INDENT token does not contai
n | 722 # docstring, the line member of the INDENT token does not contai
n |
725 # the full line; therefore we check the next token on the line. | 723 # the full line; therefore we check the next token on the line. |
726 if tok_type == tokenize.INDENT: | 724 if tok_type == tokenize.INDENT: |
727 self.new_line(TokenWrapper(tokens), idx-1, idx+1) | 725 self.new_line(TokenWrapper(tokens), idx-1, idx+1) |
728 else: | 726 else: |
729 self.new_line(TokenWrapper(tokens), idx-1, idx) | 727 self.new_line(TokenWrapper(tokens), idx-1, idx) |
730 | 728 |
731 if tok_type == tokenize.NEWLINE: | 729 if tok_type == tokenize.NEWLINE: |
732 # a program statement, or ENDMARKER, will eventually follow, | 730 # a program statement, or ENDMARKER, will eventually follow, |
733 # after some (possibly empty) run of tokens of the form | 731 # after some (possibly empty) run of tokens of the form |
734 # (NL | COMMENT)* (INDENT | DEDENT+)? | 732 # (NL | COMMENT)* (INDENT | DEDENT+)? |
735 # If an INDENT appears, setting check_equal is wrong, and will | 733 # If an INDENT appears, setting check_equal is wrong, and will |
736 # be undone when we see the INDENT. | 734 # be undone when we see the INDENT. |
737 check_equal = True | 735 check_equal = True |
738 self._process_retained_warnings(TokenWrapper(tokens), idx) | 736 self._process_retained_warnings(TokenWrapper(tokens), idx) |
739 self._current_line.next_logical_line() | 737 self._current_line.next_logical_line() |
| 738 self._check_line_ending(token, line_num) |
740 elif tok_type == tokenize.INDENT: | 739 elif tok_type == tokenize.INDENT: |
741 check_equal = False | 740 check_equal = False |
742 self.check_indent_level(token, indents[-1]+1, line_num) | 741 self.check_indent_level(token, indents[-1]+1, line_num) |
743 indents.append(indents[-1]+1) | 742 indents.append(indents[-1]+1) |
744 elif tok_type == tokenize.DEDENT: | 743 elif tok_type == tokenize.DEDENT: |
745 # there's nothing we need to check here! what's important is | 744 # there's nothing we need to check here! what's important is |
746 # that when the run of DEDENTs ends, the indentation of the | 745 # that when the run of DEDENTs ends, the indentation of the |
747 # program statement (or ENDMARKER) that triggered the run is | 746 # program statement (or ENDMARKER) that triggered the run is |
748 # equal to what's left at the top of the indents stack | 747 # equal to what's left at the top of the indents stack |
749 check_equal = True | 748 check_equal = True |
(...skipping 19 matching lines...) Expand all Loading... |
769 | 768 |
770 try: | 769 try: |
771 handler = token_handlers[token] | 770 handler = token_handlers[token] |
772 except KeyError: | 771 except KeyError: |
773 pass | 772 pass |
774 else: | 773 else: |
775 handler(tokens, idx) | 774 handler(tokens, idx) |
776 | 775 |
777 line_num -= 1 # to be ok with "wc -l" | 776 line_num -= 1 # to be ok with "wc -l" |
778 if line_num > self.config.max_module_lines: | 777 if line_num > self.config.max_module_lines: |
779 self.add_message('too-many-lines', args=line_num, line=1) | 778 # Get the line where the too-many-lines (or its message id) |
| 779 # was disabled or default to 1. |
| 780 symbol = self.linter.msgs_store.check_message_id('too-many-lines') |
| 781 names = (symbol.msgid, 'too-many-lines') |
| 782 line = next(filter(None, |
| 783 map(self.linter._pragma_lineno.get, names)), 1) |
| 784 self.add_message('too-many-lines', |
| 785 args=(line_num, self.config.max_module_lines), |
| 786 line=line) |
| 787 |
| 788 def _check_line_ending(self, line_ending, line_num): |
| 789 # check if line endings are mixed |
| 790 if self._last_line_ending is not None: |
| 791 if line_ending != self._last_line_ending: |
| 792 self.add_message('mixed-line-endings', line=line_num) |
| 793 |
| 794 self._last_line_ending = line_ending |
| 795 |
| 796 # check if line ending is as expected |
| 797 expected = self.config.expected_line_ending_format |
| 798 if expected: |
| 799 line_ending = reduce(lambda x, y: x + y if x != y else x, line_endin
g, "") # reduce multiple \n\n\n\n to one \n |
| 800 line_ending = 'LF' if line_ending == '\n' else 'CRLF' |
| 801 if line_ending != expected: |
| 802 self.add_message('unexpected-line-ending-format', args=(line_end
ing, expected), line=line_num) |
| 803 |
780 | 804 |
781 def _process_retained_warnings(self, tokens, current_pos): | 805 def _process_retained_warnings(self, tokens, current_pos): |
782 single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos,
':') | 806 single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos,
':') |
783 | 807 |
784 for indent_pos, state, offsets in self._current_line.retained_warnings: | 808 for indent_pos, state, offsets in self._current_line.retained_warnings: |
785 block_type = offsets[tokens.start_col(indent_pos)] | 809 block_type = offsets[tokens.start_col(indent_pos)] |
786 hints = dict((k, v) for k, v in offsets.iteritems() | 810 hints = dict((k, v) for k, v in six.iteritems(offsets) |
787 if v != block_type) | 811 if v != block_type) |
788 if single_line_block_stmt and block_type == WITH_BODY: | 812 if single_line_block_stmt and block_type == WITH_BODY: |
789 self._add_continuation_message(state, hints, tokens, indent_pos) | 813 self._add_continuation_message(state, hints, tokens, indent_pos) |
790 elif not single_line_block_stmt and block_type == SINGLE_LINE: | 814 elif not single_line_block_stmt and block_type == SINGLE_LINE: |
791 self._add_continuation_message(state, hints, tokens, indent_pos) | 815 self._add_continuation_message(state, hints, tokens, indent_pos) |
792 | 816 |
793 def _check_continued_indentation(self, tokens, next_idx): | 817 def _check_continued_indentation(self, tokens, next_idx): |
794 def same_token_around_nl(token_type): | 818 def same_token_around_nl(token_type): |
795 return (tokens.type(next_idx) == token_type and | 819 return (tokens.type(next_idx) == token_type and |
796 tokens.type(next_idx-2) == token_type) | 820 tokens.type(next_idx-2) == token_type) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
850 self._check_multi_statement_line(node, line) | 874 self._check_multi_statement_line(node, line) |
851 return | 875 return |
852 if line in self._visited_lines: | 876 if line in self._visited_lines: |
853 return | 877 return |
854 try: | 878 try: |
855 tolineno = node.blockstart_tolineno | 879 tolineno = node.blockstart_tolineno |
856 except AttributeError: | 880 except AttributeError: |
857 tolineno = node.tolineno | 881 tolineno = node.tolineno |
858 assert tolineno, node | 882 assert tolineno, node |
859 lines = [] | 883 lines = [] |
860 for line in xrange(line, tolineno + 1): | 884 for line in range(line, tolineno + 1): |
861 self._visited_lines[line] = 1 | 885 self._visited_lines[line] = 1 |
862 try: | 886 try: |
863 lines.append(self._lines[line].rstrip()) | 887 lines.append(self._lines[line].rstrip()) |
864 except KeyError: | 888 except KeyError: |
865 lines.append('') | 889 lines.append('') |
866 | 890 |
867 def _check_multi_statement_line(self, node, line): | 891 def _check_multi_statement_line(self, node, line): |
868 """Check for lines containing multiple statements.""" | 892 """Check for lines containing multiple statements.""" |
869 # Do not warn about multiple nested context managers | 893 # Do not warn about multiple nested context managers |
870 # in with statements. | 894 # in with statements. |
871 if isinstance(node, nodes.With): | 895 if isinstance(node, nodes.With): |
872 return | 896 return |
873 # For try... except... finally..., the two nodes | 897 # For try... except... finally..., the two nodes |
874 # appear to be on the same line due to how the AST is built. | 898 # appear to be on the same line due to how the AST is built. |
875 if (isinstance(node, nodes.TryExcept) and | 899 if (isinstance(node, nodes.TryExcept) and |
876 isinstance(node.parent, nodes.TryFinally)): | 900 isinstance(node.parent, nodes.TryFinally)): |
877 return | 901 return |
878 if (isinstance(node.parent, nodes.If) and not node.parent.orelse | 902 if (isinstance(node.parent, nodes.If) and not node.parent.orelse |
879 and self.config.single_line_if_stmt): | 903 and self.config.single_line_if_stmt): |
880 return | 904 return |
881 self.add_message('multiple-statements', node=node) | 905 self.add_message('multiple-statements', node=node) |
882 self._visited_lines[line] = 2 | 906 self._visited_lines[line] = 2 |
883 | 907 |
884 @check_messages('backtick') | |
885 def visit_backquote(self, node): | |
886 self.add_message('backtick', node=node) | |
887 | |
888 def check_lines(self, lines, i): | 908 def check_lines(self, lines, i): |
889 """check lines have less than a maximum number of characters | 909 """check lines have less than a maximum number of characters |
890 """ | 910 """ |
891 max_chars = self.config.max_line_length | 911 max_chars = self.config.max_line_length |
892 ignore_long_line = self.config.ignore_long_lines | 912 ignore_long_line = self.config.ignore_long_lines |
893 | 913 |
894 for line in lines.splitlines(True): | 914 for line in lines.splitlines(True): |
895 if not line.endswith('\n'): | 915 if not line.endswith('\n'): |
896 self.add_message('missing-final-newline', line=i) | 916 self.add_message('missing-final-newline', line=i) |
897 else: | 917 else: |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
935 if indent[0] == '\t': | 955 if indent[0] == '\t': |
936 i_type = 'tabs' | 956 i_type = 'tabs' |
937 self.add_message('bad-indentation', line=line_num, | 957 self.add_message('bad-indentation', line=line_num, |
938 args=(level * unit_size + len(suppl), i_type, | 958 args=(level * unit_size + len(suppl), i_type, |
939 expected * unit_size)) | 959 expected * unit_size)) |
940 | 960 |
941 | 961 |
942 def register(linter): | 962 def register(linter): |
943 """required method to auto register this checker """ | 963 """required method to auto register this checker """ |
944 linter.register_checker(FormatChecker(linter)) | 964 linter.register_checker(FormatChecker(linter)) |
OLD | NEW |