| OLD | NEW | 
|    1 # Copyright (c) 2003-2010 Sylvain Thenault (thenault@gmail.com). |    1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 
|    2 # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). |    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 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 re, sys |   24 import keyword | 
 |   25 import sys | 
|   25 import tokenize |   26 import tokenize | 
 |   27  | 
|   26 if not hasattr(tokenize, 'NL'): |   28 if not hasattr(tokenize, 'NL'): | 
|   27     raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") |   29     raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") | 
|   28  |   30  | 
|   29 from logilab.common.textutils import pretty_match |   31 from astroid import nodes | 
|   30 from logilab.astng import nodes |   32  | 
|   31  |   33 from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker | 
|   32 from pylint.interfaces import IRawChecker, IASTNGChecker |   34 from pylint.checkers import BaseTokenChecker | 
|   33 from pylint.checkers import BaseRawChecker |  | 
|   34 from pylint.checkers.utils import check_messages |   35 from pylint.checkers.utils import check_messages | 
 |   36 from pylint.utils import WarningScope, OPTION_RGX | 
 |   37  | 
 |   38 _CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'c
     lass'] | 
 |   39 _KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not', | 
 |   40                    'raise', 'return', 'while', 'yield'] | 
 |   41 if sys.version_info < (3, 0): | 
 |   42     _KEYWORD_TOKENS.append('print') | 
 |   43  | 
 |   44 _SPACED_OPERATORS = ['==', '<', '>', '!=', '<>', '<=', '>=', | 
 |   45                      '+=', '-=', '*=', '**=', '/=', '//=', '&=', '|=', '^=', | 
 |   46                      '%=', '>>=', '<<='] | 
 |   47 _OPENING_BRACKETS = ['(', '[', '{'] | 
 |   48 _CLOSING_BRACKETS = [')', ']', '}'] | 
 |   49 _TAB_LENGTH = 8 | 
 |   50  | 
 |   51 _EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT]) | 
 |   52 _JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL) | 
 |   53  | 
 |   54 # Whitespace checking policy constants | 
 |   55 _MUST = 0 | 
 |   56 _MUST_NOT = 1 | 
 |   57 _IGNORE = 2 | 
 |   58  | 
 |   59 # Whitespace checking config constants | 
 |   60 _DICT_SEPARATOR = 'dict-separator' | 
 |   61 _TRAILING_COMMA = 'trailing-comma' | 
 |   62 _NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR] | 
