Index: third_party/pylint/checkers/format.py |
diff --git a/third_party/pylint/checkers/format.py b/third_party/pylint/checkers/format.py |
index cb31256f56bd0a01e81b249ffdce49aadbfc9604..94a9e8eb8d038e2eacfc3e7287da38036c0d6bf3 100644 |
--- a/third_party/pylint/checkers/format.py |
+++ b/third_party/pylint/checkers/format.py |
@@ -24,9 +24,10 @@ Some parts of the process_token method is based from The Tab Nanny std module. |
import keyword |
import sys |
import tokenize |
+from functools import reduce # pylint: disable=redefined-builtin |
-if not hasattr(tokenize, 'NL'): |
- raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") |
+import six |
+from six.moves import zip, map, filter # pylint: disable=redefined-builtin |
from astroid import nodes |
@@ -65,7 +66,7 @@ MSGS = { |
'C0301': ('Line too long (%s/%s)', |
'line-too-long', |
'Used when a line is longer than a given number of characters.'), |
- 'C0302': ('Too many lines in module (%s)', # was W0302 |
+ 'C0302': ('Too many lines in module (%s/%s)', # was W0302 |
'too-many-lines', |
'Used when a module has too much lines, reducing its readability.' |
), |
@@ -105,22 +106,18 @@ MSGS = { |
{'old_names': [('C0323', 'no-space-after-operator'), |
('C0324', 'no-space-after-comma'), |
('C0322', 'no-space-before-operator')]}), |
- 'W0331': ('Use of the <> operator', |
- 'old-ne-operator', |
- 'Used when the deprecated "<>" operator is used instead ' |
- 'of "!=".', |
- {'maxversion': (3, 0)}), |
'W0332': ('Use of "l" as long integer identifier', |
'lowercase-l-suffix', |
'Used when a lower case "l" is used to mark a long integer. You ' |
'should use a upper case "L" since the letter "l" looks too much ' |
'like the digit "1"', |
{'maxversion': (3, 0)}), |
- 'W0333': ('Use of the `` operator', |
- 'backtick', |
- 'Used when the deprecated "``" (backtick) operator is used ' |
- 'instead of the str() function.', |
- {'scope': WarningScope.NODE, 'maxversion': (3, 0)}), |
+ 'C0327': ('Mixed line endings LF and CRLF', |
+ 'mixed-line-endings', |
+ 'Used when there are mixed (LF and CRLF) newline signs in a file.'), |
+ 'C0328': ('Unexpected line ending format. There is \'%s\' while it should be \'%s\'.', |
+ 'unexpected-line-ending-format', |
+ 'Used when there is different newline than expected.'), |
} |
@@ -336,7 +333,8 @@ class ContinuedLineState(object): |
# current indent level |
paren_align = self._cont_stack[-1].valid_outdent_offsets |
next_align = self._cont_stack[-1].valid_continuation_offsets.copy() |
- next_align[next_align.keys()[0] + self._continuation_size] = True |
+ next_align_keys = list(next_align.keys()) |
+ next_align[next_align_keys[0] + self._continuation_size] = True |
# Note that the continuation of |
# d = { |
# 'a': 'b' |
@@ -401,7 +399,6 @@ class FormatChecker(BaseTokenChecker): |
* unauthorized constructions |
* strict indentation |
* line length |
- * use of <> instead of != |
""" |
__implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) |
@@ -413,7 +410,7 @@ class FormatChecker(BaseTokenChecker): |
# configuration options |
# for available dict keys/values see the optik parser 'add_option' method |
options = (('max-line-length', |
- {'default' : 80, 'type' : "int", 'metavar' : '<int>', |
+ {'default' : 100, 'type' : "int", 'metavar' : '<int>', |
'help' : 'Maximum number of characters on a single line.'}), |
('ignore-long-lines', |
{'type': 'regexp', 'metavar': '<regexp>', |
@@ -442,6 +439,10 @@ class FormatChecker(BaseTokenChecker): |
{'type': 'int', 'metavar': '<int>', 'default': 4, |
'help': 'Number of spaces of indent required inside a hanging ' |
' or continued line.'}), |
+ ('expected-line-ending-format', |
+ {'type': 'choice', 'metavar': '<empty or LF or CRLF>', 'default': '', |
+ 'choices': ['', 'LF', 'CRLF'], |
+ 'help': 'Expected format of line ending, e.g. empty (any line ending), LF or CRLF.'}), |
) |
def __init__(self, linter=None): |
@@ -496,7 +497,7 @@ class FormatChecker(BaseTokenChecker): |
keyword_token = tokens[start][1] |
line_num = tokens[start][2][0] |
- for i in xrange(start, len(tokens) - 1): |
+ for i in range(start, len(tokens) - 1): |
token = tokens[i] |
# If we hit a newline, then assume any parens were for continuation. |
@@ -622,13 +623,13 @@ class FormatChecker(BaseTokenChecker): |
return 'No', 'allowed' |
def _name_construct(token): |
- if tokens[i][1] == ',': |
+ if token[1] == ',': |
return 'comma' |
- elif tokens[i][1] == ':': |
+ elif token[1] == ':': |
return ':' |
- elif tokens[i][1] in '()[]{}': |
+ elif token[1] in '()[]{}': |
return 'bracket' |
- elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='): |
+ elif token[1] in ('<', '>', '<=', '>=', '!=', '=='): |
return 'comparison' |
else: |
if self._inside_brackets('('): |
@@ -637,7 +638,8 @@ class FormatChecker(BaseTokenChecker): |
return 'assignment' |
good_space = [True, True] |
- pairs = [(tokens[i-1], tokens[i]), (tokens[i], tokens[i+1])] |
+ token = tokens[i] |
+ pairs = [(tokens[i-1], token), (token, tokens[i+1])] |
for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): |
if token_pair[other_idx][0] in _EOL or policy == _IGNORE: |
@@ -658,19 +660,15 @@ class FormatChecker(BaseTokenChecker): |
if not ok: |
warnings.append((policy, position)) |
for policy, position in warnings: |
- construct = _name_construct(tokens[i]) |
+ construct = _name_construct(token) |
count, state = _policy_string(policy) |
- self.add_message('bad-whitespace', line=tokens[i][2][0], |
+ self.add_message('bad-whitespace', line=token[2][0], |
args=(count, state, position, construct, |
- _underline_token(tokens[i]))) |
+ _underline_token(token))) |
def _inside_brackets(self, left): |
return self._bracket_stack[-1] == left |
- def _handle_old_ne_operator(self, tokens, i): |
- if tokens[i][1] == '<>': |
- self.add_message('old-ne-operator', line=tokens[i][2][0]) |
- |
def _prepare_token_dispatcher(self): |
raw = [ |
(_KEYWORD_TOKENS, |
@@ -690,7 +688,6 @@ class FormatChecker(BaseTokenChecker): |
(['lambda'], self._open_lambda), |
- (['<>'], self._handle_old_ne_operator), |
] |
dispatch = {} |
@@ -715,6 +712,7 @@ class FormatChecker(BaseTokenChecker): |
self._lines = {} |
self._visited_lines = {} |
token_handlers = self._prepare_token_dispatcher() |
+ self._last_line_ending = None |
self._current_line = ContinuedLineState(tokens, self.config) |
for idx, (tok_type, token, start, _, line) in enumerate(tokens): |
@@ -737,6 +735,7 @@ class FormatChecker(BaseTokenChecker): |
check_equal = True |
self._process_retained_warnings(TokenWrapper(tokens), idx) |
self._current_line.next_logical_line() |
+ self._check_line_ending(token, line_num) |
elif tok_type == tokenize.INDENT: |
check_equal = False |
self.check_indent_level(token, indents[-1]+1, line_num) |
@@ -776,14 +775,39 @@ class FormatChecker(BaseTokenChecker): |
line_num -= 1 # to be ok with "wc -l" |
if line_num > self.config.max_module_lines: |
- self.add_message('too-many-lines', args=line_num, line=1) |
+ # Get the line where the too-many-lines (or its message id) |
+ # was disabled or default to 1. |
+ symbol = self.linter.msgs_store.check_message_id('too-many-lines') |
+ names = (symbol.msgid, 'too-many-lines') |
+ line = next(filter(None, |
+ map(self.linter._pragma_lineno.get, names)), 1) |
+ self.add_message('too-many-lines', |
+ args=(line_num, self.config.max_module_lines), |
+ line=line) |
+ |
+ def _check_line_ending(self, line_ending, line_num): |
+ # check if line endings are mixed |
+ if self._last_line_ending is not None: |
+ if line_ending != self._last_line_ending: |
+ self.add_message('mixed-line-endings', line=line_num) |
+ |
+ self._last_line_ending = line_ending |
+ |
+ # check if line ending is as expected |
+ expected = self.config.expected_line_ending_format |
+ if expected: |
+ line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "") # reduce multiple \n\n\n\n to one \n |
+ line_ending = 'LF' if line_ending == '\n' else 'CRLF' |
+ if line_ending != expected: |
+ self.add_message('unexpected-line-ending-format', args=(line_ending, expected), line=line_num) |
+ |
def _process_retained_warnings(self, tokens, current_pos): |
single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ':') |
for indent_pos, state, offsets in self._current_line.retained_warnings: |
block_type = offsets[tokens.start_col(indent_pos)] |
- hints = dict((k, v) for k, v in offsets.iteritems() |
+ hints = dict((k, v) for k, v in six.iteritems(offsets) |
if v != block_type) |
if single_line_block_stmt and block_type == WITH_BODY: |
self._add_continuation_message(state, hints, tokens, indent_pos) |
@@ -857,7 +881,7 @@ class FormatChecker(BaseTokenChecker): |
tolineno = node.tolineno |
assert tolineno, node |
lines = [] |
- for line in xrange(line, tolineno + 1): |
+ for line in range(line, tolineno + 1): |
self._visited_lines[line] = 1 |
try: |
lines.append(self._lines[line].rstrip()) |
@@ -881,10 +905,6 @@ class FormatChecker(BaseTokenChecker): |
self.add_message('multiple-statements', node=node) |
self._visited_lines[line] = 2 |
- @check_messages('backtick') |
- def visit_backquote(self, node): |
- self.add_message('backtick', node=node) |
- |
def check_lines(self, lines, i): |
"""check lines have less than a maximum number of characters |
""" |