|   35  |   63  | 
|   36 MSGS = { |   64 MSGS = { | 
|   37     'C0301': ('Line too long (%s/%s)', |   65     'C0301': ('Line too long (%s/%s)', | 
 |   66               'line-too-long', | 
|   38               'Used when a line is longer than a given number of characters.'), |   67               'Used when a line is longer than a given number of characters.'), | 
|   39     'C0302': ('Too many lines in module (%s)', # was W0302 |   68     'C0302': ('Too many lines in module (%s)', # was W0302 | 
 |   69               'too-many-lines', | 
|   40               'Used when a module has too much lines, reducing its readability.' |   70               'Used when a module has too much lines, reducing its readability.' | 
|   41               ), |   71              ), | 
|   42  |   72     'C0303': ('Trailing whitespace', | 
 |   73               'trailing-whitespace', | 
 |   74               'Used when there is whitespace between the end of a line and the ' | 
 |   75               'newline.'), | 
 |   76     'C0304': ('Final newline missing', | 
 |   77               'missing-final-newline', | 
 |   78               'Used when the last line in a file is missing a newline.'), | 
|   43     'W0311': ('Bad indentation. Found %s %s, expected %s', |   79     'W0311': ('Bad indentation. Found %s %s, expected %s', | 
 |   80               'bad-indentation', | 
|   44               'Used when an unexpected number of indentation\'s tabulations or ' |   81               'Used when an unexpected number of indentation\'s tabulations or ' | 
|   45               'spaces has been found.'), |   82               'spaces has been found.'), | 
 |   83     'C0330': ('Wrong %s indentation%s.\n%s%s', | 
 |   84               'bad-continuation', | 
 |   85               'TODO'), | 
|   46     'W0312': ('Found indentation with %ss instead of %ss', |   86     'W0312': ('Found indentation with %ss instead of %ss', | 
 |   87               'mixed-indentation', | 
|   47               'Used when there are some mixed tabs and spaces in a module.'), |   88               'Used when there are some mixed tabs and spaces in a module.'), | 
|   48     'W0301': ('Unnecessary semicolon', # was W0106 |   89     'W0301': ('Unnecessary semicolon', # was W0106 | 
 |   90               'unnecessary-semicolon', | 
|   49               'Used when a statement is ended by a semi-colon (";"), which \ |   91               'Used when a statement is ended by a semi-colon (";"), which \ | 
|   50               isn\'t necessary (that\'s python, not C ;).'), |   92               isn\'t necessary (that\'s python, not C ;).'), | 
|   51     'C0321': ('More than one statement on a single line', |   93     'C0321': ('More than one statement on a single line', | 
|   52               'Used when more than on statement are found on the same line.'), |   94               'multiple-statements', | 
|   53     'C0322': ('Operator not preceded by a space\n%s', |   95               'Used when more than on statement are found on the same line.', | 
|   54               'Used when one of the following operator (!= | <= | == | >= | < ' |   96               {'scope': WarningScope.NODE}), | 
|   55               '| > | = | \+= | -= | \*= | /= | %) is not preceded by a space.'), |   97     'C0325' : ('Unnecessary parens after %r keyword', | 
|   56     'C0323': ('Operator not followed by a space\n%s', |   98                'superfluous-parens', | 
|   57               'Used when one of the following operator (!= | <= | == | >= | < ' |   99                'Used when a single item in parentheses follows an if, for, or ' | 
|   58               '| > | = | \+= | -= | \*= | /= | %) is not followed by a space.'), |  100                'other keyword.'), | 
|   59     'C0324': ('Comma not followed by a space\n%s', |  101     'C0326': ('%s space %s %s %s\n%s', | 
|   60               'Used when a comma (",") is not followed by a space.'), |  102               'bad-whitespace', | 
|   61     } |  103               ('Used when a wrong number of spaces is used around an operator, ' | 
|   62  |  104                'bracket or block opener.'), | 
|   63 if sys.version_info < (3, 0): |  105               {'old_names': [('C0323', 'no-space-after-operator'), | 
|   64  |  106                              ('C0324', 'no-space-after-comma'), | 
|   65     MSGS.update({ |  107                              ('C0322', 'no-space-before-operator')]}), | 
|   66     'W0331': ('Use of the <> operator', |  108     'W0331': ('Use of the <> operator', | 
|   67               'Used when the deprecated "<>" operator is used instead \ |  109               'old-ne-operator', | 
|   68               of "!=".'), |  110               'Used when the deprecated "<>" operator is used instead ' | 
|   69     'W0332': ('Use l as long integer identifier', |  111               'of "!=".', | 
 |  112               {'maxversion': (3, 0)}), | 
 |  113     'W0332': ('Use of "l" as long integer identifier', | 
 |  114               'lowercase-l-suffix', | 
|   70               'Used when a lower case "l" is used to mark a long integer. You ' |  115               'Used when a lower case "l" is used to mark a long integer. You ' | 
|   71               'should use a upper case "L" since the letter "l" looks too much ' |  116               'should use a upper case "L" since the letter "l" looks too much ' | 
|   72               'like the digit "1"'), |  117               'like the digit "1"', | 
 |  118               {'maxversion': (3, 0)}), | 
|   73     'W0333': ('Use of the `` operator', |  119     'W0333': ('Use of the `` operator', | 
 |  120               'backtick', | 
|   74               'Used when the deprecated "``" (backtick) operator is used ' |  121               'Used when the deprecated "``" (backtick) operator is used ' | 
|   75               'instead  of the str() function.'), |  122               'instead  of the str() function.', | 
|   76     }) |  123               {'scope': WarningScope.NODE, 'maxversion': (3, 0)}), | 
|   77  |  124     } | 
|   78 # simple quoted string rgx |  125  | 
|   79 SQSTRING_RGX = r'"([^"\\]|\\.)*?"' |  126  | 
|   80 # simple apostrophed rgx |  127 def _underline_token(token): | 
|   81 SASTRING_RGX = r"'([^'\\]|\\.)*?'" |  128     length = token[3][1] - token[2][1] | 
|   82 # triple quoted string rgx |  129     offset = token[2][1] | 
|   83 TQSTRING_RGX = r'"""([^"]|("(?!"")))*?(""")' |  130     return token[4] + (' ' * offset) + ('^' * length) | 
|   84 # triple apostrophed string rgx # FIXME english please |  131  | 
|   85 TASTRING_RGX = r"'''([^']|('(?!'')))*?(''')" |  132  | 
|   86  |  133 def _column_distance(token1, token2): | 
|   87 # finally, the string regular expression |  134     if token1 == token2: | 
|   88 STRING_RGX = re.compile('(%s)|(%s)|(%s)|(%s)' % (TQSTRING_RGX, TASTRING_RGX, |  135         return 0 | 
|   89                                                  SQSTRING_RGX, SASTRING_RGX), |  136     if token2[3] < token1[3]: | 
|   90                         re.MULTILINE|re.DOTALL) |  137         token1, token2 = token2, token1 | 
|   91  |  138     if token1[3][0] != token2[2][0]: | 
|   92 COMMENT_RGX = re.compile("#.*$", re.M) |  139         return None | 
|   93  |  140     return token2[2][1] - token1[3][1] | 
|   94 OPERATORS = r'!=|<=|==|>=|<|>|=|\+=|-=|\*=|/=|%' |  141  | 
|   95  |  142  | 
|   96 OP_RGX_MATCH_1 = r'[^(]*(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s).*' % OPERATORS |  143 def _last_token_on_line_is(tokens, line_end, token): | 
|   97 OP_RGX_SEARCH_1 = r'(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s)' % OPERATORS |  144     return (line_end > 0 and tokens.token(line_end-1) == token or | 
|   98  |  145             line_end > 1 and tokens.token(line_end-2) == token | 
|   99 OP_RGX_MATCH_2 = r'[^(]*(%s)(?!\s|=|>|<).*' % OPERATORS |  146             and tokens.type(line_end-1) == tokenize.COMMENT) | 
|  100 OP_RGX_SEARCH_2 = r'(%s)(?!\s|=|>)' % OPERATORS |  147  | 
|  101  |  148  | 
|  102 BAD_CONSTRUCT_RGXS = ( |  149 def _token_followed_by_eol(tokens, position): | 
|  103  |  150     return (tokens.type(position+1) == tokenize.NL or | 
|  104     (re.compile(OP_RGX_MATCH_1, re.M), |  151             tokens.type(position+1) == tokenize.COMMENT and | 
|  105      re.compile(OP_RGX_SEARCH_1, re.M), |  152             tokens.type(position+2) == tokenize.NL) | 
|  106      'C0322'), |  153  | 
|  107  |  154  | 
|  108     (re.compile(OP_RGX_MATCH_2, re.M), |  155 def _get_indent_length(line): | 
|  109      re.compile(OP_RGX_SEARCH_2, re.M), |  156     """Return the length of the indentation on the given token's line.""" | 
|  110      'C0323'), |  157     result = 0 | 
|  111  |  158     for char in line: | 
|  112     (re.compile(r'.*,[^(\s|\]|}|\))].*', re.M),  |  159         if char == ' ': | 
|  113      re.compile(r',[^\s)]', re.M), |  160             result += 1 | 
|  114      'C0324'), |  161         elif char == '\t': | 
|  115     ) |  162             result += _TAB_LENGTH | 
|  116  |  163         else: | 
|  117  |  164             break | 
|  118 def get_string_coords(line): |  165     return result | 
|  119     """return a list of string positions (tuple (start, end)) in the line |  166  | 
 |  167  | 
 |  168 def _get_indent_hint_line(bar_positions, bad_position): | 
 |  169     """Return a line with |s for each of the positions in the given lists.""" | 
 |  170     if not bar_positions: | 
 |  171         return '' | 
 |  172     markers = [(pos, '|') for pos in bar_positions] | 
 |  173     markers.append((bad_position, '^')) | 
 |  174     markers.sort() | 
 |  175     line = [' '] * (markers[-1][0] + 1) | 
 |  176     for position, marker in markers: | 
 |  177         line[position] = marker | 
 |  178     return ''.join(line) | 
 |  179  | 
 |  180  | 
 |  181 class _ContinuedIndent(object): | 
 |  182     __slots__ = ('valid_outdent_offsets', | 
 |  183                  'valid_continuation_offsets', | 
 |  184                  'context_type', | 
 |  185                  'token', | 
 |  186                  'position') | 
 |  187  | 
 |  188     def __init__(self, | 
 |  189                  context_type, | 
 |  190                  token, | 
 |  191                  position, | 
 |  192                  valid_outdent_offsets, | 
 |  193                  valid_continuation_offsets): | 
 |  194         self.valid_outdent_offsets = valid_outdent_offsets | 
 |  195         self.valid_continuation_offsets = valid_continuation_offsets | 
 |  196         self.context_type = context_type | 
 |  197         self.position = position | 
 |  198         self.token = token | 
 |  199  | 
 |  200  | 
 |  201 # The contexts for hanging indents. | 
 |  202 # A hanging indented dictionary value after : | 
 |  203 HANGING_DICT_VALUE = 'dict-value' | 
 |  204 # Hanging indentation in an expression. | 
 |  205 HANGING = 'hanging' | 
 |  206 # Hanging indentation in a block header. | 
 |  207 HANGING_BLOCK = 'hanging-block' | 
 |  208 # Continued indentation inside an expression. | 
 |  209 CONTINUED = 'continued' | 
 |  210 # Continued indentation in a block header. | 
 |  211 CONTINUED_BLOCK = 'continued-block' | 
 |  212  | 
 |  213 SINGLE_LINE = 'single' | 
 |  214 WITH_BODY = 'multi' | 
 |  215  | 
 |  216 _CONTINUATION_MSG_PARTS = { | 
 |  217     HANGING_DICT_VALUE: ('hanging', ' in dict value'), | 
 |  218     HANGING: ('hanging', ''), | 
 |  219     HANGING_BLOCK: ('hanging', ' before block'), | 
 |  220     CONTINUED: ('continued', ''), | 
 |  221     CONTINUED_BLOCK: ('continued', ' before block'), | 
 |  222 } | 
 |  223  | 
 |  224  | 
 |  225 def _Offsets(*args): | 
 |  226     """Valid indentation offsets for a continued line.""" | 
 |  227     return dict((a, None) for a in args) | 
 |  228  | 
 |  229  | 
 |  230 def _BeforeBlockOffsets(single, with_body): | 
 |  231     """Valid alternative indent offsets for continued lines before blocks. | 
 |  232  | 
 |  233     :param single: Valid offset for statements on a single logical line. | 
 |  234     :param with_body: Valid offset for statements on several lines. | 
|  120     """ |  235     """ | 
|  121     result = [] |  236     return {single: SINGLE_LINE, with_body: WITH_BODY} | 
|  122     for match in re.finditer(STRING_RGX, line): |  237  | 
|  123         result.append( (match.start(), match.end()) ) |  238  | 
|  124     return result |  239 class TokenWrapper(object): | 
|  125  |  240     """A wrapper for readable access to token information.""" | 
|  126 def in_coords(match, string_coords): |  241  | 
|  127     """return true if the match is in the string coord""" |  242     def __init__(self, tokens): | 
|  128     mstart = match.start() |  243         self._tokens = tokens | 
|  129     for start, end in string_coords: |  244  | 
|  130         if mstart >= start and mstart < end: |  245     def token(self, idx): | 
|  131             return True |  246         return self._tokens[idx][1] | 
|  132     return False |  247  | 
|  133  |  248     def type(self, idx): | 
|  134 def check_line(line): |  249         return self._tokens[idx][0] | 
|  135     """check a line for a bad construction |  250  | 
|  136     if it founds one, return a message describing the problem |  251     def start_line(self, idx): | 
|  137     else return None |  252         return self._tokens[idx][2][0] | 
|  138     """ |  253  | 
|  139     cleanstr = COMMENT_RGX.sub('', STRING_RGX.sub('', line)) |  254     def start_col(self, idx): | 
|  140     for rgx_match, rgx_search, msg_id in BAD_CONSTRUCT_RGXS: |  255         return self._tokens[idx][2][1] | 
|  141         if rgx_match.match(cleanstr): |  256  | 
|  142             string_positions = get_string_coords(line) |  257     def line(self, idx): | 
|  143             for match in re.finditer(rgx_search, line): |  258         return self._tokens[idx][4] | 
|  144                 if not in_coords(match, string_positions): |  259  | 
|  145                     return msg_id, pretty_match(match, line.rstrip()) |  260  | 
|  146  |  261 class ContinuedLineState(object): | 
|  147  |  262     """Tracker for continued indentation inside a logical line.""" | 
|  148 class FormatChecker(BaseRawChecker): |  263  | 
 |  264     def __init__(self, tokens, config): | 
 |  265         self._line_start = -1 | 
 |  266         self._cont_stack = [] | 
 |  267         self._is_block_opener = False | 
 |  268         self.retained_warnings = [] | 
 |  269         self._config = config | 
 |  270         self._tokens = TokenWrapper(tokens) | 
 |  271  | 
 |  272     @property | 
 |  273     def has_content(self): | 
 |  274         return bool(self._cont_stack) | 
 |  275  | 
 |  276     @property | 
 |  277     def _block_indent_size(self): | 
 |  278         return len(self._config.indent_string.replace('\t', ' ' * _TAB_LENGTH)) | 
 |  279  | 
 |  280     @property | 
 |  281     def _continuation_size(self): | 
 |  282         return self._config.indent_after_paren | 
 |  283  | 
 |  284     def handle_line_start(self, pos): | 
 |  285         """Record the first non-junk token at the start of a line.""" | 
 |  286         if self._line_start > -1: | 
 |  287             return | 
 |  288         self._is_block_opener = self._tokens.token(pos) in _CONTINUATION_BLOCK_O
     PENERS | 
 |  289         self._line_start = pos | 
 |  290  | 
 |  291     def next_physical_line(self): | 
 |  292         """Prepares the tracker for a new physical line (NL).""" | 
 |  293         self._line_start = -1 | 
 |  294         self._is_block_opener = False | 
 |  295  | 
 |  296     def next_logical_line(self): | 
 |  297         """Prepares the tracker for a new logical line (NEWLINE). | 
 |  298  | 
 |  299         A new logical line only starts with block indentation. | 
 |  300         """ | 
 |  301         self.next_physical_line() | 
 |  302         self.retained_warnings = [] | 
 |  303         self._cont_stack = [] | 
 |  304  | 
 |  305     def add_block_warning(self, token_position, state, valid_offsets): | 
 |  306         self.retained_warnings.append((token_position, state, valid_offsets)) | 
 |  307  | 
 |  308     def get_valid_offsets(self, idx): | 
 |  309         """"Returns the valid offsets for the token at the given position.""" | 
 |  310         # The closing brace on a dict or the 'for' in a dict comprehension may | 
 |  311         # reset two indent levels because the dict value is ended implicitly | 
 |  312         stack_top = -1 | 
 |  313         if self._tokens.token(idx) in ('}', 'for') and self._cont_stack[-1].toke
     n == ':': | 
 |  314             stack_top = -2 | 
 |  315         indent = self._cont_stack[stack_top] | 
 |  316         if self._tokens.token(idx) in _CLOSING_BRACKETS: | 
 |  317             valid_offsets = indent.valid_outdent_offsets | 
 |  318         else: | 
 |  319             valid_offsets = indent.valid_continuation_offsets | 
 |  320         return indent, valid_offsets.copy() | 
 |  321  | 
 |  322     def _hanging_indent_after_bracket(self, bracket, position): | 
 |  323         """Extracts indentation information for a hanging indent.""" | 
 |  324         indentation = _get_indent_length(self._tokens.line(position)) | 
 |  325         if self._is_block_opener and self._continuation_size == self._block_inde
     nt_size: | 
 |  326             return _ContinuedIndent( | 
 |  327                 HANGING_BLOCK, | 
 |  328                 bracket, | 
 |  329                 position, | 
 |  330                 _Offsets(indentation + self._continuation_size, indentation), | 
 |  331                 _BeforeBlockOffsets(indentation + self._continuation_size, | 
 |  332                                     indentation + self._continuation_size * 2)) | 
 |  333         elif bracket == ':': | 
 |  334             # 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 | 
 |  336             # current indent level | 
 |  337             paren_align = self._cont_stack[-1].valid_outdent_offsets | 
 |  338             next_align = self._cont_stack[-1].valid_continuation_offsets.copy() | 
 |  339             next_align[next_align.keys()[0] + self._continuation_size] = True | 
 |  340             # Note that the continuation of | 
 |  341             # d = { | 
 |  342             #       'a': 'b' | 
 |  343             #            'c' | 
 |  344             # } | 
 |  345             # is handled by the special-casing for hanging continued string inde
     nts. | 
 |  346             return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren
     _align, next_align) | 
 |  347         else: | 
 |  348             return _ContinuedIndent( | 
 |  349                 HANGING, | 
 |  350                 bracket, | 
 |  351                 position, | 
 |  352                 _Offsets(indentation, indentation + self._continuation_size), | 
 |  353                 _Offsets(indentation + self._continuation_size)) | 
 |  354  | 
 |  355     def _continuation_inside_bracket(self, bracket, pos): | 
 |  356         """Extracts indentation information for a continued indent.""" | 
 |  357         indentation = _get_indent_length(self._tokens.line(pos)) | 
 |  358         if self._is_block_opener and self._tokens.start_col(pos+1) - indentation
      == self._block_indent_size: | 
 |  359             return _ContinuedIndent( | 
 |  360                 CONTINUED_BLOCK, | 
 |  361                 bracket, | 
 |  362                 pos, | 
 |  363                 _Offsets(self._tokens.start_col(pos)), | 
 |  364                 _BeforeBlockOffsets(self._tokens.start_col(pos+1), | 
 |  365                                     self._tokens.start_col(pos+1) + self._contin
     uation_size)) | 
 |  366         else: | 
 |  367             return _ContinuedIndent( | 
 |  368                 CONTINUED, | 
 |  369                 bracket, | 
 |  370                 pos, | 
 |  371                 _Offsets(self._tokens.start_col(pos)), | 
 |  372                 _Offsets(self._tokens.start_col(pos+1))) | 
 |  373  | 
 |  374     def pop_token(self): | 
 |  375         self._cont_stack.pop() | 
 |  376  | 
 |  377     def push_token(self, token, position): | 
 |  378         """Pushes a new token for continued indentation on the stack. | 
 |  379  | 
 |  380         Tokens that can modify continued indentation offsets are: | 
 |  381           * opening brackets | 
 |  382           * 'lambda' | 
 |  383           * : inside dictionaries | 
 |  384  | 
 |  385         push_token relies on the caller to filter out those | 
 |  386         interesting tokens. | 
 |  387  | 
 |  388         :param token: The concrete token | 
 |  389         :param position: The position of the token in the stream. | 
 |  390         """ | 
 |  391         if _token_followed_by_eol(self._tokens, position): | 
 |  392             self._cont_stack.append( | 
 |  393                 self._hanging_indent_after_bracket(token, position)) | 
 |  394         else: | 
 |  395             self._cont_stack.append( | 
 |  396                 self._continuation_inside_bracket(token, position)) | 
 |  397  | 
 |  398  | 
 |  399 class FormatChecker(BaseTokenChecker): | 
|  149     """checks for : |  400     """checks for : | 
|  150     * unauthorized constructions |  401     * unauthorized constructions | 
|  151     * strict indentation |  402     * strict indentation | 
|  152     * line length |  403     * line length | 
|  153     * use of <> instead of != |  404     * use of <> instead of != | 
|  154     """ |  405     """ | 
|  155  |  406  | 
|  156     __implements__ = (IRawChecker, IASTNGChecker) |  407     __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) | 
|  157  |  408  | 
|  158     # configuration section name |  409     # configuration section name | 
|  159     name = 'format' |  410     name = 'format' | 
|  160     # messages |  411     # messages | 
|  161     msgs = MSGS |  412     msgs = MSGS | 
|  162     # configuration options |  413     # configuration options | 
|  163     # for available dict keys/values see the optik parser 'add_option' method |  414     # for available dict keys/values see the optik parser 'add_option' method | 
|  164     options = (('max-line-length', |  415     options = (('max-line-length', | 
|  165                 {'default' : 80, 'type' : "int", 'metavar' : '<int>', |  416                 {'default' : 80, 'type' : "int", 'metavar' : '<int>', | 
|  166                  'help' : 'Maximum number of characters on a single line.'}), |  417                  'help' : 'Maximum number of characters on a single line.'}), | 
 |  418                ('ignore-long-lines', | 
 |  419                 {'type': 'regexp', 'metavar': '<regexp>', | 
 |  420                  'default': r'^\s*(# )?<?https?://\S+>?$', | 
 |  421                  'help': ('Regexp for a line that is allowed to be longer than ' | 
 |  422                           'the limit.')}), | 
 |  423                ('single-line-if-stmt', | 
 |  424                 {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>', | 
 |  425                  'help' : ('Allow the body of an if to be on the same ' | 
 |  426                            'line as the test if there is no else.')}), | 
 |  427                ('no-space-check', | 
 |  428                 {'default': ','.join(_NO_SPACE_CHECK_CHOICES), | 
 |  429                  'type': 'multiple_choice', | 
 |  430                  'choices': _NO_SPACE_CHECK_CHOICES, | 
 |  431                  'help': ('List of optional constructs for which whitespace ' | 
 |  432                           'checking is disabled')}), | 
|  167                ('max-module-lines', |  433                ('max-module-lines', | 
|  168                 {'default' : 1000, 'type' : 'int', 'metavar' : '<int>', |  434                 {'default' : 1000, 'type' : 'int', 'metavar' : '<int>', | 
|  169                  'help': 'Maximum number of lines in a module'} |  435                  'help': 'Maximum number of lines in a module'} | 
|  170                 ), |  436                ), | 
|  171                ('indent-string', |  437                ('indent-string', | 
|  172                 {'default' : '    ', 'type' : "string", 'metavar' : '<string>', |  438                 {'default' : '    ', 'type' : "string", 'metavar' : '<string>', | 
|  173                  'help' : 'String used as indentation unit. This is usually \ |  439                  'help' : 'String used as indentation unit. This is usually ' | 
|  174 "    " (4 spaces) or "\\t" (1 tab).'}), |  440                           '"    " (4 spaces) or "\\t" (1 tab).'}), | 
|  175                ) |  441                ('indent-after-paren', | 
 |  442                 {'type': 'int', 'metavar': '<int>', 'default': 4, | 
 |  443                  'help': 'Number of spaces of indent required inside a hanging ' | 
 |  444                          ' or continued line.'}), | 
 |  445               ) | 
 |  446  | 
|  176     def __init__(self, linter=None): |  447     def __init__(self, linter=None): | 
|  177         BaseRawChecker.__init__(self, linter) |  448         BaseTokenChecker.__init__(self, linter) | 
|  178         self._lines = None |  449         self._lines = None | 
|  179         self._visited_lines = None |  450         self._visited_lines = None | 
|  180  |  451         self._bracket_stack = [None] | 
|  181     def process_module(self, node): |  452  | 
|  182         """extracts encoding from the stream and decodes each line, so that |  453     def _pop_token(self): | 
|  183         international text's length is properly calculated. |  454         self._bracket_stack.pop() | 
|  184         """ |  455         self._current_line.pop_token() | 
|  185         stream = node.file_stream |  456  | 
|  186         stream.seek(0) # XXX may be removed with astng > 0.23 |  457     def _push_token(self, token, idx): | 
|  187         readline = stream.readline |  458         self._bracket_stack.append(token) | 
|  188         if sys.version_info < (3, 0): |  459         self._current_line.push_token(token, idx) | 
|  189             if node.file_encoding is not None: |  460  | 
|  190                 readline = lambda: stream.readline().decode(node.file_encoding, 
     'replace') |  461     def new_line(self, tokens, line_end, line_start): | 
|  191         self.process_tokens(tokenize.generate_tokens(readline)) |  | 
|  192  |  | 
|  193     def new_line(self, tok_type, line, line_num, junk): |  | 
|  194         """a new line has been encountered, process it if necessary""" |  462         """a new line has been encountered, process it if necessary""" | 
|  195         if not tok_type in junk: |  463         if _last_token_on_line_is(tokens, line_end, ';'): | 
 |  464             self.add_message('unnecessary-semicolon', line=tokens.start_line(lin
     e_end)) | 
 |  465  | 
 |  466         line_num = tokens.start_line(line_start) | 
 |  467         line = tokens.line(line_start) | 
 |  468         if tokens.type(line_start) not in _JUNK_TOKENS: | 
|  196             self._lines[line_num] = line.split('\n')[0] |  469             self._lines[line_num] = line.split('\n')[0] | 
|  197         self.check_lines(line, line_num) |  470         self.check_lines(line, line_num) | 
|  198  |  471  | 
 |  472     def process_module(self, module): | 
 |  473         self._keywords_with_parens = set() | 
 |  474         if 'print_function' in module.future_imports: | 
 |  475             self._keywords_with_parens.add('print') | 
 |  476  | 
 |  477     def _check_keyword_parentheses(self, tokens, start): | 
 |  478         """Check that there are not unnecessary parens after a keyword. | 
 |  479  | 
 |  480         Parens are unnecessary if there is exactly one balanced outer pair on a | 
 |  481         line, and it is followed by a colon, and contains no commas (i.e. is not
      a | 
 |  482         tuple). | 
 |  483  | 
 |  484         Args: | 
 |  485         tokens: list of Tokens; the entire list of Tokens. | 
 |  486         start: int; the position of the keyword in the token list. | 
 |  487         """ | 
 |  488         # If the next token is not a paren, we're fine. | 
 |  489         if self._inside_brackets(':') and tokens[start][1] == 'for': | 
 |  490             self._pop_token() | 
 |  491         if tokens[start+1][1] != '(': | 
 |  492             return | 
 |  493  | 
 |  494         found_and_or = False | 
 |  495         depth = 0 | 
 |  496         keyword_token = tokens[start][1] | 
 |  497         line_num = tokens[start][2][0] | 
 |  498  | 
 |  499         for i in xrange(start, len(tokens) - 1): | 
 |  500             token = tokens[i] | 
 |  501  | 
 |  502             # If we hit a newline, then assume any parens were for continuation. | 
 |  503             if token[0] == tokenize.NL: | 
 |  504                 return | 
 |  505  | 
 |  506             if token[1] == '(': | 
 |  507                 depth += 1 | 
 |  508             elif token[1] == ')': | 
 |  509                 depth -= 1 | 
 |  510                 if not depth: | 
 |  511                     # ')' can't happen after if (foo), since it would be a synta
     x error. | 
 |  512                     if (tokens[i+1][1] in (':', ')', ']', '}', 'in') or | 
 |  513                             tokens[i+1][0] in (tokenize.NEWLINE, | 
 |  514                                                tokenize.ENDMARKER, | 
 |  515                                                tokenize.COMMENT)): | 
 |  516                         # The empty tuple () is always accepted. | 
 |  517                         if i == start + 2: | 
 |  518                             return | 
 |  519                         if keyword_token == 'not': | 
 |  520                             if not found_and_or: | 
 |  521                                 self.add_message('superfluous-parens', line=line
     _num, | 
 |  522                                                  args=keyword_token) | 
 |  523                         elif keyword_token in ('return', 'yield'): | 
 |  524                             self.add_message('superfluous-parens', line=line_num
     , | 
 |  525                                              args=keyword_token) | 
 |  526                         elif keyword_token not in self._keywords_with_parens: | 
 |  527                             if not (tokens[i+1][1] == 'in' and found_and_or): | 
 |  528                                 self.add_message('superfluous-parens', line=line
     _num, | 
 |  529                                                  args=keyword_token) | 
 |  530                     return | 
 |  531             elif depth == 1: | 
 |  532                 # This is a tuple, which is always acceptable. | 
 |  533                 if token[1] == ',': | 
 |  534                     return | 
 |  535                 # 'and' and 'or' are the only boolean operators with lower prece
     dence | 
 |  536                 # than 'not', so parens are only required when they are found. | 
 |  537                 elif token[1] in ('and', 'or'): | 
 |  538                     found_and_or = True | 
 |  539                 # A yield inside an expression must always be in parentheses, | 
 |  540                 # quit early without error. | 
 |  541                 elif token[1] == 'yield': | 
 |  542                     return | 
 |  543                 # A generator expression always has a 'for' token in it, and | 
 |  544                 # the 'for' token is only legal inside parens when it is in a | 
 |  545                 # generator expression.  The parens are necessary here, so bail | 
 |  546                 # without an error. | 
 |  547                 elif token[1] == 'for': | 
 |  548                     return | 
 |  549  | 
 |  550     def _opening_bracket(self, tokens, i): | 
 |  551         self._push_token(tokens[i][1], i) | 
 |  552         # Special case: ignore slices | 
 |  553         if tokens[i][1] == '[' and tokens[i+1][1] == ':': | 
 |  554             return | 
 |  555  | 
 |  556         if (i > 0 and (tokens[i-1][0] == tokenize.NAME and | 
 |  557                        not (keyword.iskeyword(tokens[i-1][1])) | 
 |  558                        or tokens[i-1][1] in _CLOSING_BRACKETS)): | 
 |  559             self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) | 
 |  560         else: | 
 |  561             self._check_space(tokens, i, (_IGNORE, _MUST_NOT)) | 
 |  562  | 
 |  563     def _closing_bracket(self, tokens, i): | 
 |  564         if self._inside_brackets(':'): | 
 |  565             self._pop_token() | 
 |  566         self._pop_token() | 
 |  567         # Special case: ignore slices | 
 |  568         if tokens[i-1][1] == ':' and tokens[i][1] == ']': | 
 |  569             return | 
 |  570         policy_before = _MUST_NOT | 
 |  571         if tokens[i][1] in _CLOSING_BRACKETS and tokens[i-1][1] == ',': | 
 |  572             if _TRAILING_COMMA in self.config.no_space_check: | 
 |  573                 policy_before = _IGNORE | 
 |  574  | 
 |  575         self._check_space(tokens, i, (policy_before, _IGNORE)) | 
 |  576  | 
 |  577     def _check_equals_spacing(self, tokens, i): | 
 |  578         """Check the spacing of a single equals sign.""" | 
 |  579         if self._inside_brackets('(') or self._inside_brackets('lambda'): | 
 |  580             self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) | 
 |  581         else: | 
 |  582             self._check_space(tokens, i, (_MUST, _MUST)) | 
 |  583  | 
 |  584     def _open_lambda(self, tokens, i): # pylint:disable=unused-argument | 
 |  585         self._push_token('lambda', i) | 
 |  586  | 
 |  587     def _handle_colon(self, tokens, i): | 
 |  588         # Special case: ignore slices | 
 |  589         if self._inside_brackets('['): | 
 |  590             return | 
 |  591         if (self._inside_brackets('{') and | 
 |  592                 _DICT_SEPARATOR in self.config.no_space_check): | 
 |  593             policy = (_IGNORE, _IGNORE) | 
 |  594         else: | 
 |  595             policy = (_MUST_NOT, _MUST) | 
 |  596         self._check_space(tokens, i, policy) | 
 |  597  | 
 |  598         if self._inside_brackets('lambda'): | 
 |  599             self._pop_token() | 
 |  600         elif self._inside_brackets('{'): | 
 |  601             self._push_token(':', i) | 
 |  602  | 
 |  603     def _handle_comma(self, tokens, i): | 
 |  604         # Only require a following whitespace if this is | 
 |  605         # not a hanging comma before a closing bracket. | 
 |  606         if tokens[i+1][1] in _CLOSING_BRACKETS: | 
 |  607             self._check_space(tokens, i, (_MUST_NOT, _IGNORE)) | 
 |  608         else: | 
 |  609             self._check_space(tokens, i, (_MUST_NOT, _MUST)) | 
 |  610         if self._inside_brackets(':'): | 
 |  611             self._pop_token() | 
 |  612  | 
 |  613     def _check_surrounded_by_space(self, tokens, i): | 
 |  614         """Check that a binary operator is surrounded by exactly one space.""" | 
 |  615         self._check_space(tokens, i, (_MUST, _MUST)) | 
 |  616  | 
 |  617     def _check_space(self, tokens, i, policies): | 
 |  618         def _policy_string(policy): | 
 |  619             if policy == _MUST: | 
 |  620                 return 'Exactly one', 'required' | 
 |  621             else: | 
 |  622                 return 'No', 'allowed' | 
 |  623  | 
 |  624         def _name_construct(token): | 
 |  625             if tokens[i][1] == ',': | 
 |  626                 return 'comma' | 
 |  627             elif tokens[i][1] == ':': | 
 |  628                 return ':' | 
 |  629             elif tokens[i][1] in '()[]{}': | 
 |  630                 return 'bracket' | 
 |  631             elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='): | 
 |  632                 return 'comparison' | 
 |  633             else: | 
 |  634                 if self._inside_brackets('('): | 
 |  635                     return 'keyword argument assignment' | 
 |  636                 else: | 
 |  637                     return 'assignment' | 
 |  638  | 
 |  639         good_space = [True, True] | 
 |  640         pairs = [(tokens[i-1], tokens[i]), (tokens[i], tokens[i+1])] | 
 |  641  | 
 |  642         for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): | 
 |  643             if token_pair[other_idx][0] in _EOL or policy == _IGNORE: | 
 |  644                 continue | 
 |  645  | 
 |  646             distance = _column_distance(*token_pair) | 
 |  647             if distance is None: | 
 |  648                 continue | 
 |  649             good_space[other_idx] = ( | 
 |  650                 (policy == _MUST and distance == 1) or | 
 |  651                 (policy == _MUST_NOT and distance == 0)) | 
 |  652  | 
 |  653         warnings = [] | 
 |  654         if not any(good_space) and policies[0] == policies[1]: | 
 |  655             warnings.append((policies[0], 'around')) | 
 |  656         else: | 
 |  657             for ok, policy, position in zip(good_space, policies, ('before', 'af
     ter')): | 
 |  658                 if not ok: | 
 |  659                     warnings.append((policy, position)) | 
 |  660         for policy, position in warnings: | 
 |  661             construct = _name_construct(tokens[i]) | 
 |  662             count, state = _policy_string(policy) | 
 |  663             self.add_message('bad-whitespace', line=tokens[i][2][0], | 
 |  664                              args=(count, state, position, construct, | 
 |  665                                    _underline_token(tokens[i]))) | 
 |  666  | 
 |  667     def _inside_brackets(self, left): | 
 |  668         return self._bracket_stack[-1] == left | 
 |  669  | 
 |  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): | 
 |  675         raw = [ | 
 |  676             (_KEYWORD_TOKENS, | 
 |  677              self._check_keyword_parentheses), | 
 |  678  | 
 |  679             (_OPENING_BRACKETS, self._opening_bracket), | 
 |  680  | 
 |  681             (_CLOSING_BRACKETS, self._closing_bracket), | 
 |  682  | 
 |  683             (['='], self._check_equals_spacing), | 
 |  684  | 
 |  685             (_SPACED_OPERATORS, self._check_surrounded_by_space), | 
 |  686  | 
 |  687             ([','], self._handle_comma), | 
 |  688  | 
 |  689             ([':'], self._handle_colon), | 
 |  690  | 
 |  691             (['lambda'], self._open_lambda), | 
 |  692  | 
 |  693             (['<>'], self._handle_old_ne_operator), | 
 |  694             ] | 
 |  695  | 
 |  696         dispatch = {} | 
 |  697         for tokens, handler in raw: | 
 |  698             for token in tokens: | 
 |  699                 dispatch[token] = handler | 
 |  700         return dispatch | 
 |  701  | 
|  199     def process_tokens(self, tokens): |  702     def process_tokens(self, tokens): | 
|  200         """process tokens and search for : |  703         """process tokens and search for : | 
|  201  |  704  | 
|  202          _ non strict indentation (i.e. not always using the <indent> parameter 
     as |  705          _ non strict indentation (i.e. not always using the <indent> parameter 
     as | 
|  203            indent unit) |  706            indent unit) | 
|  204          _ too long lines (i.e. longer than <max_chars>) |  707          _ too long lines (i.e. longer than <max_chars>) | 
|  205          _ optionally bad construct (if given, bad_construct must be a compiled |  708          _ optionally bad construct (if given, bad_construct must be a compiled | 
|  206            regular expression). |  709            regular expression). | 
|  207         """ |  710         """ | 
|  208         indent = tokenize.INDENT |  711         self._bracket_stack = [None] | 
|  209         dedent = tokenize.DEDENT |  | 
|  210         newline = tokenize.NEWLINE |  | 
|  211         junk = (tokenize.COMMENT, tokenize.NL) |  | 
|  212         indents = [0] |  712         indents = [0] | 
|  213         check_equal = 0 |  713         check_equal = False | 
|  214         line_num = 0 |  714         line_num = 0 | 
|  215         previous = None |  | 
|  216         self._lines = {} |  715         self._lines = {} | 
|  217         self._visited_lines = {} |  716         self._visited_lines = {} | 
|  218         for (tok_type, token, start, _, line) in tokens: |  717         token_handlers = self._prepare_token_dispatcher() | 
 |  718  | 
 |  719         self._current_line = ContinuedLineState(tokens, self.config) | 
 |  720         for idx, (tok_type, token, start, _, line) in enumerate(tokens): | 
|  219             if start[0] != line_num: |  721             if start[0] != line_num: | 
|  220                 if previous is not None and previous[0] == tokenize.OP and previ
     ous[1] == ';': |  | 
|  221                     self.add_message('W0301', line=previous[2]) |  | 
|  222                 previous = None |  | 
|  223                 line_num = start[0] |  722                 line_num = start[0] | 
|  224                 self.new_line(tok_type, line, line_num, junk) |  723                 # A tokenizer oddity: if an indented line contains a multi-line | 
|  225             if tok_type not in (indent, dedent, newline) + junk: |  724                 # docstring, the line member of the INDENT token does not contai
     n | 
|  226                 previous = tok_type, token, start[0] |  725                 # the full line; therefore we check the next token on the line. | 
 |  726                 if tok_type == tokenize.INDENT: | 
 |  727                     self.new_line(TokenWrapper(tokens), idx-1, idx+1) | 
 |  728                 else: | 
 |  729                     self.new_line(TokenWrapper(tokens), idx-1, idx) | 
|  227  |  730  | 
|  228             if tok_type == tokenize.OP: |  731             if tok_type == tokenize.NEWLINE: | 
|  229                 if token == '<>': |  | 
|  230                     self.add_message('W0331', line=line_num) |  | 
|  231             elif tok_type == tokenize.NUMBER: |  | 
|  232                 if token.endswith('l'): |  | 
|  233                     self.add_message('W0332', line=line_num) |  | 
|  234  |  | 
|  235             elif tok_type == newline: |  | 
|  236                 # a program statement, or ENDMARKER, will eventually follow, |  732                 # a program statement, or ENDMARKER, will eventually follow, | 
|  237                 # after some (possibly empty) run of tokens of the form |  733                 # after some (possibly empty) run of tokens of the form | 
|  238                 #     (NL | COMMENT)* (INDENT | DEDENT+)? |  734                 #     (NL | COMMENT)* (INDENT | DEDENT+)? | 
|  239                 # If an INDENT appears, setting check_equal is wrong, and will |  735                 # If an INDENT appears, setting check_equal is wrong, and will | 
|  240                 # be undone when we see the INDENT. |  736                 # be undone when we see the INDENT. | 
|  241                 check_equal = 1 |  737                 check_equal = True | 
|  242  |  738                 self._process_retained_warnings(TokenWrapper(tokens), idx) | 
|  243             elif tok_type == indent: |  739                 self._current_line.next_logical_line() | 
|  244                 check_equal = 0 |  740             elif tok_type == tokenize.INDENT: | 
 |  741                 check_equal = False | 
|  245                 self.check_indent_level(token, indents[-1]+1, line_num) |  742                 self.check_indent_level(token, indents[-1]+1, line_num) | 
|  246                 indents.append(indents[-1]+1) |  743                 indents.append(indents[-1]+1) | 
|  247  |  744             elif tok_type == tokenize.DEDENT: | 
|  248             elif tok_type == dedent: |  | 
|  249                 # there's nothing we need to check here!  what's important is |  745                 # there's nothing we need to check here!  what's important is | 
|  250                 # that when the run of DEDENTs ends, the indentation of the |  746                 # that when the run of DEDENTs ends, the indentation of the | 
|  251                 # program statement (or ENDMARKER) that triggered the run is |  747                 # program statement (or ENDMARKER) that triggered the run is | 
|  252                 # equal to what's left at the top of the indents stack |  748                 # equal to what's left at the top of the indents stack | 
|  253                 check_equal = 1 |  749                 check_equal = True | 
|  254                 if len(indents) > 1: |  750                 if len(indents) > 1: | 
|  255                     del indents[-1] |  751                     del indents[-1] | 
|  256  |  752             elif tok_type == tokenize.NL: | 
|  257             elif check_equal and tok_type not in junk: |  753                 self._check_continued_indentation(TokenWrapper(tokens), idx+1) | 
|  258                 # this is the first "real token" following a NEWLINE, so it |  754                 self._current_line.next_physical_line() | 
 |  755             elif tok_type != tokenize.COMMENT: | 
 |  756                 self._current_line.handle_line_start(idx) | 
 |  757                 # This is the first concrete token following a NEWLINE, so it | 
|  259                 # must be the first token of the next program statement, or an |  758                 # must be the first token of the next program statement, or an | 
|  260                 # ENDMARKER; the "line" argument exposes the leading whitespace |  759                 # ENDMARKER; the "line" argument exposes the leading whitespace | 
|  261                 # for this statement; in the case of ENDMARKER, line is an empty |  760                 # for this statement; in the case of ENDMARKER, line is an empty | 
|  262                 # string, so will properly match the empty string with which the |  761                 # string, so will properly match the empty string with which the | 
|  263                 # "indents" stack was seeded |  762                 # "indents" stack was seeded | 
|  264                 check_equal = 0 |  763                 if check_equal: | 
|  265                 self.check_indent_level(line, indents[-1], line_num) |  764                     check_equal = False | 
 |  765                     self.check_indent_level(line, indents[-1], line_num) | 
 |  766  | 
 |  767             if tok_type == tokenize.NUMBER and token.endswith('l'): | 
 |  768                 self.add_message('lowercase-l-suffix', line=line_num) | 
 |  769  | 
 |  770             try: | 
 |  771                 handler = token_handlers[token] | 
 |  772             except KeyError: | 
 |  773                 pass | 
 |  774             else: | 
 |  775                 handler(tokens, idx) | 
|  266  |  776  | 
|  267         line_num -= 1 # to be ok with "wc -l" |  777         line_num -= 1 # to be ok with "wc -l" | 
|  268         if line_num > self.config.max_module_lines: |  778         if line_num > self.config.max_module_lines: | 
|  269             self.add_message('C0302', args=line_num, line=1) |  779             self.add_message('too-many-lines', args=line_num, line=1) | 
|  270  |  780  | 
|  271     @check_messages('C0321' ,'C03232', 'C0323', 'C0324') |  781     def _process_retained_warnings(self, tokens, current_pos): | 
 |  782         single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos,
      ':') | 
 |  783  | 
 |  784         for indent_pos, state, offsets in self._current_line.retained_warnings: | 
 |  785             block_type = offsets[tokens.start_col(indent_pos)] | 
 |  786             hints = dict((k, v) for k, v in offsets.iteritems() | 
 |  787                          if v != block_type) | 
 |  788             if single_line_block_stmt and block_type == WITH_BODY: | 
 |  789                 self._add_continuation_message(state, hints, tokens, indent_pos) | 
 |  790             elif not single_line_block_stmt and block_type == SINGLE_LINE: | 
 |  791                 self._add_continuation_message(state, hints, tokens, indent_pos) | 
 |  792  | 
 |  793     def _check_continued_indentation(self, tokens, next_idx): | 
 |  794         def same_token_around_nl(token_type): | 
 |  795             return (tokens.type(next_idx) == token_type and | 
 |  796                     tokens.type(next_idx-2) == token_type) | 
 |  797  | 
 |  798         # Do not issue any warnings if the next line is empty. | 
 |  799         if not self._current_line.has_content or tokens.type(next_idx) == tokeni
     ze.NL: | 
 |  800             return | 
 |  801  | 
 |  802         state, valid_offsets = self._current_line.get_valid_offsets(next_idx) | 
 |  803         # Special handling for hanging comments and strings. If the last line en
     ded | 
 |  804         # with a comment (string) and the new line contains only a comment, the 
     line | 
 |  805         # may also be indented to the start of the previous token. | 
 |  806         if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(tokeni
     ze.STRING): | 
 |  807             valid_offsets[tokens.start_col(next_idx-2)] = True | 
 |  808  | 
 |  809         # We can only decide if the indentation of a continued line before openi
     ng | 
 |  810         # a new block is valid once we know of the body of the block is on the | 
 |  811         # same line as the block opener. Since the token processing is single-pa
     ss, | 
 |  812         # emitting those warnings is delayed until the block opener is processed
     . | 
 |  813         if (state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK) | 
 |  814                 and tokens.start_col(next_idx) in valid_offsets): | 
 |  815             self._current_line.add_block_warning(next_idx, state, valid_offsets) | 
 |  816         elif tokens.start_col(next_idx) not in valid_offsets: | 
 |  817             self._add_continuation_message(state, valid_offsets, tokens, next_id
     x) | 
 |  818  | 
 |  819     def _add_continuation_message(self, state, offsets, tokens, position): | 
 |  820         readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context
     _type] | 
 |  821         hint_line = _get_indent_hint_line(offsets, tokens.start_col(position)) | 
 |  822         self.add_message( | 
 |  823             'bad-continuation', | 
 |  824             line=tokens.start_line(position), | 
 |  825             args=(readable_type, readable_position, tokens.line(position), hint_
     line)) | 
 |  826  | 
 |  827     @check_messages('multiple-statements') | 
|  272     def visit_default(self, node): |  828     def visit_default(self, node): | 
|  273         """check the node line number and check it if not yet done""" |  829         """check the node line number and check it if not yet done""" | 
|  274         if not node.is_statement: |  830         if not node.is_statement: | 
|  275             return |  831             return | 
|  276         if not node.root().pure_python: |  832         if not node.root().pure_python: | 
|  277             return # XXX block visit of child nodes |  833             return # XXX block visit of child nodes | 
|  278         prev_sibl = node.previous_sibling() |  834         prev_sibl = node.previous_sibling() | 
|  279         if prev_sibl is not None: |  835         if prev_sibl is not None: | 
|  280             prev_line = prev_sibl.fromlineno |  836             prev_line = prev_sibl.fromlineno | 
|  281         else: |  837         else: | 
|  282             prev_line = node.parent.statement().fromlineno |  838             # The line on which a finally: occurs in a try/finally | 
 |  839             # is not directly represented in the AST. We infer it | 
 |  840             # by taking the last line of the body and adding 1, which | 
 |  841             # should be the line of finally: | 
 |  842             if (isinstance(node.parent, nodes.TryFinally) | 
 |  843                     and node in node.parent.finalbody): | 
 |  844                 prev_line = node.parent.body[0].tolineno + 1 | 
 |  845             else: | 
 |  846                 prev_line = node.parent.statement().fromlineno | 
|  283         line = node.fromlineno |  847         line = node.fromlineno | 
|  284         assert line, node |  848         assert line, node | 
|  285         if prev_line == line and self._visited_lines.get(line) != 2: |  849         if prev_line == line and self._visited_lines.get(line) != 2: | 
|  286             # py2.5 try: except: finally: |  850             self._check_multi_statement_line(node, line) | 
|  287             if not (isinstance(node, nodes.TryExcept) |  | 
|  288                     and isinstance(node.parent, nodes.TryFinally) |  | 
|  289                     and node.fromlineno == node.parent.fromlineno): |  | 
|  290                 self.add_message('C0321', node=node) |  | 
|  291                 self._visited_lines[line] = 2 |  | 
|  292             return |  851             return | 
|  293         if line in self._visited_lines: |  852         if line in self._visited_lines: | 
|  294             return |  853             return | 
|  295         try: |  854         try: | 
|  296             tolineno = node.blockstart_tolineno |  855             tolineno = node.blockstart_tolineno | 
|  297         except AttributeError: |  856         except AttributeError: | 
|  298             tolineno = node.tolineno |  857             tolineno = node.tolineno | 
|  299         assert tolineno, node |  858         assert tolineno, node | 
|  300         lines = [] |  859         lines = [] | 
|  301         for line in xrange(line, tolineno + 1): |  860         for line in xrange(line, tolineno + 1): | 
|  302             self._visited_lines[line] = 1 |  861             self._visited_lines[line] = 1 | 
|  303             try: |  862             try: | 
|  304                 lines.append(self._lines[line].rstrip()) |  863                 lines.append(self._lines[line].rstrip()) | 
|  305             except KeyError: |  864             except KeyError: | 
|  306                 lines.append('') |  865                 lines.append('') | 
|  307         try: |  | 
|  308             msg_def = check_line('\n'.join(lines)) |  | 
|  309             if msg_def: |  | 
|  310                 self.add_message(msg_def[0], node=node, args=msg_def[1]) |  | 
|  311         except KeyError: |  | 
|  312             # FIXME: internal error ! |  | 
|  313             pass |  | 
|  314  |  866  | 
|  315     @check_messages('W0333') |  867     def _check_multi_statement_line(self, node, line): | 
 |  868         """Check for lines containing multiple statements.""" | 
 |  869         # Do not warn about multiple nested context managers | 
 |  870         # in with statements. | 
 |  871         if isinstance(node, nodes.With): | 
 |  872             return | 
 |  873         # For try... except... finally..., the two nodes | 
 |  874         # appear to be on the same line due to how the AST is built. | 
 |  875         if (isinstance(node, nodes.TryExcept) and | 
 |  876                 isinstance(node.parent, nodes.TryFinally)): | 
 |  877             return | 
 |  878         if (isinstance(node.parent, nodes.If) and not node.parent.orelse | 
 |  879                 and self.config.single_line_if_stmt): | 
 |  880             return | 
 |  881         self.add_message('multiple-statements', node=node) | 
 |  882         self._visited_lines[line] = 2 | 
 |  883  | 
 |  884     @check_messages('backtick') | 
|  316     def visit_backquote(self, node): |  885     def visit_backquote(self, node): | 
|  317         self.add_message('W0333', node=node) |  886         self.add_message('backtick', node=node) | 
|  318  |  887  | 
|  319     def check_lines(self, lines, i): |  888     def check_lines(self, lines, i): | 
|  320         """check lines have less than a maximum number of characters |  889         """check lines have less than a maximum number of characters | 
|  321         """ |  890         """ | 
|  322         max_chars = self.config.max_line_length |  891         max_chars = self.config.max_line_length | 
|  323         for line in lines.splitlines(): |  892         ignore_long_line = self.config.ignore_long_lines | 
|  324             if len(line) > max_chars: |  893  | 
|  325                 self.add_message('C0301', line=i, args=(len(line), max_chars)) |  894         for line in lines.splitlines(True): | 
 |  895             if not line.endswith('\n'): | 
 |  896                 self.add_message('missing-final-newline', line=i) | 
 |  897             else: | 
 |  898                 stripped_line = line.rstrip() | 
 |  899                 if line[len(stripped_line):] not in ('\n', '\r\n'): | 
 |  900                     self.add_message('trailing-whitespace', line=i) | 
 |  901                 # Don't count excess whitespace in the line length. | 
 |  902                 line = stripped_line | 
 |  903             mobj = OPTION_RGX.search(line) | 
 |  904             if mobj and mobj.group(1).split('=', 1)[0].strip() == 'disable': | 
 |  905                 line = line.split('#')[0].rstrip() | 
 |  906  | 
 |  907             if len(line) > max_chars and not ignore_long_line.search(line): | 
 |  908                 self.add_message('line-too-long', line=i, args=(len(line), max_c
     hars)) | 
|  326             i += 1 |  909             i += 1 | 
|  327  |  910  | 
|  328     def check_indent_level(self, string, expected, line_num): |  911     def check_indent_level(self, string, expected, line_num): | 
|  329         """return the indent level of the string |  912         """return the indent level of the string | 
|  330         """ |  913         """ | 
|  331         indent = self.config.indent_string |  914         indent = self.config.indent_string | 
|  332         if indent == '\\t': # \t is not interpreted in the configuration file |  915         if indent == '\\t': # \t is not interpreted in the configuration file | 
|  333             indent = '\t' |  916             indent = '\t' | 
|  334         level = 0 |  917         level = 0 | 
|  335         unit_size = len(indent) |  918         unit_size = len(indent) | 
|  336         while string[:unit_size] == indent: |  919         while string[:unit_size] == indent: | 
|  337             string = string[unit_size:] |  920             string = string[unit_size:] | 
|  338             level += 1 |  921             level += 1 | 
|  339         suppl = '' |  922         suppl = '' | 
|  340         while string and string[0] in ' \t': |  923         while string and string[0] in ' \t': | 
|  341             if string[0] != indent[0]: |  924             if string[0] != indent[0]: | 
|  342                 if string[0] == '\t': |  925                 if string[0] == '\t': | 
|  343                     args = ('tab', 'space') |  926                     args = ('tab', 'space') | 
|  344                 else: |  927                 else: | 
|  345                     args = ('space', 'tab') |  928                     args = ('space', 'tab') | 
|  346                 self.add_message('W0312', args=args, line=line_num) |  929                 self.add_message('mixed-indentation', args=args, line=line_num) | 
|  347                 return level |  930                 return level | 
|  348             suppl += string[0] |  931             suppl += string[0] | 
|  349             string = string [1:] |  932             string = string[1:] | 
|  350         if level != expected or suppl: |  933         if level != expected or suppl: | 
|  351             i_type = 'spaces' |  934             i_type = 'spaces' | 
|  352             if indent[0] == '\t': |  935             if indent[0] == '\t': | 
|  353                 i_type = 'tabs' |  936                 i_type = 'tabs' | 
|  354             self.add_message('W0311', line=line_num, |  937             self.add_message('bad-indentation', line=line_num, | 
|  355                              args=(level * unit_size + len(suppl), i_type, |  938                              args=(level * unit_size + len(suppl), i_type, | 
|  356                                    expected * unit_size)) |  939                                    expected * unit_size)) | 
|  357  |  940  | 
|  358  |  941  | 
|  359 def register(linter): |  942 def register(linter): | 
|  360     """required method to auto register this checker """ |  943     """required method to auto register this checker """ | 
|  361     linter.register_checker(FormatChecker(linter)) |  944     linter.register_checker(FormatChecker(linter)) | 
| OLD | NEW |